178 lines
6.0 KiB
TypeScript
178 lines
6.0 KiB
TypeScript
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
|
|
}
|
|
}
|