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
相关产品推荐
相关产品推荐

