You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将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('支付过程中出现错误,请稍后重试');
}

关键细节注意

  1. Invoice Item的作用:它会被自动添加到客户的下一张待生成发票(也就是订阅的第一张发票),这样总价就是订阅费+一次性费用,Stripe会针对这个总价触发一次3DS验证。
  2. 试用天数的处理:如果设置了trialDays > 0,订阅的初始发票不会立即生成,一次性费用会延迟到试用结束后的第一张发票收取。如果需要立即收取一次性费用,请确保trialDays = 0,或者手动触发发票生成。
  3. 货币一致性:一次性费用的货币必须和订阅价格的货币相同,否则Stripe会报错。
  4. Stripe Dashboard验证:你可以在Stripe后台的「发票」页面查看生成的发票,确认是否包含了一次性费用和订阅费用。

内容的提问来源于stack exchange,提问作者Nicolas Tassin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.06 06:50:30