One QR your customers can scan, every wallet can read.
MerasPay QR speaks the EMVCo Merchant-Presented Mode spec end-to-end — including the CRC16 checksum every compliant wallet verifies — so a single payload works with Waafipay, EAB, SabaPay, D-Money, and any future-compliant network. Print one sticker, take payments from any wallet.
At a glance
- EMVCo CRC
- CRC16-CCITT
- Currencies
- 4
- Payload size
- ~200 B
- Generation latency
- < 30 ms
Static vs dynamic — both supported
The same endpoint generates either. Start with static QRs to launch quickly, swap to dynamic when your POS is ready — your customer-facing experience never changes.
Static QR
A single QR printed on a card, table-tent, or shop sticker. The customer scans, types the amount, and pays. No new payload per transaction. Cheapest to deploy across an estate.
Dynamic QR
A fresh QR per transaction with the amount baked in. Generated at the till in milliseconds, displayed on screen, then expired. Reduces typing errors and short-paid orders.
How the payment flows
From bill generation to settled ledger entry in under two seconds, end-to-end.
- 1
Request a QR payload
POST /v1/qr/payloads with the merchant id, amount (for dynamic), currency, and a network reference. The endpoint returns the EMVCo payload string plus a pre-rendered PNG.
- 2
Display or print
Show the QR on screen, print it on a receipt, or paste the payload string into your own renderer. The payload includes the CRC16 already.
- 3
Customer scans
Any compliant Djiboutian wallet decodes the payload, contacts MerasPay through the rail, and authorises with the customer (typically a wallet PIN or biometric).
- 4
Webhook fires
qr_payment.succeeded carries the network reference, the wallet that paid, and the linked PaymentIntent. Your POS software reconciles the order in under two seconds.
Everything you'd expect from a modern QR rail
EMVCo MPM compliant
Payload follows the EMVCo Merchant-Presented QR spec: payload format 01, point-of-initiation 11 or 12, CRC16-CCITT over the body. Any spec-compliant wallet scans it.
Multi-currency
DJF (262), USD (840), ETB (230), EUR (978) — the right ISO-4217 numeric code is encoded automatically per merchant configuration.
Per-transaction reference
Tag 62.05 carries a network reference id you control — your order number flows from QR generation through the rail and back into the webhook.
Customer wallet agnostic
A Waafipay user, an EAB user, or a SabaPay user can all scan the same MerasPay QR. We route the rail behind it; the customer never picks a network.
Static-to-dynamic upgrade
Start with a single printed QR per merchant; upgrade to dynamic-per-bill without changing your point-of-sale software. The same endpoint emits both.
Receipts and proof
Every scan produces a PaymentIntent and a settled ledger entry. Receipts, refunds, and disputes work exactly like any other charge.
Generate a payload
The QR payload is a plain string. We hand you both the string (so you can render it yourself) and a pre-rendered PNG behind a signed CDN URL.
# Dynamic QR for an in-store coffee, 1,500 DJF.
$ curl -X POST https://api.meraspay.io/v1/qr/payloads \
-u sk_live_xxx: \
-H "Idempotency-Key: bill-2026-05-24-1547" \
-d static=false \
-d amount=1500 \
-d currency=DJF \
-d merchant_name="Cafe Lagune" \
-d merchant_city="Djibouti" \
-d network_ref="ORD-1547"
{
"object": "qr_payload",
"payload": "00020101021226240010io.meraspay0108cafelg7b520400005303262540415005802DJ5910Cafe Lagune6008Djibouti6210050708ORD-15476304A1B2",
"static": false,
"amount": 1500,
"currency": "DJF",
"png_url": "https://cdn.meraspay.io/qr/abc.png",
"expires_at": "2026-05-24T15:52:00Z"
}Render it in your app
Five lines on the client.
// In-app: render a QR your customer can scan with their wallet.
const r = await fetch('/api/qr-bill', { method: 'POST', body: JSON.stringify({ amount: 1500 }) });
const { png_url, network_ref } = await r.json();
return (
<div className="text-center">
<img src={png_url} alt="Scan to pay" className="mx-auto h-64 w-64" />
<p className="mt-3 text-sm text-zinc-600">Ref {network_ref}</p>
<p className="mt-1 text-xs text-zinc-500">Expires in 5 minutes</p>
</div>
);Frequently asked
Do I need a separate POS terminal?▾
No. A printed sticker works for static QR; any tablet or phone works for dynamic QR. There is no certified hardware requirement — the merchant-presented QR pattern intentionally avoids it.
How does refunds work for a QR payment?▾
Refunds use the standard /v1/refunds endpoint against the PaymentIntent that the QR scan created. The customer receives the credit on the same wallet they paid from.
Can I issue a single QR for many merchants?▾
Marketplaces can; the QR encodes a platform-level merchant id and the platform splits on settlement using application_fee + transfer_data. Standalone merchants get a QR scoped to themselves.
What happens if the QR expires before the customer scans?▾
Dynamic QRs carry an expiry in tag 99. Scans of an expired payload return ErrRequestExpired and emit a qr_payment.expired webhook. You re-issue a new payload as part of your retry flow.
Does the QR support cross-border (Ethiopia)?▾
Yes. The currency tag accepts ETB (230) and the country tag accepts ET, so an Ethiopian wallet paying out of SantimPay can scan the same MerasPay QR a Djiboutian one can. The settlement currency is whatever the merchant chose.
Ready to integrate?
Get a sandbox account in minutes. Production goes live after a brief KYB review.