如何在Django中将Stripe Webhook数据保存到会话变量?
问题分析
你的核心问题在于:Stripe Webhook的请求是服务器与服务器之间的调用,它使用的会话和用户浏览器的会话完全无关。你在stripe_webhook视图中存入request.session['total_paid'],这个会话属于Stripe的请求,而非用户的浏览器会话,因此在用户访问的SuccessView中根本获取不到这个值,自然返回None。
解决方案:将支付总额持久化到Order模型
最可靠的方式是把包含税费的总额直接关联到对应的Order对象,而非依赖会话传递。步骤如下:
1. 给Order模型添加支付总额字段
在models.py中为Order模型新增存储支付总额的字段:
class Order(models.Model): # 原有字段保留... total_paid = models.DecimalField(max_digits=10, decimal_places=2, null=True, blank=True)
执行数据库迁移:
python manage.py makemigrations python manage.py migrate
2. 创建Checkout Session时传入订单ID到metadata
在你生成Stripe Checkout Session的代码中,将订单ID存入metadata,让Webhook能通过该ID定位到对应订单:
# 示例:生成Checkout Session的代码片段 session = stripe.checkout.Session.create( payment_method_types=['card'], line_items=[...], # 你的商品配置 mode='payment', success_url=YOUR_SUCCESS_URL, cancel_url=YOUR_CANCEL_URL, metadata={ 'order_id': str(order.id) # 把当前订单ID存入metadata } )
3. 修改Webhook视图更新Order的支付总额
在stripe_webhook中,通过metadata获取订单ID,更新对应Order的total_paid字段:
def stripe_webhook(request): payload = request.body sig_header = request.META['HTTP_STRIPE_SIGNATURE'] event = None try: event = stripe.Webhook.construct_event( payload, sig_header, settings.STRIPE_WEBHOOK_SECRET ) except ValueError as e: return HttpResponse(status=400) except stripe.error.SignatureVerificationError as e: return HttpResponse(status=400) if event['type'] == 'checkout.session.completed': session = event['data']['object'] # 从metadata提取订单ID order_id = session['metadata']['order_id'] order = Order.objects.get(id=order_id) line_items = stripe.checkout.Session.list_line_items(session["id"]) total_paid_cents = line_items["data"][0]["amount_total"] total_paid_dollars = total_paid_cents / 100 # 更新并保存订单的支付总额 order.total_paid = total_paid_dollars order.save() return HttpResponse(status=200)
4. 修改SuccessView直接从Order获取总额
不再依赖会话变量,直接从Order对象中读取已持久化的总额:
class SuccessView(TemplateView): template_name = "musicstudios/success.html" extra_context = sidebar_context def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) order_id = self.request.session.get('order_id') order = Order.objects.get(pk=order_id) # 直接通过关联关系获取关联对象,减少数据库查询 context['customer'] = order.customer context['product'] = order.product context['order'] = order # 从订单中读取支付总额 context['order_total'] = order.total_paid return context
5. 模板无需修改
你的success.html中${{ order_total }}的写法保持不变即可正常显示。
其他可选方案(不推荐)
- 通过Success URL查询参数传递session_id:在success_url中追加
?session_id={CHECKOUT_SESSION_ID},在SuccessView中用该ID调用Stripe API获取总额。但此方式需额外API调用,且存在参数被篡改的风险。 - 用缓存临时存储:以order_id为键,将总额存入Django缓存(如Redis),SuccessView中根据order_id读取。但持久化到数据库仍是最稳妥的方案。
内容的提问来源于stack exchange,提问作者Lewis F
相关产品推荐
相关产品推荐

