Skip to main content

Python SDK for Pesapal Payment Gateway API 3.0

Project description

Pesapal Python SDK

Python SDK for Pesapal Payment Gateway API 3.0 - Clean, async interface for payment processing.

Installation

pip install pesapal-python-sdk

Quick Start

Prerequisites

  1. Pesapal Account: Sign up at developer.pesapal.com
  2. Credentials: Get your consumer_key and consumer_secret
  3. Callback URL: A publicly accessible HTTPS endpoint for payment notifications

Basic Example

import asyncio
from pesapal import PesapalClient, PaymentRequest
from decimal import Decimal

async def main():
    # Initialize client
    client = PesapalClient(
        consumer_key="your_consumer_key",
        consumer_secret="your_consumer_secret",
        sandbox=True  # Set to False for production
    )
    
    # Step 1: Register IPN (Instant Payment Notification)
    ipn_response = await client.register_ipn(
        ipn_url="https://your-domain.com/callback",
        ipn_notification_type="GET"
    )
    ipn_id = ipn_response.notification_id
    
    # Step 2: Create payment request
    payment = PaymentRequest(
        id="ORDER-123",
        amount=Decimal("50000.00"),
        currency="TZS",
        description="Payment for order #123",
        callback_url="https://your-domain.com/callback",
        notification_id=ipn_id,  # Use the registered IPN ID
        billing_address={
            "email_address": "customer@example.com",
            "phone_number": "+255123456789",
            "country_code": "TZ",
            "first_name": "John",
            "last_name": "Doe",
            "line_1": "123 Main Street",
            "city": "Dar es Salaam",
            "postal_code": "11101"
        }
    )
    
    # Step 3: Submit payment
    response = await client.submit_order(payment)
    print(f"Redirect URL: {response.redirect_url}")
    print(f"Tracking ID: {response.order_tracking_id}")
    
    # Step 4: Check payment status
    status = await client.get_payment_status(response.order_tracking_id)
    print(f"Status: {status.payment_status_description}")

asyncio.run(main())

Features

  • ✅ Async/await support
  • ✅ Type-safe with Pydantic models
  • ✅ Payment processing & status checking
  • ✅ Refunds & cancellations
  • ✅ IPN management
  • ✅ Webhook signature verification
  • ✅ Sandbox & production modes

Usage Guide

1. Initialize Client

from pesapal import PesapalClient

# Sandbox (testing)
client = PesapalClient(
    consumer_key="your_key",
    consumer_secret="your_secret",
    sandbox=True
)

# Production
client = PesapalClient(
    consumer_key="your_key",
    consumer_secret="your_secret",
    sandbox=False
)

2. Register IPN (One-Time Setup)

Register your IPN URL once and save the IPN ID for all future payments.

# Register IPN URL
ipn = await client.register_ipn(
    ipn_url="https://your-domain.com/callback",
    ipn_notification_type="GET"  # or "POST"
)

# Save this IPN ID
ipn_id = ipn.notification_id
print(f"IPN ID: {ipn_id}")  # Store this in your database/config

# List all registered IPNs
ipn_list = await client.get_registered_ipns()
for registered_ipn in ipn_list:
    print(f"{registered_ipn.notification_id}: {registered_ipn.ipn_url}")

3. Create Payment Request

from pesapal import PaymentRequest
from decimal import Decimal

payment = PaymentRequest(
    id="ORDER-001",  # Unique order ID (max 50 chars)
    amount=Decimal("75000.00"),  # Use Decimal for currency
    currency="TZS",  # TZS, KES, UGX, RWF, USD
    description="Product purchase",  # Max 100 chars
    callback_url="https://your-domain.com/callback",
    notification_id=ipn_id,  # From step 2
    billing_address={
        "email_address": "customer@example.com",
        "phone_number": "+255712345678",
        "country_code": "TZ",
        "first_name": "Jane",
        "middle_name": "",  # Optional
        "last_name": "Smith",
        "line_1": "456 Market Street",
        "line_2": "",  # Optional
        "city": "Dar es Salaam",
        "state": "",  # Optional
        "postal_code": "11102",
        "zip_code": "11102"  # Same as postal_code
    }
)

4. Submit Payment Order

response = await client.submit_order(payment)

# Response contains:
print(f"Tracking ID: {response.order_tracking_id}")
print(f"Redirect URL: {response.redirect_url}")

# Redirect customer to response.redirect_url

5. Check Payment Status

# Using tracking ID (recommended)
status = await client.get_payment_status(
    order_tracking_id="tracking-id-from-response"
)

print(f"Status: {status.payment_status_description}")
print(f"Status Code: {status.status_code}")  # "0"=Pending, "1"=Completed, "2"=Failed
print(f"Amount: {status.amount} {status.currency}")
print(f"Payment Method: {status.payment_method}")
print(f"Confirmation Code: {status.confirmation_code}")

6. Process Refund

result = await client.refund_order(
    confirmation_code="confirmation-code-from-payment",
    amount=Decimal("25000.00"),  # Refund amount (must be <= original amount)
    username="admin",  # Your username/identifier
    remarks="Customer requested refund"
)

print(f"Refund Status: {result.get('status')}")
print(f"Refund Message: {result.get('message')}")

7. Cancel Pending Order

result = await client.cancel_order(
    order_tracking_id="tracking-id"
)

print(f"Cancel Status: {result.get('status')}")

Complete Payment Flow Example

import asyncio
from pesapal import PesapalClient, PaymentRequest, PesapalError
from decimal import Decimal

async def complete_payment_flow():
    # Initialize client
    client = PesapalClient(
        consumer_key="your_consumer_key",
        consumer_secret="your_consumer_secret",
        sandbox=True
    )
    
    # 1. Register IPN (do this once, reuse the IPN ID)
    try:
        ipn = await client.register_ipn(
            ipn_url="https://your-domain.com/callback",
            ipn_notification_type="GET"
        )
        print(f"Registered IPN ID: {ipn.notification_id}")
    except PesapalError as e:
        print(f"IPN already registered or error: {e}")
        # Use existing IPN ID
        ipn_id = "your-existing-ipn-id"
    else:
        ipn_id = ipn.notification_id
    
    # 2. Create payment request
    payment = PaymentRequest(
        id="ORDER-TZS-001",
        amount=Decimal("75000.00"),
        currency="TZS",
        description="Online purchase",
        callback_url="https://your-domain.com/callback",
        notification_id=ipn_id,
        billing_address={
            "email_address": "customer@example.com",
            "phone_number": "+255712345678",
            "country_code": "TZ",
            "first_name": "Jane",
            "middle_name": "",
            "last_name": "Smith",
            "line_1": "456 Market Street",
            "line_2": "",
            "city": "Dar es Salaam",
            "state": "",
            "postal_code": "11102",
            "zip_code": "11102"
        }
    )
    
    # 3. Submit payment order
    try:
        response = await client.submit_order(payment)
        print(f"Payment URL: {response.redirect_url}")
        print(f"Tracking ID: {response.order_tracking_id}")
    except PesapalError as e:
        print(f"Payment submission failed: {e}")
        return None
    
    # 4. Check payment status
    status = await client.get_payment_status(response.order_tracking_id)
    print(f"Payment Status: {status.payment_status_description}")
    print(f"Amount: {status.amount} {status.currency}")
    
    return response

asyncio.run(complete_payment_flow())

Webhook Signature Verification

Verify webhook signatures to ensure requests are from Pesapal:

from pesapal.utils import verify_webhook_signature

# In your webhook handler
webhook_data = {
    "OrderTrackingId": "...",
    "OrderMerchantReference": "...",
    "OrderNotificationType": "IPNCHANGE"
}

signature = request.headers.get("X-Pesapal-Signature")
is_valid = verify_webhook_signature(
    webhook_data,
    signature,
    consumer_secret="your_consumer_secret"
)

if is_valid:
    # Process webhook
    pass
else:
    # Reject webhook
    pass

Error Handling

from pesapal import (
    PesapalError,
    PesapalAPIError,
    PesapalAuthenticationError,
    PesapalValidationError,
    PesapalNetworkError
)

try:
    response = await client.submit_order(payment)
except PesapalAuthenticationError:
    print("Authentication failed - check your credentials")
except PesapalValidationError as e:
    print(f"Validation error: {e} - check your payment request data")
except PesapalAPIError as e:
    print(f"API error: {e} - Pesapal returned an error")
except PesapalNetworkError as e:
    print(f"Network error: {e} - check your internet connection")
except PesapalError as e:
    print(f"General error: {e}")

Payment Flow

1. Initialize Client
   → PesapalClient(consumer_key, consumer_secret, sandbox=True)

2. Register IPN (one-time setup)
   → client.register_ipn(ipn_url, ipn_notification_type="GET")
   → Save the returned notification_id

3. Create Payment Request
   → PaymentRequest(id, amount, currency, callback_url, notification_id, billing_address)

4. Submit Order
   → client.submit_order(payment_request)
   → Returns redirect_url and order_tracking_id

5. Redirect Customer
   → Customer completes payment on Pesapal using redirect_url

6. Check Status
   → client.get_payment_status(order_tracking_id)
   → Monitor payment status changes

7. Process Refund (if needed)
   → client.refund_order(confirmation_code, amount, username, remarks)

Supported Currencies

  • TZS (Tanzanian Shilling) - Example: 50000.00 TZS
  • KES (Kenyan Shilling) - Example: 1000.00 KES
  • UGX (Ugandan Shilling) - Example: 50000.00 UGX
  • RWF (Rwandan Franc) - Example: 10000.00 RWF
  • USD (US Dollar) - Example: 50.00 USD

API Reference

PesapalClient

Main client for interacting with Pesapal API.

Initialization:

PesapalClient(
    consumer_key: str,
    consumer_secret: str,
    sandbox: bool = True,
    timeout: int = 30
)

Methods:

  • register_ipn(ipn_url: str, ipn_notification_type: str = "GET") -> IPNRegistration

    • Register an IPN URL and get notification_id
  • get_registered_ipns() -> List[IPNRegistration]

    • Get list of all registered IPN URLs
  • submit_order(payment_request: PaymentRequest) -> PaymentResponse

    • Submit a payment order and get redirect URL
  • get_payment_status(order_tracking_id: str) -> PaymentStatus

    • Get current payment status by tracking ID
  • refund_order(confirmation_code: str, amount: Decimal, username: str, remarks: str) -> dict

    • Process a refund for a completed payment
  • cancel_order(order_tracking_id: str) -> dict

    • Cancel a pending payment order

Models

PaymentRequest:

PaymentRequest(
    id: str,                    # Unique order ID (max 50 chars)
    amount: Decimal,            # Payment amount
    currency: str,              # Currency code (TZS, KES, etc.)
    description: str,           # Order description (max 100 chars)
    callback_url: str,          # Callback URL for redirect
    notification_id: Optional[str] = None,  # IPN notification ID
    billing_address: Optional[dict] = None,  # Billing address
    customer: Optional[dict] = None,         # Customer info
    redirect_mode: Optional[str] = None,     # TOP_WINDOW or PARENT_WINDOW
    cancellation_url: Optional[str] = None    # Cancellation redirect URL
)

PaymentResponse:

PaymentResponse(
    order_tracking_id: str,     # Tracking ID for status checks
    redirect_url: str           # URL to redirect customer
)

PaymentStatus:

PaymentStatus(
    order_tracking_id: str,
    payment_method: Optional[str],
    amount: Decimal,
    currency: str,
    status_code: str,           # "0"=Pending, "1"=Completed, "2"=Failed
    payment_status_description: str,
    confirmation_code: Optional[str]
)

IPNRegistration:

IPNRegistration(
    notification_id: str,    # IPN ID to use in payments
    ipn_url: str,
    ipn_notification_type: str   # "GET" or "POST"
)

Exceptions

  • PesapalError - Base exception for all Pesapal errors
  • PesapalAPIError - API-related errors
  • PesapalAuthenticationError - Authentication failures
  • PesapalValidationError - Validation errors
  • PesapalNetworkError - Network/connection errors

Requirements

  • Python 3.8+
  • httpx >= 0.24.0
  • pydantic >= 2.0.0

License

MIT License

Support

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

pesapal_python_sdk-1.0.0.tar.gz (20.4 kB view details)

Uploaded Source

Built Distribution

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

pesapal_python_sdk-1.0.0-py3-none-any.whl (15.5 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for pesapal_python_sdk-1.0.0.tar.gz
Algorithm Hash digest
SHA256 aa66382997d7d999607e0a22a725d8c48400c8598d9697174b88ec6017dac495
MD5 9c3dd59b06d58c36b0977b64b3ef046d
BLAKE2b-256 e186b4dd037b0bc3df242f3043f79dae6755fc94f14554567aa7fb9907606c99

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for pesapal_python_sdk-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 2c34d7b521d7fff5e9d9c418c70951e67e89ff86fafe6aecb6eeebe1a09c8e54
MD5 2c86c8b51a6e7614c1adf5e455477eb7
BLAKE2b-256 dd3fb11dc29a2bdd6e62d12deb96ea17e435067b77473c906f9605bd7db6cdac

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