Qora by Laayo
Get API keys

Quickstart

From nothing to a scannable QR and a confirmed payment. About ten minutes, all in test mode.

Before you start

  • A Qora account — signing up issues test keys immediately.
  • Your Fonepay account connected under Settings → Fonepay. Qora walks you through it.

Business verification is not required for any of this. It gates live payments only, so you can build and ship the whole integration while your paperwork is in review.

1. Get your test secret key

Find it in the dashboard under Developers → API keys. It starts with sk_test_.

2. Create a payment

Amounts are integer paisa. 25000 means रु 250.00.

curl
curl https://qora.laayo.com.np/v1/payments \
  -H "Authorization: Bearer sk_test_YOUR_KEY" \
  -H "Idempotency-Key: order-1042" \
  -d amount=25000 \
  -d "reference=Order #1042" \
  -d "description=Two coffees"

Or run it here, against your own account — same call, same response:

POST /v1/payments No key yet
Used for this one request and then forgotten — never saved in this browser, never stored on our servers. Yours is under Developers → API keys.
Your own order number. Comes back on the payment and the webhook.
curl

You get back the payment plus the QR payload:

201 Created
{
  "id": "pay_7Fh2kQ9mXwRt3vNbLp6sDc",
  "object": "payment",
  "mode": "test",
  "amount": 25000,
  "amount_formatted": "250.00",
  "currency": "NPR",
  "status": "requires_payment",
  "prn": "Q260907091500A3KD9WQZ",
  "reference": "Order #1042",
  "qr": {
    "payload": "00020101021230590016A00555555555550208...",
    "expires_at": "2026-09-07T09:20:00+05:45"
  },
  "created_at": "2026-09-07T09:15:00+05:45"
}

3. Show the QR

qr.payload is the string your QR code has to encode. Treat it as opaque — do not parse it, build it, or cache it — and render it with any QR library.

Browser
import QRCode from 'qrcode';

// payload comes from your server; never expose a secret key to the browser.
await QRCode.toCanvas(
  document.getElementById('qr'),
  payment.qr.payload,
  { width: 240, margin: 2 }
);

The QR expires (five minutes by default). After that, create a new payment rather than re-showing the old payload — an expired QR fails at the customer's bank, which looks like your bug.

4. Know when it is paid

Use webhooks. Qora watches the payment for you and posts payment.succeeded to your endpoint the moment it is confirmed. That is the fastest and most reliable signal you can get, and there is nothing for you to connect to.

hooks/qora.php
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_QORA_SIGNATURE'] ?? '';

class="tok-key">if (! qora_verify($payload, $signature, getenv('QORA_WEBHOOK_SECRET'))) {
    http_response_code(class="tok-num">400);
    exit;
}

$event = json_decode($payload, class="tok-key">true);

class="tok-key">if ($event['type'] === 'payment.succeeded') {
    mark_order_paid($event['data']['payment']['reference']);
}

http_response_code(class="tok-num">200);

The full qora_verify implementation is on the webhooks page, in PHP, Node and Python.

If you cannot receive webhooks — a counter app on a laptop, say — poll GET /v1/payments/{id} every couple of seconds instead. Do not poll faster than that; see rate limits.

Never mark an order paid from a browser redirect or a client-side callback. Anyone can request a URL. Trust only a verified webhook or a server-side read of GET /v1/payments/{id}.

5. Go live

  1. Complete business verification.
  2. Add your live Fonepay credentials under Settings → Fonepay.
  3. Swap sk_test_ for sk_live_ and point your webhook at production.

Nothing else changes. Same endpoints, same payloads, real money.