如何将Stripe 3D Secure一次性支付与年度定期订阅合并至同一个弹窗完成验证?
解决Stripe一次性支付+订阅支付合并3D Secure验证的问题
嘿,我完全懂你的困扰——两次3DS验证弹窗确实会让用户摸不着头脑。核心问题是你现在把一次性支付和年度订阅拆成了两个独立的流程:一个单独的PaymentIntent,一个Subscription,Stripe自然会对每笔需要验证的交易触发一次弹窗。要合并成一次验证,关键是把一次性费用附加到订阅的初始发票上,让两者合并成一笔交易,只触发一次3DS验证。
为什么之前会触发两次弹窗?
你当前的逻辑是:先创建订阅(生成一个包含订阅费用的PaymentIntent),再单独创建一个一次性费用的PaymentIntent。两个PaymentIntent都需要3DS验证,所以会弹出两次窗口。
正确的解决方案:合并到订阅初始发票
不要单独创建一次性支付的PaymentIntent,而是用Stripe Invoice Item把一次性费用附加到客户的账户上,这样订阅生成的第一张发票会自动包含「订阅费用+一次性费用」,只需要一次3DS验证就能完成总价的支付。
修改后的后端代码
const customer = await stripeService.createStripeCustomer( name, email, payment_method, ); // 如果是套餐(包含一次性费用+订阅),先添加一次性发票项到客户账户 if (productIsBundle) { const amount = 4000; // 一次性费用,单位为分(例如4000 = $40) await stripeService.createStripeInvoiceItem( customer.id, amount, '一次性套餐服务费', // 费用描述,会显示在发票上 'usd' // 货币需与订阅价格的货币保持一致 ); } // 创建订阅:此时初始发票会自动包含订阅费用 + 刚才添加的一次性费用 const subscription = await stripeService.createStripeSubscription( customer, stripePriceElement, discount, trialDays, ); let status = subscription.status; // 只有当没有试用天数时,才会生成待支付的发票和对应的PaymentIntent let client_secret = trialDays > 0 ? null : subscription.latest_invoice.payment_intent.client_secret; // 现在不需要返回单独的intent_secret了,只需要返回订阅相关的信息 try { res.status(200).json({ product: 'bundle', client_secret, status, subscription, }); } catch (error) { res.status(400).json({ status: 'Subscription creation failed', error: error.message }); }
修改后的前端代码
现在只需要做一次卡片确认,用订阅发票对应的client_secret即可:
try { if (client_secret) { await stripe.confirmCardPayment(client_secret) .then(async (result) => { if (result.error) { setStripeError(result.error.message); return; } if (result.paymentIntent?.status === 'succeeded') { await axios.post( `${process.env.REACT_APP_BACKEND}/users/payment/success`, { modelId: model.id }, token ); } }); } } catch (error) { setStripeError('支付过程中出现错误,请稍后重试'); }
关键细节注意
- Invoice Item的作用:它会被自动添加到客户的下一张待生成发票(也就是订阅的第一张发票),这样总价就是订阅费+一次性费用,Stripe会针对这个总价触发一次3DS验证。
- 试用天数的处理:如果设置了
trialDays > 0,订阅的初始发票不会立即生成,一次性费用会延迟到试用结束后的第一张发票收取。如果需要立即收取一次性费用,请确保trialDays = 0,或者手动触发发票生成。 - 货币一致性:一次性费用的货币必须和订阅价格的货币相同,否则Stripe会报错。
- Stripe Dashboard验证:你可以在Stripe后台的「发票」页面查看生成的发票,确认是否包含了一次性费用和订阅费用。
内容的提问来源于stack exchange,提问作者Nicolas Tassin
相关产品推荐
相关产品推荐

