One API for every payment rail in Djibouti.
Stop maintaining five SDKs, five sandbox setups, and five reconciliation scripts. MerasPay Gateway gives you one unified interface that orchestrates Waafipay, CAC, SabaPay, D-Money, and East Africa Bank — with a single ledger, a single webhook signature, and a single dashboard.
By the numbers
- Live rails
- 5
- API resources
- 40+
- Settled p50 latency
- < 1.2s
- Webhook retry window
- 72h
Every Djiboutian rail, abstracted
The gateway ships a provider adapter for every rail that processes money in Djibouti today. Each adapter normalises its native quirks — OTP confirmation, RSA-PSS signing, JWT renewal, address-name binding — into a single set of orchestrator methods.
Waafipay
Djibouti Telecom mobile wallet. Customer pays by entering an OTP into their handset; we surface it through a requires_action → confirm step.
CAC International Bank
Retail and corporate account pull. JWT auth with automatic 401-retry and credentials cached per merchant.
SabaPay (Saba African Bank)
PayMe wallet and fund-transfer rails. Per-merchant address-name / PIN / machine binding handled by the orchestrator.
D-Money
Djibouti Telecom RSA-PSS-signed channel with per-merchant keypair management and sign+verify round-trip on every request.
East Africa Bank
Direct bank pull and push with merchant-level username/password credentials encrypted at rest under HKDF-derived keys.
Mock (sandbox)
A full provider replica used automatically when the merchant is in sandbox mode. Wired to the simulator so you can choose the response.
How a payment flows through the gateway
Four steps, the same shape every time — regardless of which rail you chose.
- 1
Create a PaymentIntent
POST /v1/payment_intents with the amount, currency, and chosen provider. Gateway looks up the merchant, resolves the adapter, and writes a pending intent.
- 2
Provider initiates
Adapter calls the rail (Waafipay PreAuthorize, CAC create, SabaPay PayMe, etc.). The merchant receives an intent in requires_action or requires_payment_method.
- 3
Customer confirms
OTP entry, popup, or in-app confirm. Merchant calls POST /v1/payment_intents/:id/confirm. The orchestrator finalises with the adapter and posts ledger entries.
- 4
Webhook fires
payment_intent.succeeded fan-outs to every registered endpoint via the webhook-dispatcher worker with HMAC signing and retry.
Built for the rails you actually have
Familiar API shapes mean your team can ship in a day, not a quarter. The grown-up parts — idempotency, versioning, signed webhooks, ledger-first design — come standard rather than as an upsell.
Idempotency, not optional
Every money-moving call requires an Idempotency-Key. Repeats inside 24h return the same response — your retries are free.
Familiar resource model
PaymentIntents, Customers, Refunds, Payouts, Transfers, Disputes — clean, separable resources that map directly to the lifecycle of every payment.
HMAC-signed webhooks
Outbound webhooks are signed HMAC-SHA256 over {timestamp}.{event_id}.{body} with exponential retry and full delivery history.
Per-merchant key vault
Provider credentials are stored AES-256-GCM-encrypted with merchant-specific data keys derived from a hardware-backed master via HKDF.
Built-in ledger
Every payment posts double-entry rows into the ledger before it touches a provider. Settlements reconcile against the ledger, not the provider report.
Versioned API
MerasPay-Version: 2026-MM-DD pinned per API key. We never break old pins — your old integrations keep working forever.
See it in code
A complete C2B PaymentIntent against the Waafipay rail. The Idempotency-Key makes retries safe; the API-version header pins you to a known surface forever.
curl -X POST https://api.meraspay.io/v1/payment_intents \
-u sk_live_xxx: \
-H "Idempotency-Key: 9b1d4f0c-7b6f-4e72-a3c2-8aef9d12c0ed" \
-H "MerasPay-Version: 2026-05-01" \
-d amount=15000 \
-d currency=DJF \
-d "payment_method[provider]=waafi" \
-d "payment_method[msisdn]=+25377123456" \
-d description="Order #1024"
# Response
{
"id": "pi_01HZF8AYJX3D8M7QK0E5V4WJ9K",
"object": "payment_intent",
"amount": 15000,
"currency": "DJF",
"status": "requires_action",
"next_action": { "type": "otp_required", "msisdn": "+25377123456" },
"client_secret": "pi_..._secret_..."
}Frequently asked
Which rails are live today?▾
Waafipay, CAC International Bank, SabaPay (Saba African Bank), D-Money (Djibouti Telecom), and East Africa Bank are production-ready. Cybersource card-not-present and SantimPay (Ethiopia outbound) come online as their respective sandbox credentials clear vendor onboarding.
Do I need a separate integration per rail?▾
No. You set payment_method.provider on the PaymentIntent and we route to the right adapter. Switching rails is a one-line change. The PaymentIntent shape stays identical.
How are settlement files produced?▾
The reconciler worker runs nightly, pulls each provider settlement report, and matches it against ledger entries. Discrepancies surface in the admin portal as reconciliation_breaks before any money lands on the merchant balance.
What happens if a provider is down?▾
The orchestrator returns ErrProviderUnavailable to you and emits a provider.unavailable webhook. We do not silently retry to another rail — provider choice is the merchant's, and silent rerouting hides cost and FX surprises.
Ready to integrate?
Get a sandbox account in minutes. Production goes live after a brief KYB review.