Python Django Stripe Payment Integration

Written by

in

Integrating Stripe with a Python Django application is one of the most reliable ways to accept online payments, subscription billing, and one‑time purchases. In this guide we’ll walk through everything you need to know—from setting up a Stripe account and installing the required packages, to building secure checkout views, handling webhooks, and testing your integration. By the end of this post, you’ll have a production‑ready Django project that can process real payments while keeping PCI compliance and user experience front and center.

Why Choose Stripe for Django Projects?

Stripe has become the go‑to payment processor for developers because it offers:

  • Developer‑friendly APIs with extensive documentation and SDKs for Python.
  • Support for one‑time payments, subscriptions, and marketplace payouts.
  • Built‑in fraud detection and PCI‑DSS compliance via Stripe Elements.
  • Transparent pricing and a global reach with support for dozens of currencies.

When combined with Django’s robust MVC architecture, Stripe lets you build scalable e‑commerce platforms, SaaS products, and donation sites with minimal friction.

Prerequisites

What You Need Before Starting

  1. A Python 3.8+ environment (virtualenv or Poetry recommended).
  2. A fresh or existing Django 4.x project.
  3. A Stripe account. Sign up at dashboard.stripe.com and obtain your Publishable key and Secret key.
  4. Basic knowledge of Django models, views, and templates.

Step 1: Install the Stripe Python SDK

The official Stripe library simplifies API calls and handles request signing for you.

pip install stripe

After installation, add the Stripe keys to your Django settings. Keep them out of version control by using environment variables:

# settings.py
import os

STRIPE_PUBLIC_KEY = os.getenv('STRIPE_PUBLIC_KEY')
STRIPE_SECRET_KEY = os.getenv('STRIPE_SECRET_KEY')

Step 2: Create a Simple Product Model

Even if you only need a one‑time charge, storing product information in the database helps you manage inventory and pricing.

# 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

After defining the model, run python manage.py makemigrations && python manage.py migrate to create the table.

Step 3: Sync Products with Stripe

Stripe uses Price objects to represent the amount you charge. You can create them programmatically when a product is saved.

# signals.py
import stripe
from django.conf import settings
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import Product

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'])

Don’t forget to import the signals module in apps.py or __init__.py so Django registers the handler.

Step 4: Build the Checkout View

We’ll use Stripe’s pre‑built Checkout Session to keep PCI compliance simple. The view creates a session and redirects the user to Stripe’s 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)

    checkout_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('payment_success')
        ) + '?session_id={CHECKOUT_SESSION_ID}',
        cancel_url=request.build_absolute_uri(
            reverse('payment_cancel')
        ),
    )
    return redirect(checkout_session.url, code=303)

Map the view in urls.py:

# urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('checkout//', views.create_checkout_session, name='checkout'),
    path('success/', views.payment_success, name='payment_success'),
    path('cancel/', views.payment_cancel, name='payment_cancel'),
]

Step 5: Handle Success and Cancellation

After a successful payment, Stripe redirects the user back to the success_url. You can retrieve the session details to confirm the payment status.

# views.py (continued)
def payment_success(request):
    session_id = request.GET.get('session_id')
    session = stripe.checkout.Session.retrieve(session_id)
    # Here you could create an Order object, send email, etc.
    return render(request, 'payment_success.html', {'session': session})

def payment_cancel(request):
    return render(request, 'payment_cancel.html')

Step 6: Secure Your Integration with Webhooks

Relying solely on the redirect flow is risky because users can close the browser before the redirect happens. Webhooks let your server be notified of payment events directly from Stripe.

Configure a Webhook Endpoint

# urls.py
urlpatterns += [
    path('webhook/', views.stripe_webhook, name='stripe_webhook'),
]
# views.py (continued)
import json
from django.http import HttpResponse, HttpResponseBadRequest

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 HttpResponseBadRequest()

    # Handle the event
    if event['type'] == 'checkout.session.completed':
        session = event['data']['object']
        # Mark the order as paid, send receipt, etc.
        handle_successful_payment(session)

    return HttpResponse(status=200)

def handle_successful_payment(session):
    # Example: create an Order model entry
    from .models import Order, Product
    product = Product.objects.get(stripe_price_id=session['display_items'][0]['price']['id'])
    Order.objects.create(
        product=product,
        stripe_session_id=session['id'],
        amount=session['amount_total'],
        email=session['customer_details']['email'],
        paid=True,
    )

To test webhooks locally, use the Stripe CLI:

stripe listen --forward-to localhost:8000/webhook/

Step 7: Front‑End Integration with Stripe Elements (Optional)

If you prefer a fully custom checkout UI, Stripe Elements lets you embed secure card fields directly in your Django templates. Below is a minimal example.


{% load static %}



    Custom Checkout
    
    


    

Pay {{ product.name }} – ${{ product.price_cents|floatformat:2|divisibleby:100 }}

Using Elements gives you full control over the checkout design while still delegating sensitive card handling to Stripe.

Best Practices for Production

  • Never expose your secret key in client‑side code. Only the publishable key should be sent to the browser.
  • Store API keys and webhook secrets in environment variables or a secrets manager.
  • Enable Stripe Radar to automatically block high‑risk transactions.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *