Files
frontend-app/utils/douyin-pay-adapter.mjs
T

41 lines
1.9 KiB
JavaScript

// Pure adapter: keeps merchant credentials and signing on the server.
export function toSdkPayInfo(payment) {
const data = payment?.douyin;
if (!data) throw new Error('后端未返回抖音支付参数');
if (data.type && data.type !== 'app') throw new Error('当前仅支持抖音 App 支付');
let nested = data.orderInfo || {};
if (typeof nested === 'string') {
try { nested = JSON.parse(nested); }
catch (_) { throw new Error('抖音支付 orderInfo 格式错误'); }
}
const field = (camel, lower) => nested[lower] ?? nested[camel] ?? data[camel] ?? data[lower];
const info = {
appid: field('appId', 'appid'),
partnerid: field('mchId', 'partnerid'),
prepayid: field('prepayId', 'prepayid'),
package: field('packageValue', 'package'),
noncestr: field('nonceStr', 'noncestr'),
timestamp: field('timeStamp', 'timestamp'),
sign: field('sign', 'sign')
};
if (typeof info.timestamp === 'number' && Number.isSafeInteger(info.timestamp)) info.timestamp = String(info.timestamp);
for (const [key, value] of Object.entries(info)) {
if (typeof value !== 'string' || !value.trim()) {
throw new Error(`缺少支付参数 ${key},请先部署后端 SDK 签名补丁`);
}
}
if (info.package !== 'Sign=DYPay') throw new Error('抖音支付 package 不正确');
return info;
}
// request is the App's existing authenticated request wrapper, returning ApiResult.data.
export async function queryUntilSettled(request, orderNum, { attempts = 5, pause = 1500 } = {}) {
for (let i = 0; i < attempts; i++) {
const result = await request({ url: '/payment/queryResult', method: 'GET', data: { orderNum } });
if (result?.state === 2) return { state: 'paid' };
if (result?.state === 3) return { state: 'failed' };
if (i + 1 < attempts) await new Promise(resolve => setTimeout(resolve, pause));
}
return { state: 'pending' };
}