All products
QR Payment

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. 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. 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. 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. 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.

bash
# 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.

tsx
// 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.