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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 23:05:27