nexus-africa
Async-first Python SDK for the Nexus/Neero payment gateway (CEMAC zone — Cameroon, Central Africa).
Why this SDK?
The official neero-gateway uses stdlib only (synchronous). This SDK adds:
- Async-first via
httpx— drop-in for FastAPI / async frameworks - Pydantic v2 models — typed request and response objects
- Typed exceptions — one class per error family (
PaymentMethodError,GatewayError,IdempotencyConflict…) - Full endpoint coverage — Payment Methods, Transaction Intents, Balances, Sessions, BaaS (cards, KYC onboarding)
- Webhook helper —
verify_and_parse()with HMAC-SHA512 + replay protection
Installation
pip install nexus-africa
Quick start — sync
from nexus_africa import NexusClient, MobileMoneyProvider, PaymentType
with NexusClient("sk_test_...", platform_code="MYAPP") as client:
# 1. Register the client's Mobile Money wallet
client_pm = client.payment_methods.create_mobile_money(
"+237691111111", "CM", MobileMoneyProvider.ORANGE_MONEY
)
# 2. Register your Nexus Merchant account
merchant_pm = client.payment_methods.create_merchant(
merchant_key="mk_...",
store_id="store_...",
balance_id="bal_...",
operator_id=9,
)
# 3. Initiate cash-in (client → merchant)
intent = client.intents.cash_in(
source_payment_method_id=client_pm.id,
destination_payment_method_id=merchant_pm.id,
amount=5000, # XAF, integer
idempotency_key="order_42", # optional but recommended
)
print(intent.status) # PENDING
print(intent.id) # intent_...
Quick start — async
import asyncio
from nexus_africa import AsyncNexusClient, MobileMoneyProvider
async def main():
async with AsyncNexusClient("sk_test_...", platform_code="MYAPP") as client:
pm = await client.payment_methods.create_mobile_money(
"+237651111111", "CM", MobileMoneyProvider.MTN_MONEY
)
intent = await client.intents.cash_in(
source_payment_method_id=pm.id,
destination_payment_method_id="<merchant_pm_id>",
amount=5000,
)
print(intent.status)
asyncio.run(main())
Webhook verification
from nexus_africa.webhook import verify_and_parse
# In your FastAPI route:
async def nexus_webhook(request: Request):
raw_body = await request.body()
event = verify_and_parse(
raw_body=raw_body,
timestamp=request.headers["X-TIMESTAMP"],
signature=request.headers["X-SIGNATURE"],
secret="wh_secret_from_dashboard",
max_age_seconds=300, # replay protection
)
if event.new_status == "SUCCESSFUL":
await handle_payment_success(event.transaction_intent_id)
return {"received": True}
Cash-out (payout)
intent = client.intents.cash_out(
source_payment_method_id=merchant_pm.id,
destination_payment_method_id=recipient_pm.id,
amount=10000,
payment_type=PaymentType.MTN_MONEY_TRANSFER,
external_transaction_id="payout_driver_42",
)
Nexus Flow (marketplace split)
from nexus_africa import FlowTransaction, PaymentType
intent = client.intents.cash_in(
source_payment_method_id=client_pm.id,
destination_payment_method_id=merchant_pm.id,
amount=10000,
flow_transactions=[
FlowTransaction(
payment_method_id=partner_pm.id,
amount=1500,
payment_type=PaymentType.TRANSFER_TO_NEERO_PERSON,
)
],
)
Hosted payment session
session = client.sessions.create(
transaction_intent_id=intent.id,
return_url="https://myapp.com/payment/callback",
title="Ma commande #42",
)
# Redirect the user to:
print(session.payment_link)
Error handling
from nexus_africa import (
IdempotencyConflict,
GatewayError,
PaymentMethodError,
TransactionIntentError,
)
try:
intent = client.intents.cash_in(...)
except IdempotencyConflict:
# GE-0002 — transaction already exists, treat as success
intent = client.intents.get(existing_id)
except GatewayError as e:
# gtw-4004 insufficient funds, gtw-4012 rejected by client, etc.
print(e.code, e.message)
except TransactionIntentError as e:
# TI-0014 missing platform code, TI-0016 duplicate external ID, etc.
print(e.code, e.message)
Test numbers (sandbox)
| Prefix | Provider | Suffix | Result |
|---|---|---|---|
65x |
MTN MoMo | ...1111111 |
SUCCESSFUL |
65x |
MTN MoMo | ...2222222 |
FAILED |
65x |
MTN MoMo | ...3333333 |
PENDING |
69x |
Orange Money | ...1111111 |
SUCCESSFUL |
69x |
Orange Money | ...2222222 |
FAILED |
Example: +237651111111 → MTN, SUCCESSFUL.
BaaS (card issuance)
# KYC onboarding
session = client.baas.onboarding.create_session(nationality_code="CM")
session = client.baas.onboarding.submit_session(session.id)
party = client.baas.onboarding.get_party(session.id)
# Issue a virtual card
card = client.baas.cards.create_virtual(party_id=party.id)
link = client.baas.cards.get_view_link(card.id)
Environments
# Sandbox (default)
client = NexusClient("test_sk_...", platform_code="X", sandbox=True)
# Live / production
client = NexusClient("live_sk_...", platform_code="X", sandbox=False)
License
MIT
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
nexus_africa-0.1.0.tar.gz
(25.5 kB
view details)
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 nexus_africa-0.1.0.tar.gz.
File metadata
- Download URL: nexus_africa-0.1.0.tar.gz
- Upload date:
- Size: 25.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
519d976cabc34aa4d0d29b89ae317f64b5446aa7deddb43c09a8b9ca5718936a
|
|
| MD5 |
b9a0778d5465e4abeaec7a5a101c1a75
|
|
| BLAKE2b-256 |
a8a1d28e0104f82d76c72c9125375a3b16e809e9bf944542422f4656b9851285
|
File details
Details for the file nexus_africa-0.1.0-py3-none-any.whl.
File metadata
- Download URL: nexus_africa-0.1.0-py3-none-any.whl
- Upload date:
- Size: 26.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via:
twine/7.0.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
30abd1d65d91dd04fafe8ad065203991136c6f497de0d48ecb9e8fa996b6da7c
|
|
| MD5 |
07878ede25546ab5f4c1cc89d7666291
|
|
| BLAKE2b-256 |
9afecaa5c61f1cc0ae18b1902de74a7970986f1c1a9a611e95553a8ce8982a46
|