import logger from 'logger/logger' import BaseController, { ROLE_ANON } from 'common/base.controller' import { ZError } from 'common/ZError' import { role, router } from 'decorators/router' import { checkPayResultSign, checkSha1Sign, checkSimpleSign } from 'service/alchemy.svr' import { PayRecord, PayStatus } from 'modules/PayRecord' import { TransferQueue } from 'queue/transfer.queue' import { TransferRecord } from 'modules/TransferRecord' import { reportPayResult } from 'service/game.svr' import { PriceSvr } from 'service/price.svr' import { OrderCacheSvr } from 'service/ordercache.svr' let errorRes = function (msg: string) { logger.info(`error res: ${msg}`) return { direct: 1, data: null, success: false, returnCode: '9999', returnMsg: msg, } } /** * for Alchemy call */ class AlchemyOutController extends BaseController { @role(ROLE_ANON) @router('post /pay/out/alchemy/buycb') async alchemyCallback(req, res) { let { orderNo, status, crypto, network, merchantOrderNo } = req.params logger.info(`alchemy callback: ${orderNo}, ${status}, ${crypto}, ${network}, ${merchantOrderNo}`) if (!merchantOrderNo) { logger.info(`alchemy callback merchantOrderNo not found`) throw new ZError(11, 'alchemy callback merchantOrderNo not found') } let record = await PayRecord.findById(merchantOrderNo) if (!record) { logger.info(`alchemy callback record not found`) throw new ZError(12, 'alchemy callback record not found') } if ( record.status !== PayStatus.PENDING && record.status !== PayStatus.TRANSFERING && record.status !== PayStatus.TRANSFERED ) { logger.info(`alchemy callback record status error`) throw new ZError(13, 'alchemy callback record status error') } if (!checkPayResultSign(req.params)) { logger.info(`alchemy callback sign error`) record.status = PayStatus.FAIL await record.save() throw new ZError(14, 'alchemy callback sign error') } let transferRecord = await TransferRecord.findByRecordId(record.id) if (transferRecord) { transferRecord.status = 9 await transferRecord.save() } record.outOrderId = orderNo record.network = network record.crypto = crypto record.outData = req.params record.status = status == 'PAY_SUCCESS' ? PayStatus.SUCCESS : PayStatus.FAIL await record.save() setImmediate(() => { reportPayResult(record) }) logger.info(`alchemy callback success, pay finished`) return {} } /** * 向商户查询币价 * TODO:: calc networkFee */ @role(ROLE_ANON) @router('get /pay/out/alchemy/queryprice') async queryToken(req, res) { const { crypto } = req.params logger.info(`alchemy query price: ${crypto}`) let { appId, appid, timestamp, sign } = req.headers logger.info(`alchemy query price headers: ${appid}, ${timestamp}, ${sign}`) if (!crypto) { return errorRes('params mismatch') } appId = appId || appid if (!appId || !timestamp || !sign) { return errorRes('headers mismatch') } if (!checkSha1Sign(req.headers)) { return errorRes('sign error') } let gas = 36000 * 0.0000000001 let price = await new PriceSvr().fetchPrice({ crypto: 'ETH', network: 'ARBITRUM' }) let networkFee = gas * parseFloat(price) let result = { direct: 1, data: { price: '1.0', networkList: [ { network: 'ARBITRUM', networkFee: networkFee.toFixed(6), }, ], }, success: true, returnCode: '0000', // false: 9999 returnMsg: 'in amet', } return result } /** * 通知商户打币 */ @role(ROLE_ANON) @router('post /pay/out/alchemy/distribute') async distributeToken(req, res) { const { orderNo, crypto, network, address, cryptoAmount, cryptoPrice, usdtAmount } = req.params logger.info( `alchemy distributeToken: orderNo: ${orderNo}, crypto: ${crypto}, network: ${network}, address: ${address}, cryptoAmount: ${cryptoAmount}, cryptoPrice: ${cryptoPrice}, usdtAmount: ${usdtAmount}`, ) let { appId, appid, timestamp, sign } = req.headers logger.info(`alchemy distributeToken: appId: ${appId || appid}, timestamp: ${timestamp}, sign: ${sign}`) if (!orderNo || !crypto || !network || !address || !cryptoAmount || !cryptoPrice || !usdtAmount) { return errorRes('params mismatch') } appId = appId || appid if (!timestamp || !sign) { return errorRes('headers mismatch') } // let signData = { orderNo, crypto, network, address, cryptoAmount, cryptoPrice, usdtAmount } if (!checkSha1Sign(req.headers)) { return errorRes('sign error') } if (new OrderCacheSvr().isOrderExist(orderNo)) { return errorRes('orderNo already processing') } new OrderCacheSvr().addOrder(orderNo) let record = await PayRecord.findByRecordId(orderNo) if (!record) { new OrderCacheSvr().removeOrder(orderNo) return errorRes('orderNo not found') } if (record.crypto != crypto || record.network != network || record.address != address) { new OrderCacheSvr().removeOrder(orderNo) return errorRes('params mismatch') } // 简单的比较一下, 如果差距太大, 就不处理 if (record.cryptoAmountEstimate) { let diff = parseFloat(cryptoAmount) / parseFloat(record.cryptoAmountEstimate) if (diff > 3 || diff < 0.5) { new OrderCacheSvr().removeOrder(orderNo) return errorRes('params mismatch, cryptoAmount too big or too small') } } record.cryptoAmount = record.network.toLowerCase() === 'agor' && record.crypto.toLowerCase() === 'agor' ? '0.001' : cryptoAmount record.cryptoPrice = cryptoPrice record.usdtAdmount = usdtAmount record.status = PayStatus.TRANSFERING await record.save() new TransferQueue().addTask(record) let result = { direct: 1, data: null, success: true, returnCode: '0000', // false: 9999 returnMsg: 'in amet', } new OrderCacheSvr().removeOrder(orderNo) return result } }