Skip to main content

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

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 API
  • consumer_secret (str): Consumer secret from Daraja API
  • pass_key (str): Lipa Na M-Pesa Online passkey
  • access_token_url (str): OAuth token endpoint URL
  • stk_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 number
  • amount (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 Till
  • transaction_desc (str): Transaction description
  • callback_url (str): URL to receive payment notifications

Returns:

  • dict: Response from M-Pesa API containing:
    • MerchantRequestID: Unique request ID
    • CheckoutRequestID: Unique checkout ID
    • ResponseCode: Response code
    • ResponseDescription: Response message
    • CustomerMessage: 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

  1. Use Environment Variables: Never hardcode credentials
  2. Implement Retry Logic: Handle connection errors gracefully
  3. Validate Callbacks: Verify callback authenticity using IP whitelisting
  4. Store Transaction Records: Log all CheckoutRequestIDs for reconciliation
  5. Handle Timeouts: Customer has 60 seconds to complete payment
  6. 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

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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

python_mpesa_daraja-1.0.0.tar.gz (14.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

python_mpesa_daraja-1.0.0-py3-none-any.whl (9.9 kB view details)

Uploaded Python 3

File details

Details for the file python_mpesa_daraja-1.0.0.tar.gz.

File metadata

  • Download URL: python_mpesa_daraja-1.0.0.tar.gz
  • Upload date:
  • Size: 14.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.10.19

File hashes

Hashes for python_mpesa_daraja-1.0.0.tar.gz
Algorithm Hash digest
SHA256 4e0b6c0d4394bd73855fb0ee1e9461062a87dfc49afd23b888eb2aec26c1c723
MD5 1814425797144b92bbbd693a1c08650e
BLAKE2b-256 fc30232790f35e648560f73a1347e0df8a728962c8c6b5d1109e65424bbf5f58

See more details on using hashes here.

File details

Details for the file python_mpesa_daraja-1.0.0-py3-none-any.whl.

File metadata

File hashes

Hashes for python_mpesa_daraja-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 426d109834fb250b5dfb7507fb7f2f2573db870153be2ee5db8f352ea52284da
MD5 ca53695f16f4a936d0897a99fedffc51
BLAKE2b-256 39124fd26d4ccea0d705e03a12b6effb24bfbc06963d4374c38629b8fc207397

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page