Python Django Stripe Payment Integration

Written by

in

Integrating a seamless payment experience is a game‑changer for any Django‑powered web app, and Stripe stands out as the most developer‑friendly gateway on the market. In this guide you’ll learn how to connect Stripe to a Python Django project from scratch—covering everything from installing the SDK, setting up secure keys, building checkout views, to handling webhooks and deploying to production. Follow the step‑by‑step instructions, copy the ready‑to‑use code snippets, and watch your e‑commerce site start accepting real‑world payments in minutes.

Why Choose Stripe with Django?

Benefits of Stripe for Python Developers

  • Robust API: Stripe’s RESTful endpoints are well‑documented and include comprehensive SDKs for Python.
  • PCI‑DSS compliance made easy: By using Stripe Elements or Checkout, sensitive card data never touches your server.
  • Rich feature set: Subscriptions, one‑time payments, Apple Pay, Google Pay, and even international payment methods.
  • Scalable pricing: Pay‑as‑you‑go model that works for startups and large enterprises alike.

Prerequisites Before You Start

Install Required Packages

pip install django stripe
# Optional: install django-environ for safer secret handling
pip install django-environ

Set Up a Stripe Account

  • Sign up at dashboard.stripe.com and verify your email.
  • Navigate to **Developers → API keys**. Copy the Publishable key and Secret key.
  • Enable Test Mode while you are developing. Production keys will be swapped later.
  • Optionally configure a webhook endpoint (e.g., /stripe/webhook/) to receive asynchronous events.

Step‑By‑Step Stripe Integration in Django

1. Configure Django Settings

Store your Stripe keys securely using environment variables. The example below uses django-environ but you can adapt it to any secret‑management strategy.

# settings.py
import environ, os

env = environ.Env()
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
environ.Env.read_env(os.path.join(BASE_DIR, '.env'))

STRIPE_PUBLIC_KEY = env('STRIPE_PUBLIC_KEY')
STRIPE_SECRET_KEY = env('STRIPE_SECRET_KEY')
STRIPE_WEBHOOK_SECRET = env('STRIPE_WEBHOOK_SECRET')

2. Create a Payment Model

Although Stripe handles the heavy lifting, keeping a local record of each transaction helps with order fulfillment and reporting.

# payments/models.py
from django.db import models

class Payment(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)

    def __str__(self):
        return f"{self.email} – {self.amount/100:.2f} {self.currency.upper()}"

3. Build the Checkout View

The view creates a Stripe Checkout Session and redirects the user to the hosted payment page.

# payments/views.py
import stripe
from django.conf import settings
from django.shortcuts import redirect
from django.views import View
from django.urls import reverse

stripe.api_key = settings.STRIPE_SECRET_KEY

class CreateCheckoutSessionView(View):
    def post(self, request, *args, **kwargs):
        domain = request.build_absolute_uri('/')[:-1]  # e.g., https://example.com
        try:
            checkout_session = stripe.checkout.Session.create(
                payment_method_types=['card'],
                line_items=[{
                    'price_data': {
                        'currency': 'usd',
                        'unit_amount': 2000,  # $20.00 in cents
                        'product_data': {
                            'name': 'Premium Membership',
                        },
                    },
                    'quantity': 1,
                }],
                mode='payment',
                success_url=domain + reverse('payments:success') + '?session_id={CHECKOUT_SESSION_ID}',
                cancel_url=domain + reverse('payments:cancel'),
                metadata={'email': request.POST.get('email')},
            )
            return redirect(checkout_session.url, code=303)
        except Exception as e:
            return redirect('payments:cancel')

4. Design the Front‑End Checkout Form

Use Stripe.js to collect the customer’s email and trigger the server‑side session creation. The form never sends raw card data to your server.

<!-- templates/payments/checkout.html -->
<form id="checkout-form" method="post" action="{% url 'payments:create_checkout_session' %}">
    {% csrf_token %}
    <label for="email">Email address</label>
    <input type="email" name="email" id="email" required>
    <button type="submit">Proceed to Payment</button>
</form>

<script src="https://js.stripe.com/v3/"></script>
<script>
    const stripe = Stripe("{{ STRIPE_PUBLIC_KEY }}");
    const form = document.getElementById('checkout-form');

    form.addEventListener('submit', async (e) => {
        e.preventDefault();
        const response = await fetch(form.action, {
            method: 'POST',
            body: new FormData(form),
            headers: {
                'X-Requested-With': 'XMLHttpRequest'
            }
        });
        // The view redirects to Stripe, so we simply follow the redirect.
        if (response.redirected) {
            window.location = response.url;
        }
    });
</script>

5. Handle Webhooks for Asynchronous Events

Webhooks let you react to successful payments, refunds, or disputes without polling Stripe.

# payments/views.py (continued)
from django.http import HttpResponse, JsonResponse
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator

@method_decorator(csrf_exempt, name='dispatch')
class StripeWebhookView(View):
    def post(self, request, *args, **kwargs):
        payload = request.body
        sig_header = request.META.get('HTTP_STRIPE_SIGNATURE')
        event = None

        try:
            event = stripe.Webhook.construct_event(
                payload, sig_header, settings.STRIPE_WEBHOOK_SECRET
            )
        except (ValueError, stripe.error.SignatureVerificationError):
            return HttpResponse(status=400)

        # Handle the checkout.session.completed event
        if event['type'] == 'checkout.session.completed':
            session = event['data']['object']
            Payment.objects.create(
                stripe_charge_id=session['payment_intent'],
                amount=session['amount_total'],
                currency=session['currency'],
                email=session['metadata']['email']
            )
        return HttpResponse(status=200)

Common Pitfalls and How to Avoid Them

  • Exposing secret keys: Never hard‑code STRIPE_SECRET_KEY in source control. Use environment variables or a secret manager.
  • Mismatched currency: Stripe expects the amount in the smallest currency unit (cents for USD). A mismatch leads to “Invalid amount” errors.
  • Missing CSRF token: AJAX post requests to Django must include the CSRF token, otherwise the view will reject the request.
  • Webhook signature verification: Skipping verification opens a security hole. Always validate the signature with your webhook secret.
  • Testing in live mode: Use the test API keys during development; live keys will reject test card numbers.

Testing Your Integration

  • Use Stripe’s

Comments

Leave a Reply

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