M-Pesa Integration Using Python
Published on December 07, 2025
M-Pesa Integration In Django (Python)
Reusable for Different Python Projects with Minimal Changes
Before We Start: Creating the Django Project
Please ensure you have a Django project already running. If not, run the following:
# Make sure Python & pip are installed
# Create and activate a virtual environment (recommended)
python -m venv myenv
source myenv/bin/activate # Windows: myenv\Scripts\activate
# Install Django
pip install django
# Create Django project
django-admin startproject myproject
cd myproject
# Create M-Pesa integration app
python manage.py startapp mpesa
# Add "mpesa" to INSTALLED_APPS in settings.py
# Learn more:
# https://docs.djangoproject.com/en/4.2/intro/tutorial01/
Step One: M-Pesa Configuration File
Create mpesa_config.py inside your mpesa app and insert:
from decimal import Decimal
import requests, json, base64
from requests.auth import HTTPBasicAuth
from datetime import datetime
from django.conf import settings
class MpesaC2bCredential:
consumer_key = 'YOUR_CONSUMER_KEY_HERE'
consumer_secret = 'YOUR_CONSUMER_SECRET_HERE'
api_URL = 'https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials'
call_back_url = 'https://www.yourdomain.com/mpesa-callback/'
request_api_url = "https://sandbox.safaricom.co.ke/mpesa/stkpush/v1/processrequest"
class MpesaAccessToken:
@staticmethod
def get_access_token():
r = requests.get(MpesaC2bCredential.api_URL,
auth=HTTPBasicAuth(MpesaC2bCredential.consumer_key, MpesaC2bCredential.consumer_secret))
return json.loads(r.text)["access_token"]
class LipanaMpesaPassword:
@staticmethod
def get_password():
timestamp = datetime.now().strftime('%Y%m%d%H%M%S')
shortcode = "174379"
passkey = 'YOUR_PASSKEY_HERE'
data = shortcode + passkey + timestamp
password = base64.b64encode(data.encode()).decode('utf-8')
return password, timestamp, shortcode
Step Two: M-Pesa Utils File
Create mpesa_utils.py inside mpesa app:
import requests
from .mpesa_config import MpesaAccessToken, MpesaC2bCredential, LipanaMpesaPassword
def trigger_stk_push(phone_number, amount):
if phone_number.startswith('0'):
phone_number = '254' + phone_number[1:]
access_token = MpesaAccessToken.get_access_token()
api_url = MpesaC2bCredential.request_api_url
headers = {"Authorization": f"Bearer {access_token}"}
password, timestamp, shortcode = LipanaMpesaPassword.get_password()
payload = {
"BusinessShortCode": shortcode,
"Password": password,
"Timestamp": timestamp,
"TransactionType": "CustomerPayBillOnline",
"Amount": int(float(amount)),
"PartyA": phone_number,
"PartyB": shortcode,
"PhoneNumber": phone_number,
"CallBackURL": MpesaC2bCredential.call_back_url,
"AccountReference": "Webinar payment",
"TransactionDesc": "STK Push"
}
response = requests.post(api_url, json=payload, headers=headers)
data = response.json()
return data.get('CheckoutRequestID'), data.get('MerchantRequestID')
Step Three: Views Logic — Initiate Payment
Add this in your views.py:
from django.shortcuts import redirect, render
from django.contrib import messages
from django.http import JsonResponse
from mpesa.models import PaymentTransaction
from django.utils.timezone import make_aware
from .mpesa_utils import trigger_stk_push
from datetime import datetime
import json
def payment_page(request):
return render(request, "payment_page.html")
def initiate_mpesa_payment(request):
if request.method == 'POST':
phone_number = request.POST.get('phone_number')
amount = 100
checkout_id, merchant_id = trigger_stk_push(phone_number, amount)
PaymentTransaction.objects.create(
amount=amount,
phone_number=phone_number,
checkout_request_id=checkout_id,
merchant_request_id=merchant_id,
is_complete=False
)
messages.success(request, "STK push sent! Check your phone.")
return redirect("payment_page")
return redirect("payment_page")
Step Four: Callback Logic
def mpesa_callback(request):
data = json.loads(request.body)
callback = data['Body']['stkCallback']
checkout_id = callback['CheckoutRequestID']
result_code = callback['ResultCode']
tx = PaymentTransaction.objects.get(checkout_request_id=checkout_id)
if result_code != 0:
tx.is_complete = False
tx.save()
return JsonResponse({"error": "Failed"})
metadata = callback['CallbackMetadata']['Item']
tx.is_complete = True
tx.save()
return JsonResponse({"success": "OK"})
Step Five: URL Endpoints
path("pay/", views.payment_page, name="payment_page"),
path("initiate-payment/", views.initiate_mpesa_payment, name="initiate_payment"),
path("mpesa-callback/", views.mpesa_callback, name="mpesa_callback"),
🎯 You have successfully integrated M-Pesa STK Push into Django!
Test thoroughly in Safaricom Sandbox before going live. If stuck, reach out. I’m happy to help!