Official PayNexus Gateway Python SDK — Accept M-Pesa payments, manage webhooks, and query merchants via the PayNexus API
Project description
PayNexus Gateway Python SDK
Official Python SDK for the PayNexus Gateway payment platform. Accept M-Pesa STK Push payments, manage webhooks, invoices, payment links, and query merchant data from any Python application.
Installation
pip install paynexus-gateway
Requires Python 3.9 or later.
Setup
Get your API keys from the PayNexus dashboard.
1. Create a .env file
# .env
PAYNEXUS_SECRET_KEY=sk_live_your_secret_key
PAYNEXUS_WEBHOOK_SECRET=whsec_your_webhook_secret # Only needed if using webhooks
2. Load environment variables
pip install python-dotenv
import os
from dotenv import load_dotenv
from paynexus_gateway import PayNexus
load_dotenv()
client = PayNexus(
secret_key=os.environ["PAYNEXUS_SECRET_KEY"],
webhook_secret=os.environ.get("PAYNEXUS_WEBHOOK_SECRET"),
)
Django users: Add the env vars to your
settings.pyor usedjango-environ. FastAPI users: Usepydantic-settingsorpython-dotenv.
Quick Start — Accept a Payment
from paynexus_gateway import PayNexus, InitiatePaymentParams
client = PayNexus(secret_key="sk_live_...")
payment = client.payments.initiate(
InitiatePaymentParams(
payment_account_id=1, # Your M-Pesa payment account ID
amount=500, # Amount in KES (1–150,000)
phone="254712345678", # Customer phone number
description="Order #1234",
)
)
print(payment["data"])
# {
# "payment_id": 42,
# "reference": "PAY-abc123",
# "checkout_request_id": "ws_CO_...",
# "status": "pending",
# "customer_message": "Success. Request accepted for processing",
# ...
# }
API Reference
Payments
from paynexus_gateway import InitiatePaymentParams, ListPaymentsParams
# Initiate STK Push
result = client.payments.initiate(
InitiatePaymentParams(
payment_account_id=1,
amount=500,
phone="254712345678",
description="Order payment",
)
)
# Check payment status by reference
by_ref = client.payments.get_by_reference("PAY-abc123")
# Check payment status by ID
by_id = client.payments.get_by_id(42)
# Check payment status by M-Pesa CheckoutRequestID
by_checkout = client.payments.get_by_checkout_id("ws_CO_123456")
# List payments (with optional filters)
payments = client.payments.list(
ListPaymentsParams(
status="completed",
payment_method="mpesa",
from_date="2025-01-01",
to_date="2025-12-31",
per_page=50,
)
)
M-Pesa (Simplified)
The M-Pesa resource provides convenience methods that auto-normalize phone numbers and use your default payment account.
# Validate a phone number
result = client.mpesa.validate_phone("0746990866")
# Returns: {"phone": "254746990866", "valid": true}
# Initiate STK Push (auto-normalizes phone, auto-selects default account)
payment = client.mpesa.initiate_payment(
amount=100,
phone="0746990866", # Will be normalized to 254746990866
description="Order #12345"
)
# Returns: {"reference": "PAY-...", "checkout_request_id": "ws_CO_...", ...}
# Check payment status by CheckoutRequestID
status = client.mpesa.get_payment_status("ws_CO_123456")
Invoices
# Create an invoice
invoice = client.invoices.create({
"amount": 1000,
"description": "Consulting services - June 2026",
"due_date": "2026-07-01",
"customer_name": "John Doe",
"customer_email": "john@example.com",
"customer_phone": "254712345678",
})
# List invoices
invoices = client.invoices.list({"status": "pending"})
# Get a specific invoice
invoice = client.invoices.get("INV-2026-001")
# Update an invoice
client.invoices.update("INV-2026-001", {"status": "paid"})
# Send invoice to customer
client.invoices.send("INV-2026-001")
# Delete an invoice
client.invoices.delete("INV-2026-001")
Checkout / Payment Links
Create shareable payment links without writing server code.
# Create a payment link session
session = client.checkout.create_session({
"amount": 100,
"description": "Premium Access - AI Course",
"reference": "INV-2026-001",
"return_url": "https://yoursite.com/thanks",
"cancel_url": "https://yoursite.com/cancelled",
})
# session["data"] contains:
# {
# "session_id": "cs_live_abc123...",
# "checkout_url": "https://paynexus.co.ke/checkout/s/cs_live_abc123...",
# "expires_at": "2026-06-21T21:25:00+03:00"
# }
Health Checks
# Basic API health
health = client.health.check()
# M-Pesa service status
mpesa_health = client.health.mpesa()
Sandbox
Use sandbox API keys (prefix sb_) for testing.
# Create a sandbox client
sandbox_client = PayNexus(
secret_key="sb_your_sandbox_key",
base_url="https://paynexus.co.ke/api"
)
# Get sandbox merchant info
merchant = sandbox_client.sandbox.get_merchant()
# Initiate sandbox payment (max 50 KES)
payment = sandbox_client.sandbox.initiate_payment({
"amount": 10,
"phone": "254746990866",
"description": "Test payment"
})
# Check sandbox payment status
status = sandbox_client.sandbox.get_payment_status("SANDBOX-123")
Webhooks
from paynexus_gateway import RegisterWebhookParams, UpdateWebhookParams
# Register a new webhook
hook = client.webhooks.register(
RegisterWebhookParams(
name="Payment alerts",
url="https://example.com/webhooks/paynexus",
events=["payment.completed", "payment.failed"],
)
)
# hook["data"]["secret"] → save this, it is only shown once
# Update a webhook
client.webhooks.update(1, UpdateWebhookParams(
events=["payment.completed", "payment.failed", "invoice.paid"],
))
# List all webhooks
hooks = client.webhooks.list()
# Delete a webhook
client.webhooks.delete(1)
Available webhook events:
| Event | Description |
|---|---|
payment.completed |
Payment succeeded |
payment.failed |
Payment failed |
payment.initiated |
STK Push sent to customer |
invoice.created |
Invoice created |
invoice.paid |
Invoice paid |
invoice.overdue |
Invoice overdue |
account.created |
Account created |
account.updated |
Account updated |
subscription.created |
Subscription created |
subscription.canceled |
Subscription canceled |
Merchant
# Get your merchant info
me = client.merchant.get()
# List your businesses
businesses = client.merchant.businesses()
# List your M-Pesa payment accounts
accounts = client.merchant.payment_accounts()
API Keys
from paynexus_gateway import CreateApiKeyParams, UpdateApiKeyParams
# Create a new API key
key = client.api_keys.create(
CreateApiKeyParams(
name="Production key",
payment_account_id=1,
permissions=["payments.create", "payments.read"],
)
)
# key["data"]["api_key"] → save this, it is only shown once
# List API keys
keys = client.api_keys.list()
# Update an API key
client.api_keys.update(1, UpdateApiKeyParams(name="Renamed", status="inactive"))
# Delete an API key
client.api_keys.delete(1)
Webhook Verification
PayNexus signs every webhook with HMAC-SHA256. Verify incoming webhooks to ensure they are authentic.
Manual verification
event = client.verify_webhook(
raw_body=request.body.decode(), # Raw body string
signature=request.headers["X-PayNexus-Signature"],
timestamp=request.headers["X-PayNexus-Timestamp"],
)
if event["event"] == "payment.completed":
print("Payment completed:", event["data"])
Standalone function
from paynexus_gateway.webhooks import verify_webhook_signature
event = verify_webhook_signature(
raw_body=raw_body,
signature=signature,
timestamp=timestamp,
secret="whsec_...",
tolerance=300, # Max age in seconds (default: 300 = 5 min)
)
Django example
# views.py
import json
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from paynexus_gateway import PayNexus
client = PayNexus(
secret_key="sk_live_...",
webhook_secret="whsec_...",
)
@csrf_exempt
def webhook(request):
event = client.verify_webhook(
raw_body=request.body.decode(),
signature=request.headers.get("X-PayNexus-Signature", ""),
timestamp=request.headers.get("X-PayNexus-Timestamp", ""),
)
if event["event"] == "payment.completed":
# Handle payment completion
pass
return JsonResponse({"received": True})
FastAPI example
from fastapi import FastAPI, Request
from paynexus_gateway import PayNexus
app = FastAPI()
client = PayNexus(
secret_key="sk_live_...",
webhook_secret="whsec_...",
)
@app.post("/webhooks/paynexus")
async def webhook(request: Request):
raw_body = (await request.body()).decode()
event = client.verify_webhook(
raw_body=raw_body,
signature=request.headers.get("X-PayNexus-Signature", ""),
timestamp=request.headers.get("X-PayNexus-Timestamp", ""),
)
return {"received": True}
Flask example
from flask import Flask, request, jsonify
from paynexus_gateway import PayNexus
app = Flask(__name__)
client = PayNexus(
secret_key="sk_live_...",
webhook_secret="whsec_...",
)
@app.route("/webhooks/paynexus", methods=["POST"])
def webhook():
event = client.verify_webhook(
raw_body=request.get_data(as_text=True),
signature=request.headers.get("X-PayNexus-Signature", ""),
timestamp=request.headers.get("X-PayNexus-Timestamp", ""),
)
return jsonify(received=True)
Error Handling
The SDK raises typed errors you can catch individually:
from paynexus_gateway import (
PayNexus,
AuthenticationError,
ValidationError,
NotFoundError,
RateLimitError,
PayNexusError,
)
try:
client.payments.initiate(...)
except AuthenticationError:
# 401 — Invalid or missing API key
pass
except ValidationError as e:
# 422 — Invalid input; e.errors has field-level details
print(e.errors) # {"phone": ["Invalid phone number"]}
except NotFoundError:
# 404 — Resource not found
pass
except RateLimitError:
# 429 — Too many requests
pass
except PayNexusError as e:
# Other API error; check e.status and e.code
print(e.status, e.code)
Configuration
client = PayNexus(
secret_key="sk_live_...", # Required
webhook_secret="whsec_...", # For webhook verification
base_url="https://paynexus.co.ke/api", # Default
timeout=30.0, # Request timeout in seconds (default: 30)
)
The client supports context manager usage for automatic cleanup:
with PayNexus(secret_key="sk_live_...") as client:
payment = client.payments.initiate(...)
Authentication
The SDK authenticates using your secret API key (sk_...) sent via the X-API-Key header. Secret keys have write access (payments, webhooks, API key management). Never expose your secret key in client-side code.
Type Hints
The SDK is fully typed and ships with a py.typed marker. All parameter and response types are available:
from paynexus_gateway import (
InitiatePaymentParams,
ListPaymentsParams,
RegisterWebhookParams,
UpdateWebhookParams,
CreateApiKeyParams,
UpdateApiKeyParams,
ValidatePhoneParams,
MpesaInitiatePaymentParams,
MpesaPaymentStatusParams,
CreateInvoiceParams,
UpdateInvoiceParams,
CheckoutSessionParams,
Payment,
PaymentData,
Webhook,
WebhookPayload,
Merchant,
Business,
PaymentAccount,
ApiKey,
ApiResponse,
Pagination,
)
License
MIT
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 paynexus_gateway-1.0.0.tar.gz.
File metadata
- Download URL: paynexus_gateway-1.0.0.tar.gz
- Upload date:
- Size: 22.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
8e5f50f1f41810305eeff94c8926a0f54d7e750ecca37c5f110af1a8e0a958e3
|
|
| MD5 |
99f92025a3ef94592f93ff8fa0907a3a
|
|
| BLAKE2b-256 |
2dd5cd33968661b4bf49bd7915944f019888821cc1d9bb4ff94c386127fcc18c
|
File details
Details for the file paynexus_gateway-1.0.0-py3-none-any.whl.
File metadata
- Download URL: paynexus_gateway-1.0.0-py3-none-any.whl
- Upload date:
- Size: 20.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6844aec6e980b9a8be9cd4badf761a49e32d206ba69042dab305f0622689161b
|
|
| MD5 |
301e0a247e700bc6e51d9f4097073699
|
|
| BLAKE2b-256 |
f0022836587b35a017276ed9fddcd453f405a8e0532ff558e08b34dacfc85c71
|