Django部署Vercel后Stripe Webhook签名验证失败求助
Django + React部署Vercel后Stripe Webhook签名验证失败
问题背景
我开发了一个Django+React应用,已在Django中集成Stripe支付服务,React构建产物配置为Django静态资源。本地通过Stripe CLI测试时,Webhook功能完全正常,但将Django项目部署到Vercel后,在Stripe测试后台创建Webhook端点处理事件时,持续出现签名验证错误。已确认新端点对应的webhook_secret_key配置正确,排除密钥不匹配的问题。
已尝试的无效解决方案
- 使用
request.body.decode('utf-8')作为验证用的payload - 使用
request.data作为验证用的payload
两种方法均无法通过签名验证。
核心代码(本地正常,部署后报错)
from django.conf import settings from django.http import HttpResponse from rest_framework.views import APIView from rest_framework.response import Response from rest_framework import status from .models import DepositHistory from django.views.decorators.csrf import csrf_exempt from decimal import Decimal import stripe from rest_framework.decorators import api_view from usersAuth.models import AppUser stripe.api_key = settings.STRIPE_SECRET_KEY class StripeCheckoutView(APIView): def post(self, request): if self.request.user.is_authenticated: try: checkout_session = stripe.checkout.Session.create( line_items=[ { 'price': 'price_1PY4d6GKlfpQfnx9EFKpod75', 'quantity': 1, }, ], metadata={"user": f'{self.request.user}'}, mode='payment', success_url=settings.SITE_URL + '/?success=true&session_id={CHECKOUT_SESSION_ID}', cancel_url=settings.SITE_URL + '/?canceled=true', ) return Response({"url": checkout_session.url}) except: return Response( {'error': 'Something went wrong when creating stripe checkout session'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR ) return Response({'error': 'You must be logged in to pay'}, status=status.HTTP_401_UNAUTHORIZED) @csrf_exempt @api_view(['POST']) def stripe_webhook_view(request): payload = request.body sig_header = request.META['HTTP_STRIPE_SIGNATURE'] event = None try: event = stripe.Webhook.construct_event( payload, sig_header, settings.STRIPE_SECRET_WEBHOOK ) except ValueError as e: # Invalid payload #print('Error parsing payload: {}'.format(str(e))) return HttpResponse({'Error parsing payload'},status=400) except stripe.error.SignatureVerificationError as e: # Invalid signature #print('Error verifying webhook signature: {}'.format(str(e))) return HttpResponse({'Error verifying webhook signature'},status=400) if event['type'] == 'checkout.session.completed': print('we are in checkout.session.completed') session = event['data']['object'] user = session.metadata.user amount = Decimal(session.amount_total / 100) appuser = AppUser.objects.get(email=user) DepositHistory.objects.create(user=appuser, amount=amount) appuser.balance += amount appuser.save() if event.type == 'payment_intent.succeeded': payment_intent = event.data.object # contains a stripe.PaymentIntent print('PaymentIntent was successful!') elif event.type == 'payment_method.attached': payment_method = event.data.object # contains a stripe.PaymentMethod print('PaymentMethod was attached to a Customer!') # ... handle other event types else: print('Unhandled event type {}'.format(event.type)) return HttpResponse(status=200)
Webhook错误对应的请求内容
{ "id": "evt_1PaIDNGKlfpQfnx92DWXRjHv", "object": "event", "api_version": "2024-06-20", "created": 1720447609, "data": { "object": { "id": "cs_test_a1RF6rCujYP6HqWdSmm3iZGxtdRNlpoSUlzvw09krFzU0UPgwWOyhwoIk7", "object": "checkout.session", "after_expiration": null, "allow_promotion_codes": null, "amount_subtotal": 500, "amount_total": 500, "automatic_tax": { "enabled": false, "liability": null, "status": null }, "billing_address_collection": null, "cancel_url": "{url_website}/?canceled=true", "client_reference_id": null, "client_secret": null, "consent": null, "consent_collection": null, "created": 1720447585, "currency": "usd", "currency_conversion": null, "custom_fields": [ ], "custom_text": { "after_submit": null, "shipping_address": null, "submit": null, "terms_of_service_acceptance": null }, "customer": null, "customer_creation": "if_required", "customer_details": { "address": { "city": null, "country": "PN", "line1": null, "line2": null, "postal_code": null, "state": null }, "email": "email", "name": "name", "phone": null, "tax_exempt": "none", "tax_ids": [ ] }, "customer_email": null, "expires_at": 1720533984, "invoice": null, "invoice_creation": { "enabled": false, "invoice_data": { "account_tax_ids": null, "custom_fields": null, "description": null, "footer": null, "issuer": null, "metadata": { }, "rendering_options": null } }, "livemode": false, "locale": null, "metadata": { "user": "user_who_loged" }, "mode": "payment", "payment_intent": "pi_3PaIDLGKlfpQfnx918HiVozd", "payment_link": null, "payment_method_collection": "if_required", "payment_method_configuration_details": { "id": "pmc_1PY9NLGKlfpQfnx9LdBoV5BK", "parent": null }, "payment_method_options": { "card": { "request_three_d_secure": "automatic" } }, "payment_method_types": [ "card", "link" ], "payment_status": "paid", "phone_number_collection": { "enabled": false }, "recovered_from": null, "saved_payment_method_options": null, "setup_intent": null, "shipping_address_collection": null, "shipping_cost": null, "shipping_details": null, "shipping_options": [ ], "status": "complete", "submit_type": null, "subscription": null, "success_url": "{url_website}/?success=true&session_id={CHECKOUT_SESSION_ID}", "total_details": { "amount_discount": 0, "amount_shipping": 0, "amount_tax": 0 }, "ui_mode": "hosted", "url": null } }, "livemode": false, "pending_webhooks": 1, "request": { "id": null, "idempotency_key": null }, "type": "checkout.session.completed" }
服务器日志输出
Payload
b'{\n "id": "evt_1PaLctGKlfpQfnx9CVEMf0LE",\n "object": "event",\n "api_version": "2024-06-20",\n "created": 1720460723,\n "data": {\n "object": {\n "id": "cs_test_a1W2LUKRDeLyo3fwTaoVpxA1oaBbQ8Bdwe4KKPTX0F7gGg4LSCg9BoAxJk",\n "object": "checkout.session",\n "after_expiration": null,\n "allow_promotion_codes": null,\n "amount_subtotal": 500,\n "amount_total": 500,\n "automatic_tax": {\n "enabled": false,\n "liability": null,\n "status": null\n },\n "billing_address_collection": null,\n "cancel_url": "{url_server}/?canceled=true",\n "client_reference_id": null,\n "client_secret": null,\n "consent": null,\n "consent_collection": null,\n "created": 1720460707,\n "currency": "usd",\n "currency_conversion": null,\n "custom_fields": [\n\n ],\n "custom_text": {\n "after_submit": null,\n "shipping_address": null,\n "submit": null,\n "terms_of_service_acceptance": null\n },\n "customer": null,\n "customer_creation": "if_required",\n "customer_details": {\n "address": {\n "city": null,\n "country": "QA",\n "line1": null,\n "line2": null,\n "postal_code": null,\n "state": null\n },\n "email": "email",\n "name": "name",\n "phone": null,\n "tax_exempt": "none",\n "tax_ids": [\n\n ]\n },\n "customer_email": null,\n "expires_at": 1720547107,\n "invoice": null,\n "invoice_creation": {\n "enabled": false,\n "invoice_data": {\n "account_tax_ids": null,\n "custom_fields": null,\n "description": null,\n "footer": null,\n "issuer": null,\n "metadata": {\n },\n "rendering_options": null\n }\n },\n "livemode": false,\n "locale": null,\n "metadata": {\n "user": "user_who_logged"\n },\n "mode": "payment",\n "payment_intent": "pi_3PaLcrGKlfpQfnx91DXMhSXB",\n "payment_link": null,\n "payment_method_collection": "if_required",\n "payment_method_configuration_details": {\n "id": "pmc_1PY9NLGKlfpQfnx9LdBoV5BK",\n "parent": null\n },\n "payment_method_options": {\n "card": {\n "request_three_d_secure": "automatic"\n }\n },\n "payment_method_types": [\n "card",\n "link"\n ],\n "payment_status": "paid",\n "phone_number_collection": {\n "enabled": false\n },\n "recovered_from": null,\n "saved_payment_method_options": null,\n "setup_intent": null,\n "shipping_address_collection": null,\n "shipping_cost": null,\n "shipping_details": null,\n "shipping_options": [\n\n ],\n "status": "complete",\n "submit_type": null,\n "subscription": null,\n "success_url": "{web_server}/?success=true\\u0026session_id={CHECKOUT_SESSION_ID}",\n "total_details": {\n "amount_discount": 0,\n "amount_shipping": 0,\n "amount_tax": 0\n },\n "ui_mode": "hosted",\n "url": null\n }\n },\n "livemode": false,\n "pending_webhooks": 3,\n "request": {\n "id": null,\n "idempotency_key": null\n },\n "type": "checkout.session.completed"\n}'
Sig Header
t=1720460723,v1=633e50ecd694f51fc6ee2b38586304cd3d73575b2958a1863c5e4ca0854b00b8,v0=a82427ef23faaf0a8d6e5b6b6e466263379fe6f835e8bcd99bd6cde4d4bcb138
内容的提问来源于stack exchange,提问作者itan mare
相关产品推荐
相关产品推荐

