McTaba Labs logo
Software EngineeringThe WhiteboardProjectsEventsMasterclassesWebinarsLearn
Book Call

500+ developers already in the community

Get answers, find accountability partners, and stay ahead

Join Discord →
McTaba Labs logo

Educating 1 million students in tech by 2030.

Programs

  • HighSchool Graduates Launchpad
  • AI & Business Automation
  • Corporate Training
  • McTaba Story & Mission
  • Full-Stack Curriculum
  • Course Pricing & Plans
  • Frequently Asked Questions

Resources

  • Join Discord Community
  • Tech Events in Nairobi
  • Masterclasses
  • Free Resources
  • Learn
  • Campus
  • Africa Tech Talent Report
  • Partner With Us
  • Start a Project
  • Our Work

Contact Us

  • 124 Manyani East Road, Lavington, Nairobi
  • +254727795603
  • Book a Discovery Call

Office Hours:

Mon-Fri: 9am - 2pm EAT

Sat: 10am - 12pm EAT

© 2027 McTaba Labs - A product of Edtech Industries. All rights reserved.

Privacy PolicyTerms of ServiceRefund Policy
  1. Learn
  2. Paystack for African Developers
  3. Accept Payments with Paystack in Django
Bonaventure OgetoBy Bonaventure Ogeto|Updated July 20, 2026

Accept Payments with Paystack in Django

To accept Paystack payments in Django, create a view that initializes a transaction with the Paystack API using the requests library and your secret key. Return the authorization URL to your template, redirect the user to Paystack checkout, then verify the payment in a callback view before granting value.

In this article

  1. 1. Project Setup
  2. 2. Create an Order Model
  3. 3. Initialize a Transaction
  4. 4. Handle the Redirect Callback
  5. 5. URL Configuration
  6. 6. Checkout Template
  7. 7. Security and Production Checklist
  8. 8. Ship Payments Faster

Project Setup

Install the requests library if you have not already:

pip install requests python-decouple

Add your Paystack keys to a .env file:

PAYSTACK_SECRET_KEY=sk_test_xxxxxxxxxxxxxxxxxxxxx
PAYSTACK_PUBLIC_KEY=pk_test_xxxxxxxxxxxxxxxxxxxxx

Add the keys to your Django settings:

# settings.py
from decouple import config

PAYSTACK_SECRET_KEY = config('PAYSTACK_SECRET_KEY')
PAYSTACK_PUBLIC_KEY = config('PAYSTACK_PUBLIC_KEY')

Create an Order Model

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

class Order(models.Model):
    reference = models.CharField(max_length=100, unique=True)
    email = models.EmailField()
    amount = models.IntegerField(help_text='Amount in kobo')
    currency = models.CharField(max_length=3, default='NGN')
    paid = models.BooleanField(default=False)
    paid_at = models.DateTimeField(null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

    def save(self, *args, **kwargs):
        if not self.reference:
            self.reference = 'order_' + str(uuid.uuid4().hex[:12])
        super().save(*args, **kwargs)

    def __str__(self):
        return self.reference

Run migrations after creating the model:

python manage.py makemigrations payments
python manage.py migrate

Get weekly developer tips

Join 25,000+ developers. Practical guides, job tips, and new content — straight to your inbox.

No spam. Unsubscribe anytime.

Initialize a Transaction

# payments/views.py
import requests
from django.conf import settings
from django.shortcuts import redirect, render
from django.http import JsonResponse
from .models import Order

def initialize_payment(request):
    if request.method == 'POST':
        email = request.POST.get('email')
        amount = int(request.POST.get('amount', 5000))

        order = Order.objects.create(
            email=email,
            amount=amount * 100,  # Convert to kobo
        )

        headers = {
            'Authorization': 'Bearer ' + settings.PAYSTACK_SECRET_KEY,
            'Content-Type': 'application/json',
        }

        data = {
            'email': email,
            'amount': order.amount,
            'reference': order.reference,
            'callback_url': request.build_absolute_uri('/payments/callback/'),
        }

        response = requests.post(
            'https://api.paystack.co/transaction/initialize',
            json=data,
            headers=headers,
        )

        result = response.json()

        if result.get('status'):
            return redirect(result['data']['authorization_url'])

        return render(request, 'payments/checkout.html', {
            'error': result.get('message', 'Initialization failed'),
        })

    return render(request, 'payments/checkout.html')

Handle the Redirect Callback

# payments/views.py (continued)
from django.utils import timezone
from django.views.decorators.csrf import csrf_exempt

def payment_callback(request):
    reference = request.GET.get('reference') or request.GET.get('trxref')

    if not reference:
        return render(request, 'payments/failed.html', {'error': 'Missing reference'})

    headers = {
        'Authorization': 'Bearer ' + settings.PAYSTACK_SECRET_KEY,
    }

    response = requests.get(
        'https://api.paystack.co/transaction/verify/' + reference,
        headers=headers,
    )

    result = response.json()

    if result.get('status') and result['data']['status'] == 'success':
        try:
            order = Order.objects.get(reference=reference)
        except Order.DoesNotExist:
            return render(request, 'payments/failed.html', {'error': 'Order not found'})

        # Check amount
        if result['data']['amount'] != order.amount:
            return render(request, 'payments/failed.html', {'error': 'Amount mismatch'})

        # Idempotent fulfillment
        if not order.paid:
            order.paid = True
            order.paid_at = timezone.now()
            order.save()

        return render(request, 'payments/success.html', {
            'reference': reference,
            'amount': order.amount / 100,
        })

    return render(request, 'payments/failed.html', {
        'error': 'Payment was not successful',
    })

URL Configuration

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

urlpatterns = [
    path('checkout/', views.initialize_payment, name='checkout'),
    path('callback/', views.payment_callback, name='payment_callback'),
]
# project/urls.py
from django.urls import path, include

urlpatterns = [
    path('payments/', include('payments.urls')),
]

Checkout Template

<!-- templates/payments/checkout.html -->
<form method="post" action="{% url 'checkout' %}">
  {% csrf_token %}
  <input type="email" name="email" placeholder="Email" required />
  <input type="hidden" name="amount" value="5000" />
  <p>Amount: NGN 5,000</p>
  <button type="submit">Pay Now</button>
  {% if error %}
    <p style="color: red;">{{ error }}</p>
  {% endif %}
</form>

The form posts to your Django view, which initializes the transaction and redirects to Paystack. The CSRF token is needed because this is a Django form submission, not a Paystack request.

Security and Production Checklist

  1. Validate amounts server-side. Look up the correct price from your database or product catalog. Never trust form data.
  2. Exempt webhooks from CSRF. Use @csrf_exempt on your webhook view. Paystack requests will not have Django CSRF tokens.
  3. Set up webhooks. The redirect can fail. See Handle Paystack Webhooks in Django.
  4. Use HTTPS. Required for callback and webhook URLs in production.
  5. Switch keys. Use sk_live_ in production. Keep test keys for development only.

Ship Payments Faster

This guide is part of the Paystack integration guides by language and framework series.

Sign up for the McTaba newsletter

Key Takeaways

  • ✓Django views handle the Paystack API calls server-side. Use the requests library to call the Paystack REST API.
  • ✓Store your Paystack secret key in environment variables or Django settings. Never hardcode it.
  • ✓Amounts are in kobo (NGN), pesewas (GHS), or cents (ZAR/KES/USD). Multiply by 100 before sending to Paystack.
  • ✓Exempt your callback and webhook views from CSRF protection since the requests come from Paystack, not your frontend.
  • ✓Always verify the transaction server-side after the redirect. The redirect alone is not proof of payment.
  • ✓Use Django models to store orders and their payment status. Check the status before fulfilling to prevent double-granting.

Frequently Asked Questions

Do I need a Paystack Python package?
No. The Paystack API is a standard REST API. The requests library handles all the HTTP calls. Community packages exist but add unnecessary abstraction for most projects.
Can I use class-based views instead of function-based views?
Yes. The Paystack API calls are the same. Use View or TemplateView and override get() and post() methods. The logic is identical.
How do I handle CSRF for Paystack callbacks?
The callback is a GET request (a redirect from Paystack), so CSRF does not apply. CSRF only affects POST requests. For webhooks (which are POST), use the @csrf_exempt decorator.
Can I use Django async views with Paystack?
Yes. Django 4.1+ supports async views. Use httpx instead of requests for async HTTP calls to the Paystack API. The API endpoints and logic are the same.

Learn Paystack Integration

KES 2,999

The definitive guide to building payment systems with Paystack — from your first transaction to production-grade payment infrastructure across Africa. 9 modules, 47 lessons. Free preview available.

Start Paystack Course

Not ready? Create Free Account

Keep reading

Paystack Integration Guides by Language and Framework

A complete directory of Paystack integration guides organized by programming language and framework. Each guide covers accepting payments, verifying transactions, handling webhooks, and building subscriptions in your specific stack.

Read more

Accept Payments with Paystack in Svelte and SvelteKit

Accept Paystack payments in SvelteKit using server endpoints for backend logic and Svelte components for the frontend. This guide covers initializing transactions, inline popup checkout, redirect checkout, and verification in a single full-stack SvelteKit project.

Read more

Handle Paystack Webhooks in Flask

Receive and verify Paystack webhooks in your Flask application. This guide covers accessing request.data for raw bytes, HMAC SHA-512 signature verification, processing charge.success events, and making handlers idempotent.

Read more

Build Paystack Subscriptions in ASP.NET Core

Implement Paystack recurring billing in ASP.NET Core. This guide covers creating plans, subscribing customers through transaction initialization, handling renewal webhooks with C#, and cancelling subscriptions.

Read more

Paystack Callback URLs: Per-Transaction vs Dashboard Configuration

Paystack gives you two ways to set where customers land after paying: a default callback URL in your dashboard settings, or a per-transaction callback_url in the initialize request. Getting the priority order wrong, or not understanding how query parameters work, leads to lost customers and silent fulfillment failures.

Read more

Paystack Popup Not Opening: Frontend Debug Guide

The Paystack Inline JS popup silently fails to open for several reasons: the script did not load, the browser blocked it as a popup, PaystackPop is undefined, the public key is wrong, or a Content Security Policy blocks the iframe. This guide walks through each cause with a verification step.

Read more

Explore related topics

M-Pesa & African Stack

M-Pesa Daraja API tutorials, USSD development, and WhatsApp Business API guides. The African Stack that global bootcamps do not teach.

Learn to Code in Kenya

Everything a Kenyan needs to know before writing their first line of code. Career paths, real costs, hardware requirements, and honest answers about what it takes.

Learning to Code

Roadmaps, language guides, and practical advice for learning to code and launching a programming career.

Breaking Into Tech

Practical, no-nonsense guides for anyone looking to start or change their career in tech. From choosing your first language to acing interviews.

The African Stack: Skills for This Market

The skills that make Kenyan developers hard to replace: M-Pesa, USSD, WhatsApp, fintech, and building for the African market.

Bonaventure Ogeto

Bonaventure Ogeto

Founder, McTaba Labs

Bonaventure is the founder of McTaba Labs, a coding bootcamp in Nairobi, Kenya. He has spent years building products for the African market and mentoring the next generation of developers across the continent.