Integrating Mobile Money Payments
Without Losing Transactions
Mobile wallets are how many customers prefer to pay. What to get right: callbacks, idempotency, status checks, reconciliation and clear failure states.
In Ethiopia, mobile wallets such as telebirr and M-PESA, and bank wallets such as CBE Birr, are a normal way to pay for goods and services. For many products supporting them is not optional. Each provider documents its API; the hard part is building a payment flow that never loses or double-counts money when networks and callbacks misbehave.
Understand the asynchronous flow
Most mobile money APIs follow the same pattern. Your server creates a payment request, the customer approves it on their phone (often with a PIN prompt or USSD push), and the provider later notifies your server through a callback. Approval can take seconds or minutes, and sometimes never happens.
- Create an order with status "pending" before calling the provider.
- Store the provider's transaction reference against your order as soon as you receive it.
- Treat the callback or a status query as the source of truth for success, never the customer's redirect back to your app.
Make every step idempotent
Callbacks can arrive twice, late or out of order, and customers tap "Pay" twice. Your system must reach the same result whether a message arrives once or five times.
- Send your own unique payment ID as the reference or idempotency key wherever the provider's API supports it.
- Process each callback inside a database transaction that checks the current status before changing it.
- Verify every callback with the signature or verification method the provider documents, and check that amount and currency match the order.
Always have a status check
Never rely on callbacks alone. A scheduled job should query the provider's transaction status endpoint for payments that have stayed pending longer than expected, and resolve them either way. This catches callbacks lost to downtime or network failures.
Payment states to model
| State | Meaning | What the customer sees |
|---|---|---|
| Pending | Request sent, awaiting approval | "Approve on your phone" |
| Succeeded | Confirmed by callback or status check | Receipt and confirmation |
| Failed or expired | Declined, timed out or insufficient balance | Clear reason and a retry option |
| Refunded | Money returned after success | Refund confirmation |
Reconcile every day
Fetch the provider's settlement report and match it against your own records daily. Mismatches are uncommon, but when they happen you want to find them within a day, not at the end of the quarter. Automate the matching and alert a person only when something differs.
Design for the customer's phone
Many customers pay from entry-level Android phones on limited data. Keep the payment screen light, explain exactly what will appear on their phone, and show a "waiting for approval" state with a timeout and a retry path instead of a frozen screen.
Questions we hear when teams add mobile money to a product.
Should we integrate each wallet directly or use an aggregator?
Direct integrations give more control and can lower fees at volume. A payment aggregator gets you several wallets and cards with one integration. Many teams start with an aggregator and add direct integrations later.
Do we need a licence to accept mobile money?
Merchants usually accept payments through a licensed provider or aggregator under a merchant agreement. Requirements depend on your business model, so confirm with the provider and legal counsel, especially if you hold funds on behalf of others.
How do we test without moving real money?
Most providers offer a sandbox or test credentials. Combine that with automated tests that replay duplicate, delayed and missing callbacks.
Want to talk to an Arki engineer?
Before any commercial conversation, we'll put you on a 30-min call with a senior engineer in your stack. No salespeople. Just a technical sanity check.
Tell us what you're working on.
We'll respond within one business day with the right next step.