feat: wire payment return url payloads

This commit is contained in:
IanShaw027
2026-04-20 20:19:23 +08:00
parent 7826e9880c
commit b51bc7ee24
8 changed files with 215 additions and 21 deletions

View File

@@ -1,6 +1,7 @@
import { describe, expect, it } from 'vitest'
import type { CreateOrderResult, MethodLimit } from '@/types/payment'
import {
buildCreateOrderPayload,
decidePaymentLaunch,
getVisibleMethods,
readPaymentRecoverySnapshot,
@@ -106,6 +107,42 @@ describe('decidePaymentLaunch', () => {
})
})
describe('buildCreateOrderPayload', () => {
it('normalizes visible method aliases and attaches a canonical result URL', () => {
expect(buildCreateOrderPayload({
amount: 88,
paymentType: 'alipay_direct',
orderType: 'balance',
origin: 'https://app.example.com/',
isWechatBrowser: false,
})).toEqual({
amount: 88,
payment_type: 'alipay',
order_type: 'balance',
return_url: 'https://app.example.com/payment/result',
payment_source: 'hosted_redirect',
})
})
it('uses WeChat in-app resume source for visible WeChat payments in the WeChat browser', () => {
expect(buildCreateOrderPayload({
amount: 128,
paymentType: 'wxpay',
orderType: 'subscription',
planId: 7,
origin: 'https://app.example.com',
isWechatBrowser: true,
})).toEqual({
amount: 128,
payment_type: 'wxpay',
order_type: 'subscription',
plan_id: 7,
return_url: 'https://app.example.com/payment/result',
payment_source: 'wechat_in_app_resume',
})
})
})
describe('readPaymentRecoverySnapshot', () => {
it('restores an unexpired snapshot when the resume token matches', () => {
const snapshot: PaymentRecoverySnapshot = {

View File

@@ -1,4 +1,4 @@
import type { CreateOrderResult, MethodLimit, OrderType } from '@/types/payment'
import type { CreateOrderRequest, CreateOrderResult, MethodLimit, OrderType } from '@/types/payment'
export const PAYMENT_RECOVERY_STORAGE_KEY = 'payment.recovery.current'
@@ -49,6 +49,15 @@ export interface PaymentLaunchDecision {
stripeMethod?: StripeVisibleMethod
}
export interface BuildCreateOrderPayloadInput {
amount: number
paymentType: string
orderType: OrderType
planId?: number
origin?: string
isWechatBrowser: boolean
}
type CreateOrderFlowResult = CreateOrderResult & {
resume_token?: string
}
@@ -77,6 +86,28 @@ export function getVisibleMethods(methods: Record<string, MethodLimit>): Record<
return visible
}
export function buildCreateOrderPayload(input: BuildCreateOrderPayloadInput): CreateOrderRequest {
const visibleMethod = normalizeVisibleMethod(input.paymentType) || input.paymentType.trim()
const normalizedOrigin = (input.origin || '').trim().replace(/\/+$/, '')
const payload: CreateOrderRequest = {
amount: input.amount,
payment_type: visibleMethod,
order_type: input.orderType,
payment_source: visibleMethod === 'wxpay' && input.isWechatBrowser
? 'wechat_in_app_resume'
: 'hosted_redirect',
}
if (input.planId) {
payload.plan_id = input.planId
}
if (normalizedOrigin) {
payload.return_url = `${normalizedOrigin}/payment/result`
}
return payload
}
export function decidePaymentLaunch(
result: CreateOrderFlowResult,
context: PaymentLaunchContext,