| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081 |
- 'use strict';
- const { randomBytes } = require('crypto');
- const DEFAULT_PARSE_FUNCTIONS_URL = 'https://server.fmode.cn/parse/functions';
- const DEFAULT_PAY_COMPANY = '1AiWpTEDH9';
- const DEFAULT_NEW_API_CALLBACK = 'WbyPrzMI3A';
- function createTradeNo(prefix = 'VOC') {
- const stamp = new Date().toISOString().replace(/[-:.TZ]/g, '');
- return `${prefix}${stamp}${randomBytes(4).toString('hex')}`;
- }
- function buildRechargePaymentRequest({ amount, title = 'VOC 数据调用充值', tradeNo, sessionToken } = {}) {
- const numericAmount = Number(amount);
- if (!Number.isFinite(numericAmount) || numericAmount <= 0) {
- throw new Error('充值金额必须大于 0');
- }
- const outTradeNo = tradeNo || createTradeNo();
- const parseFunctionsUrl = String(process.env.FMODE_PARSE_FUNCTIONS_URL || DEFAULT_PARSE_FUNCTIONS_URL).replace(/\/$/, '');
- const body = {
- company: process.env.FMODE_PAY_COMPANY || DEFAULT_PAY_COMPANY,
- out_trade_no: outTradeNo,
- total_fee: Number(numericAmount.toFixed(2)),
- body: String(title).slice(0, 80),
- fun_id: process.env.FMODE_NEWAPI_RECHARGE_CALLBACK_FUN_ID || DEFAULT_NEW_API_CALLBACK
- };
- return {
- endpoint: `${parseFunctionsUrl}/pay_code2`,
- method: 'POST',
- headers: sessionToken ? { 'X-Parse-Session-Token': sessionToken } : {},
- body,
- tradeNo: outTradeNo,
- amountCny: body.total_fee,
- polling: {
- endpoint: `${parseFunctionsUrl}/order_status2`,
- method: 'POST',
- body: {
- out_trade_no: outTradeNo,
- company: body.company
- }
- }
- };
- }
- async function requestPaymentQr({ amount, title, sessionToken, tradeNo, fetchImpl = fetch } = {}) {
- const request = buildRechargePaymentRequest({ amount, title, sessionToken, tradeNo });
- const response = await fetchImpl(request.endpoint, {
- method: request.method,
- headers: { Accept: 'application/json', 'Content-Type': 'application/json', ...request.headers },
- body: JSON.stringify(request.body)
- });
- const payload = await response.json().catch(() => ({}));
- const codeUrl = Array.isArray(payload?.code_url) ? payload.code_url[0] : payload?.code_url;
- if (!response.ok || !codeUrl) {
- const error = new Error(payload?.error || payload?.message || `pay_code2 HTTP ${response.status}`);
- error.httpStatus = response.status;
- throw error;
- }
- const nonceStr = payload?.nonce_str || '';
- return {
- ...request,
- paymentMode: 'qr',
- qrCodeUrl: String(codeUrl),
- nonceStr,
- polling: {
- ...request.polling,
- body: {
- ...request.polling.body,
- ...(nonceStr ? { nonce_str: nonceStr } : {})
- }
- }
- };
- }
- module.exports = {
- DEFAULT_PARSE_FUNCTIONS_URL,
- createTradeNo,
- buildRechargePaymentRequest,
- requestPaymentQr
- };
|