171 lines
5.5 KiB
TypeScript
171 lines
5.5 KiB
TypeScript
import logger from 'logger/logger'
|
|
import { checkPrePaySign, createOrder, queryFiat, queryPrice, refreshToken } from 'service/alchemy.svr'
|
|
import { PayRecord, PayStatus } from 'modules/PayRecord'
|
|
import { PriceSvr } from 'service/price.svr'
|
|
import { reportPayResult } from 'service/game.svr'
|
|
import { GasSvr } from 'service/gas.svr'
|
|
import { BaseController, ROLE_ANON, ZError, role, router } from 'zutils'
|
|
|
|
const CALL_BACK_URL = `${process.env.ALCHEMY_PAY_CB_URL}/pay/out/alchemy/result`
|
|
class AlchemyController extends BaseController {
|
|
@router('post /pay/alchemy/buy')
|
|
async beginApiPay(req, res) {
|
|
const user = req.user
|
|
logger.db('alchemy_begin_pay', req)
|
|
let { network, crypto, address, fiat, fiatAmount, payWayCode, country, accountId, orderId, env, sign } = req.params
|
|
const envStr = req.headers['api-env'] || env || 'dev'
|
|
if (!fiat || !fiatAmount || !country || !payWayCode) {
|
|
throw new ZError(11, 'fiat, fiatAmount payWayCode and country must be provided')
|
|
}
|
|
if (!network || !crypto) {
|
|
throw new ZError(12, 'network and crypto must be provided')
|
|
}
|
|
const apiVersion = +(req.headers['api-version'] || '0')
|
|
if (apiVersion > 0) {
|
|
if (!sign) {
|
|
throw new ZError(14, 'sign must be provided')
|
|
}
|
|
if (!checkPrePaySign(req.params)) {
|
|
throw new ZError(15, 'sign error')
|
|
}
|
|
}
|
|
const tokenResult = await refreshToken(user.emailReal || user.email)
|
|
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 { accessToken } = tokenResult.data
|
|
if (crypto.toLowerCase() === 'agor') {
|
|
let today = new Date()
|
|
today.setHours(0, 0, 0, 0)
|
|
let count = await PayRecord.countDocuments({
|
|
account: user.id,
|
|
crypto,
|
|
status: PayStatus.SUCCESS,
|
|
createdAt: { $gte: today },
|
|
})
|
|
if (count >= 3) {
|
|
throw new ZError(13, 'daily limit')
|
|
}
|
|
}
|
|
// 根据 `查询数字货币接口` 返回的数据, 预估可获得的数字货币数量
|
|
// 用户法币接口做简单的验证
|
|
if (network.toLowerCase() === 'agor') {
|
|
network = 'ARBITRUM'
|
|
}
|
|
let priceData = await queryPrice({ crypto, network, fiat, amount: fiatAmount, payWayCode, country })
|
|
if (!priceData.success) {
|
|
throw new ZError(14, priceData.returnMsg || 'fetch price error')
|
|
}
|
|
logger.debug('pirce data::', JSON.stringify(priceData))
|
|
let amountEstimate = priceData.data.cryptoQuantity
|
|
let record = new PayRecord({
|
|
account: user.id,
|
|
address,
|
|
network,
|
|
crypto,
|
|
env: envStr,
|
|
gameAccountId: accountId,
|
|
gameOrderId: orderId,
|
|
cryptoAmountEstimate: amountEstimate,
|
|
fiat,
|
|
fiatAmount,
|
|
country,
|
|
payWayCode,
|
|
versionCode: apiVersion,
|
|
})
|
|
await record.save()
|
|
let payData: any = {
|
|
side: 'BUY',
|
|
merchantOrderNo: record.id,
|
|
amount: record.fiatAmount,
|
|
fiatCurrency: record.fiat,
|
|
cryptoCurrency: record.crypto,
|
|
depositType: '2',
|
|
address: address,
|
|
network: record.network,
|
|
payWayCode,
|
|
alpha2: record.country,
|
|
callbackUrl: CALL_BACK_URL,
|
|
merchantName: 'CEBG',
|
|
}
|
|
if (process.env.ALCHEMY_PAY_REDIRECT) {
|
|
payData.redirectUrl = process.env.ALCHEMY_PAY_REDIRECT
|
|
}
|
|
logger.info(`create order data::${JSON.stringify(payData)}`)
|
|
let payRes = await createOrder(accessToken, payData)
|
|
logger.info(`create order result::${JSON.stringify(payRes)}`)
|
|
record.outData = payRes.data
|
|
if (payRes.success) {
|
|
record.outOrderId = payRes.data.orderNo
|
|
await record.save()
|
|
} else {
|
|
record.status = PayStatus.FAIL
|
|
await record.save()
|
|
setImmediate(() => {
|
|
reportPayResult(record)
|
|
})
|
|
throw new ZError(payRes.returnCode, payRes.returnMsg)
|
|
}
|
|
return { url: payRes.data.payUrl }
|
|
}
|
|
|
|
@router('post /pay/alchemy/crypto_price')
|
|
@router('get /pay/alchemy/crypto_price')
|
|
async queryCryptoPrice(req, res) {
|
|
let { token, crypto, chain, currency, env } = req.params
|
|
crypto = crypto || token
|
|
if (!crypto || !chain) {
|
|
throw new ZError(11, 'token or network not found')
|
|
}
|
|
// ceg价格固定为0.1
|
|
if (crypto.toLowerCase() === 'ceg') {
|
|
return { price: '0.1' }
|
|
}
|
|
if ((chain.toLowerCase() === 'agor' || chain.toLowerCase() === 'eth') && crypto.toLowerCase() === 'agor') {
|
|
crypto = 'ETH'
|
|
chain = 'ARBITRUM'
|
|
}
|
|
|
|
let data = {
|
|
crypto,
|
|
network: chain,
|
|
fiat: currency || 'USD',
|
|
}
|
|
let result = await new PriceSvr().fetchPrice(data)
|
|
return { price: result }
|
|
}
|
|
|
|
@router('get /pay/alchemy/fait_list')
|
|
async cryptoList(req, res) {
|
|
let result = await queryFiat()
|
|
if (!result.success) {
|
|
throw new ZError(10, result.returnMsg || 'fetch fiat list error')
|
|
}
|
|
return result.data
|
|
}
|
|
|
|
@router('get /pay/alchemy/can_i_buy_treasury')
|
|
async canIBuyEth(req, res) {
|
|
const user = req.user
|
|
const crypto = process.env.CF_ETH_NAME
|
|
let today = new Date()
|
|
today.setHours(0, 0, 0, 0)
|
|
const count = await PayRecord.countDocuments({
|
|
account: user.id,
|
|
crypto,
|
|
status: PayStatus.SUCCESS,
|
|
createdAt: { $gte: today },
|
|
})
|
|
return { result: count >= 3 ? 0 : 1 }
|
|
}
|
|
|
|
@role(ROLE_ANON)
|
|
@router('post /pay/alchemy/estimateGas')
|
|
async estimateGas(req, res) {
|
|
const { crypto, network } = req.params
|
|
const gas = await new GasSvr().estimateGas({ crypto, network })
|
|
return { gas }
|
|
}
|
|
}
|