fix: 移除抖音支付额外弹窗并统一成功页跳转
This commit is contained in:
+58
-33
@@ -5,9 +5,12 @@ import request from './request.js';
|
||||
import { BASE_URL } from './config.js';
|
||||
import { TOKEN_NAME, USER_DATA } from './auth.js';
|
||||
import { toSdkPayInfo, queryUntilSettled } from './douyin-pay-adapter.mjs';
|
||||
import { jumpToPaymentSuccess } from './payment-navigation.js';
|
||||
|
||||
let active = false;
|
||||
let resuming = false;
|
||||
let inFlight = null;
|
||||
let checkingForeground = false;
|
||||
function accountKey() {
|
||||
if (!uni.getStorageSync(TOKEN_NAME)) return '';
|
||||
const user = uni.getStorageSync(USER_DATA) || {};
|
||||
@@ -44,12 +47,21 @@ export function filterDouyinPayWays(ways) {
|
||||
return (ways || []).filter(item => item.type !== 'douyin');
|
||||
// #endif
|
||||
}
|
||||
function showState(state, key, orderNum) {
|
||||
async function showState(state, key, orderNum) {
|
||||
if (key !== accountKey()) return;
|
||||
if (state.state === 'paid') {
|
||||
const navigated = await jumpToPaymentSuccess();
|
||||
if (key !== accountKey()) return;
|
||||
if (!navigated) {
|
||||
// Retain the record so the next onShow/click can retry navigation without paying again.
|
||||
uni.showToast({ title: '支付成功,请在订单中查看', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
}
|
||||
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'
|
||||
? '支付未成功,请查看订单' : state.state === 'closed' ? '原支付已关闭,可重新下单'
|
||||
if (state.state === 'paid' || state.state === 'closed') return;
|
||||
uni.showToast({ title: state.state === 'failed' ? '支付未成功,请查看订单'
|
||||
: state.message || '支付结果待确认,请稍后查看订单', icon: 'none', duration: 2500 });
|
||||
}
|
||||
|
||||
@@ -65,7 +77,7 @@ async function continuePendingPayment(key, orderNum) {
|
||||
// The existing query endpoint also reconciles business orders when a webhook was missed.
|
||||
const state = result.state === 'paid' ? await query(orderNum) : { state: 'closed' };
|
||||
if (key === accountKey() && state.state !== 'pending') resetDypay();
|
||||
showState(state, key, orderNum);
|
||||
await showState(state, key, orderNum);
|
||||
return state;
|
||||
}
|
||||
if (result.state !== 'unpaid') {
|
||||
@@ -76,21 +88,14 @@ async function continuePendingPayment(key, orderNum) {
|
||||
CHANNEL_PENDING: '抖音支付处理中,请稍后查看订单'
|
||||
};
|
||||
const state = { state: 'pending', message: messages[result.reason] };
|
||||
showState(state, key, orderNum);
|
||||
await 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.
|
||||
// The user's payment click already authorizes continuation. No additional modal.
|
||||
// Validate the original server parameters; never store the SDK signature locally.
|
||||
toSdkPayInfo(result.payment);
|
||||
resetDypay();
|
||||
const confirmed = await new Promise(resolve => uni.showModal({
|
||||
title: '继续上一笔支付',
|
||||
content: `上一笔抖音支付尚未完成,是否继续支付原订单?${result.payment.amount != null
|
||||
? `金额:¥${result.payment.amount}。` : ''}不会创建新订单。`,
|
||||
confirmText: '继续支付', cancelText: '暂不支付',
|
||||
success: res => resolve(res.confirm === true), fail: () => resolve(false)
|
||||
}));
|
||||
if (!confirmed || key !== accountKey()) return { state: 'pending' };
|
||||
return executePayment(result.payment, key);
|
||||
}
|
||||
|
||||
@@ -104,20 +109,31 @@ async function executePayment(payment, key) {
|
||||
}
|
||||
// Persist before entering native code so a killed process remains recoverable.
|
||||
uni.setStorageSync(key, payment.orderNum);
|
||||
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;
|
||||
const session = { key, orderNum: payment.orderNum, settled: false, finish: null };
|
||||
inFlight = session;
|
||||
try {
|
||||
const nativeResult = await new Promise(resolve => {
|
||||
session.finish = result => {
|
||||
if (session.settled) return;
|
||||
session.settled = true;
|
||||
resolve(result);
|
||||
};
|
||||
openDypay({ payInfo, showLoading: true }, session.finish);
|
||||
});
|
||||
const state = nativeResult?.serverState || 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)] || '支付未完成'},请查询订单后重试`;
|
||||
}
|
||||
await showState(state, key, payment.orderNum);
|
||||
return state;
|
||||
} finally { if (inFlight === session) inFlight = null; }
|
||||
// #endif
|
||||
}
|
||||
|
||||
// Runs BEFORE creating an order. A pending record offers explicit continuation, not a new order.
|
||||
// Runs on the payment click BEFORE creating an order. Resume a live original transaction directly.
|
||||
export async function prepareDouyinPay(payway) {
|
||||
if (payway !== 'douyin') return true;
|
||||
try {
|
||||
@@ -134,13 +150,6 @@ export async function prepareDouyinPay(payway) {
|
||||
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;
|
||||
}
|
||||
@@ -182,6 +191,22 @@ export async function appDypayFun(payment) {
|
||||
|
||||
export async function resumeDouyinPay() {
|
||||
// #ifdef APP-PLUS
|
||||
// The SDK callback may be lost when returning from Douyin. Reconcile onShow even
|
||||
// while native payment is waiting; only a terminal server result can finish it.
|
||||
const session = inFlight;
|
||||
if (session) {
|
||||
if (checkingForeground || session.settled || session.key !== accountKey()) return;
|
||||
checkingForeground = true;
|
||||
try {
|
||||
const state = await query(session.orderNum);
|
||||
if (session === inFlight && !session.settled && session.key === accountKey() && state.state !== 'pending') {
|
||||
resetDypay();
|
||||
session.finish({ serverState: state });
|
||||
}
|
||||
} catch (_) { /* A failed foreground query must not unlock a live transaction. */ }
|
||||
finally { checkingForeground = false; }
|
||||
return;
|
||||
}
|
||||
if (active || resuming) return;
|
||||
const key = accountKey();
|
||||
const pending = key && uni.getStorageSync(key);
|
||||
@@ -190,7 +215,7 @@ export async function resumeDouyinPay() {
|
||||
try {
|
||||
const state = await query(pending);
|
||||
if (key === accountKey() && state.state !== 'pending') resetDypay();
|
||||
showState(state, key, pending);
|
||||
await showState(state, key, pending);
|
||||
}
|
||||
catch (_) { /* Keep the order for a later onShow. Never infer a payment failure. */ }
|
||||
finally { resuming = false; }
|
||||
|
||||
Reference in New Issue
Block a user