如何在DRF后端基于Stripe实现客户间可变金额自动付款
核心方案概述
你的场景是客户间可变金额的自定义周期转账(收款方为其他客户而非平台),Stripe订阅服务不适用,需采用Stripe Connect + Setup Intents + Payment Intents + 后端定时任务的组合方案,以下是具体实现步骤和代码片段:
1. 前期准备
- 注册Stripe Connect平台账户并启用转账功能
- 在DRF项目中安装Stripe SDK:
pip install stripe - 在
settings.py中配置密钥:STRIPE_SECRET_KEY = "你的Stripe秘钥" STRIPE_PUBLISHABLE_KEY = "你的Stripe公钥" STRIPE_WEBHOOK_SECRET = "你的Webhook秘钥"
2. 为收款客户创建Stripe Connect账户
每个收款用户需要拥有独立的Stripe Express账户,用于接收转账:
# views.py import stripe from django.conf import settings from rest_framework.response import Response from rest_framework.views import APIView from django.contrib.auth import get_user_model User = get_user_model() stripe.api_key = settings.STRIPE_SECRET_KEY class CreateConnectAccountView(APIView): def post(self, request): user = request.user # 创建Express账户 account = stripe.Account.create( type="express", country="CN", # 根据业务地区调整 email=user.email, business_type="individual", individual={ "first_name": user.first_name, "last_name": user.last_name, }, capabilities={"transfers": {"requested": True}}, ) # 生成账户验证链接,引导用户完成身份认证 account_link = stripe.AccountLink.create( account=account.id, refresh_url="https://你的域名/account-refresh", return_url="https://你的域名/account-success", type="account_onboarding", ) # 将Stripe账户ID保存到本地用户模型 user.stripe_connect_account_id = account.id user.save() return Response({"verify_url": account_link.url})
3. 付款客户绑定支付方式(Setup Intent)
让付款用户预先绑定银行卡,后续无需重复授权:
class SetupPaymentMethodView(APIView): def post(self, request): user = request.user # 若用户未创建Stripe Customer,先创建 if not user.stripe_customer_id: customer = stripe.Customer.create(email=user.email) user.stripe_customer_id = customer.id user.save() # 创建Setup Intent setup_intent = stripe.SetupIntent.create( customer=user.stripe_customer_id, payment_method_types=["card"], ) # 返回client_secret给前端,用于Stripe.js完成绑定 return Response({"client_secret": setup_intent.client_secret})
前端通过Stripe.js调用stripe.confirmCardSetup完成支付方式绑定,绑定成功后将payment_method_id保存到本地数据库。
4. 定义付款计划模型
在数据库中存储用户的付款规则:
# models.py from django.db import models from django.contrib.auth import get_user_model User = get_user_model() class PaymentSchedule(models.Model): ACTIVE = "active" CANCELLED = "cancelled" STATUS_CHOICES = [(ACTIVE, "生效中"), (CANCELLED, "已取消")] payer = models.ForeignKey(User, related_name="initiated_plans", on_delete=models.CASCADE) payee = models.ForeignKey(User, related_name="received_plans", on_delete=models.CASCADE) amount = models.DecimalField(max_digits=10, decimal_places=2) # 单位:元 frequency = models.CharField(max_length=20, null=True, blank=True) # 示例:"15_days", "monthly", "one_time" next_payment_date = models.DateField() status = models.CharField(max_length=20, choices=STATUS_CHOICES, default=ACTIVE) stripe_payment_method_id = models.CharField(max_length=100) # 付款方绑定的支付方式ID
5. 定时触发付款(Celery示例)
每天扫描付款计划,发起符合条件的转账:
# tasks.py import stripe from django.conf import settings from django.utils import timezone from .models import PaymentSchedule from dateutil.relativedelta import relativedelta stripe.api_key = settings.STRIPE_SECRET_KEY def process_scheduled_payments(): today = timezone.now().date() # 筛选今日需执行的生效计划 schedules = PaymentSchedule.objects.filter( next_payment_date__lte=today, status=PaymentSchedule.ACTIVE ) for schedule in schedules: amount_cents = int(schedule.amount * 100) # 转换为分(Stripe最小货币单位) try: # 发起转账Payment Intent payment_intent = stripe.PaymentIntent.create( amount=amount_cents, currency="cny", customer=schedule.payer.stripe_customer_id, payment_method=schedule.stripe_payment_method_id, confirm=True, metadata={"schedule_id": schedule.id}, # 关联本地计划ID,用于Webhook处理 transfer_data={ "destination": schedule.payee.stripe_connect_account_id, }, # 可选:平台收取手续费 # application_fee_amount=int(amount_cents * 0.02) ) # 更新下一次付款日期 if schedule.frequency == "15_days": schedule.next_payment_date += timezone.timedelta(days=15) elif schedule.frequency == "monthly": schedule.next_payment_date += relativedelta(months=1) schedule.save() except stripe.error.StripeError as e: # 记录失败日志,通知用户 print(f"计划{schedule.id}付款失败:{e.user_message}")
可通过Celery配置每日定时任务执行process_scheduled_payments,或使用Apscheduler替代。
6. Webhook处理付款状态
监听Stripe事件,同步本地记录状态:
# views.py class StripeWebhookView(APIView): authentication_classes = [] permission_classes = [] def post(self, request): payload = request.body sig_header = request.META.get("HTTP_STRIPE_SIGNATURE") try: event = stripe.Webhook.construct_event( payload, sig_header, settings.STRIPE_WEBHOOK_SECRET ) except (ValueError, stripe.error.SignatureVerificationError): return Response(status=400) # 处理付款成功事件 if event["type"] == "payment_intent.succeeded": payment_intent = event["data"]["object"] schedule_id = payment_intent["metadata"]["schedule_id"] # 记录付款成功日志、通知双方用户 pass # 处理付款失败事件 elif event["type"] == "payment_intent.failed": payment_intent = event["data"]["object"] schedule_id = payment_intent["metadata"]["schedule_id"] # 通知付款用户失败原因、标记计划状态 pass return Response(status=200)
关键注意事项
- 所有金额需转换为对应货币的最小单位(如人民币用分)
- 收款用户必须完成Stripe Connect账户的身份验证,否则无法接收转账
- 需为付款失败场景添加重试机制和用户通知逻辑
- 确保业务符合Stripe Connect的合规条款
内容的提问来源于stack exchange,提问作者stackuser
相关产品推荐
相关产品推荐

