如何在Django Rest Framework中保存Stripe用户卡片信息用于后续交易
Stripe 存储用户支付信息实现免重复输卡方案(DRF 适配)
方案可行性
完全可行,但绝对不能自行存储用户银行卡信息(这会触发极其严格的PCI合规要求,操作成本极高且风险大)。正确的做法是利用Stripe的Customer对象托管用户支付方式:首次支付完成后,将用户的支付方式与专属的Stripe Customer绑定,后续交易直接调用已绑定的支付方式发起扣款,无需用户重复输入卡片信息,实现和Fiverr一致的体验。
前端需传入的信息
分两种业务场景:
首次支付(需绑定支付方式)
- 支付金额(单位为对应货币的最小单位,比如1美元传100)
- 货币类型(默认可设为
usd) - 必传:Stripe前端SDK生成的
payment_method_id(前端通过Stripe Elements收集卡信息后生成,禁止传递原始卡号、CVV等敏感数据) - 用户标识(登录状态下DRF可直接通过
request.user获取,匿名用户需前端传唯一标识)
后续支付(已绑定支付方式)
- 支付金额
- 货币类型
- 用户标识(用于关联对应的Stripe Customer)
- 可选:
payment_method_id(如果用户有多个绑定支付方式,需指定;否则默认使用用户的默认支付方式)
后端(DRF)响应内容
首次支付响应
{ "client_secret": "pi_xxx_secret_xxx", // 前端用于完成支付确认 "customer_id": "cus_xxx", // 绑定的Stripe Customer ID,可存入后端用户表 "payment_method_id": "pm_xxx", // 绑定的支付方式ID,可选存储 "payment_intent_id": "pi_xxx", "amount": 100 }
后续支付响应
{ "client_secret": "pi_xxx_secret_xxx", // 前端直接用此发起扣款,无需收集卡信息 "payment_intent_id": "pi_xxx", "amount": 200 }
DRF 改造后的核心代码示例
import stripe from django.conf import settings from rest_framework.response import Response from rest_framework.views import APIView from django.contrib.auth.models import User # 从配置文件读取密钥,不要硬编码 stripe.api_key = settings.STRIPE_SECRET_KEY class StripePaymentView(APIView): def post(self, request): user = request.user # 假设用户已登录 amount = request.data.get('amount') currency = request.data.get('currency', 'usd') payment_method_id = request.data.get('payment_method_id') # 首次支付:创建/关联Stripe Customer并绑定支付方式 if payment_method_id: # 检查用户是否已有绑定的Stripe Customer if hasattr(user, 'stripe_customer_id') and user.stripe_customer_id: customer = stripe.Customer.retrieve(user.stripe_customer_id) else: # 创建新的Stripe Customer customer = stripe.Customer.create(email=user.email) # 将Customer ID存入用户表(需给User模型添加stripe_customer_id字段) user.stripe_customer_id = customer.id user.save() # 绑定支付方式到Customer stripe.PaymentMethod.attach( payment_method_id, customer=customer.id, ) # 设置为默认支付方式(可选) stripe.Customer.modify( customer.id, invoice_settings={'default_payment_method': payment_method_id}, ) # 创建关联Customer的PaymentIntent intent = stripe.PaymentIntent.create( amount=amount, currency=currency, customer=customer.id, payment_method=payment_method_id, confirm=True, # 可选自动确认,需前端处理3DS验证 automatic_payment_methods={'enabled': True}, ) else: # 后续支付:使用已绑定的默认支付方式 if not hasattr(user, 'stripe_customer_id') or not user.stripe_customer_id: return Response({'error': '用户未绑定支付方式'}, status=400) intent = stripe.PaymentIntent.create( amount=amount, currency=currency, customer=user.stripe_customer_id, payment_method='default', # 使用默认支付方式 confirm=True, automatic_payment_methods={'enabled': True}, ) # 返回响应数据 response_data = { 'client_secret': intent['client_secret'], 'payment_intent_id': intent['id'], 'amount': intent['amount'], } if hasattr(user, 'stripe_customer_id'): response_data['customer_id'] = user.stripe_customer_id return Response(response_data)
关键注意事项
- 绝对禁止前端将原始卡号、CVV、过期日期等敏感信息传给后端,必须通过Stripe Elements或Stripe.js处理,仅传递
payment_method_id - 需在Django用户模型中添加
stripe_customer_id字段,用于关联Stripe的Customer对象 - 若支付触发3DS验证,前端需调用
stripe.confirmCardPayment完成验证流程 - 所有支付方式托管在Stripe,无需自行处理PCI合规
内容的提问来源于stack exchange,提问作者Mossaddak
相关产品推荐
相关产品推荐

