Production-grade Python SDK for the PalmPesa API — sync & async, USSD payments, webhooks, and transactions
Project description
PalmPesa Python SDK
Production-grade Python SDK for the PalmPesa payment API — supports both sync and async usage, automatic retries, typed exceptions, and webhook validation.
Features
- Sync & Async —
PalmPesafor blocking code,AsyncPalmPesafor asyncio - USSD Push payments — initiate STK prompts and poll order status
- Auto retries — exponential backoff on transient 5xx errors
- Typed exceptions — structured error hierarchy (never a bare
Exception) - Thread-safe — share a single client across threads
- Context manager —
with/async withfor automatic cleanup - Webhook validation — verify and parse incoming payment callbacks
- PEP 561 compliant — ships with
py.typed
Installation
pip install palmpesa-python-sdk
Requires Python 3.10 or newer.
Quick Start
Synchronous
from palmpesa import PalmPesa
with PalmPesa(api_token="your-api-token") as client:
# Initiate a USSD push payment (minimum amount: 500 TZS)
result = client.payments.initiate_mobile_payment(
amount=1000,
phone="0693662424",
transaction_id="TXN-001",
name="John Doe",
email="john@example.com",
callback_url="https://yourapp.com/webhook/palmpesa",
)
print(result["order_id"])
# Check payment status
status = client.payments.get_order_status(result["order_id"])
print(status["payment_status"]) # PENDING | COMPLETED | FAILED
# List transactions
txns = client.transactions.list_all(page=1, per_page=20)
Asynchronous
import asyncio
from palmpesa import AsyncPalmPesa
async def main():
async with AsyncPalmPesa(api_token="your-api-token") as client:
result = await client.payments.initiate_mobile_payment(
amount=1000,
phone="0693662424",
transaction_id="TXN-001",
name="John Doe",
email="john@example.com",
callback_url="https://yourapp.com/webhook/palmpesa",
)
status = await client.payments.get_order_status(result["order_id"])
asyncio.run(main())
USSD Push Payments
Minimum amount: 500 TZS
The PalmPesa gateway enforces a minimum payment amount of TZS 500 for USSD push (STK) requests. Amounts below this threshold will be rejected by the API.
initiate_mobile_payment
Sends a USSD STK push prompt to the customer's phone.
| Parameter | Type | Required | Description |
|---|---|---|---|
amount |
int |
Yes | Amount in TZS — minimum 500 |
phone |
str |
Yes | Customer phone in any Tanzanian format ("0693662424", "+255693662424") |
transaction_id |
str |
Yes | Unique reference for this payment |
name |
str |
Yes | Customer full name |
email |
str |
Yes | Customer email address |
callback_url |
str |
Yes | Publicly reachable URL for payment result callbacks |
address |
str |
No | Customer address (default: "Dar es Salaam") |
postcode |
str |
No | Customer postcode (default: "11111") |
Returns: {"order_id": "...", "message": "..."}
get_order_status
Poll the result of a previously initiated payment.
| Parameter | Type | Description |
|---|---|---|
order_id |
str |
The order_id returned by initiate_mobile_payment |
Returns: {"payment_status": "COMPLETED" | "PENDING" | "FAILED", ...}
Transactions
# Paginated transaction history
txns = client.transactions.list_all(page=1, per_page=50)
Webhook Validation
Validate and parse callbacks sent to your webhook endpoint:
from palmpesa import WebhookValidator, PAYMENT_STATUS_COMPLETED
validator = WebhookValidator(secret="your-webhook-secret")
# Flask example
@app.route("/webhook/palmpesa", methods=["POST"])
def handle_webhook():
event = validator.validate(
payload=request.data,
signature=request.headers.get("X-PalmPesa-Signature", ""),
)
if event.payment_status == PAYMENT_STATUS_COMPLETED:
# fulfil order
...
return "", 200
Error Handling
from palmpesa import (
PalmPesa,
ValidationError,
InsufficientFundsError,
AuthenticationError,
RateLimitError,
ServerError,
PalmPesaError,
)
with PalmPesa(api_token="your-api-token") as client:
try:
result = client.payments.initiate_mobile_payment(
amount=1000, phone="0693662424", transaction_id="TXN-001",
name="John Doe", email="john@example.com",
callback_url="https://yourapp.com/webhook",
)
except ValidationError as e:
print(f"Bad request: {e}") # e.g. amount below 500, bad phone
except InsufficientFundsError as e:
print(f"Insufficient funds: {e}")
except AuthenticationError as e:
print(f"Invalid API token: {e}")
except RateLimitError as e:
print(f"Rate limited: {e}")
except ServerError as e:
print(f"PalmPesa server error: {e}")
except PalmPesaError as e:
print(f"Unexpected error: {e}")
Exception Hierarchy
PalmPesaError
├── AuthenticationError (HTTP 401)
├── ForbiddenError (HTTP 403)
├── ValidationError (HTTP 400 — includes invalid phone, bad payload)
├── InsufficientFundsError(HTTP 400 — insufficient balance)
├── NotFoundError (HTTP 404)
├── ConflictError (HTTP 409)
├── RateLimitError (HTTP 429)
└── ServerError (HTTP 5xx)
Configuration
client = PalmPesa(
api_token="your-api-token",
base_url="https://palmpesa.drmlelwa.co.tz", # override for staging
timeout=30.0, # request timeout in seconds
max_retries=3, # retry attempts on transient 5xx errors
)
Health Check
if client.is_healthy():
print("API reachable and token valid")
Development
# Install dev dependencies
pip install -e ".[dev]"
# Run tests
pytest
# Lint & format
ruff check src/
black src/
mypy src/
License
MIT — see LICENSE.
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 palmpesa_python_sdk-0.1.0.tar.gz.
File metadata
- Download URL: palmpesa_python_sdk-0.1.0.tar.gz
- Upload date:
- Size: 14.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
63c3477b587d6f9922320f5c431fad2eaaf85da6e547345442ae7033f3a8f659
|
|
| MD5 |
f403edde560049022bce946d0177bdfb
|
|
| BLAKE2b-256 |
bfac82712496920853ff39aeec07afb0ecf2151813f0c4b33023914c13af79ab
|
File details
Details for the file palmpesa_python_sdk-0.1.0-py3-none-any.whl.
File metadata
- Download URL: palmpesa_python_sdk-0.1.0-py3-none-any.whl
- Upload date:
- Size: 15.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.13.5
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
20a2554acbe858582182cb7adb33b614e44c48d2431d13fd052f01f501c08e9f
|
|
| MD5 |
ff5ae0e7640804ad143d1573218aad61
|
|
| BLAKE2b-256 |
79801ed747c22e32f0ca0a1ab2ba954b5a2a899af7a32308c621028caad0c3ea
|