fix: 恢复抖音过期支付并完善原生状态确认

This commit is contained in:
Codex
2026-09-09 16:29:30 +08:00
parent fd3dc6870a
commit 2e9a732f9d
9 changed files with 142 additions and 19 deletions
+40 -10
View File
@@ -1,5 +1,5 @@
// #ifdef APP-PLUS
import { initDypay, canOpenDypay, openDypay } from '@/uni_modules/tb-douyin-pay';
import { initDypay, canOpenDypay, openDypay, resetDypay } from '@/uni_modules/tb-douyin-pay';
// #endif
import request from './request.js';
import { BASE_URL } from './config.js';
@@ -16,7 +16,7 @@ function accountKey() {
}
async function query(orderNum) {
return queryUntilSettled(async options => {
const response = await request({ ...options, isShowLoading: false });
const response = await request({ ...options, isShowLoading: false, timeout: 30000 });
if (response?.bizcode !== 100) throw new Error('查询支付结果失败');
return response.data;
}, orderNum);
@@ -49,31 +49,40 @@ function showState(state, key, orderNum) {
if (state.state !== 'pending' && uni.getStorageSync(key) === orderNum) uni.removeStorageSync(key);
uni.$emit('douyin-payment-result', { orderNum, state: state.state });
uni.showToast({ title: state.state === 'paid' ? '支付成功' : state.state === 'failed'
? '支付未成功,请查看订单' : '支付结果待确认,请稍后查看订单', icon: 'none', duration: 2500 });
? '支付未成功,请查看订单' : state.state === 'closed' ? '原支付已关闭,可重新下单'
: state.message || '支付结果待确认,请稍后查看订单', icon: 'none', duration: 2500 });
}
// Called only by a user payment action, never by onShow. Renew the SAME transaction on the server.
async function continuePendingPayment(key, orderNum) {
const response = await request({ url: '/payment/douyin/resume', method: 'POST',
data: { orderNum }, isShowLoading: false });
data: { orderNum }, isShowLoading: false, timeout: 60000 });
if (key !== accountKey()) return { state: 'pending' };
if (response?.bizcode !== 100) throw new Error('暂时无法续付,请查看订单后重试');
const result = response.data;
if (!result || result.orderNum !== orderNum) throw new Error('续付订单不匹配,请查看订单');
if (result.state === 'paid' || result.state === 'closed') {
// The existing query endpoint also reconciles business orders when a webhook was missed.
const state = result.state === 'paid' ? await query(orderNum) : { state: 'failed' };
const state = result.state === 'paid' ? await query(orderNum) : { state: 'closed' };
if (key === accountKey() && state.state !== 'pending') resetDypay();
showState(state, key, orderNum);
return state;
}
if (result.state !== 'unpaid') {
const state = { state: 'pending' };
const messages = {
QUERY_FAILED: '抖音查单失败,暂不能重新支付,请稍后重试',
CLOSE_UNCONFIRMED: '旧支付关单尚未确认,请稍后重试',
RECONCILIATION_REQUIRED: '订单状态需核对,请联系客服,勿重复付款',
CHANNEL_PENDING: '抖音支付处理中,请稍后查看订单'
};
const state = { state: 'pending', message: messages[result.reason] };
showState(state, key, orderNum);
return state;
}
if (result.payment?.orderNum !== orderNum) throw new Error('续付订单不匹配,请查看订单');
// Validate before the confirmation dialog. Never store the SDK signature locally.
toSdkPayInfo(result.payment);
resetDypay();
const confirmed = await new Promise(resolve => uni.showModal({
title: '继续上一笔支付',
content: `上一笔抖音支付尚未完成,是否继续支付原订单?${result.payment.amount != null
@@ -95,8 +104,14 @@ async function executePayment(payment, key) {
}
// Persist before entering native code so a killed process remains recoverable.
uni.setStorageSync(key, payment.orderNum);
await new Promise(resolve => openDypay({ payInfo, showLoading: true }, resolve));
const nativeResult = await new Promise(resolve => openDypay({ payInfo, showLoading: true }, resolve));
const state = await query(payment.orderNum);
if (state.state === 'pending' && nativeResult?.resultCode && !['0', '3'].includes(String(nativeResult.resultCode))) {
// A native error is diagnostic only: never clear a potentially live payment on its authority.
const descriptions = { '1': '支付已取消', '2': '支付 SDK 调用失败', '100': '请安装或升级抖音客户端',
'103': '原生支付仍在处理中', '-1': '支付参数校验失败' };
state.message = `${descriptions[String(nativeResult.resultCode)] || '支付未完成'},请查询订单后重试`;
}
showState(state, key, payment.orderNum);
return state;
// #endif
@@ -116,9 +131,20 @@ export async function prepareDouyinPay(payway) {
const previous = uni.getStorageSync(key);
if (previous) {
resuming = true;
try { await continuePendingPayment(key, previous); }
try {
const result = await continuePendingPayment(key, previous);
if (result.state !== 'closed' || key !== accountKey() || uni.getStorageSync(key)) return false;
const confirmed = await new Promise(resolve => uni.showModal({
title: '原支付已关闭',
content: '上一笔支付已确认关闭。是否按当前页面重新发起支付?原商品订单若已过期,请返回商品页重新下单。',
confirmText: '重新支付', cancelText: '暂不支付',
success: res => resolve(res.confirm === true), fail: () => resolve(false)
}));
if (!confirmed || key !== accountKey() || uni.getStorageSync(key)) return false;
if (!canOpenDypay()) throw new Error('请先安装或升级抖音客户端');
return true;
}
finally { resuming = false; }
return false;
}
if (!canOpenDypay()) throw new Error('请先安装或升级抖音客户端');
return true;
@@ -161,7 +187,11 @@ export async function resumeDouyinPay() {
const pending = key && uni.getStorageSync(key);
if (!pending) return;
resuming = true;
try { showState(await query(pending), key, pending); }
try {
const state = await query(pending);
if (key === accountKey() && state.state !== 'pending') resetDypay();
showState(state, key, pending);
}
catch (_) { /* Keep the order for a later onShow. Never infer a payment failure. */ }
finally { resuming = false; }
// #endif