M-Pesa Python Integration Library
A simple Python library for integrating M-Pesa STK Push (Lipa Na M-Pesa Online) payments into your applications.
Features
- Easy STK Push integration
- Automatic phone number validation and formatting
- Secure token management with auto-refresh
- Amount validation with M-Pesa limits
- Comprehensive error handling
- Kenyan phone number support
Installation
pip install python-mpesa-daraja
Requirements
- Python 3.8+
- requests
- phonenumbers
Quick Start
from python_mpesa import MpesaGateway
# Initialize the gateway
mpesa = MpesaGateway(
business_shortcode="your_business_shortcode",
consumer_key="your_consumer_key",
consumer_secret="your_consumer_secret",
pass_key="your_pass_key",
access_token_url="https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials", # Use production URL in live environment
stk_push_url="https://sandbox.safaricom.co.ke/mpesa/stkpush/v1/processrequest" # Use production URL in live environment
)
# Initiate STK Push
response = mpesa.stk_push(
phone_number="0712345678",
amount=100,
account_reference="account_reference", # e.g., invoice number
transaction_type="CustomerPayBillOnline", # or "CustomerBuyGoodsOnline" for Till
transaction_desc="Payment description", # e.g., "Payment for Order #12345"
callback_url="your_callback_url" # e.g., "https://yourdomain.com/mpesa/callback"
)
print(response)
Configuration
Sandbox Environment
ACCESS_TOKEN_URL = "https://sandbox.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials"
STK_PUSH_URL = "https://sandbox.safaricom.co.ke/mpesa/stkpush/v1/processrequest"
Production Environment
ACCESS_TOKEN_URL = "https://api.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials"
STK_PUSH_URL = "https://api.safaricom.co.ke/mpesa/stkpush/v1/processrequest"
API Reference
MpesaGateway
The main class for interacting with the M-Pesa API.
Initialization
MpesaGateway(
business_shortcode: str,
consumer_key: str,
consumer_secret: str,
pass_key: str,
access_token_url: str,
stk_push_url: str
)
Parameters:
business_shortcode(str): Your M-Pesa business shortcode (Paybill or Till number)consumer_key(str): Consumer key from Daraja APIconsumer_secret(str): Consumer secret from Daraja APIpass_key(str): Lipa Na M-Pesa Online passkeyaccess_token_url(str): OAuth token endpoint URLstk_push_url(str): STK Push API endpoint URL
Methods
stk_push()
Initiates an STK Push payment request.
stk_push(
phone_number: str,
amount: int,
account_reference: str,
transaction_type: str,
transaction_desc: str,
callback_url: str
) -> dict
Parameters:
phone_number(str): Customer's phone numberamount(int): Amount to charge (1 - 250,000 KES)account_reference(str): Account reference (e.g., invoice number, order ID)transaction_type(str): Usually"CustomerPayBillOnline"for Paybill or"CustomerBuyGoodsOnline"for Tilltransaction_desc(str): Transaction descriptioncallback_url(str): URL to receive payment notifications
Returns:
dict: Response from M-Pesa API containing:MerchantRequestID: Unique request IDCheckoutRequestID: Unique checkout IDResponseCode: Response codeResponseDescription: Response messageCustomerMessage: Message to display to customer
Example:
response = mpesa.stk_push(
phone_number="0712345678",
amount=1500.50,
account_reference="ORDER-12345",
transaction_type="CustomerPayBillOnline",
transaction_desc="Payment for Order 12345",
callback_url="https://yourdomain.com/mpesa/callback"
)
Phone Number Validation
The library automatically validates and formats phone numbers for Kenyan mobile networks.
Supported Formats
# All these formats are valid and will be converted to 254712345678
"0712345678" # Local format
"254712345678" # International without +
"+254712345678" # International with +
"712345678" # Without leading zero
Validation Rules
- Must be a valid Kenyan phone number
- Automatically formatted to E.164 standard (without the + prefix)
Amount Validation
Amounts are validated against M-Pesa transaction limits.
Rules
- Must be a positive number
- Minimum: 1 KES
- Maximum: 250,000 KES
- Decimal values are converted to integers by discarding the fractional part (e.g., 1500.75 becomes 1500)
# Valid amounts
mpesa.stk_push(..., amount=100, ...) # ✓
mpesa.stk_push(..., amount=1500.75, ...) # ✓ Converted to 1500
mpesa.stk_push(..., amount=250000, ...) # ✓
# Invalid amounts
mpesa.stk_push(..., amount=0, ...) # ✗ InvalidAmountError
mpesa.stk_push(..., amount=-100, ...) # ✗ InvalidAmountError
mpesa.stk_push(..., amount=300000, ...) # ✗ InvalidAmountError
Error Handling
The library provides comprehensive error handling with custom exceptions.
Exception Types
InvalidPhoneNumberError
Raised when the phone number is invalid.
from python_mpesa.exceptions import InvalidPhoneNumberError
try:
response = mpesa.stk_push(
phone_number="1234", # Invalid
amount=100,
...
)
except InvalidPhoneNumberError as e:
print(f"Invalid phone number: {e}")
InvalidAmountError
Raised when the amount is invalid.
from python_mpesa.exceptions import InvalidAmountError
try:
response = mpesa.stk_push(
phone_number="0712345678",
amount=-100, # Invalid
...
)
except InvalidAmountError as e:
print(f"Invalid amount: {e}")
MpesaConnectionError
Raised when there's a connection error to M-Pesa servers.
from python_mpesa.exceptions import MpesaConnectionError
try:
response = mpesa.stk_push(...)
except MpesaConnectionError as e:
print(f"Connection error: {e}")
# Retry logic here
PaymentError
Raised for general payment processing errors.
from python_mpesa.exceptions import PaymentError
try:
response = mpesa.stk_push(...)
except PaymentError as e:
print(f"Payment error: {e}")
Complete Error Handling Example
from python_mpesa import MpesaGateway
from python_mpesa.exceptions import (
InvalidPhoneNumberError,
InvalidAmountError,
MpesaConnectionError,
PaymentError
)
mpesa = MpesaGateway(...)
try:
response = mpesa.stk_push(
phone_number="0712345678",
amount=1000,
account_reference="INV-001",
transaction_type="CustomerPayBillOnline",
transaction_desc="Payment",
callback_url="https://yourdomain.com/callback"
)
print(f"Success! Checkout ID: {response['CheckoutRequestID']}")
except InvalidPhoneNumberError as e:
print(f"Invalid phone number: {e}")
except InvalidAmountError as e:
print(f"Invalid amount: {e}")
except MpesaConnectionError as e:
print(f"Connection error: {e}")
# Implement retry logic
except PaymentError as e:
print(f"Payment failed: {e}")
Token Management
The library automatically handles OAuth token generation and refresh.
Features
- Automatic token generation on initialization
- Auto-refresh before expiration (tokens last ~3400 seconds)
- Transparent to the developer
Manual Token Refresh
While automatic refresh is built-in, you can also manually refresh:
# Token is automatically refreshed when needed
# No manual intervention required
mpesa.stk_push(...) # Token refreshed if expired
Callback Handling
When a payment is processed, M-Pesa sends a callback to your specified URL.
Example Callback Response
{
"Body": {
"stkCallback": {
"MerchantRequestID": "29115-34620561-1",
"CheckoutRequestID": "ws_CO_191220191020363925",
"ResultCode": 0,
"ResultDesc": "The service request is processed successfully.",
"CallbackMetadata": {
"Item": [
{
"Name": "Amount",
"Value": 1.00
},
{
"Name": "MpesaReceiptNumber",
"Value": "NLJ7RT61SV"
},
{
"Name": "TransactionDate",
"Value": 20191219102115
},
{
"Name": "PhoneNumber",
"Value": 254712345678
}
]
}
}
}
}
Flask Example
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/mpesa/callback', methods=['POST'])
def mpesa_callback():
data = request.get_json()
callback = data['Body']['stkCallback']
result_code = callback['ResultCode']
if result_code == 0:
# Payment successful
metadata = callback['CallbackMetadata']['Item']
receipt = next(item['Value'] for item in metadata if item['Name'] == 'MpesaReceiptNumber')
print(f"Payment successful! Receipt: {receipt}")
else:
# Payment failed
print(f"Payment failed: {callback['ResultDesc']}")
return jsonify({"ResultCode": 0, "ResultDesc": "Success"})
Django Example
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
import json
@csrf_exempt
def mpesa_callback(request):
if request.method == 'POST':
data = json.loads(request.body)
callback = data['Body']['stkCallback']
result_code = callback['ResultCode']
if result_code == 0:
# Payment successful
metadata = callback['CallbackMetadata']['Item']
receipt = next(item['Value'] for item in metadata if item['Name'] == 'MpesaReceiptNumber')
# Save to database
return JsonResponse({"ResultCode": 0, "ResultDesc": "Success"})
Advanced Usage
Environment Variables
Store sensitive credentials in environment variables:
import os
from python_mpesa import MpesaGateway
mpesa = MpesaGateway(
business_shortcode=os.getenv("MPESA_SHORTCODE"),
consumer_key=os.getenv("MPESA_CONSUMER_KEY"),
consumer_secret=os.getenv("MPESA_CONSUMER_SECRET"),
pass_key=os.getenv("MPESA_PASS_KEY"),
access_token_url=os.getenv("MPESA_TOKEN_URL"),
stk_push_url=os.getenv("MPESA_STK_PUSH_URL")
)
Logging
Add logging for debugging:
import logging
from python_mpesa import MpesaGateway
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
mpesa = MpesaGateway(...)
try:
response = mpesa.stk_push(...)
logger.info(f"STK Push initiated: {response['CheckoutRequestID']}")
except Exception as e:
logger.error(f"STK Push failed: {str(e)}")
Testing
For testing, use M-Pesa sandbox credentials:
# Sandbox test credentials
BUSINESS_SHORTCODE = "174379"
PASS_KEY = "bfb279f9aa9bdbcf158e97dd71a467cd2e0c893059b10f78e6b72ada1ed2c919"
# Test phone numbers (use actual Safaricom numbers registered in sandbox)
TEST_PHONE = "254712345678"
Best Practices
- Use Environment Variables: Never hardcode credentials
- Implement Retry Logic: Handle connection errors gracefully
- Validate Callbacks: Verify callback authenticity using IP whitelisting
- Store Transaction Records: Log all CheckoutRequestIDs for reconciliation
- Handle Timeouts: Customer has 60 seconds to complete payment
- Test Thoroughly: Use sandbox environment before going live
Common Issues
Issue: "Invalid Access Token"
Solution: Check that your consumer key and secret are correct.
Issue: "Invalid Shortcode"
Solution: Ensure the shortcode matches your Paybill/Till number.
Issue: "Unable to lock subscriber"
Solution: Customer's phone is busy with another transaction. Retry after a few seconds.
Issue: "Timeout"
Solution: Customer didn't complete payment within 60 seconds. This is normal behavior.
Response Codes
| Code | Description |
|---|---|
| 0 | Success |
| 1 | Insufficient Balance |
| 1032 | Request cancelled by user |
| 1037 | Timeout (user didn't enter PIN) |
| 2001 | Invalid initiator information |
Support & Resources
- M-Pesa Daraja API: https://developer.safaricom.co.ke/
- API Documentation: https://developer.safaricom.co.ke/dashboard/apis?api=MpesaExpressSimulate
- Sandbox Portal: https://developer.safaricom.co.ke/dashboard/myapps
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
License
MIT License - see LICENSE file for details
Changelog
Version 1.0.0
- Initial release
- STK Push integration
- Phone number validation
- Amount validation
- Automatic token refresh
- Comprehensive error handling
Made with ❤️ for Developers integrating M-Pesa payments in Python applications.
Metadata
Release files for python-mpesa-daraja 1.0.0.post1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| python_mpesa_daraja-1.0.0.post1.tar.gz | 14.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| python_mpesa_daraja-1.0.0.post1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 24.2 kB
Release files / python_mpesa_daraja-1.0.0.post1.tar.gz
| Download URL | python_mpesa_daraja-1.0.0.post1.tar.gz |
|---|---|
| Size | 14.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
92dcd666079b91afdc47d0d3068dac9953aeb7082cedb3f28926a7a7291aac34
|
|
BLAKE2b-256 checksum How to use checksums |
0e6fc15c25a5ccbe35e58d55800d3bb40b2a49606caa8ccfdd349d5689a801b9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.10.19
|
Release files / python_mpesa_daraja-1.0.0.post1-py3-none-any.whl
| Download URL | python_mpesa_daraja-1.0.0.post1-py3-none-any.whl |
|---|---|
| Size | 9.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
818edf98d069e544525ae128431ebcbe244ee3e6c2d8fb9aae6a1bd6917bd32b
|
|
BLAKE2b-256 checksum How to use checksums |
1ad4004f8cbc36be3020ddf66417563a79715994dce99e4aa0dc9ba412898014
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.10.19
|