DEV Community

Cover image for Integrating Paystack payments in Django website
Joshua Hassan
Joshua Hassan

Posted on

Integrating Paystack payments in Django website

TABLE OF CONTENTS

Introduction

Paystack is a payment gateway that helps individuals and businesses in Africa to accept payments from anywhere in the world. In this guide, I will be showing you how to use Paystack as a third-party API to facilitate payments in your Django website.

Prerequisites

To follow through with this guide you will need:

  • A good understanding of Python
  • Basic understanding of Django
  • Basic understanding of Html
  • Usage of the Python request library
  • A Paystack account

Now, let's dive into the guide, all codes can be found on Github in this repository.

Project Setup

Create a folder, start a virtual environment and start basic project
create a virtual environment using virtualenv env
activate the virtual environment using source env/Scripts/activate
install django using pip install django this will install the latest version of django, however I am going to use version 3.0 in this guide, to install that do pip install django==3.0

Create a project and install apps
Now start the project using django-admin startproject zcore .
zcore is the name of the project and dot is used to start the project in the current directory.
Next step is two create two apps, one for authentication and another to handle all payments related code

python manage.py startapp accounts
python manage.py startapp payments
Enter fullscreen mode Exit fullscreen mode

project setup 1

project setup 2

project setup 3

Add the newly installed apps in settings.py
Also add paystack secret and public keys in settings.py



PAYSTACK_SECRET_KEY = ""
PAYSTACK_PUBLIC_KEY = ""


Enter fullscreen mode Exit fullscreen mode

where to find them on paystack

keys on paystack

Navigate into both folders created and add a new file called urls.py and add the following code



from django.urls import path

urlpatterns = [

]


Enter fullscreen mode Exit fullscreen mode

Include these urls in main url file zcore.urls
Your file should look like this



from  django.contrib  import  admin
from  django.urls  import  path, include

urlpatterns  = [
path('admin/', admin.site.urls),
path('accounts/', include("accounts.urls")),
path('payments/', include("payments.urls")),
]


Enter fullscreen mode Exit fullscreen mode

Creating models

Next step is to create a wallet model to keep track of funds deposited by a user and a payment model to describe what a payment is.
In payments.model



from django.db import models
from django.contrib.auth.models import User
from django.utils import timezone
import secrets
from .paystack  import  Paystack

# Create your models here.
class UserWallet(models.Model):
    user = models.OneToOneField(User, null=True, on_delete=models.CASCADE)
    currency = models.CharField(max_length=50, default='NGN')
    created_at = models.DateTimeField(default=timezone.now, null=True)

    def __str__(self):
        return self.user.__str__()

class Payment(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True)
    amount = models.PositiveIntegerField()
    ref = models.CharField(max_length=200)
    email = models.EmailField()
    verified = models.BooleanField(default=False)
    date_created = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ('-date_created',)

    def __str__(self):
        return f"Payment: {self.amount}"

    def save(self, *args, **kwargs):
        while not self.ref:
            ref = secrets.token_urlsafe(50)
            object_with_similar_ref = Payment.objects.filter(ref=ref)
            if not object_with_similar_ref:
                self.ref = ref

        super().save(*args, **kwargs)



Enter fullscreen mode Exit fullscreen mode

We will need another helper class to handle payment verification, so in the payments folder create a file called paystack.py
paste this block of code in the file



from django.conf import settings
import requests

class Paystack:
    PAYSTACK_SK = settings.PAYSTACK_SECRET_KEY
    base_url = "https://api.paystack.co/"

    def verify_payment(self, ref, *args, **kwargs):
        path = f'transaction/verify/{ref}'
        headers = {
            "Authorization": f"Bearer {self.PAYSTACK_SK}",
            "Content-Type": "application/json",
        }
        url = self.base_url + path
        response = requests.get(url, headers=headers)

        if response.status_code == 200:
            response_data = response.json()
            return response_data['status'], response_data['data']

        response_data = response.json()

        return response_data['status'], response_data['message']



Enter fullscreen mode Exit fullscreen mode

you will need to install requests for this so do a quick pip install requests
with that we can now add a verify payment property on our payment model



def amount_value(self):
        return int(self.amount) * 100

def verify_payment(self):
    paystack = Paystack()
    status, result = paystack.verify_payment(self.ref, self.amount)
    if status:
        if result['amount'] / 100 == self.amount:
            self.verified = True
        self.save()
    if self.verified:
        return True
    return False


Enter fullscreen mode Exit fullscreen mode

User registration and login

For the registration and login, I will be doing something very simple as it is not the focus of this guide.
Paste the code in accounts.views



from django.shortcuts import render, redirect
from django.contrib.auth.models import User
from payments.models import UserWallet
from django.contrib.auth import login, logout, authenticate
from django.contrib.auth.decorators import login_required

# Create your views here.
def register(request):
    if request.method == "POST":
        username = request.POST['username']
        password = request.POST['password']
        user = User.objects.create(username=username, password=password)
        UserWallet.objects.create(user=user)
    return render(request, "register.html")

def login_view(request):    
    if request.method == "POST":
        username = request.POST['username']
        password = request.POST['password']
        user = authenticate(username=username, password=password)
        if user is not None:
            login(request, user)
                    return redirect('initiate_payment')
        else:
            return redirect('login')

    return render(request, 'login.html')

@login_required(login_url="login")
def logout_view(request):
    logout(request)
    return redirect('login')



Enter fullscreen mode Exit fullscreen mode

Add the corresponding url paths for the views in urls.py



from  django.urls  import  path
from . import views

urlpatterns = [
    path('register/', views.register, name="register"),
    path('login/', views.login_view, name="login"),
    path('logout/', views.logout_view, name="logout"),
]


Enter fullscreen mode Exit fullscreen mode

We will use dummy login and register page from bootstrap, file can be found in the github repo .

Setup templates dirs by adding 'DIRS': [os.path.join(BASE_DIR, 'templates')] in settings.py

template dirs settings

At this point you can run python manage.py runserver and navigate to 127.0.0.1:8000/accounts/register in your browser window to see the registration page.

register page

You will see some warnings, to get rid of them makemigrations and migrate using;

python manage.py makemigrations 
python manage.py migrate
Enter fullscreen mode Exit fullscreen mode

Create a superuser so that we can login to django admin via 127.0.0.1:8000/admin to see all the magic happening.

migrations

python manage.py createsuperuser
Enter fullscreen mode Exit fullscreen mode

To see all payment objects in admin area register the payments model in payments.admin



from  django.contrib  import  admin
from .models  import  Payment, UserWallet

class  PaymentAdmin(admin.ModelAdmin):
    list_display  = ["id", "ref", 'amount', "verified", "date_created"]

admin.site.register(Payment, PaymentAdmin)
admin.site.register(UserWallet)



Enter fullscreen mode Exit fullscreen mode

Implementing deposit view

Create two views, one to initiate the payment and another to verify payments.
In payments.views



from django.shortcuts import render, redirect
from .models import Payment, UserWallet
from django.conf import settings

def initiate_payment(request):
    if request.method == "POST":
        amount = request.POST['amount']
        email = request.POST['email']

        pk = settings.PAYSTACK_PUBLIC_KEY

        payment = Payment.objects.create(amount=amount, email=email, user=request.user)
        payment.save()

        context = {
            'payment': payment,
            'field_values': request.POST,
            'paystack_pub_key': pk,
            'amount_value': payment.amount_value(),
        }
        return render(request, 'make_payment.html', context)

    return render(request, 'payment.html')


def verify_payment(request, ref):
    payment = Payment.objects.get(ref=ref)
    verified = payment.verify_payment()

    if verified:
        user_wallet = UserWallet.objects.get(user=request.user)
        user_wallet.balance += payment.amount
        user_wallet.save()
        print(request.user.username, " funded wallet successfully")
        return render(request, "success.html")
    return render(request, "success.html")



Enter fullscreen mode Exit fullscreen mode

In payment.urls



from django.urls import path
from . import views

urlpatterns = [
    path('initiate-payment/', views.initiate_payment, name='initiate_payment'),
    path('verify-payment/<str:ref>/', views.verify_payment, name='verify_payment'),
]



Enter fullscreen mode Exit fullscreen mode

The process flow goes like this;

  • User inputs an amount to fund his wallet
  • Clicks on pay then it takes them to page that displays the payment details
  • Then proceeds to pay by clicking the pay button, this will open a payment modal handled by paystack, when the process is complete paystack will send a request to the callback URL which we set up to confirm the details of the transaction.

payment flow 1

payment flow 2

payment flow 3

payment flow 4

payment flow 5
Code for the payment pages can be found in the repo

Conclusion

We have successfully integrated Paystack into our Django application and deposited an amount successfully.

All codes can be found in this repository

Reference

Paystack docs

Connect with me on Twitter | Github
I hope this was helpful

Thanks for reading.

Top comments (5)

Collapse
 
successgande1 profile image
Apollos Gande

I am yet to implement this but it looks simple to implement compare with those Paystack payment integration documents I have seen out there. Thanks and more grace

Collapse
 
joshthecodingaddict profile image
Joshua Hassan

thanks

Collapse
 
sanchez profile image
Sanchez

Thank you so much for this tutorial. It really helpful. Everything worked fine. I like the way you braked things down step by step. Thank you one more time.

Collapse
 
success_gande_0d47c0a229a profile image
Success Gande

I am currently implementing your solution for Django Paystack but I am confused. Do you used a inline JS? or how. I can't see anything of such here

Collapse
 
joshthecodingaddict profile image
Joshua Hassan

yes, you can check the code repository