
How to Integrate Stripe with Django: A Step-by-Step Guide (2025)
Co-Founder
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:
A Stripe account with your business profile verified.
👉 Sign up for Stripe
👉 Country-specific requirementsBasic knowledge of Django, Django REST Framework, and React.
Python 3.8+ and Node.js installed.
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
- Navigate to Developers → API keys.
- Copy your Publishable key and Secret key.
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
Go to Developers → Webhooks.
Add an endpoint like: https://api.example.com/webhook/
Subscribe to these events:
checkout.session.completed
- 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")
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)
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.