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

Stripe订阅+试用期+3DS SCA问题:如何实现自动扣款?

Stripe订阅3DS验证问题解决方案

核心结论

  1. 主流网站均在用户首次提交支付方式时完成3DS验证,确保后续自动扣款无需用户介入;
  2. 完全可以在用户输入卡信息时完成3DS验证,避免试用期结束后出现逾期未付的情况。

问题分析

你当前的代码逻辑存在两个关键问题:

  • 使用payment_behavior: 'default_incomplete'会在创建订阅时生成待支付发票,而试用期内本应优先验证支付方式而非立即扣费;
  • 3DS验证触发逻辑不明确,导致支付方式未经过预验证,试用期结束后扣款时需要用户再次验证。

代码修改方案

后端订阅创建逻辑修改

将订阅创建时的支付行为改为pending_incomplete,强制触发Setup Intent完成3DS预验证:

export const stripeSubscriptionCreate = async (
  subscriptionReq: StripeSubscriptionCreateReq,
): Promise<StripeSubscriptionCreateRes> => {
  try {
    const { customerId, priceId } = subscriptionReq;
    if (!customerId || !priceId) throw new Error('Missing data');

    const client = await stripeClient();
    const subscription = await client.subscriptions.create({
      customer: customerId,
      items: [{ price: priceId }],
      // 试用期内触发支付方式验证,而非立即生成待支付发票
      payment_behavior: 'pending_incomplete',
      payment_settings: {
        payment_method_options: {
          card: {
            // 强制要求完成3DS验证,确保支付方式可用于后续自动扣款
            request_three_d_secure: 'required',
          },
        },
        payment_method_types: ['card'],
        save_default_payment_method: 'on_subscription',
      },
      // 若需要12小时试用期,替换为trial_end: Math.floor(Date.now() / 1000) + 43200
      trial_period_days: 1,
      expand: ['pending_setup_intent'],
    });

    const pendingSetupIntent = subscription.pending_setup_intent as Stripe.SetupIntent | null;

    let resData: StripeSubscriptionCreateRes = {
      success: true,
      subscriptionId: subscription.id,
      type: '',
      clientSecret: '',
    };

    if (pendingSetupIntent !== null) {
      resData.type = 'setup';
      resData.clientSecret = pendingSetupIntent.client_secret;
    } 
    return resData;
  } catch {
    return { success: false };
  }
};

前端验证逻辑优化

聚焦处理Setup Intent的3DS验证流程,确保用户提交卡信息时完成验证:

const PaymentContainer: FunctionComponent<PaymentContainerProps> = ({ type }) => {
  const stripe = useStripe();
  const stripeElements = useElements();

  const handleSubmit = async (event: FormEvent) => {
    event.preventDefault();

    if (!stripe || !stripeElements || type !== "setup") {
      return;
    }

    const confirmOptions = {
      elements: stripeElements,
      confirmParams: {
        return_url: `${window.location.origin}${buildRoute("ACCOUNT.WELCOMECOMPLETE")}`,
      },
    };

    const result = await stripe.confirmSetup(confirmOptions);

    if (result.error) {
      console.error('验证失败:', result.error.message);
      // 替换为用户友好的错误提示
    } else {
      // 验证完成后,Stripe会自动激活订阅,可直接跳转至成功页面
    }
  };

  return (
    <form onSubmit={handleSubmit}>
      <PaymentElement />
      <Button type="submit" disabled={!stripe}>
        提交并完成验证
      </Button>
    </form>
  );
};

补充说明

  • 试用期时长:Stripe的trial_period_days最小为1天,若需要12小时试用期,可使用trial_end参数设置时间戳(当前时间+43200秒);
  • 测试验证:使用测试卡4000000000003220可触发3DS验证,用于测试流程;
  • 订阅状态:用户完成验证后,订阅会从pending_incomplete变为active,试用期结束后自动扣款,无需用户再次操作。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 16:04:53