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

Stripe离线会话支付(试用订阅)3DS验证未触发致扣费失败求助

问题分析与修正方案

你的操作流程

1. 创建Customer和SetupIntent

public async Task<Stripe.Customer> CreateCustomer(Stripe.Customer stripeCustomer)
{
    var options = new CustomerCreateOptions
    {
        Email = stripeCustomer.Email,
        Name = stripeCustomer.Name,
        Phone = stripeCustomer.Phone,
    };

    return await _customerService.CreateAsync(options);
}

public async Task<SetupIntent> CreateSetupIntent(string customerId)
{
    var setupIntentOptions = new SetupIntentCreateOptions
    {
        Customer = customerId,
        PaymentMethodTypes = new List<string>
        {
            "card"
        },
        Usage = "off_session"
    };

    var setupIntent = await _setupIntentService.CreateAsync(setupIntentOptions);

    return setupIntent;
}

2. Stripe JS关联卡片到SetupIntent

const stripeCardElement: StripeCardElement = this.card!.element;

this.stripeService.confirmCardSetup('seti_*****', {
  payment_method: {
    card: stripeCardElement,
    billing_details: {
      name: 'Jenny Rosen',
    }
  },
});

3. 创建带试用期的订阅

var optionsSubscription = new SubscriptionCreateOptions
{
    Customer = customerId,
    ProrationBehavior = "none",
    Items = new List<SubscriptionItemOptions>
    {
        new SubscriptionItemOptions
        {
            Price = priceId
        }
    },                   
    DefaultPaymentMethod = "pm_******",
    CollectionMethod = "charge_automatically",
    PaymentBehavior = "allow_incomplete",
    Currency = "usd",
    TrialEnd = DateTime.Now.AddSeconds(30),
    OffSession = true,
};

optionsSubscription.AddExpand(SubscriptonCreateOptionExpand);

var newSubscription = await _subscriptionService.CreateAsync(optionsSubscription);

核心错误点

  1. SetupIntent未强制触发SCA验证
    你使用的「3DS Required」测试卡属于需要强客户认证(SCA)的场景,但创建SetupIntent时未明确要求在卡片绑定阶段完成3DS验证,导致保存的PaymentMethod未通过SCA认证,后续off_session自动扣费会直接被拒绝。

  2. confirmCardSetup未处理requires_action分支
    调用confirmCardSetup后,若返回的SetupIntent状态为requires_action,说明需要弹出3DS验证窗口,但你的代码未处理该逻辑,直接跳过了验证步骤,导致PaymentMethod处于未认证状态。

  3. OffSession支付的逻辑矛盾
    订阅创建时设置OffSession=true,但未确保PaymentMethod已完成SCA认证。Stripe在off_session场景下无法主动触发客户交互的3DS验证,因此直接拒绝扣费。


修正方案

1. 修改SetupIntent创建逻辑,强制触发3DS验证

添加RequestThreeDSecure="automatic",确保需要SCA的卡片在绑定阶段完成验证:

public async Task<SetupIntent> CreateSetupIntent(string customerId)
{
    var setupIntentOptions = new SetupIntentCreateOptions
    {
        Customer = customerId,
        PaymentMethodTypes = new List<string> { "card" },
        Usage = "off_session",
        PaymentMethodOptions = new SetupIntentPaymentMethodOptions
        {
            Card = new SetupIntentPaymentMethodOptionsCard
            {
                RequestThreeDSecure = "automatic"
            }
        }
    };

    return await _setupIntentService.CreateAsync(setupIntentOptions);
}

2. 完整处理confirmCardSetup的返回结果

必须处理requires_action状态,完成3DS验证流程后再创建订阅:

this.stripeService.confirmCardSetup('seti_*****', {
  payment_method: {
    card: stripeCardElement,
    billing_details: { name: 'Jenny Rosen' }
  }
}).then((result) => {
  if (result.error) {
    // 处理绑定错误
    return;
  }

  const setupIntent = result.setupIntent;
  if (setupIntent.status === 'succeeded') {
    // 验证完成,调用后端创建订阅
    createSubscription(setupIntent.payment_method);
  } else if (setupIntent.status === 'requires_action') {
    // 触发3DS验证弹窗
    return this.stripeService.handleCardSetup(setupIntent.client_secret);
  }
}).then((verificationResult) => {
  if (verificationResult?.setupIntent.status === 'succeeded') {
    // 验证通过,创建订阅
    createSubscription(verificationResult.setupIntent.payment_method);
  }
});

3. 订阅创建的前置校验

仅在SetupIntent状态为succeeded(即PaymentMethod完成SCA认证)后,再调用后端创建订阅,确保后续自动扣费符合SCA要求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 10:52:17