Skip to main content

namibrapay (Python)

Official NamibraPay client for Python. Collections, payouts, verification, invoices and status — with zero dependencies.

  • Standard library only (urllib, hmac, json)
  • Sync client, Python 3.9+
  • PEP 561 typed (py.typed included)
  • Retries that never double-charge

Install

pip install namibrapay

Quick start

import os
from namibrapay import NamibraPay

client = NamibraPay(
    api_key=os.environ["NAMBRAPAY_API_KEY"],
    api_secret=os.environ["NAMBRAPAY_API_SECRET"],
)

tx = client.transactions.collect(
    amount=2500,  # GHS 25.00, in minor units
    channel="mtn_momo",
    payer_identifier="0244000000",
    reference="ORDER-1042",
)

print(tx["reference"], tx["status"])

Omit the credentials to read NAMBRAPAY_API_KEY and NAMBRAPAY_API_SECRET from the environment:

client = NamibraPay()

Python arguments are snake_case; the SDK maps them to the API's camelCase fields for you (payer_identifier → payerIdentifier).

Configuration

Option Default Notes
api_key / api_secret env vars Sent as X-API-Key / X-API-Secret
base_url https://api.namibrapay.com/api/v1 Must include the version segment
timeout 30.0 Per attempt, in seconds
max_retries 2 Applies only to retry-safe calls
headers None Extra headers on every request
transport urllib Advanced/testing hook

Resources

client.transactions.collect(amount=..., channel=..., payer_identifier=..., reference=..., idempotency_key=None)
client.transactions.initialize(amount=..., reference=...)
client.transactions.payout(amount=..., channel=..., recipient_account=..., recipient_bank=..., recipient_name=..., reference=...)
client.transactions.get(reference)
client.transactions.receipt(reference)
client.transactions.reverse(transaction_id, reason=..., amount=None)

client.checkout.retrieve(reference)
client.checkout.pay(reference, channel=..., payer_identifier=...)

client.payment_links.retrieve(slug)
client.payment_links.charge(slug, channel=..., payer_identifier=..., amount=None)

client.invoices.retrieve(code)
client.invoices.pay(code)

client.virtual_accounts.create(customer_name=..., customer_phone=..., ghana_card_number=...)
client.virtual_accounts.list()
client.virtual_accounts.sync_balance(account_id)

client.verification.banks()
client.verification.networks()
client.verification.resolve_account(bank_reference=..., account_number=..., amount=None)
client.verification.resolve_wallet(network=..., wallet_number=...)
client.verification.verify_id(id_type="ghanacard", id_number=...)
client.verification.confirm_payee(payee_type=..., expected_name=..., account_number=..., bank_reference=None, network=None)

client.status.snapshot()
client.status.incidents(page=None, limit=None)   # {"data": [...], "pagination": {...}}
client.status.feed()                             # RSS 2.0 as a string

client.health()

Every method returns the data object from the envelope directly — a dict, not a custom model, so new API fields arrive without an SDK release.

Money amounts

Amounts are integers in minor units (pesewas) and must be whole cedis: GHS 10.00 → 1000. Collections have a minimum of 100, payouts 1000. The SDK raises NamibraPayError before any request is made when the value is invalid.

Hosted checkout

transactions.initialize gives the payer a payment page NamibraPay hosts. Send them to the returned URL and let the page collect the mobile-money details:

tx = client.transactions.initialize(
    amount=2500,
    reference="ORDER-1042",
    email="ama@example.com",
    callback_url="https://yourapp.com/order/1042/thanks",
    metadata={
        # Keys inside metadata keep the API's camelCase names.
        "productName": "Aurora Desk Lamp",
        "productDescription": "Warm dimmable lamp with a walnut base.",
        "productImage": "https://cdn.shop.example.com/lamp.png",
        "logoUrl": "https://cdn.shop.example.com/logo.png",
        "brandColor": "#0E7C66",
    },
)

redirect(tx["authorizationUrl"])

Only these metadata keys are rendered by the page; every other key is echoed back on the transaction and webhooks untouched:

Key Type Notes
productName str Item name, truncated at 120 characters
productDescription str Line under the item name, truncated at 500 characters
productImage http(s) URL Thumbnail; other URLs are dropped
logoUrl http(s) URL Overrides the logo on your account
brandColor #rrggbb Accent on buttons, the selected network and links

The page offers MTN MoMo, Telecel Cash and AirtelTigo Money. Building your own checkout? checkout.retrieve(reference) returns the same session data — businessName, logoUrl, brandColor, product, amount, fee, totalAmount, awaitingPaymentDetails, callbackUrl and completedAt — and checkout.pay(reference, channel=..., payer_identifier=...) starts the charge.

Errors

from namibrapay import NamibraPayAPIError, NamibraPayConnectionError, NamibraPayError

try:
    client.transactions.payout(...)
except NamibraPayAPIError as error:
    if error.code == "BIZ_009":
        ...  # insufficient balance, etc.
    print(error.status_code, error.errors)
except NamibraPayConnectionError as error:
    print("network failed", error.cause)

NamibraPayAPIError exposes status_code, code, errors, body, retry_after_seconds and is_retryable.

Retries and idempotency

The SDK retries 408, 429, 500, 502, 503 and 504 with backoff and honours Retry-After.

Call Retried? Why
GET (get, receipt, status, …) Yes Reads are safe to repeat
collect, initialize, payout Yes The API deduplicates on reference
Any call with idempotency_key Yes The key makes the retry safe
reverse, checkout.pay, payment_links.charge, invoices.pay, virtual_accounts.create No A retry would create a second money movement

Webhooks

from flask import Flask, request, abort
from namibrapay.webhooks import construct_webhook_event, WebhookSignatureError

app = Flask(__name__)

@app.post("/webhooks/namibrapay")
def receive():
    try:
        event = construct_webhook_event(
            request.get_data(),  # raw bytes, not request.json
            request.headers.get("X-Webhook-Signature", ""),
            app.config["NAMBRAPAY_WEBHOOK_SECRET"],
        )
    except WebhookSignatureError:
        abort(401)

    # Acknowledge fast; do the work out of band.
    return "", 200

verify_webhook_signature and compute_webhook_signature are available if you prefer to handle the details yourself. Comparison is constant-time.

Async

This release is synchronous. Inside async frameworks (FastAPI, Starlette, aiohttp) run calls in a worker thread so the event loop stays free:

from starlette.concurrency import run_in_threadpool

tx = await run_in_threadpool(client.transactions.get, "ORDER-1042")

Versioning

The package follows semver. Additions are minor, breaking changes are major. namibrapay.__version__ matches pyproject.toml.

License

MIT

Metadata

Release files for namibrapay 0.2.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for namibrapay 0.2.0
File Size Uploaded Provenance Actions
namibrapay-0.2.0.tar.gz 23.3 kB Without provenance Details for namibrapay-0.2.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for namibrapay 0.2.0
File Interpreter ABI Platform Actions
namibrapay-0.2.0-py3-none-any.whl Python 3 none any Details for namibrapay-0.2.0-py3-none-any.whl

Total release size: 44.7 kB

Release files / namibrapay-0.2.0.tar.gz

Download URL namibrapay-0.2.0.tar.gz
Size 23.3 kB
Tags Source
SHA-256 checksum
How to use checksums
a680a10f275d8002db805eef9a6e11caf3ec677cb705f6d75765e975f90b349a
BLAKE2b-256 checksum
How to use checksums
b4b36e8d88bb692b98f5dbb64bc9bd977d411fb2ed774a787910643652a81f83
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.5

Release files / namibrapay-0.2.0-py3-none-any.whl

Download URL namibrapay-0.2.0-py3-none-any.whl
Size 21.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e4b42dc110c5ba8983289a360dce11e67c82bfc16ccea68f24487f432cfc2006
BLAKE2b-256 checksum
How to use checksums
11dac46ca4d034ec1bf63f381ef66e53473d1dd7a81d4a0c9c1d7f4b33cdb9c0
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.14.5

Security for namibrapay 0.2.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files No provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page