Books that can't drift
A real chart of accounts, with append-only journal lines that balance in every currency. Fees and margin post as explicit legs. Month-end stops being an argument.
Payment orchestration for Africa
DeltraPay is a payments switch. One integration unlocks mobile money, bank transfers and wallets across ten African markets — routed intelligently across providers, reconciled against a real double-entry ledger, and settled in the currency your customer actually paid.
curl https://api.deltrapay.com/v1/payins \
-H "X-API-Key: dp_live_…" \
-H "Idempotency-Key: 8f14e45f" \
-d '{
"amount": "100.00",
"currency": "GHS",
"country": "GH",
"method": "mobile_money",
"operator": "MTN",
"phone": "233551234567",
"externalId": "order-2026-0007"
}'
The network
Mobile money and bank transfer — Lagos to Nairobi, Accra to Cairo. You integrate DeltraPay once; the rails behind it are ours to add, maintain and route between.
16 of the rails we route to today
A different provider per country. A different payload per operator. Naira has two decimal places, the CFA franc has none. One provider goes down on a Friday and you find out from your customers.
DeltraPay puts a switch in front of all of it. Country, currency and payment instrument are data you send — never a new endpoint you integrate.
How it works
You send one corridor. The switch picks the provider, watches it, and covers for it — without you shipping anything.
POST /v1/payins
GHS · GH · mobile_money · MTN
Amount, currency, country, method and operator travel in one request body. Adding Cameroon is not a new integration — it's two letters in a field you already send.
Providers are scored on cost, success rate and priority before the money leaves. When the chosen one stalls mid-Friday, the next takes the payment. You ship nothing.
Not what was requested. Under-pay a bank transfer and DeltraPay re-queries the provider, posts the settled amount, and flags the variance instead of crediting you wrongly.
Callbacks are enqueued inside the same transaction that completes the payment, then retried with backoff into a dead-letter queue. Delivery survives a dead process.
Coverage
Every corridor is reference data in the switch — currencies carry their own decimal exponent, so the CFA franc is never accidentally multiplied by 100. Opening the next market is a data change, not a release.
Ten markets live, routing real money today. Hover a market — or a pin on the map — to see the rails we collect on there.
Developers
Standard REST, plural nouns, real verbs, OpenAPI 3.0 — and a contract that does not change shape when you add a market. Integrate once; expand by configuration.
{
"data": {
"id": 10427,
"orderId": "DP-3F9A2C8E",
"externalId": "order-2026-0007",
"type": "payin",
"status": "pending",
"country": "GH",
"currency": "GHS",
"method": "mobile_money",
"operator": "MTN",
"amount": "100.00",
"amountMinor": 10000,
"fee": "2.00",
"net": "98.00",
"nextAction": {
"type": "authorize_on_device",
"instructions": "Approve the prompt on your handset."
},
"createdAt": "2026-08-02T10:14:22Z"
}
}
// POSTed to your callback URL when a pay-in reaches
// a terminal state. Retried with backoff until you 2xx.
{
"event": "payin.completed",
"orderId": "DP-3F9A2C8E",
"externalId": "order-2026-0007",
"status": "completed",
"amount": "96.50",
// what actually settled — not what was requested
"fee": "1.93",
"currency": "GHS"
}
// Never hardcode a corridor. Ask for it.
{
"data": [
{ "country": "KE", "method": "mobile_money",
"code": "MPESA", "name": "M-Pesa" },
{ "country": "KE", "method": "mobile_money",
"code": "AIRTEL", "name": "Airtel Money" },
{ "country": "KE", "method": "mobile_money",
"code": "EQUITEL", "name": "Equitel" }
]
}
Retries are safe by construction. The same Idempotency-Key — or your own externalId — returns the original transaction and its stored response. A network timeout never becomes a duplicate charge or a duplicate settlement.
Amounts are held in minor units against each currency's ISO 4217 exponent, resolved from reference data. Zero-decimal currencies like XOF and UGX are handled natively — no rounding drift, no reconciliation breaks.
Notifications are enqueued in the same database transaction that finalises the payment, then retried with exponential backoff until you acknowledge. Provider webhooks are HMAC-verified and independently re-queried before anything is posted to the ledger.
Sandbox and production share the same endpoints, schema and semantics — only the key prefix changes. Certify your integration once, then go live with a credential swap instead of a rewrite.
Platform
Anyone can forward a request to a provider. Staying right about the money afterwards is the part that keeps merchants.
A real chart of accounts, with append-only journal lines that balance in every currency. Fees and margin post as explicit legs. Month-end stops being an argument.
Route by priority, weight, least cost or success rate, per corridor, from the back office. Reorder providers at 2am and the next transaction follows it.
A merchant portal your customers log into themselves — live transactions, balances, statements and CSV exports, straight off the ledger. Fewer tickets, faster answers.
Workers re-check every in-flight transaction against the provider, around the clock. A dropped webhook becomes a resolved payment instead of a mystery.
Scoped API keys, server-issued roles, HMAC-verified provider webhooks and row-level locking on every ledger write. The answers are ready before your compliance team asks.
No new endpoint, no new SDK, no release train. Send a different country code and you are collecting in a new market — the integration you already shipped keeps working.
Get sandbox keys, run a real test collection end-to-end, and watch the ledger entry it produces. Your second market is a country code — not another project.
Sandbox keys are free. You only pay for collections that succeed.