fix(payment): restore public resume and result flows

This commit is contained in:
IanShaw027
2026-04-22 11:17:23 +08:00
parent c229f33e9e
commit dd314c41e3
15 changed files with 435 additions and 90 deletions

View File

@@ -73,6 +73,7 @@ describe('decidePaymentLaunch', () => {
expect(decision.paymentState.paymentType).toBe('alipay')
expect(decision.stripeMethod).toBe('alipay')
expect(decision.recovery.resumeToken).toBe('resume-1')
expect(decision.recovery.outTradeNo).toBe('')
})
it('uses Stripe route flow for mobile WeChat client secret', () => {
@@ -94,6 +95,7 @@ describe('decidePaymentLaunch', () => {
pay_url: 'https://pay.example.com/session/abc',
payment_mode: 'popup',
resume_token: 'resume-2',
out_trade_no: 'sub2_abc',
}), {
visibleMethod: 'wxpay',
orderType: 'balance',
@@ -103,6 +105,7 @@ describe('decidePaymentLaunch', () => {
expect(decision.kind).toBe('redirect_waiting')
expect(decision.paymentState.payUrl).toBe('https://pay.example.com/session/abc')
expect(decision.recovery.paymentMode).toBe('popup')
expect(decision.recovery.outTradeNo).toBe('sub2_abc')
expect(decision.recovery.resumeToken).toBe('resume-2')
})
@@ -225,6 +228,7 @@ describe('readPaymentRecoverySnapshot', () => {
expiresAt: '2099-01-01T00:10:00.000Z',
paymentType: 'alipay',
payUrl: 'https://pay.example.com/session/33',
outTradeNo: 'sub2_33',
clientSecret: '',
payAmount: 18,
orderType: 'balance',
@@ -249,6 +253,7 @@ describe('readPaymentRecoverySnapshot', () => {
expiresAt: '2024-01-01T00:10:00.000Z',
paymentType: 'wxpay',
payUrl: 'https://pay.example.com/session/55',
outTradeNo: 'sub2_55',
clientSecret: '',
payAmount: 18,
orderType: 'balance',
@@ -264,10 +269,34 @@ describe('readPaymentRecoverySnapshot', () => {
expect(readPaymentRecoverySnapshot(JSON.stringify({
...expiredSnapshot,
outTradeNo: 'sub2_55',
expiresAt: '2099-01-01T00:10:00.000Z',
}), {
now: Date.UTC(2099, 0, 1, 0, 1, 0),
resumeToken: 'other-token',
})).toBeNull()
})
it('keeps backward compatibility with snapshots written before outTradeNo existed', () => {
const restored = readPaymentRecoverySnapshot(JSON.stringify({
orderId: 44,
amount: 18,
qrCode: '',
expiresAt: '2099-01-01T00:10:00.000Z',
paymentType: 'alipay',
payUrl: 'https://pay.example.com/session/44',
clientSecret: '',
payAmount: 18,
orderType: 'balance',
paymentMode: 'popup',
resumeToken: 'resume-44',
createdAt: Date.UTC(2099, 0, 1, 0, 0, 0),
}), {
now: Date.UTC(2099, 0, 1, 0, 1, 0),
resumeToken: 'resume-44',
})
expect(restored?.orderId).toBe(44)
expect(restored?.outTradeNo).toBe('')
})
})

View File

@@ -34,6 +34,7 @@ export interface PaymentRecoverySnapshot {
expiresAt: string
paymentType: string
payUrl: string
outTradeNo: string
clientSecret: string
payAmount: number
orderType: OrderType | ''
@@ -132,6 +133,7 @@ export function decidePaymentLaunch(
expiresAt: result.expires_at || '',
paymentType: visibleMethod,
payUrl: result.pay_url || '',
outTradeNo: result.out_trade_no || '',
clientSecret: result.client_secret || '',
payAmount: result.pay_amount,
orderType: context.orderType,
@@ -227,6 +229,7 @@ export function readPaymentRecoverySnapshot(
|| typeof parsed.expiresAt !== 'string'
|| typeof parsed.paymentType !== 'string'
|| typeof parsed.payUrl !== 'string'
|| (parsed.outTradeNo != null && typeof parsed.outTradeNo !== 'string')
|| typeof parsed.clientSecret !== 'string'
|| typeof parsed.payAmount !== 'number'
|| typeof parsed.paymentMode !== 'string'
@@ -241,7 +244,7 @@ export function readPaymentRecoverySnapshot(
if (Number.isFinite(expiresAt) && expiresAt <= now) {
return null
}
if (options.resumeToken && parsed.resumeToken && parsed.resumeToken !== options.resumeToken) {
if (options.resumeToken && parsed.resumeToken !== options.resumeToken) {
return null
}
@@ -252,6 +255,7 @@ export function readPaymentRecoverySnapshot(
expiresAt: parsed.expiresAt,
paymentType: parsed.paymentType,
payUrl: parsed.payUrl,
outTradeNo: parsed.outTradeNo || '',
clientSecret: parsed.clientSecret,
payAmount: parsed.payAmount,
orderType: parsed.orderType === 'subscription' ? 'subscription' : 'balance',