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

Django中使用Stripe实现三方支付分账的问题求助

Django + Stripe 分账问题:资金不足&卖家未收款的解决方案

核心问题分析

  • 当前代码在循环中创建了两个独立的Checkout Session,每个都要求买家支付全额total_charges。实际场景中买家只会完成一次支付,导致第二个Session对应的Transfer因无资金来源触发insufficient funds错误。
  • 卖家未收款是因为买家大概率只完成了推荐人对应的Session支付,卖家的Session未被触发支付,自然没有转账。

最优解决方案:使用Stripe Connect 分账模式

针对三部分拆分需求(平台手续费、卖家收款、推荐人收款),推荐采用Separate Charges and Transfers模式:买家支付全额到平台账户,支付成功后,平台通过异步事件分别向卖家和推荐人发起转账。这种模式避免了重复创建支付会话的问题,且流程更可靠。

修正后的前端支付代码

if buyer_address.state.lower() in ['alabama', 'al']:
    taxes = int(round(float(price * 0.08)))
else:
    taxes = 0  # 补充其他州的默认税值
shipping_charges = courier.price
total_charges = price + taxes

# 重新计算分账金额:确保总和等于total_charges
seller_amount = int(total_charges * 0.8)  # 对应原逻辑中平台抽20%后的卖家所得
referral_amount = int(total_charges * 0.004)  # 原逻辑中推荐人拿平台手续费的2%,即总金额的0.4%
admin_amount = total_charges - seller_amount - referral_amount

stripe.api_key = "sk_test_EI5Kddsg2MCELde0vbX2cDGw"
my_domain = "your-domain.com"  # 替换为实际业务域名

try:
    # 仅创建一个Checkout Session,收取全额款项
    session = stripe.checkout.Session.create(
        success_url=f"{my_domain}/payment_success/{auction_id}/{courier_id}?mode={mode}&session_id={{CHECKOUT_SESSION_ID}}",
        cancel_url=f"{my_domain}/payment_failed/{auction_id}/{courier_id}?mode={mode}",
        payment_method_types=['card'],
        mode='payment',
        line_items=[{
            'price_data': {
                'currency': 'usd',
                'product_data': {
                    'name': 'NailSent Nails',
                },
                'unit_amount': total_charges,  # 单位:分
            },
            'quantity': 1,
        }],
        payment_intent_data={
            'transfer_group': f"auction_{auction_id}"  # 分组标识,用于关联后续转账
        }
    )

    # 跳转到Stripe支付页面
    return redirect(session.url, code=303)

except stripe.error.StripeError as e:
    return HttpResponse(f"Error: {e}")

关键补充:Webhook异步处理转账

必须通过Stripe Webhook监听支付成功事件,在事件触发时执行转账(同步创建转账易因网络问题失败,Webhook更可靠):

# views.py 中的Webhook处理视图
import stripe
from django.http import HttpResponse
from django.views.decorators.http import require_POST
from django.views.decorators.csrf import csrf_exempt

@csrf_exempt
@require_POST
def stripe_webhook(request):
    stripe.api_key = "sk_test_EI5Kddsg2MCELde0vbX2cDGw"
    endpoint_secret = "your_webhook_secret"  # 从Stripe Dashboard获取签名密钥

    payload = request.body
    sig_header = request.META.get('HTTP_STRIPE_SIGNATURE')
    event = None

    try:
        event = stripe.Webhook.construct_event(
            payload, sig_header, endpoint_secret
        )
    except (ValueError, stripe.error.SignatureVerificationError):
        return HttpResponse(status=400)

    # 处理支付成功事件
    if event['type'] == 'payment_intent.succeeded':
        payment_intent = event['data']['object']
        transfer_group = payment_intent['transfer_group']
        # 从分组标识解析拍卖ID,从数据库读取对应分账金额
        auction_id = transfer_group.split('_')[1]
        # 替换为实际数据库查询逻辑,获取该订单的seller_amount和referral_amount
        seller_amount = ... 
        referral_amount = ...

        # 转账给卖家
        stripe.Transfer.create(
            amount=seller_amount,
            currency='usd',
            destination='acct_1OoODQIbLDChIvG2',
            transfer_group=transfer_group,
        )

        # 转账给推荐人
        stripe.Transfer.create(
            amount=referral_amount,
            currency='usd',
            destination='acct_1OrzPRI5g3KKKWKv',
            transfer_group=transfer_group,
        )

    return HttpResponse(status=200)

额外注意事项

  • 金额校验:确保seller_amount + referral_amount + admin_amount严格等于total_charges,避免分账金额总和超出收款总额。
  • 账户状态:卖家和推荐人的Connected Account必须完成Stripe入驻流程(onboarding),否则转账会失败。
  • Webhook配置:在Stripe Dashboard中配置Webhook端点,启用payment_intent.succeeded事件,并使用签名密钥验证事件真实性。
  • 测试验证:使用Stripe测试卡号(如4242 4242 4242 4242)在测试环境中验证分账流程,确认资金流向正确。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 00:53:20