基于Django/Python构建多订阅Stripe支付通道的技术咨询
解决方案:一次性结算混合商品+多频次独立订阅的Stripe实现
核心问题说明
Stripe Checkout不支持在同一个会话中添加不同计费频次的多个订阅(如月付和年付订阅无法共存于一个Checkout Session),因此需要采用自定义支付流程结合批量订阅创建的方案,既能实现一次性结算,又能保证每个订阅独立可取消。
推荐API方案:Setup Intent + Payment Intent + 独立订阅创建
通过以下组合实现需求:
- Setup Intent:安全收集并保存客户支付方式到客户对象
- Payment Intent:一次性收取所有商品(单次购买+订阅初始费用)的总金额
- Subscription API:为每个订阅产品单独创建独立订阅,关联同一支付方式
开发流程大纲(Django/Python)
1. 前置校验
确保已完成:
- 客户对象的创建/检索(绑定当前Django用户)
- 所有商品(单次+订阅)的
price_id已生成并可用 - 确认所有金额的货币单位统一(如人民币分、美元分)
2. 前端收集支付方式
- 在Django模板中引入Stripe.js,初始化Stripe Elements组件用于支付信息输入
- 后端创建Setup Intent,传递
client_secret给前端:import stripe stripe.api_key = "你的Stripe秘钥" def create_setup_intent(request): customer_id = request.user.stripe_customer_id # 假设已关联 setup_intent = stripe.SetupIntent.create( customer=customer_id, payment_method_types=["card"], ) return JsonResponse({"client_secret": setup_intent.client_secret}) - 前端通过Stripe Elements完成支付方式确认,成功后获取
payment_method_id并传递给后端
3. 计算一次性结算总金额
- 单次购买商品金额:通过
stripe.Price.retrieve(one_time_price_id).unit_amount获取 - 每个订阅的初始费用:获取对应
price_id的首月/首期金额(含设置费、优惠等) - 总金额 = 单次商品金额 + 所有订阅首期费用总和(单位为货币最小单位,如分)
4. 创建并确认Payment Intent收取费用
- 后端创建并确认Payment Intent,完成一次性扣费:
def process_payment(request): customer_id = request.user.stripe_customer_id payment_method_id = request.POST.get("payment_method_id") total_amount = 计算好的总金额(如1000表示10元) payment_intent = stripe.PaymentIntent.create( amount=total_amount, currency="cn", customer=customer_id, payment_method=payment_method_id, confirm=True, return_url=f"{request.scheme}://{request.get_host()}/payment-success/", ) if payment_intent.status == "succeeded": # 继续创建订阅 return JsonResponse({"status": "success", "payment_intent_id": payment_intent.id}) else: return JsonResponse({"status": "failed", "error": payment_intent.last_payment_error.message})
5. 批量创建独立订阅
支付成功后,为每个订阅产品单独创建Subscription:
def create_subscriptions(request): customer_id = request.user.stripe_customer_id payment_method_id = request.POST.get("payment_method_id") subscription_price_ids = request.POST.getlist("subscription_price_ids") # 多个订阅的price_id列表 created_subscriptions = [] for price_id in subscription_price_ids: subscription = stripe.Subscription.create( customer=customer_id, items=[{"price": price_id}], default_payment_method=payment_method_id, proration_behavior="create_prorations", # 根据业务需求调整 ) created_subscriptions.append(subscription.id) return JsonResponse({"subscriptions": created_subscriptions})
每个订阅为独立对象,后续可单独调用stripe.Subscription.cancel(subscription_id)取消,不影响其他订阅。
6. 订阅管理前端实现
- 通过
stripe.Subscription.list(customer=customer_id)获取客户所有订阅 - 为每个订阅展示详情(产品名称、频次、金额)及取消按钮
- 点击取消按钮时,后端调用Stripe API取消对应订阅即可
关键注意事项
- 错误回滚:若Payment Intent成功但部分订阅创建失败,需处理退款逻辑或重新创建订阅
- 支付方式默认设置:确保保存的支付方式设为客户默认支付方式,方便后续订阅自动扣费
- 合规处理:遵循Stripe的PCI合规要求,避免直接处理信用卡信息
内容的提问来源于stack exchange,提问作者Ahima
相关产品推荐
相关产品推荐

