Hey and welcome to this Python/Django tutorial series, my name is Henry Mbugua and I will be taking you through the various aspect and new answer of M-pesa Integration using Django web framework and Python 3.7. In lesson three we learned how to develop an STK push integration, in this lesson we are going to learn how to develop customer to business mpesa integration (C2B).
Problem Statement
From small business to multi-billion companies are using paybill number which allows their customer to pay for services and goods via Mpesa. One way for businesses to manage the Mpesa transactions is to wait for Mpesa statement every end month. However, with Mpesa API integration, business can link their systems to Mpesa and effortlessly process payments in real-time.
Database Preparation
Before we can begin to develop our C2B integration to Mpesa api, we need to prepare our database in order for us to be able to store the Mpesa transactions. The next step is to open the mpesa_api/models.py file and make sure it has the following code:
from django.db import models class BaseModel(models.Model): created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: abstract = True # M-pesa Payment models class MpesaCalls(BaseModel): ip_address = models.TextField() caller = models.TextField() conversation_id = models.TextField() content = models.TextField() class Meta: verbose_name = 'Mpesa Call' verbose_name_plural = 'Mpesa Calls' class MpesaCallBacks(BaseModel): ip_address = models.TextField() caller = models.TextField() conversation_id = models.TextField() content = models.TextField() class Meta: verbose_name = 'Mpesa Call Back' verbose_name_plural = 'Mpesa Call Backs' class MpesaPayment(BaseModel): amount = models.DecimalField(max_digits=10, decimal_places=2) description = models.TextField() type = models.TextField() reference = models.TextField() first_name = models.CharField(max_length=100) middle_name = models.CharField(max_length=100) last_name = models.CharField(max_length=100) phone_number = models.TextField() organization_balance = models.DecimalField(max_digits=10, decimal_places=2) class Meta: verbose_name = 'Mpesa Payment' verbose_name_plural = 'Mpesa Payments' def __str__(self): return self.first_name
Let’s understand the code in this file, each model is represented by a class that subclasses django.db.models.Model. Each model has a number of a class variable, each of which represents a database field in each model. Each field is represented by an instance of a field class e.g.:
- CharField – for character fields
- DateTimeField – for date times.
- TextField – for the large text field.
- DecimalField – for a fixed-precision decimal number.
Learn more about Django model field reference. Now let’s have a look at the class we have created in our mpesa_api/models.py file:
- Line 1 – we import the Django model class.
- Line 4 to 9 – we create a class called BaseModel which subclasses from the Django model. The main reason for doing this is to track at what time a record was recorded in the database without repetition. Django encourages developers to follow the dry principle.
- Line 14 to 22 – we create a class called MpesaCalls. This class has four fields for capturing the IP address, caller, conversation_id, content. In case you need to do validation before accepting payments, you can use this model to store the MpesaCalls for later analysis.
- Line 25 to 33 – we create a class called MpesaCallBacks which has similar fields as MpesaCalls. This is used to store accepted Mpesa transactions without accessing each field in the body.
- Line 36 to 52 – we create a class called MpesaPayment which we use to store successful transactions. We store the amount, description of the payment, type of payment, reference, first name, middle name, last name, phone number used for payment and organization mpesa balance.
Now that we have database classes ready, on your terminal navigate to where manage.py file is located and run the following command:
python manage.py makemigrations mpesa_api
You should see something similar to the following:
By running makemigrations, you are telling Django that you have made some changes to your model (in our case, we have created new ones) and that you would like the changes to be stored as a migration. Now, run migrate to create our Mpesa model tables in our database:
python manage.py migrate
You should see something similar to the following:
Learn more about django migration systems.
Registering Our Mpesa Model in Django Admin
First, we will need to create a user who can log in to the Django admin site. Run the following command:
python manage.py createsuperuser
Fill in your desired username, email, and password. Let’s start our development server by running the following command:
python manage.py runserver
Now, open your favorite browser and go to “/admin/” on your local domain – e.g., http://127.0.0.1:8000/admin/ you should see the following login screen:
Now, try logging in with the superuser account you created in the previous step. You should see the following Django admin index page:
Great, we are going to add our Mpesa payment model to show up here so that we are able to monitor payments transactions. Just one thing to do, we need to tell the admin that MpesaPayment objects should appear in Django admin interface. To do this, open the mpesa_api/admin.py file, and edit it to look like this:
from django.contrib import admin from .models import MpesaPayment admin.site.register(MpesaPayment)
Now that we have registered MpesaPayment, Django knows that it should be displayed on the admin index page:
You can explore the free Django admin functionality. Learn more about customizing Django Admin.
Mpesa C2B integration Code
Now, open mpesa_api/views.py file and make sure it has the following code:
from django.http import HttpResponse, JsonResponse import requests from requests.auth import HTTPBasicAuth import json from . mpesa_credentials import MpesaAccessToken, LipanaMpesaPpassword from django.views.decorators.csrf import csrf_exempt from .models import MpesaPayment def getAccessToken(request): consumer_key = 'cHnkwYIgBbrxlgBoneczmIJFXVm0oHky' consumer_secret = '2nHEyWSD4VjpNh2g' api_URL = 'https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials' r = requests.get(api_URL, auth=HTTPBasicAuth(consumer_key, consumer_secret)) mpesa_access_token = json.loads(r.text) validated_mpesa_access_token = mpesa_access_token['access_token'] return HttpResponse(validated_mpesa_access_token) def lipa_na_mpesa_online(request): access_token = MpesaAccessToken.validated_mpesa_access_token api_url = "https://sandbox.safaricom.co.ke/mpesa/stkpush/v1/processrequest" headers = {"Authorization": "Bearer %s" % access_token} request = { "BusinessShortCode": LipanaMpesaPpassword.Business_short_code, "Password": LipanaMpesaPpassword.decode_password, "Timestamp": LipanaMpesaPpassword.lipa_time, "TransactionType": "CustomerPayBillOnline", "Amount": 1, "PartyA": 254728851119, # replace with your phone number to get stk push "PartyB": LipanaMpesaPpassword.Business_short_code, "PhoneNumber": 254728851119, # replace with your phone number to get stk push "CallBackURL": "https://sandbox.safaricom.co.ke/mpesa/", "AccountReference": "Henry", "TransactionDesc": "Testing stk push" } response = requests.post(api_url, json=request, headers=headers) return HttpResponse('success') @csrf_exempt def register_urls(request): access_token = MpesaAccessToken.validated_mpesa_access_token api_url = "https://sandbox.safaricom.co.ke/mpesa/c2b/v1/registerurl" headers = {"Authorization": "Bearer %s" % access_token} options = {"ShortCode": LipanaMpesaPpassword.Business_short_code, "ResponseType": "Completed", "ConfirmationURL": "http://127.0.0.1:8000/api/v1/c2b/confirmation", "ValidationURL": "http://127.0.0.1:8000/api/v1/c2b/validation"} response = requests.post(api_url, json=options, headers=headers) return HttpResponse(response.text) @csrf_exempt def call_back(request): pass @csrf_exempt def validation(request): context = { "ResultCode": 0, "ResultDesc": "Accepted" } return JsonResponse(dict(context)) @csrf_exempt def confirmation(request): mpesa_body =request.body.decode('utf-8') mpesa_payment = json.loads(mpesa_body) payment = MpesaPayment( first_name=mpesa_payment['FirstName'], last_name=mpesa_payment['LastName'], middle_name=mpesa_payment['MiddleName'], description=mpesa_payment['TransID'], phone_number=mpesa_payment['MSISDN'], amount=mpesa_payment['TransAmount'], reference=mpesa_payment['BillRefNumber'], organization_balance=mpesa_payment['OrgAccountBalance'], type=mpesa_payment['TransactionType'], ) payment.save() context = { "ResultCode": 0, "ResultDesc": "Accepted" } return JsonResponse(dict(context))
Let’s understand the code we have added:
- Line 1 – we import Json response from Django HTTP response.
- Line 6 – we import Django csrf_exempt decorator which we are going to use to allow MpesaCalls to post data in our applications.
- Line 7 – we import MpesaPayment model.
- Line 45 to 55 – we create a function called register_urls. We use this method to register our confirmation and validation URL with Safaricom.
- Line 46 – we access out mpesa access token.
- Line 47 – we pass the mpesa URL for registering the urls.
- Line 48 – we pass our mpesa tokens to the header of the request.
- Line 49 to 52 – we define a variable called option where are passing our Shortcode, ResponseType, ConfirmationUrl, and ValidationUrl.
- Line 53 – we initiate a post request to mpesa api bypassing the URL, our option variable and passing the headers.
- Line 55 – we return the mpesa response as an HTTP response.
- Line 59 to 60 – we define a function called call_back. In this method, you can capture the mpesa calls. At the moment, the method does nothing.
- Line 64 to 70 – we define our validation function. In this function, you can do a lot but for this tutorial purpose, we create a context and we accept the payment by responding with ResultCode: 0 and ResultDesc: Accepted. Note if change 0 to any other number, you reject the payment.
- Line 70 – we turn our context to json format since Mpesa expects json format.
- Line 74 to 97 – we define a function called confirmation which we use to save successfully transaction in our database.
- Line 75 – we get the mpesa transaction from the body by decoding using utf-8
- Line 76 – we use json.loads method which will assist us to access variables in our request.
- Line 78 to 89 – we create MpesaPayment model instance.
- Line 79 – we access the first name in mpesa transaction and map it to our database first name field
- Line 80 – we access the last name in mpesa transaction and map it to our database last name field
- Line 81 – we access middle name in mpesa transaction and map it to our database middle name field
- Line 82 – we access TransID in mpesa transaction and map it to our database description field
- Line 83 – we access MSISDN in mpesa transaction and map it to our database phone number field
- Line 84 – we access TransAmount in mpesa transaction and map it to our database amount field
- Line 85 – we access BillRefNumber in mpesa transaction and map it to our database reference field
- Line 86 – we access OrgAccountBalance in mpesa transaction and map it to our database organization balance field
- Line 87 – we access TransactionType in mpesa transaction and map it to our database type field
- Line 90 – we save our instance in the database.
- Line 92 to 95 – we a create context where we define transaction acceptance
- Line 97 – we return a json response bypassing our context.
Phew! That was a lot of work. We are going to end this tutorial there. In the next tutorial, we will be wrapping the C2B mpesa integrations.
Task Before Next Lesson
Since Safaricom cannot call our localhost, we will need to find a creative way to create a secure introspectable tunnel to our localhost. In order to achieve this, we will use Ngrok. The task is to familiarize yourself with Ngrok.
Goal Achieved in This Lesson
In this lesson, we have achieved the following:
- We have prepared our database to store the C2B transactions.
- We have learned how to register our MpesaPayment object in Django Admin.
- We have created our logic for the Mpesa C2B integration.
To get the code associated with this lesson visit Python/Django Mpesa Integration. See you in lesson 5 where we will do the actual test of C2B integration.
Facebook Comments