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

Stripe试用期后订阅支付失败:如何设置默认支付方式?

问题解决方案

核心问题

你当前的流程中,虽然通过confirmCardSetup将支付方式绑定到了客户,但未将该支付方式设置为客户的默认支付方式,导致试用期结束后订阅自动扣款时,Stripe找不到可用的支付方式,从而失败。

解决步骤

1. 前端修改:获取Payment Method ID并发送给后端

在stripe.confirmCardSetup成功回调中,提取绑定后的Payment Method ID,通过AJAX请求发送到后端,用于更新客户默认支付方式,同时补充错误状态下的按钮恢复逻辑:

var stripe = Stripe($('#public-key').val());
var elements = stripe.elements();
var style = {
   base: {
     color: "#32325d",
     }
   };

var card = elements.create("card", { style: style });
card.mount("#card-element");

//Capture modal payment button click
$(document).on('click', '#submit-payment-btn', function (e) {
e.preventDefault();
//Send an ajax call to backend to create a subscription
$(this).prop('disabled', true);
let spinnerHtml = '<div class="spinner-border text-light"></div>';
let origButtonHtml = $(this).html();
$(this).html(spinnerHtml);

$.ajax({
url: $(this).data('create-sub-url'),
type: 'POST',
data: {
  csrfmiddlewaretoken: $('[name="csrfmiddlewaretoken"]').val(),
  'price_id': $(this).data('price-id'),
  'coupon_id': $("#coupon_id").val()
},
success: function (result) {
  if(!result.clientSecret)
  {
      console.log("result not okay")
      window.location.href = '/'
  }
  else{
    const customerId = result.customerId; // 接收后端返回的客户ID
    $('#client-secret').val(result.clientSecret);
    console.log('set up confirmed');
    stripe.confirmCardSetup(result.clientSecret, {
      payment_method: {
        card: card,
        billing_details: {
          name: $('#cardholder-name').val(),
        },
      }
    }).then((setupResult) => {
      if (setupResult.error) {
        alert('Payment failed: ' + setupResult.error.message);
        // 恢复按钮状态
        $('#submit-payment-btn').html(origButtonHtml);
        $('#submit-payment-btn').prop('disabled', false);
      } else {
        // 获取绑定成功的Payment Method ID
        const paymentMethodId = setupResult.setupIntent.payment_method;
        // 发送请求更新默认支付方式
        $.ajax({
          url: '/update-customer-default-payment/',
          type: 'POST',
          data: {
            csrfmiddlewaretoken: $('[name="csrfmiddlewaretoken"]').val(),
            'payment_method_id': paymentMethodId,
            'customer_id': customerId
          },
          success: function() {
            window.location.href = '/'
          },
          error: function() {
            alert('Failed to set default payment method');
            $('#submit-payment-btn').html(origButtonHtml);
            $('#submit-payment-btn').prop('disabled', false);
          }
        });
      }
    });
  }
},
error: function (result){
  $('#submit-payment-btn').html(origButtonHtml);
  $('#submit-payment-btn').prop('disabled', false);
  if(result.responseJSON.invalid_coupon)
  {
    if($(".coupon-error").length == 0)
      $('.coupon-div').append('<p class="coupon-error text-center" style="color:red">Invalid coupon code</p>')
  }
  else{
    $('#payment-modal').modal('hide');
    alert('Something Went Wrong!')
  }
 }
 });
 })

2. 后端修改:返回客户ID并添加更新默认支付方式的视图

第一步:在SubscriptionCreateView中返回客户ID并完善SetupIntent配置

在post方法的返回context里添加customerId,同时给SetupIntent添加setup_future_usage参数(告知Stripe该支付方式用于离线自动扣款,是订阅到期扣款的必要配置):

class SubscriptionCreateView(View, SubscriptionCancellationMixin, 
                         CustomerMixin, StripeReferenceMixin, FetchCouponMixin):

"""View to Create subscription and sync with stripe"""
def post(self, request, *args, **kwargs):
    context = {}
    subscription_payload = {}
    price_id = request.POST.get('price_id')
    coupon_id = request.POST.get('coupon_id')
    customer = self.fetch_customer(request.user)

    #Retrieve stripe coupon id or None
    if coupon_id != '':
        stripe_coupon_id, is_valid = self.is_coupon_valid(coupon_id)
        #Set payload to create a subscription
        subscription_payload['coupon'] = stripe_coupon_id
        #Send invalid coupon response
        if not is_valid:
            context['invalid_coupon'] = True
            response = JsonResponse(context)
            response.status_code = 400
            return response

    if not customer:
        customer = self.stripe.Customer.create(
                email=request.user.email
        )
    try:
        now = int(time.time())
        # Cancel the previous subscription.
        self.cancel_subscription(customer)
        #Create a new subscription
        subscription_payload.update({
            'customer':customer.id,
            'items':  [{
                    'price': price_id,
                },
                ],  
            'trial_end': now +30,
            
        },
            )

        #create a setup intent
        setup_intent = self.stripe.SetupIntent.create(
             payment_method_types=["card"],
             customer=customer.id,
             setup_future_usage='off_session'  # 添加此行
        )
        subscription = self.stripe.Subscription.create(**subscription_payload)


        # Sync the Stripe API return data to the database,
        # this way we don't need to wait for a webhook-triggered sync
        Subscription.sync_from_stripe_data(subscription)
        request.session['created_subscription_id'] = subscription.get('id')

        # Note we're sending the Subscription's
        # latest invoice and client secret
        # to the front end to confirm the payment
        context['subscriptionId'] = subscription.id
        context['clientSecret'] = setup_intent['client_secret']
        context['customerId'] = customer.id  # 添加此行

    except Exception as e:
        response = JsonResponse({})
        response.status_code = 400
        return response

    return JsonResponse(context)

第二步:创建更新客户默认支付方式的视图

class UpdateCustomerDefaultPaymentView(View, CustomerMixin):
    """View to update customer's default payment method"""
    def post(self, request, *args, **kwargs):
        payment_method_id = request.POST.get('payment_method_id')
        customer_id = request.POST.get('customer_id')
        
        if not payment_method_id or not customer_id:
            return JsonResponse({'error': 'Missing required parameters'}, status=400)
        
        try:
            # 更新客户的默认支付方式(用于发票和订阅扣款)
            self.stripe.Customer.modify(
                customer_id,
                invoice_settings={
                    'default_payment_method': payment_method_id
                }
            )
            
            # 同时更新订阅的默认支付方式(双重保障)
            subscription_id = request.session.get('created_subscription_id')
            if subscription_id:
                self.stripe.Subscription.modify(
                    subscription_id,
                    default_payment_method=payment_method_id
                )
            
            return JsonResponse({'success': True})
        except Exception as e:
            return JsonResponse({'error': str(e)}, status=400)

第三步:添加路由

在项目的urls.py中添加对应路由:

from django.urls import path
from .views import SubscriptionCreateView, UpdateCustomerDefaultPaymentView

urlpatterns = [
    # ... 其他路由
    path('create-subscription/', SubscriptionCreateView.as_view(), name='create-subscription'),
    path('update-customer-default-payment/', UpdateCustomerDefaultPaymentView.as_view(), name='update-customer-default-payment'),
]

关键说明

  • setup_future_usage='off_session':必须设置此参数,否则Stripe不会允许该支付方式用于离线自动扣款(试用期结束后的扣款属于离线场景)。
  • 同时更新客户和订阅的默认支付方式:确保订阅在扣款时优先使用该支付方式,避免因配置不一致导致的失败。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 13:10:37