创建订单时, 预估可获得的加密币数量, 在发币时,如果差距太大, 则拒绝发币

This commit is contained in:
CounterFire2023 2023-07-05 13:41:07 +08:00
parent 5708c3a8db
commit 367d9b516a
5 changed files with 100 additions and 58 deletions

View File

@ -2,7 +2,7 @@ import logger from 'logger/logger'
import BaseController from 'common/base.controller'
import { ZError } from 'common/ZError'
import { router } from 'decorators/router'
import { createOrder, createPageSign, queryFiat, queryPrice, refreshToken } from 'service/alchemy.svr'
import { createOrder, queryFiat, queryPrice, refreshToken } from 'service/alchemy.svr'
import { generateKVStr } from 'utils/net.util'
import { PayRecord, PayStatus } from 'modules/PayRecord'
import { PriceSvr } from 'service/price.svr'
@ -44,6 +44,11 @@ class AlchemyController extends BaseController {
throw new ZError(13, 'daily limit')
}
}
// 根据 `查询数字货币接口` 返回的数据, 预估可获得的数字货币数量
// 用户法币接口做简单的验证
let priceData = await queryPrice({ crypto, network, fiat, amount: fiatAmount, payWayCode, country })
logger.debug('pirce data::', JSON.stringify(priceData))
let amountEstimate = priceData.data.cryptoAmount
let record = new PayRecord({
account: user.id,
address,
@ -52,6 +57,7 @@ class AlchemyController extends BaseController {
env: envStr,
gameAccountId: accountId,
gameOrderId: orderId,
cryptoAmountEstimate: amountEstimate,
})
if (fiat) record.fiat = fiat
if (fiatAmount) record.fiatAmount = fiatAmount
@ -88,59 +94,59 @@ class AlchemyController extends BaseController {
}
return { url: payRes.data.payUrl }
}
@router('post /pay/alchemy/buypage')
async beginPagePay(req, res) {
const user = req.user
const { network, crypto, address, fiat, fiatAmount, country } = req.params
if (fiat || fiatAmount || country) {
if (!fiat || !fiatAmount || !country) {
throw new ZError(11, 'fiat, fiatAmount and country must be provided')
}
}
if (network || crypto) {
if (!network || !crypto) {
throw new ZError(12, 'network and crypto must be provided')
}
}
const tokenResult = await refreshToken(user.emailReal || user.email)
console.log(tokenResult)
if (!tokenResult.success || tokenResult.returnCode !== '0000') {
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
let record = new PayRecord({ account: user.id, address, network, crypto })
if (fiat) record.fiat = fiat
if (fiatAmount) record.fiatAmount = fiatAmount
if (country) record.country = country
await record.save()
const merchantOrderNo = record.id
let dataOrign: any = {
token: accessToken,
email,
id,
showTable: 'buy',
merchantOrderNo,
}
if (network) dataOrign.network = network
if (crypto) dataOrign.crypto = crypto
if (fiat) dataOrign.fiat = fiat
if (fiatAmount) dataOrign.fiatAmount = fiatAmount
if (country) dataOrign.country = country
let dataSign: any = {
appId: process.env.ALCHEMY_APPID,
address,
callbackUrl: CALL_BACK_URL,
}
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 }
}
// @router('post /pay/alchemy/buypage')
// async beginPagePay(req, res) {
// const user = req.user
// const { network, crypto, address, fiat, fiatAmount, country } = req.params
// if (fiat || fiatAmount || country) {
// if (!fiat || !fiatAmount || !country) {
// throw new ZError(11, 'fiat, fiatAmount and country must be provided')
// }
// }
// if (network || crypto) {
// if (!network || !crypto) {
// throw new ZError(12, 'network and crypto must be provided')
// }
// }
// const tokenResult = await refreshToken(user.emailReal || user.email)
// console.log(tokenResult)
// if (!tokenResult.success || tokenResult.returnCode !== '0000') {
// 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
// let record = new PayRecord({ account: user.id, address, network, crypto })
// if (fiat) record.fiat = fiat
// if (fiatAmount) record.fiatAmount = fiatAmount
// if (country) record.country = country
// await record.save()
// const merchantOrderNo = record.id
// let dataOrign: any = {
// token: accessToken,
// email,
// id,
// showTable: 'buy',
// merchantOrderNo,
// }
// if (network) dataOrign.network = network
// if (crypto) dataOrign.crypto = crypto
// if (fiat) dataOrign.fiat = fiat
// if (fiatAmount) dataOrign.fiatAmount = fiatAmount
// if (country) dataOrign.country = country
// let dataSign: any = {
// appId: process.env.ALCHEMY_APPID,
// address,
// callbackUrl: CALL_BACK_URL,
// }
// 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 }
// }
@router('post /pay/alchemy/crypto_price')
async queryCryptoPrice(req, res) {

View File

@ -8,6 +8,7 @@ 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}`)
@ -135,14 +136,27 @@ class AlchemyOutController extends BaseController {
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
@ -157,6 +171,7 @@ class AlchemyOutController extends BaseController {
returnCode: '0000', // false: 9999
returnMsg: 'in amet',
}
new OrderCacheSvr().removeOrder(orderNo)
return result
}
}

View File

@ -6,7 +6,8 @@ import { PayRecord, PayRecordClass, PayStatus } from 'modules/PayRecord'
import { TransferRecord, TransferRecordClass } from 'modules/TransferRecord'
import { hmacsha256 } from 'utils/security.util'
import { DocumentType } from '@typegoose/typegoose'
import { queryPrice, updateOrderStatus } from 'service/alchemy.svr'
import { updateOrderStatus } from 'service/alchemy.svr'
import { PriceSvr } from 'service/price.svr'
const calcHash = function (data: any) {
let signStr = JSON.stringify(data)
@ -26,8 +27,8 @@ const notify = async function (record: DocumentType<PayRecordClass>, subTask: Do
status: record.status === PayStatus.TRANSFERED ? 'SUCCESS' : 'FAIL', // SUCCESS/FAIL
}
try {
let priceData = await queryPrice({ gas: subTask.gas })
data.networkFee = priceData.leagel
let price = await new PriceSvr().fetchPrice({ crypto: 'ETH', network: 'ARBITRUM' })
data.networkFee = parseFloat(subTask.gas) * parseFloat(subTask.gasPrice) * parseFloat(price) + ''
let result = await updateOrderStatus(data)
logger.info('update transfer status success::', JSON.stringify(result))
if (result.success) {

View File

@ -57,6 +57,9 @@ export class PayRecordClass extends BaseModule {
// 加密货币数量
@prop()
public cryptoAmount?: string
// 开始创建记录时, 估算的加密货币数量
@prop()
public cryptoAmountEstimate: string
// 加密货币价格
@prop()

View File

@ -0,0 +1,17 @@
import { singleton } from 'decorators/singleton'
@singleton
export class OrderCacheSvr {
private orderSet: Set<string> = new Set()
public addOrder(orderId: string) {
this.orderSet.add(orderId)
}
public isOrderExist(orderId: string) {
return this.orderSet.has(orderId)
}
public removeOrder(orderId: string) {
this.orderSet.delete(orderId)
}
}