From 8f28a834f86a0711a002702fb04b8a66bb9f920c Mon Sep 17 00:00:00 2001 From: shaw Date: Sat, 25 Apr 2026 09:46:27 +0800 Subject: [PATCH] =?UTF-8?q?fix(payment):=20=E5=90=8C=E6=97=B6=E5=90=AF?= =?UTF-8?q?=E7=94=A8=E6=98=93=E6=94=AF=E4=BB=98=E5=92=8C=20Stripe=20?= =?UTF-8?q?=E6=97=B6=E6=98=BE=E7=A4=BA=20Stripe=20=E6=8C=89=E9=92=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit VISIBLE_METHOD_ALIASES 漏了 stripe,导致 getVisibleMethods 把后端返回 的 stripe 过滤掉。点 Stripe 按钮时省略 method 查询参数,让落地页渲染 完整的 Payment Element。 --- .../payment/__tests__/paymentFlow.spec.ts | 16 +++++++++++++++- frontend/src/components/payment/paymentFlow.ts | 10 ++++++++-- frontend/src/views/user/PaymentView.vue | 8 ++++++-- 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/frontend/src/components/payment/__tests__/paymentFlow.spec.ts b/frontend/src/components/payment/__tests__/paymentFlow.spec.ts index 72dc61f1..aebec8e5 100644 --- a/frontend/src/components/payment/__tests__/paymentFlow.spec.ts +++ b/frontend/src/components/payment/__tests__/paymentFlow.spec.ts @@ -33,7 +33,7 @@ function createOrderResult(overrides: Partial = {}): CreateOr } describe('getVisibleMethods', () => { - it('filters hidden provider methods and normalizes aliases', () => { + it('normalizes provider aliases and keeps stripe as a top-level method', () => { const visible = getVisibleMethods({ alipay_direct: methodLimit({ single_min: 5 }), wxpay: methodLimit({ single_max: 100 }), @@ -43,6 +43,7 @@ describe('getVisibleMethods', () => { expect(visible).toEqual({ alipay: methodLimit({ single_min: 5 }), wxpay: methodLimit({ single_max: 100 }), + stripe: methodLimit({ fee_rate: 3 }), }) }) @@ -76,6 +77,19 @@ describe('decidePaymentLaunch', () => { expect(decision.recovery.outTradeNo).toBe('') }) + it('routes Stripe button click to the full Payment Element without a preselected sub-method', () => { + const decision = decidePaymentLaunch(createOrderResult({ + client_secret: 'cs_test', + }), { + visibleMethod: 'stripe', + orderType: 'balance', + isMobile: false, + }) + + expect(decision.kind).toBe('stripe_route') + expect(decision.stripeMethod).toBeUndefined() + }) + it('uses Stripe route flow for mobile WeChat client secret', () => { const decision = decidePaymentLaunch(createOrderResult({ client_secret: 'cs_test', diff --git a/frontend/src/components/payment/paymentFlow.ts b/frontend/src/components/payment/paymentFlow.ts index 2a5f93dc..318f3882 100644 --- a/frontend/src/components/payment/paymentFlow.ts +++ b/frontend/src/components/payment/paymentFlow.ts @@ -14,9 +14,10 @@ const VISIBLE_METHOD_ALIASES = { alipay_direct: 'alipay', wxpay: 'wxpay', wxpay_direct: 'wxpay', + stripe: 'stripe', } as const -export type VisiblePaymentMethod = 'alipay' | 'wxpay' +export type VisiblePaymentMethod = 'alipay' | 'wxpay' | 'stripe' export type StripeVisibleMethod = 'alipay' | 'wechat_pay' export type PaymentLaunchKind = | 'qr_waiting' @@ -144,7 +145,12 @@ export function decidePaymentLaunch( }, context.now) if (baseState.clientSecret) { - const stripeMethod: StripeVisibleMethod = visibleMethod === 'wxpay' ? 'wechat_pay' : 'alipay' + // visibleMethod === 'stripe' means the user clicked the dedicated Stripe button + // and should land on the full Payment Element to choose a sub-method themselves. + const isStripeButton = visibleMethod === 'stripe' + const stripeMethod: StripeVisibleMethod | undefined = isStripeButton + ? undefined + : visibleMethod === 'wxpay' ? 'wechat_pay' : 'alipay' const kind: PaymentLaunchKind = stripeMethod === 'alipay' && !context.isMobile ? 'stripe_popup' : 'stripe_route' diff --git a/frontend/src/views/user/PaymentView.vue b/frontend/src/views/user/PaymentView.vue index 7cb4343d..7bbdf708 100644 --- a/frontend/src/views/user/PaymentView.vue +++ b/frontend/src/views/user/PaymentView.vue @@ -693,14 +693,18 @@ async function createOrder(orderAmount: number, orderType: OrderType, planId?: n } } const visibleMethod = normalizeVisibleMethod(requestType) || requestType - const stripeMethod = visibleMethod === 'wxpay' ? 'wechat_pay' : 'alipay' + // When user clicks the dedicated Stripe button, leave method blank so the + // landing page renders Stripe's full Payment Element (card/link/alipay/wxpay). + const stripeMethod = visibleMethod === 'stripe' + ? '' + : visibleMethod === 'wxpay' ? 'wechat_pay' : 'alipay' const stripeRouteUrl = result.client_secret ? router.resolve({ path: '/payment/stripe', query: { order_id: String(result.order_id), client_secret: result.client_secret, - method: stripeMethod, + method: stripeMethod || undefined, resume_token: result.resume_token || undefined, }, }).href