The payments API for Central Africa
Collect and send Mobile Money payments in Cameroon, integrated directly with the operators. Start in the sandbox today: self-service signup, nothing to sign, no fees.
- 2
- Operators, integrated directly
- XAF
- Central African CFA franc
- FR / EN
- Every screen, both languages
- 6
- Deterministic test numbers
- Operator
- MTN Mobile Money
- Customer
- 237 690 000 001
- Fee
- 100 XAF
- Net to you
- 4 900 XAF
- Accepted — request recorded14:02:11
- Submitted to the operator14:02:12
- Completed — confirmed by poll14:02:19
POST /your-endpoint
MoniWave-Signature: t=1755…,v1=9f3c…
{ "event": "collection.completed",
"id": "evt_8c2f…", "amount": 5000 }What is true today, and what is not
We would rather you find the limits here than after a day of integration work.
- French and English
- Every screen a merchant or a customer sees exists in both languages. French is the reference, not a translation added later.
- Direct integration
- MTN Mobile Money, then Orange Money — with no aggregator sitting between you and the operator.
- Deterministic sandbox
- Test numbers that always produce the same outcome: success, decline, insufficient funds, expiry.
- Published contract
- A versioned, documented /v1 API whose error codes do not change without a new version.
Three steps to your first payment
Nothing to sign to begin: the test key is created with your account.
- 1
Create your account
Self-service signup. Your workspace and your first sk_test_ key exist immediately, with no step that waits on us.
- 2
Call the API
One request to collect: the amount, the currency and the customer's number. An idempotency key makes the call safe to replay without charging twice.
- 3
Get the outcome
A signed webhook tells you, and the status stays available to poll at any time — the notification is never the only source of truth.
curl -X POST https://api-sandbox.moni-wave.com/v1/collections \
-H "Authorization: Bearer sk_test_…" \
-H "Idempotency-Key: 3f9c1e2a-7b4d-4a1f-9c8e-2d6b5a0f3e11" \
-H "Content-Type: application/json" \
-d '{
"amount": 5000,
"currency": "XAF",
"msisdn": "237690000001",
"description": "Commande #1042"
}'What you can build
One API for money coming in, money going out, and everything needed to account for both.
Collections
Charge a customer's mobile wallet, confirmed on their own phone.
Payouts
Send money to a mobile wallet, one at a time or in bulk.
Refunds
Refund all or part of a collection, without calling anyone.
Hosted checkout
A link to send a customer when you do not have their number.
Webhooks
Signed notifications, retried, and replayable from the dashboard.
Dashboard
Transactions, balances, keys, team and the webhook delivery log.
Four ways to connect
From a raw HTTP call to a plugin you install — the same API underneath, so moving between them changes nothing about how money is handled.
REST API
Versioned /v1 endpoints, OpenAPI described, no SDK required. If you can make an HTTP request you can take a payment.
AvailableHosted checkout
Create a session, send the customer a link. They pick their operator and enter their number on a page we host, so you never touch it.
AvailableServer SDKs
Node and PHP libraries generated from the same OpenAPI description as the docs, so they cannot drift from the contract.
In progressE-commerce plugins
A WooCommerce plugin first, so a shop can take Mobile Money without anyone writing code.
PlannedA status you can actually trust
Most integration pain comes from a gateway that says “success” when it means “we sent it”. Here the status is folded from an immutable event stream, and the three terminal states absorb: nothing ever leaves them.
An operator callback is recorded as a claim, never as truth — only a status poll makes an outcome final. That is why a missing webhook can never leave you guessing, and why a late duplicate callback cannot reopen a transaction you have already settled.
Six numbers, six outcomes, every time
The sandbox is deterministic rather than random: each test number always produces the same result, so a failing test means your code changed, not that the fake operator felt different today.
| Test number | Always produces |
|---|---|
| 237 690 000 001 | Succeeds on the first status poll |
| 237 690 000 002 | Stays pending ~10 s, then succeeds — exercises your poll loop |
| 237 690 000 003 | Fails: the payer declined the prompt |
| 237 690 000 004 | Fails: insufficient funds |
| 237 690 000 005 | Stays pending forever — exercises expiry |
| 237 690 000 006 | Rejected at submission: not a registered subscriber |
These numbers are part of the published sandbox contract. Merchants script against them, so changing one would be a breaking change and is treated as such. Any other number behaves like the first row.
Get a test keyBuilt for developers
The guarantees that matter when money is involved, written into the contract rather than promised.
Idempotency is required
Every request that moves money carries a key; replaying it returns the original response.
Signed webhooks
Per-merchant HMAC-SHA256 signatures, with a reference implementation to verify them.
Status is confirmed, not claimed
An operator callback is evidence; only a status poll makes an outcome final.
A versioned contract
Error codes are stable; changing one would require a new version.
Built like a ledger, not like a form
The parts that decide whether money is correct are not features bolted on later — they are the shape of the system.
Double-entry ledger
Every movement is a set of postings that sum to zero. A balance is derived, never edited, so it cannot silently disagree with its history.
Event-sourced history
The audit log is the source of truth rather than a copy of it. Status is folded from the events, so the record and the reality cannot diverge.
Signed webhooks
HMAC-SHA256 with a per-merchant secret and a timestamp, so you can verify a notification really came from us and is not a replay.
Hashed API keys
Keys are stored as SHA-256 digests and shown in plaintext exactly once, at creation. We cannot recover yours, which is the point.
Private data plane
The database has no public endpoint and is reachable only over a private link from the application, never from the internet.
Regional hosting
Runs in Azure South Africa North, the closest region to the CEMAC market with three availability zones.
We do not claim certifications we have not been through. PCI DSS is not relevant while no card data is touched, and any certification we do obtain will be named here with its scope rather than shown as a badge.
Where the money sits, and when it moves
Collections do not vanish into an opaque pool: each one posts to your balance the moment it is confirmed, and you can see the posting.
- 01
A collection completes
The moment a status poll confirms it, the ledger posts the gross amount, the fee and the net — atomically with the terminal event, so the two can never disagree.
- 02
Your balance updates
Available funds move in real time and are visible in the dashboard and over the API. A payout is checked against them before submission.
- 03
You pay out
Send to any mobile wallet, singly or in bulk. A payout never leaves against a balance that is not there.
Direct rails, not an aggregator of aggregators
Most of what is sold locally is a layer over other people’s integrations. That is a legitimate way to launch quickly, and it changes what you can be told when something goes wrong.
| Aspect | Through an aggregator | MoniWave |
|---|---|---|
| Hops to the operator | Two or more, each able to time out or transform an error. | One. We hold the operator contract and the connector. |
| What “success” means | Often “the upstream accepted it”, which is not the same as the customer paying. | Confirmed by a status poll against the operator, never by a callback alone. |
| Dispute evidence | Whatever the middle layer chose to keep, requested by email. | Every provider exchange is logged and attached to the transaction. |
| Error codes | Re-mapped, and liable to change without a version. | Contractual and stable — changing one requires a new API version. |
| Fees | Two margins stacked: the operator’s and the aggregator’s. | One. There is nobody else in the chain to pay. |
Cameroon first, CEMAC next
One monetary zone, one currency, six countries. The rails are the same in each, which is what makes expansion a connector rather than a rebuild.
Countries
- CameroonLaunch market
- GabonPlanned
- Republic of the CongoPlanned
- ChadPlanned
- Central African RepublicPlanned
- Equatorial GuineaPlanned
All six share the Central African CFA franc (XAF), so a merchant integrating once does not re-integrate per country.
What is done, what is next
Public sandbox
DoneSelf-service signup, a test key in one call, deterministic outcomes.
Merchant dashboard
DoneTransactions, balances, API keys, team and the webhook delivery log, in both languages.
SDKs and plugins
In progressNode and PHP first, then a WooCommerce plugin.
Licensing
NextThe regulatory step that has to precede real money. Not ours to schedule, so no date is promised here.
MTN Mobile Money, live
NextThe same API, the same webhooks — you swap the base URL and the key.
Orange Money, live
NextThe second operator on the same connector interface.
Pricing
You pay only for successful payments. A failure or an expiry costs nothing.
The rate card is being finalised: the figures below will be published before real payments open. We would rather show an empty cell than a number nobody has agreed to yet.
See the rate cardWho it is for
The API is the same for everyone; these are the shapes it is being designed against.
- Online retailOnline retail: collect at checkout, refund without calling anyone.
- Software and subscriptionsSoftware and subscriptions: repeat collections and automatic reconciliation.
- MarketplacesMarketplaces: collect on one side, pay sellers on the other.
- Gaming and bettingGaming and betting: high volumes, fast payouts, a status you can trust.
Common questions
Including the ones a landing page usually avoids.
Can I take real payments today?
Not yet. The sandbox is open and behaves exactly as production will; real payments follow our licensing and operator agreements.
How do we go live?
Swap the base URL and the key. Nothing else changes: same requests, same responses, same webhooks.
Which operators are covered?
MTN Mobile Money first, Orange Money next, by direct integration rather than through an aggregator.
How do I get help?
Write to us — we answer in French and English, and the technical documentation is public.
What does it cost to test?
Nothing, with no time limit. The sandbox is free because it is how you decide whether to integrate at all, and a trial that expires mid-evaluation helps nobody.
What happens if a webhook never arrives?
Nothing is lost. Delivery is retried eight times with growing delays, every attempt is logged and redeliverable by hand, and the status stays available to poll — so a webhook is never your only way to know.
Do you handle card payments?
No. Mobile Money is the payment method that matters in this market, and doing one rail properly is worth more than doing several partially.
Who is behind MoniWave?
Awumba, building for the CEMAC market. Write to us and you will get a person who works on it, not a ticket queue.
Start in the sandbox
Create an account, take a test key, and run your first simulated collection within minutes.
No card, no contract, no sales call. Signing up creates a workspace and a test key.