Integrating Stripe payments into a Python Django application can turn a simple web project into a revenue‑generating platform in just a few steps. In this guide we’ll walk through everything you need to know—from setting up your Stripe account to handling webhooks securely—so you can launch a reliable, PCI‑compliant checkout experience. Whether you’re building an e‑commerce store, a SaaS subscription service, or a donation page, this step‑by‑step tutorial will give you a solid foundation for a Python Django Stripe payment integration that’s both developer‑friendly and SEO‑optimized.
Why Choose Stripe for Django Projects?
- Developer‑first API: Clear documentation, extensive SDKs, and sandbox mode for testing.
- Built‑in PCI compliance: Offload sensitive card handling to Stripe’s hosted UI.
- Flexible pricing: Pay‑as‑you‑go model with no monthly fees.
- Global support: Accepts over 135 currencies and multiple payment methods.
Prerequisites Before You Start
Technical Requirements
- Python 3.9+ and Django 4.2+ installed.
- A virtual environment (venv or conda) to isolate dependencies.
- Basic knowledge of Django models, views, and URL routing.
- Git for version control (optional but recommended).
Stripe Account Setup
- Sign up at stripe.com and verify your email.
- Navigate to Developers → API keys. Copy the
Publishable keyandSecret key. - Enable Test Mode to avoid real charges during development.
Installing the Stripe Python SDK
Stripe provides an official Python library that works seamlessly with Django. Install it using pip:
pip install stripe
After installation, add the secret key to your Django settings. Keeping keys out of source control is essential, so we’ll use environment variables.
# settings.py
import os
STRIPE_PUBLIC_KEY = os.getenv('STRIPE_PUBLIC_KEY')
STRIPE_SECRET_KEY = os.getenv('STRIPE_SECRET_KEY')
Don’t forget to set the variables in your .env file or your deployment platform.
Configuring Django for Stripe Checkout
1. Create a Simple Product Model
Even if you’re selling a single service, storing product data in the database makes future expansion easier.
# models.py
from django.db import models
class Product(models.Model):
name = models.CharField(max_length=255)
description = models.TextField(blank=True)
price_cents = models.PositiveIntegerField(help_text="Price in cents")
stripe_price_id = models.CharField(max_length=255, blank=True, null=True)
def __str__(self):
return self.name
2. Sync Products with Stripe
When a product is saved, we’ll create a corresponding Stripe Price object. Use Django signals for automation.
# signals.py
import stripe
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import Product
from django.conf import settings
stripe.api_key = settings.STRIPE_SECRET_KEY
@receiver(post_save, sender=Product)
def create_stripe_price(sender, instance, created, **kwargs):
if created and not instance.stripe_price_id:
price = stripe.Price.create(
unit_amount=instance.price_cents,
currency='usd',
product_data={'name': instance.name},
)
instance.stripe_price_id = price.id
instance.save(update_fields=['stripe_price_id'])
3. Register the Signal
Add the following line to apps.py of your Django app so the signal loads on startup.
# apps.py
from django.apps import AppConfig
class ShopConfig(AppConfig):
name = 'shop'
def ready(self):
import shop.signals
Building the Checkout Flow
Step 1: Create a Checkout Session View
This view contacts Stripe, creates a checkout.session, and redirects the user to the hosted payment page.
# views.py
import stripe
from django.conf import settings
from django.shortcuts import get_object_or_404, redirect
from django.urls import reverse
from .models import Product
stripe.api_key = settings.STRIPE_SECRET_KEY
def create_checkout_session(request, product_id):
product = get_object_or_404(Product, pk=product_id)
session = stripe.checkout.Session.create(
payment_method_types=['card'],
line_items=[{
'price': product.stripe_price_id,
'quantity': 1,
}],
mode='payment',
success_url=request.build_absolute_uri(
reverse('checkout_success')
) + '?session_id={CHECKOUT_SESSION_ID}',
cancel_url=request.build_absolute_uri(
reverse('checkout_cancel')
),
)
return redirect(session.url, code=303)
Step 2: Success and Cancel Pages
Simple templates that confirm the transaction or allow the user to try again.
# urls.py
from django.urls import path
from . import views
urlpatterns = [
path('checkout//', views.create_checkout_session, name='checkout'),
path('checkout/success/', views.checkout_success, name='checkout_success'),
path('checkout/cancel/', views.checkout_cancel, name='checkout_cancel'),
]
# views.py (continued)
from django.http import HttpResponse
def checkout_success(request):
return HttpResponse("Payment Successful!
Thank you for your purchase.
")
def checkout_cancel(request):
return HttpResponse("Payment Cancelled
You can try again anytime.
")
Handling Post‑Payment Events with Webhooks
Relying only on the client‑side success URL is risky—users can close the browser before Stripe redirects. Webhooks let your server react to real‑time events such as checkout.session.completed.
1. Set Up a Webhook Endpoint
# urls.py (add)
path('stripe/webhook/', views.stripe_webhook, name='stripe_webhook')
# views.py (add)
import json
from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponse
@csrf_exempt
def stripe_webhook(request):
payload = request.body
sig_header = request.META.get('HTTP_STRIPE_SIGNATURE')
endpoint_secret = os.getenv('STRIPE_WEBHOOK_SECRET')
try:
event = stripe.Webhook.construct_event(
payload, sig_header, endpoint_secret
)
except (ValueError, stripe.error.SignatureVerificationError):
return HttpResponse(status=400)
if event['type'] == 'checkout.session.completed':
session = event['data']['object']
# Example: Mark order as paid, send email, etc.
handle_successful_payment(session)
return HttpResponse(status=200)
def handle_successful_payment(session):
# Retrieve the related product if needed
# Update order status, send receipt, etc.
pass
2. Register the Endpoint in Stripe Dashboard
- Go to Developers → Webhooks → Add endpoint.
- Enter your public URL (e.g.,
https://yourdomain.com/stripe/webhook/). - Select the event
checkout.session.completed. - Copy the generated Signing secret and set it as
STRIPE_WEBHOOK_SECRETin your environment.
Testing Your Integration
- Use Stripe test cards: 4242 4242 4242 4242 (Visa) works for any amount.
- Enable
DEBUG = Truelocally to see detailed error messages. - Run
stripe listen --forward-to localhost:8000/stripe/webhook/to forward webhook events to your development server.
Common Pitfalls & How to Avoid Them
- Hard‑coding API keys: Always load keys from environment variables; committing them can expose your account.
- Missing CSRF exemption on webhook view: Stripe sends POST requests without a CSRF token, so
@csrf_exemptis required. - Incorrect currency or amount units: Stripe expects amounts in the smallest currency unit (cents for USD). Double‑check
price_centsfields. - Not verifying webhook signatures: Skipping signature verification opens a security hole where anyone could fake a payment event.
- Using the live secret key in test mode: Separate keys for test and production prevent accidental real charges.
Deploying to Production
- Switch Stripe keys to the live version in your environment variables.
- Update the webhook endpoint to point to your live domain and replace the signing secret.
- Set
DEBUG = Falseand configureALLOWED_HOSTSinsettings.py. - Consider adding HTTPS via Let’s Encrypt or your cloud provider to meet Stripe’s security requirements.
SEO Tips for Your Stripe‑Enabled Django Site
- Use descriptive page titles:
Leave a Reply