Integrating Stripe payments into a Python Django application is one of the most effective ways to turn a simple web project into a revenue‑generating platform. In this guide we’ll walk through every step—from setting up a Stripe account to handling webhooks—so you can build a secure, scalable, and SEO‑friendly checkout experience that Google loves. Whether you’re launching a SaaS product, an e‑commerce store, or a subscription‑based service, mastering Django‑Stripe integration will give you the confidence to accept credit cards worldwide.
Why Combine Django and Stripe?
- Rapid development: Django’s batteries‑included philosophy pairs perfectly with Stripe’s well‑documented API.
- Security compliance: Stripe handles PCI‑DSS requirements, letting you focus on business logic.
- Scalable pricing: Pay‑as‑you‑go model works for startups and enterprises alike.
- Rich feature set: One‑time payments, recurring subscriptions, saved cards, and Apple/Google Pay are all available out of the box.
Prerequisites Before You Start
- Python 3.9+ and a virtual environment (venv or conda).
- Basic Django project already created (
django-admin startproject mysite). - A Stripe account (you can sign up for free at dashboard.stripe.com).
- Familiarity with Django models, views, and templates.
Step 1 – Install Required Packages
First, add the official Stripe Python library and Django‑Stripe helper packages to your environment.
pip install stripe django-environ
# Optional: django-widget-tweaks for nicer forms
pip install django-widget-tweaks
Step 2 – Configure Django Settings
Store your Stripe keys securely using django-environ. Create a .env file in the project root:
# .env
STRIPE_PUBLIC_KEY=pk_test_XXXXXXXXXXXXXXXXXXXXXXXX
STRIPE_SECRET_KEY=sk_test_XXXXXXXXXXXXXXXXXXXXXXXX
STRIPE_WEBHOOK_SECRET=whsec_XXXXXXXXXXXXXXXXXXXXXXXX
DEBUG=True
Then load these variables in settings.py:
import environ
env = environ.Env()
environ.Env.read_env()
STRIPE_PUBLIC_KEY = env('STRIPE_PUBLIC_KEY')
STRIPE_SECRET_KEY = env('STRIPE_SECRET_KEY')
STRIPE_WEBHOOK_SECRET = env('STRIPE_WEBHOOK_SECRET')
Step 3 – Create a Payment Model
Even if you only need a one‑time charge, persisting transaction data is a best practice. Add a simple model to payments/models.py:
from django.db import models
from django.conf import settings
class Charge(models.Model):
stripe_charge_id = models.CharField(max_length=255, unique=True)
amount = models.PositiveIntegerField(help_text="Amount in cents")
currency = models.CharField(max_length=10, default='usd')
email = models.EmailField()
created_at = models.DateTimeField(auto_now_add=True)
paid = models.BooleanField(default=False)
def __str__(self):
return f"{self.email} – {self.amount/100:.2f} {self.currency.upper()}"
Run migrations:
python manage.py makemigrations payments
python manage.py migrate
Step 4 – Build the Checkout View
We’ll use Stripe Checkout, a hosted payment page that reduces PCI scope. Create a view in payments/views.py:
import stripe
from django.conf import settings
from django.shortcuts import render, redirect
from django.urls import reverse
from .models import Charge
stripe.api_key = settings.STRIPE_SECRET_KEY
def create_checkout_session(request):
if request.method == "POST":
# Example: $29.99 one‑time payment
amount = 2999
domain = request.build_absolute_uri('/')[:-1] # strip trailing slash
checkout_session = stripe.checkout.Session.create(
payment_method_types=['card'],
line_items=[{
'price_data': {
'currency': 'usd',
'product_data': {
'name': 'Premium Subscription',
},
'unit_amount': amount,
},
'quantity': 1,
}],
mode='payment',
success_url=domain + reverse('payments:success') + '?session_id={CHECKOUT_SESSION_ID}',
cancel_url=domain + reverse('payments:cancel'),
)
# Store a placeholder Charge record (will be updated via webhook)
Charge.objects.create(
stripe_charge_id=checkout_session.payment_intent,
amount=amount,
email=request.POST.get('email', ''),
)
return redirect(checkout_session.url, code=303)
return render(request, 'payments/checkout.html')
Template Example (checkout.html)
<form method="post">
{% csrf_token %}
<label for="email">Email:</label>
<input type="email" name="email" id="email" required>
<button type="submit">Pay $29.99</button>
</form>
Step 5 – Handle Success and Cancel Pages
Add two simple views that display friendly messages and optionally retrieve the session details.
def success(request):
session_id = request.GET.get('session_id')
session = stripe.checkout.Session.retrieve(session_id)
return render(request, 'payments/success.html', {'session': session})
def cancel(request):
return render(request, 'payments/cancel.html')
Step 6 – Secure Webhook Endpoint
Stripe sends asynchronous events (e.g., checkout.session.completed) to a webhook URL you define. This is where you mark the charge as paid and trigger post‑payment actions such as sending a receipt.
import json
from django.http import HttpResponse, HttpResponseBadRequest
from django.views.decorators.csrf import csrf_exempt
@csrf_exempt
def stripe_webhook(request):
payload = request.body
sig_header = request.META.get('HTTP_STRIPE_SIGNATURE')
try:
event = stripe.Webhook.construct_event(
payload, sig_header, settings.STRIPE_WEBHOOK_SECRET
)
except (ValueError, stripe.error.SignatureVerificationError):
return HttpResponseBadRequest()
# Handle the checkout.session.completed event
if event['type'] == 'checkout.session.completed':
session = event['data']['object']
charge = Charge.objects.filter(stripe_charge_id=session['payment_intent']).first()
if charge:
charge.paid = True
charge.save()
# TODO: send email, activate account, etc.
return HttpResponse(status=200)
Step 7 – Register URLs
Map the views in payments/urls.py and include them in the project’s main urls.py:
# payments/urls.py
from django.urls import path
from . import views
app_name = 'payments'
urlpatterns = [
path('checkout/', views.create_checkout_session, name='checkout'),
path('success/', views.success, name='success'),
path('cancel/', views.cancel, name='cancel'),
path('webhook/', views.stripe_webhook, name='webhook'),
]
Step 8 – Test in Stripe’s Sandbox
- Enable Test Mode in the Stripe Dashboard.
- Use test card numbers (e.g.,
4242 4242 4242 4242) to simulate successful and declined payments. - Visit
/payments/webhook/on a publicly reachable URL (ngrok is handy):ngrok http 8000. - In the Dashboard, add the ngrok URL as the webhook endpoint and subscribe to
checkout.session.completed.
Step 9 – Deploy to Production
When you’re ready to go live, switch the API keys in your .env file to the live keys, disable DEBUG, and ensure the webhook secret matches the production endpoint. Remember to set ALLOWED_HOSTS and use HTTPS—Stripe will reject non‑secure callbacks.
Best Practices & Common Pitfalls
Security Tips
- Never hard‑code API keys; always load them from environment variables.
- Validate webhook signatures to prevent forged events.
- Store only the
payment_intentID; do not keep raw card data.
Performance Optimizations
- Cache the public key in your template context to avoid repeated settings lookups.
- Use asynchronous tasks (Celery or Django Q) for post‑payment emails and provisioning.
SEO Considerations
Search engines love clear, keyword‑rich content. To boost the visibility of this guide, we’ve included the primary phrase Python Django Stripe payment integration multiple times, along with related terms such as “Stripe Checkout”, “Django webhook”, and “PCI‑DSS compliance”. Additionally, use descriptive meta tags
Leave a Reply