Integrating Flutterwave Payment Gateway into Django: A Step-by-Step Guide

Joel InyangJoel Inyang
4 min read

For Django web applications looking to streamline online payments, Flutterwave offers a versatile payment gateway solution. This step-by-step guide will walk you through the process of integrating Flutterwave into your Django application.

Before we delve into the integration process, let's consider why Flutterwave might be the ideal choice for your Django project. Key factors to assess include ease of use, supported payment methods, security features, and comprehensive documentation.

Step 1: Create a Flutterwave Account

Begin by signing up for a Flutterwave account. Head over to the official flutterwave website and follow the straightforward registration process.
https://www.flutterwave.com/

Step 2: Obtain API Keys

Upon creating your account, navigate to the Flutterwave dashboard to retrieve your API keys and other essential credentials. These keys will be pivotal for authenticating requests from your Django application. These keys are;
1. Public Key
2. Secret Key

Step 3: Packages

You might want to install packages such as:

pip install cryptography
pip install pycryptodome

Step 4: Create models

Depending on your project requirements, you might need to expand or modify your models accordingly.
But find an example below;

class Payment(models.Model):
    user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
    email = models.EmailField()
    first_name = models.CharField(max_length=20, blank=True, null=True)
    total_amount = models.DecimalField(max_digits=10, decimal_places=2)
    reference = models.UUIDField(unique=True, default=uuid.uuid4, editable=False)
    status = models.CharField(max_length=20, default='pending')
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

Step 5: Setup Views

Depending on your project requirements, you might need to expand or modify your views accordingly but following this pattern.

from rest_framework.views import APIView
from rest_framework.response import Response
import requests
from rest_framework.permissions import IsAuthenticated


class InitiatePayment(APIView):

    permission_classes = [IsAuthenticated]

    def post(self, request):
        total_amount = request.data.get('total_amount')  # Change to POST for form data(if you are using templates) or request.data.get for JSON
        email = request.data.get('email')
        reference = str(uuid.uuid4())

        user = request.user

        if user.is_anonymous:
            return Response({'error': 'User is not authenticated'}, status=400)

        # Replace these with your actual Flutterwave details
        flutterwave_url = "https://api.flutterwave.com/v3/payments"
        secret_key = "FLUTTERWAVE_SECRET_KEY"  #"your_flutterwave_secret_key_here"

        payload = {
            "tx_ref": reference,
            "amount": total_amount,
            "currency": "NGN",
            "redirect_url": "http://127.0.0.1:8000/payment/callback",#(Note this url must be hosted)  # Replace with your callback URL
            "payment_type": "card",
            "customer": {
                "email": email
                }
            }

        headers = {
            "Authorization": f"Bearer {django_secret_key}",
            "Content-Type": "application/json"
        }

        try:
            payment = Payment(user=user, amount=total_amount, reference=reference, status="pending")
            payment.save()

            response = requests.post(flutterwave_url, json=payload, headers=headers)
            response_data = response.json()
            return Response(response_data, status=status.HTTP_200_OK)

        except requests.exceptions.RequestException as err:
            # Handle request exceptions
            return JsonResponse({'error': 'Payment initiation failed'}, status=500)
        except ValueError as err:
            # Handle JSON decoding error
            return JsonResponse({'error': 'Payment initiation failed'}, status=500)
class PaymentCallback(APIView):
    def get(self, request):
        status = request.GET.get('status')
        tx_ref = request.GET.get('tx_ref')

        if status == 'successful':
            try:
                payment = Payment.objects.get(reference=tx_ref)
                payment.status = "successful"
                payment.save()

                cart_items = Cart.objects.filter(user=payment.user, payment__isnull=True)
                payment.items.set(cart_items.values_list('items', flat=True))

                # Create an order with payment information
                order = Order.objects.create(
                    user=payment.user,
                    total_cost=payment.amount,
                    payment=payment
                )

                # Add the items related to the payment to the order
                order.items.set(cart_items.values_list('items', flat=True))
                cart_items.update(payment=payment)

                return Response({'message': 'Payment was successful'})

            except Payment.DoesNotExist:
                return Response({'error': 'Payment not found'}, status=status.HTTP_404_NOT_FOUND)

        return Response({'error': 'Payment failed'})

Step 6: Setup Serializer

Depending on your project requirements setup for your model. You can use this;

class PaymentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Payment
        fields = '__all__'

Step 7: Setup Urls

Depending on your project requirements setup for your model, so the initiate-payment url redirects to flutterwave web application where the payment transaction is done. After payment is successful it redirects to the payment-callback endpoint. You can use this;

from django.urls import path
from . import views
from django.views.decorators.csrf import csrf_exempt

urlpatterns = [
    path('initiate-payment/', csrf_exempt(views.InitiatePayment.as_view()), name='initiate_payment'),
    path('payment/callback/', views.PaymentCallback.as_view(), name='payment_callback'),
]

Step 8: Settings Configurations

Configure flutterwave payment gateway in to your django settings.
Find the structure below:

FLUTTERWAVE_PUBLIC_KEY = 'FLUTTERWAVE_PUBLIC_KEY'
FLUTTERWAVE_SECRET_KEY = 'FLUTTERWAVE_PUBLIC_KEY'

So far, this are all you need to integrate the flutterwave payment gateway successfully.

When you test the endpoint, you should see something like this below:

{ "status": "success", 
  "message": "Hosted Link", 
  "data": { 
        "link": "https://ravemodal-
                  dev.herokuapp.com/v3/hosted/pay/4426840j3nbf08f8e" } }

So depending on your response in the views determine, your response during testing and beyond.

I hope this helps.

Interested in giving feedback or having any issues or confusion?
Reach me via
twitter: Joel Inyang

linkedIn: Joel Inyang

3
Subscribe to my newsletter

Read articles from Joel Inyang directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Joel Inyang
Joel Inyang