QR Payments
EMVCo MPM static and dynamic QR codes. Every Djiboutian mobile wallet scans — Waafi, D-Money, CAC, SabaPay, and EAB.
Two modes
Printed, reusable
Generated once per outlet and printed at the point of sale. The customer scans and manually enters the transaction amount in their wallet app. Best for small merchants, market stalls, and low-volume counters.
- + No API call per transaction
- + Works offline (QR never expires)
- – Amount not embedded; customer inputs manually
Per-transaction, amount embedded
Generated fresh for each transaction with the amount pre-encoded in the EMVCo payload. The customer scans and confirms — no manual entry. Sessions expire after a configurable window (default 5 minutes).
- + Amount pre-filled — no customer input errors
- + Idempotent per transaction; no double-pay risk
- – Requires an API call per transaction
How it works — dynamic QR
- 1
Create a QR session
CallPOST /v1/qr/sessionswithamount,currency, andprovider. Useprovider=anyfor a universal QR that the wallet app resolves automatically. - 2
Display the QR to the customer
The response includesqr_data(base64 PNG, ready to render in an<img>tag) andqr_string(the raw EMVCo MPM payload for custom rendering or printing). Show either on screen or on a receipt printer. - 3
Customer scans with wallet app
The customer opens their Waafi, D-Money, CAC, SabaPay, or EAB wallet app and scans the QR code. The wallet resolves the EMVCo payload, displays the amount and merchant name for confirmation, then the customer approves. - 4
Provider confirms payment
The wallet app sends the payment instruction to the provider. The provider settles the transaction and notifies MerasPay via the provider callback. - 5
Webhook fires
MerasPay books the ledger and dispatches apayment_intent.succeededwebhook. The QR session transitions tocompleted. PollGET /v1/qr/sessions/{id}or subscribe to the webhook — whichever fits your architecture.
Endpoints
/v1/qr/sessionsCreate a dynamic QR session with an embedded amount. Returns a PNG and EMVCo payload.
/v1/qr/sessions/{id}Retrieve a session's current status and payment details.
/v1/qr/staticGenerate a static QR code for a merchant outlet. No expiry — suitable for printing.
Dynamic session — parameters
| Name | Type | Description |
|---|---|---|
amountrequired | integer | Minor units. DJF is zero-decimal; pass integer DJF. |
currencyrequired | ISO 4217 | "DJF" for domestic. "USD" accepted on international-enabled accounts. |
providerrequired | enum | One of waafi · dmoney · cac · sabapay · eab · any. Use any to generate a universal QR. |
expiry_seconds | integer | Session TTL in seconds. Default 300 (5 min). Maximum 3600 (1 hour). |
description | string | ≤140 chars. Appears as the payment reference in the customer's wallet history. |
metadata | map | Free-form string-string map; round-trips on every response and webhook. |
provider=anygenerates a universal QR that the customer's wallet resolves automatically — supported by Waafi and D-Money as of 2026-Q1. Additional wallets are being onboarded; check the changelog for updates.Static QR — parameters
| Name | Type | Description |
|---|---|---|
providerrequired | enum | One of waafi · dmoney · cac · sabapay · eab · any. |
outlet_label | string | Display name embedded in the QR (e.g. "Café Djibouti — Table 4"). ≤60 chars. |
merchant_category_code | string | ISO 18245 MCC (4 digits). Defaults to the merchant account's registered MCC. |
Sample — create dynamic session
curl -X POST https://api.merashub.com/v1/qr/sessions \
-H "Authorization: Bearer sk_test_..." \
-H "Idempotency-Key: order_99" \
-H "Content-Type: application/json" \
-d '{
"amount": 35000,
"currency": "DJF",
"provider": "any",
"expiry_seconds": 300,
"description": "Order #99 — table service"
}'{
"id": "qr_01HZK5GYJX9J4T3WR6K1C0DP7S",
"status": "pending",
"amount_minor": 35000,
"currency": "DJF",
"provider": "any",
"qr_string": "00020101021226430014DJ.MERASPAY.COM...",
"qr_data": "data:image/png;base64,iVBORw0KGgo...",
"expires_at": "2026-05-25T10:20:30Z",
"created_at": "2026-05-25T10:15:30Z"
}Session status states
pendingSession created — QR displayed, awaiting customer scan.
scannedCustomer has scanned the QR; wallet is processing.
completedPayment confirmed. Ledger booked, webhook fired.
expiredSession TTL elapsed before the customer paid.
cancelledMerchant cancelled the session before expiry.
Dynamic QR session expiry
expiry_seconds (default 5 minutes). Once expired, the QR code becomes invalid and the customer cannot pay. Poll GET /v1/qr/sessions/{id} to check status, or subscribe to the qr_session.expired webhook to trigger a UI refresh or new session creation automatically.