Accept Payments with Paystack in Django REST Framework
To accept Paystack payments in DRF, create an APIView that receives email and amount via a serializer, initializes a transaction with the Paystack API, and returns the authorization URL as JSON. Your SPA or mobile frontend redirects to Paystack or opens the inline popup. A separate APIView verifies the payment.
Setup
Install DRF and requests if not already installed:
pip install djangorestframework requests python-decouple
Add 'rest_framework' to INSTALLED_APPS in settings.py. Configure your Paystack keys as shown in the Django guide.
Payment Serializer
# payments/serializers.py
from rest_framework import serializers
class InitializePaymentSerializer(serializers.Serializer):
email = serializers.EmailField()
amount = serializers.DecimalField(max_digits=10, decimal_places=2, min_value=1)
class VerifyPaymentSerializer(serializers.Serializer):
reference = serializers.CharField(max_length=100)
Get weekly developer tips
Join 25,000+ developers. Practical guides, job tips, and new content — straight to your inbox.
No spam. Unsubscribe anytime.
Initialize Transaction APIView
# payments/views.py
import uuid
import requests as http_requests
from django.conf import settings
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from .serializers import InitializePaymentSerializer
from .models import Order
class InitializePaymentView(APIView):
def post(self, request):
serializer = InitializePaymentSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
email = serializer.validated_data['email']
amount = serializer.validated_data['amount']
reference = 'order_' + uuid.uuid4().hex[:12]
# Save order to database
order = Order.objects.create(
email=email,
amount=int(amount * 100),
reference=reference,
)
# Call Paystack
response = http_requests.post(
'https://api.paystack.co/transaction/initialize',
json={
'email': email,
'amount': order.amount,
'reference': reference,
'callback_url': request.build_absolute_uri('/api/payments/callback/'),
},
headers={
'Authorization': 'Bearer ' + settings.PAYSTACK_SECRET_KEY,
'Content-Type': 'application/json',
},
)
result = response.json()
if not result.get('status'):
return Response(
{'error': result.get('message', 'Initialization failed')},
status=status.HTTP_400_BAD_REQUEST,
)
return Response({
'authorization_url': result['data']['authorization_url'],
'access_code': result['data']['access_code'],
'reference': result['data']['reference'],
})
Verify Payment APIView
# payments/views.py (continued)
from django.utils import timezone
class VerifyPaymentView(APIView):
def get(self, request):
reference = request.query_params.get('reference')
if not reference:
return Response(
{'error': 'Missing reference'},
status=status.HTTP_400_BAD_REQUEST,
)
# Call Paystack verify
response = http_requests.get(
'https://api.paystack.co/transaction/verify/' + reference,
headers={
'Authorization': 'Bearer ' + settings.PAYSTACK_SECRET_KEY,
},
)
result = response.json()
if not result.get('status') or result['data']['status'] != 'success':
return Response(
{'error': 'Payment not successful'},
status=status.HTTP_400_BAD_REQUEST,
)
# Look up and validate order
try:
order = Order.objects.get(reference=reference)
except Order.DoesNotExist:
return Response(
{'error': 'Order not found'},
status=status.HTTP_404_NOT_FOUND,
)
if result['data']['amount'] != order.amount:
return Response(
{'error': 'Amount mismatch'},
status=status.HTTP_400_BAD_REQUEST,
)
# Idempotent fulfillment
updated = Order.objects.filter(
reference=reference, paid=False
).update(paid=True, paid_at=timezone.now())
return Response({
'success': True,
'already_fulfilled': updated == 0,
'amount': order.amount / 100,
'currency': order.currency,
})
URL Configuration
# payments/urls.py
from django.urls import path
from .views import InitializePaymentView, VerifyPaymentView
urlpatterns = [
path('initialize/', InitializePaymentView.as_view(), name='initialize_payment'),
path('verify/', VerifyPaymentView.as_view(), name='verify_payment'),
]
# project/urls.py
from django.urls import path, include
urlpatterns = [
path('api/payments/', include('payments.urls')),
]
Frontend Integration
Your React, Vue, or mobile frontend calls the API:
// Frontend (React/Vue/etc.)
async function initializePayment(email, amount) {
var res = await fetch('/api/payments/initialize/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Token ' + authToken,
},
body: JSON.stringify({ email: email, amount: amount }),
});
var data = await res.json();
if (data.authorization_url) {
// Redirect checkout
window.location.href = data.authorization_url;
}
}
For inline popup, use the access_code with Paystack Inline.js. For redirect checkout, use the authorization_url.
Security Notes
- Authentication. Add authentication to your initialize endpoint so only logged-in users can start payments.
- Amount validation. Look up the correct price from your product catalog. Never trust the client-submitted amount.
- Webhooks. Set up a webhook endpoint for reliable payment notification. See Handle Paystack Webhooks in DRF.
- CORS. Configure django-cors-headers to allow requests from your frontend domain.
Key Takeaways
- ✓DRF APIViews return JSON responses, making them ideal backends for React, Vue, Angular, or mobile apps using Paystack.
- ✓Use serializers to validate payment input (email, amount) before calling the Paystack API.
- ✓The Paystack secret key stays in your Django settings. The API returns only the authorization URL and access code to the client.
- ✓Set authentication_classes and permission_classes appropriately. Initialize endpoints usually require authentication. Webhook endpoints require none.
- ✓Amounts are in kobo (NGN), pesewas (GHS), or cents (ZAR/KES/USD). Validate and convert on the server.
- ✓For inline popup checkout, return the access_code. For redirect checkout, return the authorization_url.
Frequently Asked Questions
- Should I use function-based views or class-based APIViews?
- APIViews are the DRF convention and give you built-in serializer validation, content negotiation, and authentication handling. Use them for consistency with the rest of your DRF API.
- How do I add authentication to the payment endpoint?
- Set authentication_classes = [TokenAuthentication] and permission_classes = [IsAuthenticated] on your APIView. DRF handles the rest. You can then use request.user to get the customer email.
- Can I use ViewSets and Routers for Paystack endpoints?
- ViewSets work best for CRUD operations on models. Payment initialization and verification are action-oriented, not model CRUD, so APIViews are a better fit.
- How do I handle CORS with DRF and Paystack?
- Install django-cors-headers and add your frontend domain to CORS_ALLOWED_ORIGINS. This has nothing to do with Paystack directly. It allows your SPA to call your DRF backend.
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