不能直接在Django视图里调用Stripe或支付宝支付接口,因为前端需生成支付凭证(如client_secret或pay_order),而敏感操作必须由服务端完成且密钥绝不能暴露给浏览器;正确做法是后端生成一次性凭据供前端消费,密钥须从环境变量读取,Webhook验证须使用原始body字节流并严格校验签名。

为什么不能直接在Django视图里调用Stripe或支付宝的支付接口
因为前端需要生成支付凭证(如Stripe的client_secret或支付宝的pay_order),而这些敏感操作必须在服务端完成,但又不能把密钥暴露给浏览器。常见错误是把stripe.api_key或支付宝的app_private_key写在JS里,结果被爬虫一键提取——这等于把银行卡密码贴在玻璃门上。
正确做法是:Django后端只负责生成一次性支付凭据,前端用Stripe.js或alipay-sdk-js消费它。后端不处理跳转、不渲染支付页、不接收异步通知(那是Webhook的事)。
- Stripe:用
stripe.PaymentIntent.create()生成client_secret,返回给前端调用stripe.confirmCardPayment() - 支付宝:用
alipay.api_alipay_trade_page_pay()生成跳转URL,或用alipay.api_alipay_trade_precreate()生成二维码(扫码支付场景) - 密钥必须从环境变量读取:
os.getenv('STRIPE_SECRET_KEY'),绝不能硬编码
Django中处理Stripe Webhook时400错误的三个原因
Stripe Webhook endpoint返回400,90%不是代码逻辑错,而是签名验证失败。Django默认会解析request.body为JSON,但Stripe签名基于原始字节流,一旦被解析就破坏了哈希值。
- 必须用
request.body原始二进制数据做签名验证,不能先json.loads(request.body) - 确保
STRIPE_WEBHOOK_SECRET和Stripe后台配置完全一致(注意末尾换行符) - View必须用
@csrf_exempt,且Content-Type头必须是application/json(Stripe固定发送这个)
示例验证逻辑:
提供使用 django-unfold (v0.56+) 构建高级 Django 管理界面的专业指导。当用户提及 django-unfold、unfold admin 等内容时使用此技能。
立即学习“Python免费学习笔记(深入)”;
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:
return HttpResponse(status=400)
except stripe.error.SignatureVerificationError:
return HttpResponse(status=400)
支付宝同步回调(return_url)和异步回调(notify_url)的区别与陷阱
支付宝文档里这两个URL都叫“回调”,但行为完全不同:return_url是用户付款后浏览器跳转回的地址,可能被用户手动刷新、F5重发;notify_url是支付宝服务器POST推送,只发一次,且要求200响应,否则持续重试。
-
return_url仅用于展示成功页,**不能更新订单状态**(用户关掉页面就丢失了) -
notify_url才是唯一可信的支付结果来源,必须校验sign参数,且只处理trade_status为TRADE_SUCCESS或TRADE_FINISHED的请求 - Django接收
notify_url时要用request.POST.dict()而非request.POST,因为支付宝POST的是表单编码(application/x-www-form-urlencoded),不是JSON
本地调试Webhook最省事的方法
Stripe和支付宝都不允许localhost地址注册Webhook,但你不需要部署到公网就能测通。Stripe官方推荐用stripe-cli,支付宝可用ngrok,但更轻量的做法是临时改Django路由。
- Stripe:运行
stripe listen --forward-to http://localhost:8000/webhook/,它会把事件转发到你的本地URL,并自动注入签名头 - 支付宝:用
ngrok http 8000获取临时域名,填入沙箱后台;测试完立刻删掉,避免密钥泄露 - 关键点:本地Webhook View开头加
if settings.DEBUG:绕过部分签名校验(仅限开发),但正式环境必须严格校验
别信“用Postman模拟Webhook请求”这种说法——少了原始body、少了正确header、少了时间戳防重放,模拟了也白模拟。


















