Skip to main content

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.py or use django-environ. FastAPI users: Use pydantic-settings or python-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


Download files

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

Source Distribution

paynexus_gateway-1.0.0.tar.gz (22.7 kB view details)

Uploaded Source

Built Distribution

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

paynexus_gateway-1.0.0-py3-none-any.whl (20.3 kB view details)

Uploaded Python 3

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

Hashes for paynexus_gateway-1.0.0.tar.gz
Algorithm Hash digest
SHA256 8e5f50f1f41810305eeff94c8926a0f54d7e750ecca37c5f110af1a8e0a958e3
MD5 99f92025a3ef94592f93ff8fa0907a3a
BLAKE2b-256 2dd5cd33968661b4bf49bd7915944f019888821cc1d9bb4ff94c386127fcc18c

See more details on using hashes here.

File details

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

File metadata

File hashes

Hashes for paynexus_gateway-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 6844aec6e980b9a8be9cd4badf761a49e32d206ba69042dab305f0622689161b
MD5 301e0a247e700bc6e51d9f4097073699
BLAKE2b-256 f0022836587b35a017276ed9fddcd453f405a8e0532ff558e08b34dacfc85c71

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