fix: 对齐抖音支付失败及杀进程恢复跳转

This commit is contained in:
Codex
2026-09-10 09:20:50 +08:00
committed by charles
parent b5b6359d8e
commit f4c67cb20b
7 changed files with 142 additions and 1555 deletions
+76 -3
View File
@@ -52,6 +52,7 @@ async function payContext(overrides = {}, app = true) {
return loadModule('utils/douyin-pay.js', context, app);
}
const successPage = '/pages/order_package/order_submit_ok/order_submit_ok';
const failurePage = '/pages/order_package/order_submit_error/order_submit_error';
test('SDK success is confirmed with server before showing success', async () => {
let queries = 0;
const ctx = await payContext({ request: async () => { queries++; return { bizcode:100, data:{state:2} }; } });
@@ -65,7 +66,7 @@ test('SDK success plus network failure remains recoverable, never success', asyn
await ctx.appDypayFun(payment);
assert.equal(ctx.uni.storage.get('tb-douyin:test-api:7'), 'order-1');
assert.equal(ctx.uni.messages.includes('支付成功'), false);
assert.deepEqual(ctx.uni.navigations, []);
assert.deepEqual(ctx.uni.navigations, [failurePage]);
});
test('cancel still queries authoritative server state', async () => {
const ctx = await payContext({ openDypay: (_, cb) => cb({ resultCode:'1' }) });
@@ -195,7 +196,7 @@ test('cold start never opens the SDK or asks for payment and preserves pending s
ctx.uni.showModal = () => assert.fail('onShow must not ask for payment');
await ctx.resumeDouyinPay();
assert.equal(ctx.uni.storage.get('tb-douyin:test-api:7'), payment.orderNum);
assert.deepEqual(ctx.uni.navigations, []);
assert.deepEqual(ctx.uni.navigations, [failurePage]);
});
test('unknown, processing, network error and missing backend remain locked', async () => {
@@ -219,7 +220,8 @@ test('terminal server result clears saved order without payment and allows the n
? ({bizcode:100,data:{state,orderNum:payment.orderNum}})
: ({bizcode:100,data:{state:2}})});
ctx.uni.storage.set('tb-douyin:test-api:7', payment.orderNum);
assert.equal(await ctx.prepareDouyinPay('douyin'), state === 'closed');
assert.equal(await ctx.prepareDouyinPay('douyin'), false);
assert.deepEqual(ctx.uni.navigations, [state === 'paid' ? successPage : failurePage]);
assert.equal(ctx.uni.storage.has('tb-douyin:test-api:7'), false);
assert.equal(await ctx.prepareDouyinPay('douyin'), true);
}
@@ -231,6 +233,9 @@ test('closed old transaction unlocks a fresh server order, never reopens old par
? {bizcode:100,data:{state:'closed',orderNum:'expired-order'}} : {bizcode:100,data:{state:2}},
openDypay:(options,cb)=>{opened=options.payInfo.prepayid; cb({resultCode:'0'});}});
ctx.uni.storage.set('tb-douyin:test-api:7','expired-order');
assert.equal(await ctx.prepareDouyinPay('douyin'),false);
assert.deepEqual(ctx.uni.navigations,[failurePage]);
// A NEW user action may now create a fresh order; the closed attempt cannot do it implicitly.
assert.equal(await ctx.prepareDouyinPay('douyin'),true);
assert.equal(resets,1);
const fresh={...payment,orderNum:'fresh-order',douyin:{...payment.douyin,prepayId:'fresh-prepay'}};
@@ -405,4 +410,72 @@ test('native initialization failure before first payment does not leave a perman
const ctx = await payContext({initDypay:()=>false,openDypay:()=>assert.fail('Invalid SDK init')});
assert.equal((await ctx.appDypayFun(payment)).state, 'error');
assert.equal(ctx.uni.storage.has('tb-douyin:test-api:7'), false);
assert.deepEqual(ctx.uni.navigations, [failurePage]);
});
test('cancel and native failure use the same failure page without losing the unresolved transaction', async () => {
for (const code of ['1','2','100','103','-1']) {
const ctx=await payContext({openDypay:(_,cb)=>cb({resultCode:code}),queryUntilSettled:async()=>({state:'pending'})});
await ctx.appDypayFun(payment);
assert.deepEqual(ctx.uni.navigations,[failurePage]);
assert.equal(ctx.uni.storage.get('tb-douyin:test-api:7'),payment.orderNum);
assert.equal(ctx.uni.messages.includes('支付成功'),false);
}
});
test('killing the App during SDK payment restores into the failure page rather than checkout or a modal', async () => {
const before=await payContext({openDypay:()=>{ /* Process dies before callback. */ }});
before.appDypayFun(payment);
assert.equal(before.uni.storage.get('tb-douyin:test-api:7'),payment.orderNum);
const restarted=await payContext({openDypay:()=>assert.fail('Restart must never launch payment'),
queryUntilSettled:async()=>({state:'pending'})});
restarted.uni.storage.set('tb-douyin:test-api:7',before.uni.storage.get('tb-douyin:test-api:7'));
await restarted.resumeDouyinPay();
await restarted.resumeDouyinPay();
assert.deepEqual(restarted.uni.navigations,[failurePage]);
assert.equal(restarted.uni.storage.get('tb-douyin:test-api:7'),payment.orderNum);
restarted.queryUntilSettled=async()=>({state:'paid'});
await restarted.resumeDouyinPay();
assert.deepEqual(restarted.uni.navigations,[failurePage,successPage]);
assert.equal(restarted.uni.storage.has('tb-douyin:test-api:7'),false);
});
test('restarting after a closed payment shows failure and clears only the confirmed terminal record', async () => {
const ctx=await payContext({queryUntilSettled:async()=>({state:'failed'})});
ctx.uni.storage.set('tb-douyin:test-api:7',payment.orderNum);
await ctx.resumeDouyinPay();
assert.deepEqual(ctx.uni.navigations,[failurePage]);
assert.equal(ctx.uni.storage.has('tb-douyin:test-api:7'),false);
});
test('offline restart exits checkout but retains the order for later authoritative reconciliation', async () => {
const ctx=await payContext({queryUntilSettled:async()=>{throw new Error('offline');}});
ctx.uni.storage.set('tb-douyin:test-api:7',payment.orderNum);
await ctx.resumeDouyinPay();
await ctx.resumeDouyinPay();
assert.deepEqual(ctx.uni.navigations,[failurePage]);
assert.equal(ctx.uni.storage.get('tb-douyin:test-api:7'),payment.orderNum);
assert.match(ctx.uni.messages.at(-1),/待确认/);
});
test('failure navigation fallback and retry never forget a terminal order before presenting the result', async () => {
const ctx=await payContext({queryUntilSettled:async()=>({state:'failed'})});
ctx.uni.storage.set('tb-douyin:test-api:7',payment.orderNum);
ctx.uni.navigateTo=options=>options.fail();
ctx.uni.redirectTo=options=>options.fail();
await ctx.resumeDouyinPay();
assert.equal(ctx.uni.storage.get('tb-douyin:test-api:7'),payment.orderNum);
ctx.uni.redirectTo=options=>{ctx.uni.navigations.push(options.url);options.success();};
await ctx.resumeDouyinPay();
assert.deepEqual(ctx.uni.navigations,[failurePage]);
assert.equal(ctx.uni.storage.has('tb-douyin:test-api:7'),false);
});
test('WeChat and Alipay failures retain the same shared failure page', async () => {
const ctx=await payContext({console:{log(){},error(){}}});
loadModule('utils/payUtils.js',ctx);
ctx.uni.requestPayment=options=>options.fail({});
ctx.appWxpayFun({type:'app'},1,'wx');
ctx.zfbPayFun({type:'app'},2,'ali');
assert.deepEqual(ctx.uni.navigations,[failurePage,failurePage]);
});