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
- Pesapal Account: Sign up at developer.pesapal.com
- Credentials: Get your
consumer_keyandconsumer_secret - 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 errorsPesapalAPIError- API-related errorsPesapalAuthenticationError- Authentication failuresPesapalValidationError- Validation errorsPesapalNetworkError- Network/connection errors
Requirements
- Python 3.8+
- httpx >= 0.24.0
- pydantic >= 2.0.0
License
MIT License
Support
- GitHub Issues: github.com/erickblema/pesapal-python-sdk/issues
- Pesapal Docs: developer.pesapal.com
- Contact: ericklema360@gmail.com
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 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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
aa66382997d7d999607e0a22a725d8c48400c8598d9697174b88ec6017dac495
|
|
| MD5 |
9c3dd59b06d58c36b0977b64b3ef046d
|
|
| BLAKE2b-256 |
e186b4dd037b0bc3df242f3043f79dae6755fc94f14554567aa7fb9907606c99
|
File details
Details for the file pesapal_python_sdk-1.0.0-py3-none-any.whl.
File metadata
- Download URL: pesapal_python_sdk-1.0.0-py3-none-any.whl
- Upload date:
- Size: 15.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.9.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2c34d7b521d7fff5e9d9c418c70951e67e89ff86fafe6aecb6eeebe1a09c8e54
|
|
| MD5 |
2c86c8b51a6e7614c1adf5e455477eb7
|
|
| BLAKE2b-256 |
dd3fb11dc29a2bdd6e62d12deb96ea17e435067b77473c906f9605bd7db6cdac
|