Django接收Stripe订阅扣费失败webhook时出现MultiValueDictKeyError报错
报错原因
从回溯信息可以定位到错误出现在zebra/views.py第38行,代码直接从request.POST中读取request_data字段,而当前Stripe发送的webhook请求不会携带该表单字段,因此触发MultiValueDictKeyError异常。
你使用的django-zebra库已经多年停止维护,适配的是非常早期的Stripe API版本,和当前Stripe webhook的请求格式不兼容。
解决方案
方案1:替换为自定义Stripe webhook视图(更稳定,推荐)
直接废弃django-zebra的内置webhook视图,自己实现逻辑适配当前Stripe的请求格式,示例代码如下:
import json import stripe from django.conf import settings from django.http import HttpResponse from django.views.decorators.csrf import csrf_exempt from django.views.decorators.http import require_POST stripe.api_key = settings.STRIPE_SECRET_KEY webhook_secret = settings.STRIPE_WEBHOOK_SECRET @require_POST @csrf_exempt def stripe_webhook(request): payload = request.body sig_header = request.META.get('HTTP_STRIPE_SIGNATURE') event = None try: event = stripe.Webhook.construct_event( payload, sig_header, webhook_secret ) except ValueError: # 无效payload return HttpResponse(status=400) except stripe.error.SignatureVerificationError: # 签名验证失败 return HttpResponse(status=403) # 处理订阅扣费失败事件 if event['type'] == 'invoice.payment_failed': payment_failed_event = event['data']['object'] # 此处写入你的业务逻辑,例如给用户发送通知、标记订阅失效状态等 pass return HttpResponse(status=200)
修改urls.py里的webhook路由,指向你自己实现的这个视图即可。
方案2:临时修复django-zebra源码(仅应急使用,不推荐)
找到报错的zebra/views.py文件,把第38行的取值逻辑修改为直接读取请求体:
# 注释原有错误逻辑 # request_data = json.loads(request.POST["request_data"]) # 替换为以下代码 request_data = json.loads(request.body)
该方案属于第三方库源码魔改,后续升级库时修改会被覆盖,仅适合临时应急使用。
注意事项
- 生产环境必须开启Stripe webhook签名校验,避免伪造请求触发业务逻辑
- 处理webhook事件时不要加入过长的同步逻辑,Stripe有请求超时限制,超时会触发重复回调
内容的提问来源于stack exchange,提问作者Aditya Kumar
相关产品推荐
相关产品推荐

