How to Integrate Stripe with Django: A Step-by-Step Guide (2025)

Co-Founder

6 min read

Sep 9, 2025

If you’re building a SaaS product or any subscription-based platform, integrating Stripe is one of the easiest ways to start accepting payments. In this guide, I’ll walk you through setting up Stripe with Django (backend) and React (frontend) so you can start charging users for subscriptions.

We’ll cover two approaches:

  • Manual Stripe integration – the traditional way, more setup and code required.
  • No-code/low-code with ParityDeals SDK – the faster option, easier to set up and maintain.

✅ Prerequisites

Before we dive in, make sure you have:


Option 1: Native Stripe Integration (Manual Setup)

In this approach, you’ll write all the integration logic inside your codebase. This gives you flexibility, but it’s also harder to maintain as your product scales.

Step 1: Stripe Setup

1. Create Products & Pricing
  • Go to the Stripe Dashboard → Products.
  • Create a product and add a recurring price (e.g., monthly subscription).
  • Copy the Price ID (looks like price_12345...) — you’ll need this in your code.
2. Get Your Stripe API Keys

Store them securely in your .env file:

# Stripe Keys
STRIPE_PUBLISHABLE_KEY=pk_test_xxxxxxx
STRIPE_SECRET_KEY=sk_test_xxxxxxx

⚠️ Note: Production mode uses a different set of keys, so don’t forget to switch them before going live.

3. Set Up Webhooks

  1. Go to Developers → Webhooks.

  2. Add an endpoint like: https://api.example.com/webhook/

  3. Subscribe to these events:

  • checkout.session.completed
  1. Copy your Webhook Secret and store it in .env:
STRIPE_WEBHOOK_SECRET=whsec_xxxxxxx

Step 2: Backend (Django Setup)

Now that Stripe is configured, let’s set up the Django backend to handle subscriptions, checkout, and webhooks.


1. Install Dependencies

We need two main packages:

  • stripe → Stripe’s official Python SDK.
  • python-decouple → to manage environment variables safely.

Run these commands:

pip install stripe python-decouple
python manage.py startapp payments

2. Configure Django Settings

Add your Stripe credentials in settings.py using environment variables for security.

settings.py

from decouple import config # helps load environment variables from .env file

Stripe keys from your Stripe dashboard

STRIPE_PUBLISHABLE_KEY = config("STRIPE_PUBLISHABLE_KEY")
STRIPE_SECRET_KEY = config("STRIPE_SECRET_KEY")
STRIPE_WEBHOOK_SECRET = config("STRIPE_WEBHOOK_SECRET")
👉 This way, you don’t hardcode keys in your project.

3. Create a Subscription Model

This model will store subscription details like start/end date, linked user, and Stripe IDs.

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

class StripeSubscription(models.Model):
    # When subscription starts and ends
    start_date = models.DateField()
    end_date = models.DateField(null=True, blank=True)

    # Optional product name
    product_name = models.CharField(max_length=200, null=True, blank=True)

    # Link subscription to a user in Django
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True, blank=True)

    # Stripe identifiers
    subscription_id = models.CharField(max_length=100, null=True)
    customer_id = models.CharField(max_length=100, null=True)

👉 This lets you track which Stripe subscription belongs to which user.

4. Create a Checkout Session API

When a user clicks “Subscribe”, we’ll create a Stripe Checkout Session and return the checkout URL.

# payments/views.py
import stripe, logging
from django.conf import settings
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework import status

logger = logging.getLogger(__name__)

@api_view(["POST"])
def create_checkout_session(request):
    try:
        # Get the current logged-in user
        user_id = request.user.id

        # Create a Stripe checkout session
        checkout_session = stripe.checkout.Session.create(
            api_key=settings.STRIPE_SECRET_KEY,
            success_url="https://yourfrontendurl.com/success/",  # redirect after success
            cancel_url="https://yourfrontendurl.com/cancel/",    # redirect if cancelled
            line_items=[{"price": "price_12345", "quantity": 1}],  # use your Price ID here
            mode="subscription",  # since we're handling subscriptions
            client_reference_id=user_id,  # keep track of which user started checkout
            allow_promotion_codes=True,   # optional: allow discounts
            payment_method_types=["card"], # accept cards
        )

        # Return checkout URL so frontend can redirect user
        return Response({"checkout_url": checkout_session.url})

    except Exception:
        logger.exception("Unable to create stripe checkout session")
        return Response({}, status=status.HTTP_400_BAD_REQUEST)
👉 This is the API endpoint your frontend will call to start a checkout.

5. Handle Webhooks

Stripe sends webhooks to notify your app when important events happen (like successful payments).

We’ll create a view that listens for those events and updates our database.

# payments/webhooks.py
import stripe, datetime, logging
from django.conf import settings
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import permissions
from .models import StripeSubscription
from django.contrib.auth.models import User

logger = logging.getLogger(__name__)

class StripeWebhook(APIView):
    # No authentication (Stripe calls this directly)
    authentication_classes = []
    permission_classes = [permissions.AllowAny]

    # Events we want to handle
    SUPPORTED_EVENTS = ["checkout.session.completed"]

    def handle_checkout_session_complete(self, event_data_obj, customer, current_date):
        """
        Save subscription details to our database when checkout succeeds.
        """
        user_id = event_data_obj.get("client_reference_id")
        subscription = event_data_obj.get("subscription")
        user = User.objects.filter(id=int(user_id)).first()

        StripeSubscription.objects.create(
            subscription_id=subscription,
            customer_id=customer,
            user=user,
            start_date=current_date,
        )

    def post(self, request):
        payload = request.body
        sig_header = request.META.get("HTTP_STRIPE_SIGNATURE")

        # Verify that the request really came from Stripe
        try:
            event = stripe.Webhook.construct_event(
                payload, sig_header, settings.STRIPE_WEBHOOK_SECRET,
                api_key=settings.STRIPE_SECRET_KEY
            )
        except (ValueError, stripe.error.SignatureVerificationError):
            return Response({}, status=400)

        # If event is supported, handle it
        if event["type"] in self.SUPPORTED_EVENTS:
            event_data_obj = event["data"]["object"]
            customer = event_data_obj.get("customer")
            current_date = datetime.datetime.utcnow().date()

            self.handle_checkout_session_complete(event_data_obj, customer, current_date)

        return Response({}, status=200)
        

👉 You can extend SUPPORTED_EVENTS to also handle subscription updates or cancellations.

Step 3: Frontend (React Setup)

1. Install Dependencies

In your React project, make sure you have Axios (or Fetch) to make API requests to your Django backend.

npm install axios

2. Create a Subscribe Button

Add a button that users can click to start the subscription process.

// SubscribeButton.js
import React from "react";
import axios from "axios";

function SubscribeButton() {
  const handleSubscribe = async () => {
    try {
      // Call Django backend to create checkout session
      const res = await axios.post("/api/create-checkout-session/");
      
      // Redirect user to Stripe checkout page
      window.location.href = res.data.checkout_url;
    } catch (err) {
      console.error("Error creating checkout session:", err);
    }
  };

  return (
    <button onClick={handleSubscribe}>
      Subscribe Now
    </button>
  );
}

export default SubscribeButton;

👉 This calls your Django API (/api/create-checkout-session/) and redirects the user to Stripe.

3. Handle Redirects After Payment

Stripe will redirect users back to your app after payment:

  • Success URL → user is redirected here if payment is successful.
  • Cancel URL → user is redirected here if they cancel checkout.

For example, in your React Router setup:

// App.js
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";

import SubscribeButton from "./SubscribeButton";

function SuccessPage() {
  return <h2>✅ Payment Successful! Welcome aboard.</h2>;
}

function CancelPage() {
  return <h2>❌ Payment Cancelled. Please try again.</h2>;
}

function App() {
  return (
    <Router>
      <Routes>
        <Route path="/" element={<SubscribeButton />} />
        <Route path="/success" element={<SuccessPage />} />
        <Route path="/cancel" element={<CancelPage />} />
      </Routes>
    </Router>
  );
}

export default App;

👉 These routes should match the success_url and cancel_url you defined in Django’s checkout session.

4. Test with Stripe’s Test Cards

Before going live, use Stripe’s test mode to simulate payments.

For example:

  • Successful payment: 4242 4242 4242 4242 (any future expiry + any CVC).
  • Authentication required: 4000 0027 6000 3184.
  • Declined payment: 4000 0000 0000 9995.

👉 Full list: Stripe Test Card Numbers

Option 2: No-Code/Low-Code Setup with ParityDeals SDK

If you don’t want to manage Stripe’s native complexity (models, webhooks, checkout session handling, etc.), you can use ParityDeals SDK.
This SDK provides a plug-and-play integration for subscriptions and payments, making setup much easier to maintain.


🚀 Why ParityDeals SDK?

  • No need to manually manage Stripe webhooks.
  • Ready-made subscription management APIs.
  • Faster integration, minutes instead of days.
  • Built-in support for handling region based pricing.

🧩 Add Pricing Table to Your UI

Instead of building your own pricing and checkout flow, ParityDeals provides a ready-to-use Pricing Table that you can embed directly in your React app.

📖 Follow the official docs here:
👉 ParityDeals React UI Integration Guide


Best For: teams that want to save time, avoid boilerplate, and focus on building product features instead of billing infrastructure.