Integrating Stripe with a Python Django application is one of the most reliable ways to add secure, fast, and flexible payment processing to your web project. Whether you’re building a SaaS platform, an e‑commerce store, or a subscription‑based service, Stripe’s robust API combined with Django’s clean architecture makes the implementation both straightforward and scalable. In this guide we’ll walk through every step of a full‑stack Python Django Stripe payment integration, from setting up the Stripe account to handling webhooks and testing in a live environment. By the end of this article you’ll have a production‑ready checkout flow that follows best practices for security, error handling, and SEO‑friendly content.
Why Choose Stripe for Django Projects?
Before diving into the code, it’s worth understanding why Stripe is a top choice for Django developers:
- Developer‑first API: Stripe’s documentation is clear, and the official
stripe-pythonlibrary mirrors the API closely. - PCI‑DSS compliance: By using Stripe Elements or Checkout, sensitive card data never touches your servers.
- Extensive payment methods: Credit cards, Apple Pay, Google Pay, ACH, and even international wallets are supported out of the box.
- Built‑in subscription management: Recurring billing, trial periods, and prorations are handled automatically.
- Scalable webhooks: Real‑time event handling lets you update order status, send receipts, or trigger fulfillment workflows.
Prerequisites
Make sure you have the following before you start the integration:
- Python 3.9+ and Django 4.2+ installed.
- A Stripe account (you can sign up for a free test account).
- Basic knowledge of Django models, views, and templates.
- Git installed for version control (optional but recommended).
Step‑by‑Step Django Stripe Integration
1. Install Required Packages
First, add the Stripe Python library and Django‑CORS‑Headers (if you plan to use a JavaScript front‑end) to your project:
pip install stripe django-cors-headers
Then, add 'corsheaders' to INSTALLED_APPS and insert the middleware before Django’s common middleware:
# settings.py
INSTALLED_APPS = [
# …
'corsheaders',
# …
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
# …
]
CORS_ALLOW_ALL_ORIGINS = True # For development only; restrict in production
2. Configure Stripe Keys
Store your secret and publishable keys safely using Django’s settings.py and environment variables. Never hard‑code them in source code.
# .env (example)
STRIPE_PUBLISHABLE_KEY=pk_test_XXXXXXXXXXXXXXXXXXXXXXXX
STRIPE_SECRET_KEY=sk_test_XXXXXXXXXXXXXXXXXXXXXXXX
# settings.py
import os
from pathlib import Path
from dotenv import load_dotenv
BASE_DIR = Path(__file__).resolve().parent.parent
load_dotenv(os.path.join(BASE_DIR, '.env'))
STRIPE_PUBLISHABLE_KEY = os.getenv('STRIPE_PUBLISHABLE_KEY')
STRIPE_SECRET_KEY = os.getenv('STRIPE_SECRET_KEY')
3. Create a Simple Product Model
For demonstration, let’s define a Product model that holds the price in cents (Stripe expects the smallest currency unit).
# shop/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, e.g., $9.99 = 999")
def __str__(self):
return self.name
@property
def price_display(self):
return f"${self.price_cents / 100:.2f}"
4. Build the Checkout View
We’ll use Stripe Checkout, a pre‑built, PCI‑compliant payment page. The view creates a Checkout Session and redirects the user.
# shop/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)
domain = request.build_absolute_uri('/')[:-1] # Remove trailing slash
checkout_session = stripe.checkout.Session.create(
payment_method_types=['card'],
line_items=[{
'price_data': {
'currency': 'usd',
'product_data': {
'name': product.name,
},
'unit_amount': product.price_cents,
},
'quantity': 1,
}],
mode='payment',
success_url=domain + reverse('checkout_success') + '?session_id={CHECKOUT_SESSION_ID}',
cancel_url=domain + reverse('checkout_cancel'),
)
return redirect(checkout_session.url, code=303)
5. Define Success and Cancel Views
These simple views render a template that thanks the user or informs them of a cancelled payment.
# shop/views.py (continued)
from django.shortcuts import render
def checkout_success(request):
return render(request, 'shop/success.html')
def checkout_cancel(request):
return render(request, 'shop/cancel.html')
6. Add URLs
# shop/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('checkout//', views.create_checkout_session, name='create_checkout'),
path('success/', views.checkout_success, name='checkout_success'),
path('cancel/', views.checkout_cancel, name='checkout_cancel'),
]
7. Create Front‑End Templates with Stripe.js
Even though Stripe Checkout handles most of the UI, you may want a custom button that loads the session via JavaScript. Below is a minimal template that lists products and triggers the checkout flow.
<!-- templates/shop/product_list.html -->
<h2>Our Products</h2>
<ul>
{% for product in products %}
<li>
<strong>{{ product.name }}</strong> – {{ product.price_display }}<br>
{{ product.description|linebreaksbr }}<br>
<button class="checkout-button" data-id="{{ product.id }}">Buy Now</button>
</li>
{% endfor %}
</ul>
<script src="https://js.stripe.com/v3/"></script>
<script>
const stripe = Stripe("{{ stripe_publishable_key }}");
document.querySelectorAll('.checkout-button').forEach(btn => {
btn.addEventListener('click', () => {
const productId = btn.dataset.id;
fetch(`/checkout/${productId}/`, {
method: 'GET',
redirect: 'follow'
})
.then(response => {
if (response.redirected) {
window.location.href = response.url;
}
})
.catch(err => console.error('Checkout error:', err));
});
});
</script>
Don’t forget to pass stripe_publishable_key from the view context.
# shop/views.py (add product list view)
def product_list(request):
products = Product.objects.all()
return render(request, 'shop/product_list.html', {
'products': products,
'stripe_publishable_key': settings.STRIPE_PUBLISHABLE_KEY,
})
8. Secure the Webhook Endpoint
Stripe sends asynchronous events (e.g., checkout.session.completed) to a webhook URL you configure in the Dashboard. Handling these events ensures your database stays in sync with payment status.
# shop/webhooks.py
import json, stripe
from django.conf import settings
from django.http import HttpResponse, JsonResponse
from django.views.decorators.csrf import csrf_exempt
from .models import Product
stripe.api_key = settings.STRIPE_SECRET_KEY
@csrf_exempt
def stripe_webhook(request):
payload = request.body
sig_header = request.META.get('HTTP_STRIPE_SIGNATURE')
endpoint_secret = settings.STRIPE_WEBHOOK_SECRET # Set in .env
try:
event = stripe.Webhook.construct_event(
payload, sig_header, endpoint_secret
)
except ValueError as e:
# Invalid payload
return HttpResponse(status=400)
except stripe.error.SignatureVerificationError as e:
# Invalid signature
return HttpResponse(status=400)
# Handle the event
if event['type'] == 'checkout.session.completed':
session = event['data']['object']
# Example: mark order as paid, send email, etc.
# You could create an Order model and link it here.
print(f"Payment succeeded for session {session['id']}")
# ... handle other event types ...
return HttpResponse(status=200)
Add the webhook URL to urls.py and register the endpoint secret in your environment.
# shop/urls.py (add)
path('webhook/stripe/', views.stripe_webhook, name='stripe_webhook')
Testing the Integration
Stripe provides a set of test card numbers that you can use without real money. Follow these steps to verify everything works before going live:
- Set
STRIPE_SECRET_KEYandSTRIPE_PUBLISH
Leave a Reply