114 lines
4.0 KiB
TypeScript
114 lines
4.0 KiB
TypeScript
import logger from 'logger/logger'
|
|
import { PayRecord, PayRecordClass, PayStatus } from 'modules/PayRecord'
|
|
import { TransferRecord, TransferRecordClass } from 'modules/TransferRecord'
|
|
import { DocumentType } from '@typegoose/typegoose'
|
|
import { updateOrderStatus } from 'service/alchemy.svr'
|
|
import { PriceSvr } from 'service/price.svr'
|
|
import { assembleGameData, checkGameSign } from 'service/game.svr'
|
|
import { BaseController, role, ROLE_ANON, router, ZError } from 'zutils'
|
|
import { hmacSha256 } from 'zutils/utils/security.util'
|
|
|
|
const calcHash = function (data: any) {
|
|
let signStr = JSON.stringify(data)
|
|
return hmacSha256(signStr, process.env.HASH_SALT_CHAIN).toLowerCase()
|
|
}
|
|
|
|
const notify = async function (record: DocumentType<PayRecordClass>, subTask: DocumentType<TransferRecordClass>) {
|
|
let data: any = {
|
|
orderNo: record.outOrderId, // AlchemyPay订单号
|
|
crypto: record.crypto, // 用户购买的数字货币
|
|
cryptoAmount: record.cryptoSend, //用户的提币数量
|
|
cryptoPrice: record.cryptoPrice, // 实时的价格/USDT CEG锚定USDT
|
|
txHash: record.txHash, // 给用户转账的hash
|
|
network: record.network, // 用户购买的数字货币对应的网络
|
|
// networkFee: record.networkFee, // 网络费用/USDT
|
|
address: record.address, // 用户的提币地址
|
|
status: record.tranStatus === 2 ? 'SUCCESS' : 'FAIL', // SUCCESS/FAIL
|
|
}
|
|
try {
|
|
let price = await new PriceSvr().fetchPrice({ crypto: 'ETH', network: 'ARBITRUM', fiat: 'USD' })
|
|
data.networkFee = parseFloat(subTask.gas) * parseFloat(subTask.gasPrice) * 0.0000000001 * parseFloat(price) + ''
|
|
let result = await updateOrderStatus(data)
|
|
logger.info('update transfer status success::', JSON.stringify(result))
|
|
if (result.success) {
|
|
subTask.status = 8
|
|
await subTask.save()
|
|
}
|
|
} catch (err) {
|
|
logger.error(`notify alchemy error:: ${err.message}`)
|
|
record.notofyErrCount = record.notofyErrCount + 1
|
|
await record.save()
|
|
if (record.notofyErrCount < 6) {
|
|
await notify(record, subTask)
|
|
}
|
|
}
|
|
}
|
|
|
|
export default class InternalController extends BaseController {
|
|
@role(ROLE_ANON)
|
|
@router('post /api/internal/update_task')
|
|
async updateTaskInfo(req) {
|
|
let { sign, id, result, successCount, errorCount, gas, gasPrice, hashList } = req.params
|
|
if (!sign) {
|
|
throw new ZError(10, 'sign not found')
|
|
}
|
|
let hash = calcHash({ id, result, successCount, errorCount, gas, gasPrice, hashList })
|
|
console.log(hash, sign)
|
|
if (sign !== hash) {
|
|
throw new ZError(11, 'sign not match')
|
|
}
|
|
logger.info(`task report:: ${id}|${result}|${successCount}|${errorCount}|${JSON.stringify(hashList)}}`)
|
|
if (!id) {
|
|
throw new ZError(11, 'taskId not found')
|
|
}
|
|
let record = await TransferRecord.findById(id)
|
|
if (!record) {
|
|
throw new ZError(12, 'TransferRecord not found')
|
|
}
|
|
let task = await PayRecord.findById(record.recordId)
|
|
if (!task) {
|
|
throw new ZError(13, 'PayRecord not found')
|
|
}
|
|
if (result === 2) {
|
|
record.status = 9
|
|
record.gas = gas
|
|
record.gasPrice = gasPrice
|
|
record.hashList = hashList
|
|
task.txHash = hashList[0]
|
|
task.tranStatus = 2
|
|
} else {
|
|
record.status = 10
|
|
task.tranStatus = 99
|
|
}
|
|
await record.save()
|
|
await task.save()
|
|
setImmediate(async () => {
|
|
await notify(task, record)
|
|
})
|
|
return {}
|
|
}
|
|
/**
|
|
* 供Game端二次确认支付结果
|
|
*/
|
|
@role(ROLE_ANON)
|
|
@router('get /api/internal/alchemy/order/:id')
|
|
async queryAlchemyPayOrder(req) {
|
|
let { id, timestamp, sign } = req.params
|
|
if (!id || !timestamp || !sign) {
|
|
throw new ZError(10, 'params mismatch')
|
|
}
|
|
if (timestamp < Date.now() / 1000 - 60 * 5) {
|
|
throw new ZError(11, 'timestamp expired')
|
|
}
|
|
if (!checkGameSign({ id, timestamp, sign })) {
|
|
throw new ZError(12, 'sign not match')
|
|
}
|
|
let record = await PayRecord.findById(id)
|
|
if (!record) {
|
|
throw new ZError(13, 'PayRecord not found')
|
|
}
|
|
const repData = assembleGameData(record, false)
|
|
return repData
|
|
}
|
|
}
|