python
46 lines · 7 steps
Handling Stripe webhooks in Django
A Django view that verifies Stripe webhook signatures and fulfills paid orders idempotently.
Explained by
highlit
1import json
2import logging
3
4import stripe
5from django.conf import settings
6from django.http import HttpResponse, HttpResponseBadRequest
7from django.views.decorators.csrf import csrf_exempt
8from django.views.decorators.http import require_POST
9
10from .models import Order
11from .tasks import send_receipt_email
12
13logger = logging.getLogger(__name__)
14
15
16@csrf_exempt
17@require_POST
18def stripe_webhook(request):
19 payload = request.body
20 sig_header = request.META.get("HTTP_STRIPE_SIGNATURE")
21
22 try:
23 event = stripe.Webhook.construct_event(
24 payload=payload,
25 sig_header=sig_header,
26 secret=settings.STRIPE_WEBHOOK_SECRET,
27 )
28 except ValueError:
29 logger.warning("Stripe webhook received malformed payload")
30 return HttpResponseBadRequest("Invalid payload")
31 except stripe.error.SignatureVerificationError:
32 logger.warning("Stripe webhook signature verification failed")
33 return HttpResponseBadRequest("Invalid signature")
34
35 if event.type == "checkout.session.completed":
36 session = event.data.object
37 order = Order.objects.filter(
38 stripe_session_id=session.id, status=Order.Status.PENDING
39 ).first()
40 if order is not None:
41 order.mark_paid(payment_intent=session.payment_intent)
42 send_receipt_email.delay(order.pk)
43 else:
44 logger.info("Ignoring unhandled Stripe event %s", event.type)
45
46 return HttpResponse(status=200)
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Always verify webhook signatures against a shared secret before trusting the payload.
- 2Filtering on a pending status makes fulfillment idempotent against duplicate webhook deliveries.
- 3Offload slow side effects like email to a background task and return 200 promptly.
Related explainers
python
from fastapi import FastAPI, WebSocket, WebSocketDisconnect app = FastAPI()
Building a WebSocket chat with FastAPI
websockets
broadcast
connection-management
Intermediate
9 steps
python
import time import uuid from django.utils.deprecation import MiddlewareMixin
Attaching per-request context in Django
middleware
request lifecycle
multi-tenancy
Intermediate
7 steps
python
import random from typing import Iterator, List
How reservoir sampling picks k items
reservoir-sampling
streaming
randomness
Intermediate
5 steps
python
import secrets from django.contrib.auth import authenticate, login from django.core.cache import cache
Two-factor login with OTP in Django
two-factor-auth
one-time-passwords
caching
Intermediate
9 steps
python
import re from functools import total_ordering from typing import Optional
Parsing and comparing semantic versions
regex
operator-overloading
sorting
Intermediate
7 steps
python
from typing import Any, Sequence, Mapping def render_markdown_table(
Rendering an aligned Markdown table in Python
string formatting
data transformation
closures
Intermediate
8 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/handling-stripe-webhooks-in-django-explained-python-e56e/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.