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

Django Stripe支付视图报错:InvalidRequestError,需提供source或customer

解决Stripe InvalidRequestError:Must provide source or customer

嘿,这个错误我太熟悉了!你遇到的问题核心是:Stripe创建Charge(扣款)时,必须指定source(支付令牌)或者customer(已保存的客户ID),而你复制的教程代码大概率漏了这个关键参数。

错误原因拆解

Stripe的安全机制不允许后端直接处理用户的银行卡信息,必须通过前端Stripe.js生成一个临时的支付令牌(stripeToken),然后把这个令牌传给后端,后端用它来发起扣款。你的代码里没有接收这个令牌,也没在Charge.create里传入source参数,所以才会触发这个错误。

修复步骤&代码示例

1. 补全后端视图代码

修改你的pay视图,确保接收前端传来的stripeToken,并在创建Charge时传入这个关键参数:

import stripe
from django.shortcuts import get_object_or_404, render
from .models import AdvertisePost

# 替换成你的Stripe测试密钥(生产环境要换成live密钥)
stripe.api_key = "sk_test_xxxxxxxxxxxxxx"

def pay(request, context):
    ad = get_object_or_404(AdvertisePost, hash=context['hash'])
    # 转换为最小货币单位(比如美元是分),必须是整数
    amount = int(ad.total_price * 100)

    if request.method == 'POST':
        # 获取前端提交的Stripe支付令牌
        stripe_token = request.POST.get('stripeToken')
        
        try:
            # 核心:这里必须传入source参数(就是前端生成的令牌)
            charge = stripe.Charge.create(
                amount=amount,
                currency='usd',  # 根据你的业务调整货币,比如'cny'
                description=f"广告支付:{ad.title}",
                source=stripe_token,
            )
            # 支付成功后的逻辑:比如标记广告为已支付
            ad.paid = True
            ad.save()
            return render(request, 'payment_success.html')
        
        # 捕获Stripe的各类错误,返回给用户友好提示
        except stripe.error.InvalidRequestError as e:
            return render(request, 'payment_error.html', {'error': f"请求错误:{str(e)}"})
        except stripe.error.StripeError as e:
            return render(request, 'payment_error.html', {'error': f"支付失败:{str(e)}"})
    
    # GET请求时,渲染支付页面,把Stripe公钥传给前端
    return render(request, 'payment.html', {
        'ad': ad,
        'amount': amount,
        # 替换成你的Stripe测试公钥
        'stripe_public_key': "pk_test_xxxxxxxxxxxxxx"
    })

2. 前端添加Stripe支付表单

你需要在前端页面(比如payment.html)中用Stripe.js生成支付令牌,再提交给后端:

<form action="{% url 'pay' hash=ad.hash %}" method="POST">
  {% csrf_token %}
  <!-- 引入Stripe.js -->
  <script src="https://js.stripe.com/v3/"></script>
  
  <!-- 卡片输入区域 -->
  <div id="card-element" style="border:1px solid #ccc; padding:10px; border-radius:4px;"></div>
  <!-- 错误提示区域 -->
  <div id="card-errors" role="alert" style="color:red; margin-top:10px;"></div>
  
  <button type="submit" style="margin-top:20px; padding:8px 16px;">支付 {{ amount|div:100 }} 美元</button>
</form>

<script>
  // 初始化Stripe实例
  const stripe = Stripe('{{ stripe_public_key }}');
  const elements = stripe.elements();

  // 创建卡片输入组件
  const cardElement = elements.create('card');
  cardElement.mount('#card-element');

  // 监听卡片输入错误
  cardElement.addEventListener('change', (event) => {
    const errorElement = document.getElementById('card-errors');
    if (event.error) {
      errorElement.textContent = event.error.message;
    } else {
      errorElement.textContent = '';
    }
  });

  // 表单提交时生成令牌并提交
  const form = document.querySelector('form');
  form.addEventListener('submit', (event) => {
    event.preventDefault();

    stripe.createToken(cardElement).then((result) => {
      if (result.error) {
        const errorElement = document.getElementById('card-errors');
        errorElement.textContent = result.error.message;
      } else {
        // 把令牌隐藏在表单里提交给后端
        const hiddenInput = document.createElement('input');
        hiddenInput.setAttribute('type', 'hidden');
        hiddenInput.setAttribute('name', 'stripeToken');
        hiddenInput.setAttribute('value', result.token.id);
        form.appendChild(hiddenInput);

        // 提交表单
        form.submit();
      }
    });
  });
</script>

关键注意事项

  • 测试阶段用Stripe的测试密钥/公钥,上线前务必切换为生产环境的live密钥
  • 金额必须是整数,因为Stripe使用最小货币单位(比如1美元=100分)
  • 绝对不要在后端处理用户的原始银行卡信息,必须通过Stripe生成的令牌完成支付流程

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 03:46:07