From a9feeda7b9157d4abcbe28c4b3d498061c779cc2 Mon Sep 17 00:00:00 2001 From: zhl Date: Fri, 17 Mar 2023 19:22:23 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0alchemy=E6=94=AF=E4=BB=98?= =?UTF-8?q?=E7=9A=84=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .env.development | 9 +++- src/controllers/alchemy.controller.ts | 44 +++++++++++++++++ src/net/NetClient.ts | 5 +- src/service/alchemy.svr.ts | 53 ++++++++++++++++++++ src/service/email.svr.ts | 2 +- src/utils/net.util.ts | 70 +++++++++++++++++++++++++++ src/utils/security.util.ts | 10 ++++ 7 files changed, 189 insertions(+), 4 deletions(-) create mode 100644 src/controllers/alchemy.controller.ts create mode 100644 src/service/alchemy.svr.ts diff --git a/.env.development b/.env.development index fb1c6d7..f9703b7 100644 --- a/.env.development +++ b/.env.development @@ -7,4 +7,11 @@ API_TOKEN_EXPIRESIN=1d GOOGLE_OAUTH_CLIENT="53206975661-asnf3qe4bg29p8h981pgf099osvrjbme.apps.googleusercontent.com" GOOGLE_OAUTH_CLIENT2="53206975661-ih3r0ubph3rqejdq97b029difbrk2bqj.apps.googleusercontent.com" GOOGLE_OAUTH_CLIENT_IOS="53206975661-qan0rnefniegjv53ohild375pv0p7ekd.apps.googleusercontent.com" -DB_MAIN=mongodb://localhost/wallet-development \ No newline at end of file +DB_MAIN=mongodb://localhost/wallet-development + +EMAIL_VERIFY_URL="http://127.0.0.1:3007" + +ALCHEMY_APPID="f83Is2y7L425rxl8" +ALCHEMY_APP_SECRET="4Yn8RkxDXN71Q3p0" +ALCHEMY_API_BASE="https://openapi-test.alchemypay.org" +ALCHEMY_PAGE_BASE="https://ramptest.alchemypay.org" diff --git a/src/controllers/alchemy.controller.ts b/src/controllers/alchemy.controller.ts new file mode 100644 index 0000000..fb753a2 --- /dev/null +++ b/src/controllers/alchemy.controller.ts @@ -0,0 +1,44 @@ +import logger from 'logger/logger' +import BaseController from 'common/base.controller' +import {ZError} from 'common/ZError' +import { router } from 'decorators/router' +import {createPageSign, refreshToken} from 'service/alchemy.svr' +import {generateKVStr} from 'utils/net.util' + +class AlchemyController extends BaseController { + @router('post /pay/alchemy/buy') + async beginPay(req, res) { + const user = req.user + const {chain, currency, address} = req.params + const tokenResult = await refreshToken(user.emailReal) + console.log(tokenResult) + if (!tokenResult.success) { + logger.info(`fetch pay token error::code: ${tokenResult.returnCode} msg: ${tokenResult.returnMsg}`) + throw new ZError(10, 'fetch pay token error') + } + const { id, email, accessToken } = tokenResult.data + const redirectUrl = '' + const callbackUrl = '' + const merchantOrderNo = '' + let dataOrign:any = { + token: accessToken, + email, + id, + showTable: 'buy', + } + if (chain) dataOrign.network=chain + if (currency) dataOrign.crypto=currency + let dataSign = { + appId: process.env.ALCHEMY_APPID, + address + } + let signStr = generateKVStr({data: dataSign, sort: true}) + let sign = createPageSign(signStr) + dataOrign.sign = sign + Object.assign(dataOrign, dataSign) + const urlBase = process.env.ALCHEMY_PAGE_BASE + let url = `${urlBase}/` + url = generateKVStr({data: dataOrign, encode: true, uri: url}) + return {url} + } +} diff --git a/src/net/NetClient.ts b/src/net/NetClient.ts index 2c90807..56ebd5a 100644 --- a/src/net/NetClient.ts +++ b/src/net/NetClient.ts @@ -22,13 +22,14 @@ export class NetClient { return this.request(reqData) } - request(data: AxiosRequestConfig): Promise { + async request(data: AxiosRequestConfig): Promise { let defaultCfg: AxiosRequestConfig = { method: 'get', headers: {'Content-Type': 'application/json'} } Object.assign(defaultCfg, data) console.log(defaultCfg) - return axios(defaultCfg).then(res => res.data) + const res = await axios(defaultCfg) + return res.data } } diff --git a/src/service/alchemy.svr.ts b/src/service/alchemy.svr.ts new file mode 100644 index 0000000..e159d15 --- /dev/null +++ b/src/service/alchemy.svr.ts @@ -0,0 +1,53 @@ +import axios from 'axios' +import {sha1} from 'utils/security.util' +import crypto from 'crypto' + +export function createSimpleSign() { + let timestamp = Date.now() + let appid = process.env.ALCHEMY_APPID + let secret = process.env.ALCHEMY_APP_SECRET + let sign = sha1(appid+secret+timestamp) + return { + appid, + timestamp, + sign + } +} + +export function createPageSign(plainText: string) { + let secret = process.env.ALCHEMY_APP_SECRET + try { + const plainTextData = Buffer.from(plainText, 'utf8'); + const secretKey = Buffer.from(secret, 'utf8'); + const iv = secret.substring(0, 16); + + const cipher = crypto.createCipheriv('aes-128-cbc', secretKey, iv); + + let encrypted = cipher.update(plainTextData); + encrypted = Buffer.concat([encrypted, cipher.final()]); + + return encrypted.toString('base64'); + } catch (e) { + console.log(`AES encrypting exception, msg is ${e.toString()}`); + } + return null; +} + +export async function refreshToken(email: string) { + const data = JSON.stringify({email}) + const {appid, timestamp, sign} = createSimpleSign() + const host = process.env.ALCHEMY_API_BASE + const config = { + method: 'post', + url: `${host}/merchant/getToken`, + headers: { + 'appId': appid, + 'timestamp': timestamp, + 'sign': sign, + 'Content-Type': 'application/json' + }, + data : data + } + let response = await axios(config) + return response.data +} diff --git a/src/service/email.svr.ts b/src/service/email.svr.ts index b8899f6..d2dd6a7 100644 --- a/src/service/email.svr.ts +++ b/src/service/email.svr.ts @@ -17,7 +17,7 @@ export interface IMailData { } const DEFAULT_MSG_DATA: IMailData = { - from: '自己人 ', + from: 'CEBG ', to: '', subject: 'Please verify your email address' } diff --git a/src/utils/net.util.ts b/src/utils/net.util.ts index 44d556c..456606b 100644 --- a/src/utils/net.util.ts +++ b/src/utils/net.util.ts @@ -114,3 +114,73 @@ function logOrRethrowError(error: any, codesToCatch: number[] = []) { throw error } } + +/** + * 生成 key1=val1&key2=val2的字符串 + * @param {object} data 需要处理的对象 + * @param {boolean} sort 是否按key生序重排 + * @param {boolean} ignoreNull 是否过滤空值(空格或者null值不参与拼接) + * @param splitChar 连接的字符, 默认是& + * @param equalChar = + */ +export function generateKVStr({ + data = {}, + sort = false, + encode = false, + ignoreNull = true, + splitChar = "&", + equalChar = "=", + uri = "", +}: { + data?: any; + sort?: boolean; + encode?: boolean; + ignoreNull?: boolean; + splitChar?: string; + equalChar?: string; + uri?: string; +}) { + const keys = Object.keys(data); + sort && keys.sort(); + let result = ""; + let i = 0; + for (let key of keys) { + if (ignoreNull && !data[key]) { + continue; + } + if (i++ > 0) result += splitChar; + if (encode) { + result += `${key}${equalChar}${encodeURIComponent(data[key])}`; + } else { + result += `${key}${equalChar}${data[key]}`; + } + } + if (uri) { + const joinChar = uri.search(/\?/) === -1 ? "?" : "&"; + result = uri + joinChar + result; + } + return result; +} + +/** + * 将key1=val&key2=val的字符串组装成对象 + * @param str key1=val&key2=val的字符串 + * @param splitChar 连接的字符, 默认是& + * @param equalChar = + */ +export function keyValToObject( + str: string, + splitChar: string = "&", + equalChar = "=" +): {} { + let result: any = {}; + if (!str) { + return result; + } + let arrs = str.split(splitChar); + for (let sub of arrs) { + let subArr = sub.split(equalChar); + result[subArr[0]] = subArr[1]; + } + return result; +} diff --git a/src/utils/security.util.ts b/src/utils/security.util.ts index f33a05d..444d6d4 100644 --- a/src/utils/security.util.ts +++ b/src/utils/security.util.ts @@ -32,3 +32,13 @@ export function shortUuid() { let uid = uuid() return compressUuid(uid) } + +export function md5(content: string) { + var md5 = crypto.createHash('md5') + return md5.update(content).digest('hex') +} + +export function sha1(content: string) { + var md5 = crypto.createHash('sha1') + return md5.update(content).digest('hex') +}