A Python library for integrating with the M-Pesa payment system.
Project description
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.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file python_mpesa_daraja-1.0.0.post1.tar.gz.
File metadata
- Download URL: python_mpesa_daraja-1.0.0.post1.tar.gz
- Upload date:
- Size: 14.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.19
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
92dcd666079b91afdc47d0d3068dac9953aeb7082cedb3f28926a7a7291aac34
|
|
| MD5 |
7d97c5ef469266b034253d3d6dec3cd9
|
|
| BLAKE2b-256 |
0e6fc15c25a5ccbe35e58d55800d3bb40b2a49606caa8ccfdd349d5689a801b9
|
File details
Details for the file python_mpesa_daraja-1.0.0.post1-py3-none-any.whl.
File metadata
- Download URL: python_mpesa_daraja-1.0.0.post1-py3-none-any.whl
- Upload date:
- Size: 9.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.19
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
818edf98d069e544525ae128431ebcbe244ee3e6c2d8fb9aae6a1bd6917bd32b
|
|
| MD5 |
de924ffad92e9880480fa5c1f2c4340e
|
|
| BLAKE2b-256 |
1ad4004f8cbc36be3020ddf66417563a79715994dce99e4aa0dc9ba412898014
|