Hello 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 answers of M-pesa Integration using Django web framework and Python 3.7. In lesson 5, we learned how to register confirmation and validation URL’s, however, we had some challenges with getting a successful response from Safaricom M-pesa API, in this lesson we are going to learn how we can fix our bug.

The Error we encountered in Lesson 5

In lesson 5, we successfully called Safaricom sandbox C2B register endpoint we got the following error.

Where the response code was 409, in the HTTP status code, the 409 code is used in situations where the user might able to resolve the conflict. You can learn more about the HTTP code status here.

Fixing Registering Confirmation and Validation

The first step to do is to log in to the M-pesa Daraja portal and visit this URL: https://developer.safaricom.co.ke/test_credentials this is where you get the test credentials for C2B sandbox. Here is a screenshot of the page:

test credentials

If you take a close look at the test credentials provided by Safaricom you will see we have the following:

Shortcode 1 – 600344 Shortcode 2 – 600000 Lipa na Mpesa Online Shortcode – 174379

Our Bug

In lesson 3, we learned how to create an STK push and we used Lipa na Mpesa Online Shortcode (174379). When you open mpesa_api/mpesa_credentials.py file you get the following code:

import requests import json from requests.auth import HTTPBasicAuth from datetime import datetime import base64 class MpesaC2bCredential: consumer_key = 'cHnkwYIgBbrxlgBoneczmIJFXVm0oHky' consumer_secret = '2nHEyWSD4VjpNh2g' api_URL = 'https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials' class MpesaAccessToken: r = requests.get(MpesaC2bCredential.api_URL, auth=HTTPBasicAuth(MpesaC2bCredential.consumer_key, MpesaC2bCredential.consumer_secret)) mpesa_access_token = json.loads(r.text) validated_mpesa_access_token = mpesa_access_token['access_token'] class LipanaMpesaPpassword: lipa_time = datetime.now().strftime('%Y%m%d%H%M%S') Business_short_code = "174379" passkey = 'bfb279f9aa9bdbcf158e97dd71a467cd2e0c893059b10f78e6b72ada1ed2c919' data_to_encode = Business_short_code + passkey + lipa_time online_password = base64.b64encode(data_to_encode.encode()) decode_password = online_password.decode('utf-8')

On line 23, we defined our Shortcode which is Lipa na Mpesa Online Shortcode. From Safaricom, “the Lipa na Mpesa Online Shortcode is meant for STK push only.” And that is why we were not able to register our confirmation and validation URL.

To fix our bug, update mpesa_api/mpesa_credentials.py file and make sure it has the following code:

import requests import json from requests.auth import HTTPBasicAuth from datetime import datetime import base64 class MpesaC2bCredential: consumer_key = 'cHnkwYIgBbrxlgBoneczmIJFXVm0oHky' consumer_secret = '2nHEyWSD4VjpNh2g' api_URL = 'https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials' class MpesaAccessToken: r = requests.get(MpesaC2bCredential.api_URL, auth=HTTPBasicAuth(MpesaC2bCredential.consumer_key, MpesaC2bCredential.consumer_secret)) mpesa_access_token = json.loads(r.text) validated_mpesa_access_token = mpesa_access_token['access_token'] class LipanaMpesaPpassword: lipa_time = datetime.now().strftime('%Y%m%d%H%M%S') Business_short_code = "174379" Test_c2b_shortcode = "600344" passkey = 'bfb279f9aa9bdbcf158e97dd71a467cd2e0c893059b10f78e6b72ada1ed2c919' data_to_encode = Business_short_code + passkey + lipa_time online_password = base64.b64encode(data_to_encode.encode()) decode_password = online_password.decode('utf-8')

On line 24, we have defined a new variable called Test_c2b_shortcode and assigned it Shortcode 1 (600344). 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.Test_c2b_shortcode, "ResponseType": "Completed", "ConfirmationURL": "https://79372821.ngrok.io/api/v1/c2b/confirmation", "ValidationURL": "https://79372821.ngrok.io/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))

The only thing we have changed is on line 49, we are passing the new Shortcode 600344 to our options when registering our urls.

Running Our Local Development Server Using Ngrok

Running our application our Django application using Ngrok is super easy, we are assuming that you have already downloaded Ngrok to your machine. On my terminal, I am going to run my Django application using the following command:

python manage.py runserver

Here is the output of my terminal:

Development server

From my terminal, I am able to tell that my Django application is running on port 8000. The next is run my Ngrok, All I have to do is to locate my Ngrok download and run the following command:

./ngrok http 8000

Here is the output of my terminal:

Ngrok server

From my terminal, I am able to locate the secure tunnel which is forwarding to my localhost at port 8000. Our secure URL is the one that has https, in this case, https://79372821.ngrok.io/

NB: Remember to update your settings.py file and views.py file as we did in lesson 5

How to Register Confirmation and Validation URL

To register the confirmation and validation URL, we are going to use a postman. Open postman and in the URL use https://91563395.ngrok.io/api/c2b/register

And hit send.

NB: Make sure to replace the URL with your Ngrok URL.

Here is a screenshot of my postman results:

Now we get a success message from Safaricom Mpesa API. Cheers to a job well done!

Task

Now you are all set with C2B M-pesa Integration, in this tutorial series we have learned a lot of great stuff regard Mpesa API integration using the Django web framework. I believe now we ready to take up the next challenge of exploring Mpesa API on our own.

The following are some of the Mpesa API you can explore:

B2C Transactions status Account balance.

Mpesa API’s

Important Links

With that, we conclude the Mpesa Tutorial series. It has been a joy learning and sharing knowledge on Mpesa API integration using the Django web framework and python 3.7.

Goal Achieved in This Lesson

In this lesson, we have achieved the following:

We have learned how to debug our URL registering error. We have successfully registered our confirmation and validation urls.

To get the code associated with this lesson visit Python/Django Mpesa Integration.