Skip to main content

Official Python SDK for ProStack NG — Africa's unified API gateway

Project description

prostackng (Python)

Official Python SDK for ProStack NG — Africa's unified API gateway.

One API key. Five services. Billed in Naira.

PyPI version Python License: MIT


Installation

pip install prostackng

Quick Start

from prostackng import ProStack

client = ProStack(api_key="psk_live_...")

# Send an SMS
sms = client.notifications.send_sms(
    to="08012345678",
    message="Your OTP is 4821. Valid for 5 minutes.",
)
print(sms.status)  # "sent"

# Verify a BVN
bvn = client.identity.verify_bvn(
    bvn="12345678901",
    first_name="John",
    last_name="Doe",
)
if bvn.status == "verified":
    print(f"Verified: {bvn.first_name} {bvn.last_name}")

# Initiate a payment
payment = client.payments.initiate(amount=5000, email="user@app.com")
# Redirect user to:
print(payment.authorization_url)

Get your API key at prostack-api-dashboard.vercel.app.


Async Support (FastAPI, aiohttp)

from prostackng import AsyncProStack

client = AsyncProStack(api_key="psk_live_...")

# All methods are awaitable
sms = await client.notifications.send_sms(to="08012345678", message="Hello")
bvn = await client.identity.verify_bvn(bvn="12345678901")
payment = await client.payments.initiate(amount=5000, email="user@app.com")

FastAPI example:

from fastapi import FastAPI
from prostackng import AsyncProStack

app = FastAPI()
prostack = AsyncProStack(api_key="psk_live_...")

@app.post("/send-otp")
async def send_otp(phone: str, otp: str):
    result = await prostack.notifications.send_sms(
        to=phone,
        message=f"Your OTP is {otp}. Valid for 5 minutes.",
    )
    return {"status": result.status, "cost": result.cost}

Services

Notifications

# Single SMS
result = client.notifications.send_sms(
    to="08012345678",
    message="Hello!",
    sender_id="MyApp",    # optional
)

# Email
result = client.notifications.send_email(
    to="user@example.com",
    subject="Welcome to MyApp",
    body="Thanks for signing up.",
    html="<p>Thanks for signing up.</p>",  # optional
)

# Bulk SMS (up to 10,000 recipients — async processing)
job = client.notifications.send_bulk_sms(
    recipients=[
        {"to": "08012345678", "message": "Hi John!"},
        {"to": "08098765432", "message": "Hi Jane!"},
    ],
    callback_url="https://yourapp.com/webhooks/bulk",  # optional
)
print(f"Job {job.job_id}: {job.total} recipients queued")

# Poll job status
status = client.notifications.get_bulk_job_status(job.job_id)
print(f"{status.sent}/{status.total} sent")

Payments

# Initiate
payment = client.payments.initiate(
    amount=5000,               # ₦5,000 — kobo conversion handled automatically
    email="user@example.com",
    provider="paystack",       # or "flutterwave"
    reference="MY-REF-001",    # optional
    metadata={"order_id": "99"},  # optional
)
# Redirect to payment.authorization_url

# Verify
result = client.payments.verify("MY-REF-001")
if result.status == "success":
    pass  # fulfill order

# List transactions
transactions = client.payments.list(limit=50)

Identity / KYC

BVN and NIN verification require a LIVE API key.

# BVN
result = client.identity.verify_bvn(
    bvn="12345678901",
    first_name="John",         # optional cross-validation
    last_name="Doe",
    date_of_birth="1990-01-15",  # YYYY-MM-DD
)

# NIN
result = client.identity.verify_nin(nin="12345678901", first_name="Jane")

# Phone
result = client.identity.verify_phone(phone="08012345678", network="mtn")

print(result.status)       # "verified"
print(result.first_name)   # "John"
print(result.cost)         # 100.0

VTU / Utility Bills

# Airtime
client.vtu.buy_airtime(phone="08012345678", amount=500, network="mtn")

# Data — list plans first
plans = client.vtu.list_data_plans("airtel")
client.vtu.buy_data(phone="08012345678", plan_id=plans[0].id, network="airtel")

# Electricity
result = client.vtu.pay_electricity(
    meter_number="12345678901",
    disco="phed",
    meter_type="prepaid",
    amount=5000,
)
print(result.token)  # "1234-5678-9012-3456" (prepaid token)

# Cable TV
client.vtu.pay_cable_tv(decoder_number="1234567890", provider="dstv", package="CONFAM")

Reports

# Generate (async — poll for completion)
report = client.reports.generate(
    title="April 2026 Sales",
    output_format="pdf",      # "pdf" | "excel" | "both"
    data=[
        {"date": "2026-04-01", "product": "Widget A", "amount": 15000},
        {"date": "2026-04-02", "product": "Widget B", "amount": 7500},
    ],
)

# Poll until complete
import time
while report.status in ("pending", "processing"):
    time.sleep(3)
    report = client.reports.get_by_id(report.id)

print(report.output_url)  # download URL

Webhooks

wh = client.webhooks.create(
    url="https://yourapp.com/webhooks/prostack",
    events=["payment.success", "identity.verified", "vtu.success"],
)
webhooks = client.webhooks.list()
client.webhooks.delete(wh.id)

Webhook Verification

from prostackng import ProStack, verify_webhook, parse_webhook_payload

# Flask
from flask import Flask, request
app = Flask(__name__)

@app.route("/webhooks/prostack", methods=["POST"])
def prostack_webhook():
    is_valid = verify_webhook(
        payload=request.get_data(),       # raw bytes — do NOT parse first
        signature=request.headers.get("X-ProStack-Signature", ""),
        secret=os.environ["PROSTACK_WEBHOOK_SECRET"],
    )
    if not is_valid:
        return "Invalid signature", 400

    event = parse_webhook_payload(request.get_data())

    if event["event"] == "payment.success":
        payment_id = event["data"]["id"]
        # fulfill order...

    return "OK", 200


# Django
from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt

@csrf_exempt
def prostack_webhook(request):
    is_valid = verify_webhook(
        payload=request.body,
        signature=request.headers.get("X-Prostack-Signature", ""),
        secret=settings.PROSTACK_WEBHOOK_SECRET,
    )
    if not is_valid:
        return HttpResponse(status=400)
    event = parse_webhook_payload(request.body)
    return HttpResponse(status=200)

Error Handling

from prostackng import (
    ProStack,
    ProStackError,
    AuthenticationError,
    ValidationError,
    InsufficientBalanceError,
    RateLimitError,
    NetworkError,
    ServerError,
)
import time

try:
    result = client.identity.verify_bvn(bvn="12345678901")

except InsufficientBalanceError as e:
    print(f"Top up needed. Have ₦{e.balance}, need ₦{e.required}")

except RateLimitError as e:
    print(f"Rate limited. Retry in {e.retry_after}s")
    time.sleep(e.retry_after)

except AuthenticationError:
    print("Invalid API key — check your ProStack dashboard")

except ValidationError as e:
    print(f"Validation failed: {e.fields}")

except NetworkError as e:
    print(f"Connection failed: {e}")

except ProStackError as e:
    print(f"API error {e.status_code}: {e.message}")

The SDK automatically retries 429 and 5xx errors with exponential backoff. Auth errors (401/403) and validation errors (400/422) are not retried.


Context Managers

# Sync — auto-closes connection pool
with ProStack(api_key="psk_live_...") as client:
    result = client.vtu.buy_airtime(phone="080...", amount=500, network="mtn")

# Async — auto-closes async connection pool
async with AsyncProStack(api_key="psk_live_...") as client:
    result = await client.payments.initiate(amount=5000, email="u@e.com")

Requirements

  • Python 3.8+
  • httpx >= 0.27
  • pydantic >= 2.0

License

MIT © ProStack NG Technologies Ltd

Built in Port Harcourt, Nigeria 🇳🇬

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

prostackng-1.0.0.tar.gz (22.2 kB view details)

Uploaded Source

Built Distribution

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

prostackng-1.0.0-py3-none-any.whl (19.4 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for prostackng-1.0.0.tar.gz
Algorithm Hash digest
SHA256 41ee6e618f94917c97d8da928d61ded2e2cc6ea833001a2ebe4701bb27b6a351
MD5 bed71e576eedbef11275fbc1e1c86c97
BLAKE2b-256 396d667c8954bf493629695772c9e3896f7df33cd4948f36c40669130b3d8a51

See more details on using hashes here.

File details

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

File metadata

  • Download URL: prostackng-1.0.0-py3-none-any.whl
  • Upload date:
  • Size: 19.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.9

File hashes

Hashes for prostackng-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 a837ab8f8fdff3e69152b47a3630e961c6df197a91dc4d46e3535c3b203e4802
MD5 962ca094aa198664c2e1692c81e03b71
BLAKE2b-256 678e3a9613f46a873ca1e200cb4ee26d67a42534421689d2d34bb6fe2d38dfaa

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