fix: 隔离抖音当前支付订单与缓存旧单

This commit is contained in:
Codex
2026-09-10 11:12:30 +08:00
parent e692f3d9b1
commit cb3f8bbaba
3 changed files with 168 additions and 46 deletions
+13 -24
View File
@@ -79,7 +79,8 @@ async function showState(state, key, orderNum) {
uni.showToast({ title: state.message || '支付结果待确认,请稍后查看订单', icon: 'none', duration: 2500 });
}
// Called only by a user payment action, never by onShow. Renew the SAME transaction on the server.
// Only called when the CURRENT server response names the cached transaction.
// A payment click for another product must never authorize this old transaction.
async function continuePendingPayment(key, orderNum) {
const response = await request({ url: '/payment/douyin/resume', method: 'POST',
data: { orderNum }, isShowLoading: false, timeout: 60000 });
@@ -148,10 +149,10 @@ async function executePayment(payment, key) {
// #endif
}
// Runs on the payment click BEFORE creating an order. Resume a live original transaction directly.
// Like WeChat/Alipay, let the current checkout/recharge/order-payment request
// choose the transaction. The account-wide recovery record is NOT that choice.
export async function prepareDouyinPay(payway) {
if (payway !== 'douyin') return true;
let paymentKey = '';
try {
// #ifndef APP-PLUS
throw new Error('抖音支付仅支持 Android/iOS App');
@@ -159,29 +160,11 @@ export async function prepareDouyinPay(payway) {
// #ifdef APP-PLUS
if (active || resuming) throw new Error('正在处理支付,请勿重复点击');
const key = accountKey();
paymentKey = key;
if (!key) throw new Error('请先登录后再支付');
const previous = uni.getStorageSync(key);
if (previous) {
failureShown.delete(key);
resuming = true;
try {
await continuePendingPayment(key, previous);
// A completed/closed attempt has already entered its result page. Do not create
// another order behind that page during the same click.
return false;
}
finally { resuming = false; }
}
if (!canOpenDypay()) throw new Error('请先安装或升级抖音客户端');
return true;
// #endif
} catch (error) {
const pending = paymentKey && paymentKey === accountKey() && uni.getStorageSync(paymentKey);
if (pending) {
await showState({ state: 'pending', message: error.message }, paymentKey, pending);
return false;
}
uni.showToast({ title: error.message || '暂时无法支付,请稍后重试', icon: 'none' });
return false;
}
@@ -200,10 +183,16 @@ export async function appDypayFun(payment) {
try {
if (!key || !payment.orderNum) throw new Error('登录身份或支付订单编号缺失');
const previous = uni.getStorageSync(key);
if (previous) {
// Even legacy callers must not open a different payment while the saved one is unresolved.
return await continuePendingPayment(key, previous);
if (previous === payment.orderNum) {
// An explicit retry of this same server-selected order still needs reconciliation.
failureShown.delete(key);
return await continuePendingPayment(key, payment.orderNum);
}
// Validate the CURRENT response before releasing any stale native callback.
// This only resets the client bridge, not the previous server order. Its
// settlement remains handled by backend callbacks/query jobs/order details.
toSdkPayInfo(payment);
if (previous) resetDypay();
return await executePayment(payment, key);
} catch (error) {
if (key && key === accountKey()) {