डिफ़ॉल्ट रूप से, Django हर वेबहुक POST को 403 के साथ अस्वीकार कर देगा। दोषी है CSRF middleware जो अपना काम कर रहा है — cross-site request forgery से बचाव — बस फ़र्क यह कि प्रोवाइडर कोई ब्राउज़र नहीं है और न ही session cookie या CSRF token रखता है। समाधान एक decorator और रिक्वेस्ट बॉडी एक्सेस के बारे में एक पंक्ति की जागरूकता है।
वेबहुक व्यू के लिए CSRF छोड़ें
Django का तरीका:
from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponse, HttpResponseBadRequest
import hmac, hashlib
@csrf_exempt
def stripe_webhook(request):
if request.method != 'POST':
return HttpResponseBadRequest('POST only')
payload = request.body # कच्चे बाइट, अछूते
signature = request.headers.get('Stripe-Signature', '')
# सिग्नेचर सत्यापित करें (असली कोड में stripe SDK इस्तेमाल करें)
if not verify(payload, signature, settings.STRIPE_WEBHOOK_SECRET):
return HttpResponse(status=401)
event = json.loads(payload)
handle_stripe_event(event)
return HttpResponse(status=200)
@csrf_exempt decorator middleware की जाँच केवल इस व्यू के लिए हटाता है — आपके बाक़ी फ़ॉर्म सुरक्षित रहते हैं। request.body कच्चे बाइट लौटाता है जैसे वे आए, और सिग्नेचर सत्यापन को ठीक यही चाहिए।
अगर आप Django REST Framework इस्तेमाल कर रहे हैं
DRF रिक्वेस्ट को अपने Request ऑब्जेक्ट में लपेटता है और content-type के अनुसार parser चलाता है। जब तक आपका व्यू कोड request.data पढ़ता है, JSON parser बॉडी पहले ही खा चुका होता है। दो रास्ते:
विकल्प A: इसे सादा Django व्यू रहने दें
वेबहुक handler को DRF के parser, browsable API या auth तंत्र की ज़रूरत नहीं। आपके DRF एंडपॉइंट के बगल में एक सादा @csrf_exempt व्यू सबसे साफ़ पैटर्न है।
विकल्प B: DRF के साथ कच्चा एक्सेस
अगर आप सच में DRF चाहते हैं, तो request.data के बजाय request.body (नीचे के Django रिक्वेस्ट) पढ़ें:
from rest_framework.decorators import api_view, permission_classes, authentication_classes
from rest_framework.permissions import AllowAny
@api_view(['POST'])
@authentication_classes([]) # वेबहुक के लिए कोई auth नहीं
@permission_classes([AllowAny]) # प्रोवाइडर पहले ही सिग्नेचर से auth करता है
def stripe_webhook(request):
payload = request._request.body # नीचे का Django रिक्वेस्ट, कच्चे बाइट
signature = request.META.get('HTTP_STRIPE_SIGNATURE', '')
# ...सत्यापित कर संभालें
request._request वाला घुमाव अटपटा है पर जब DRF की पार्सिंग बायपास करनी हो तो यही आधिकारिक रास्ता है।
टनल सेट करें
- अपना Django dev सर्वर चलाएँ:
python manage.py runserver 8000। - टनल का hostname
settings.pyमेंALLOWED_HOSTSमें जोड़ें। लोकल dev के दौरान सबसे आसान है['*'], या चाहें तो एक विशिष्ट टनल डोमेन। - अलग टर्मिनल में
npx portpreview 8000। - टनल URL के साथ अपना वेबहुक पथ प्रोवाइडर के डैशबोर्ड में पेस्ट करें।
- एक टेस्ट इवेंट ट्रिगर करें और उसे अपने व्यू में आते देखें।
ALLOWED_HOSTS भूलने पर बिना स्पष्ट कारण के एक सामान्य Django 400 मिलता है। अगर आपकी पहली वेबहुक डिलीवरी 400 लौटाती है और आपको व्यू के लॉग नहीं दिखते, तो यही वजह है।
आम जाल
बॉडी दो बार पढ़ना
हर फ़्रेमवर्क की तरह Django बॉडी एक स्ट्रीम के रूप में देता है जिसे केवल एक बार पढ़ा जा सकता है। अगर middleware या logging wrapper ने request.body पहले ही छू लिया, तो आपका व्यू कोड एक खाली bytes ऑब्जेक्ट देखता है। जाँच के लिए len(request.body) प्रिंट करें।
टाइम ज़ोन और timestamp सहनशीलता
अगर आप साइन किए timestamp की तुलना datetime.now() से कर रहे हैं, तो timezone.now() इस्तेमाल करें या UTC में तुलना करें। naive datetime तुलनाएँ daylight saving बदलाव पर रुक-रुक कर फ़ेल होती हैं।
async व्यू
Django 4+ async def व्यू सपोर्ट करता है। वेबहुक handler async हो सकते हैं, पर ध्यान रहे कि सिग्नेचर सत्यापन SDK (Stripe Python आदि) sync हैं। उन्हें asgiref.sync_to_async से चलाएँ या पूरे व्यू को sync रखें।
प्रोडक्शन डिप्लॉयमेंट नोट्स
लोकल टेस्टिंग के बाद वही व्यू प्रोडक्शन में काम करता है। Gunicorn, uWSGI, Daphne — इनमें से कोई आपके व्यू के देखने से पहले request.body को नहीं छूता। आपका reverse proxy छू सकता है (उदाहरण के लिए proxy_buffering चालू वाला nginx), पर वह buffering की बात है, बॉडी बदलने की नहीं।
अंतर्निहित सिग्नेचर सिद्धांत के लिए सिग्नेचर सत्यापन गाइड देखें। Stripe-विशिष्ट पैटर्न के लिए डैशबोर्ड पक्ष को Stripe लोकल टेस्टिंग गाइड कवर करती है। Python dev सर्वर के साथ अच्छे से चलने वाले टनल के लिए PortPreview वेटलिस्ट में शामिल हों।