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
+55 -2
View File
@@ -40,7 +40,7 @@ const payment = { orderNum: 'order-1', douyin: { appId: 'app', mchId: 'mch', pre
async function payContext(overrides = {}, app = true) {
const adapter = await import('../utils/douyin-pay-adapter.mjs');
const context = { uni: uniMock(), BASE_URL: 'test-api', TOKEN_NAME: 'token', USER_DATA: 'user',
...adapter, canOpenDypay: () => true, initDypay: () => true,
...adapter, canOpenDypay: () => true, initDypay: () => true, resetDypay: () => {},
openDypay: (_, cb) => cb({ resultCode: '0' }),
request: async options => options.url === '/payment/douyin/resume'
? ({ bizcode: 100, data: { state: 'paid', orderNum: options.data.orderNum } })
@@ -214,12 +214,65 @@ 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'), false);
assert.equal(await ctx.prepareDouyinPay('douyin'), state === 'closed');
assert.equal(ctx.uni.storage.has('tb-douyin:test-api:7'), false);
assert.equal(await ctx.prepareDouyinPay('douyin'), true);
}
});
test('closed old transaction unlocks a fresh server order, never reopens old parameters', async () => {
let resets=0, opened;
const ctx=await payContext({resetDypay:()=>resets++, request:async options => options.url === '/payment/douyin/resume'
? {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'),true);
assert.equal(resets,1);
const fresh={...payment,orderNum:'fresh-order',douyin:{...payment.douyin,prepayId:'fresh-prepay'}};
await ctx.appDypayFun(fresh);
assert.equal(opened,'fresh-prepay');
assert.equal(ctx.uni.storage.has('tb-douyin:test-api:7'),false);
});
test('declining new payment or switching accounts after closure never creates a payment', async () => {
for(const mode of ['cancel','account']) {
const ctx=await payContext({request:async()=>({bizcode:100,data:{state:'closed',orderNum:'expired-order'}})});
ctx.uni.storage.set('tb-douyin:test-api:7','expired-order');
ctx.uni.showModal=options=>{
if(mode==='account') ctx.uni.storage.set('user',{userId:8});
options.success({confirm:mode!=='cancel'});
};
assert.equal(await ctx.prepareDouyinPay('douyin'),false);
}
});
test('unconfirmed server state never resets the native lock and explains why', async () => {
const ctx=await payContext({resetDypay:()=>assert.fail('Unconfirmed native reset'),
request:async()=>({bizcode:100,data:{state:'pending',reason:'QUERY_FAILED',orderNum:payment.orderNum}})});
ctx.uni.storage.set('tb-douyin:test-api:7',payment.orderNum);
assert.equal(await ctx.prepareDouyinPay('douyin'),false);
assert.match(ctx.uni.messages.at(-1),/查单失败/);
assert.equal(ctx.uni.storage.get('tb-douyin:test-api:7'),payment.orderNum);
});
test('unpaid server confirmation resets a missing native callback before reinitializing SDK', async () => {
const sequence=[];
const ctx=await payContext({resetDypay:()=>sequence.push('reset'),initDypay:()=>{sequence.push('init');return true;},
request:async()=>({bizcode:100,data:{state:'unpaid',orderNum:payment.orderNum,payment}}),
queryUntilSettled:async()=>({state:'pending'})});
ctx.uni.storage.set('tb-douyin:test-api:7',payment.orderNum);
assert.equal(await ctx.prepareDouyinPay('douyin'),false);
assert.deepEqual(sequence,['reset','init']);
});
test('SDK rejection stays recoverable but no longer hides the native failure', async () => {
const ctx=await payContext({openDypay:(_,cb)=>cb({resultCode:'103'}),queryUntilSettled:async()=>({state:'pending'})});
await ctx.appDypayFun(payment);
assert.match(ctx.uni.messages.at(-1),/原生支付仍在处理中/);
assert.equal(ctx.uni.storage.get('tb-douyin:test-api:7'),payment.orderNum);
assert.equal(ctx.uni.messages.includes('支付成功'),false);
});
test('account switch during resume response or confirmation cannot invoke SDK', async () => {
for (const phase of ['response','confirmation']) {
const ctx = await payContext({openDypay:()=>assert.fail('Old account reached SDK')});