Direct API

QRIS

One code, scannable by every Indonesian e-wallet and banking app.

QRIS is the national QR standard, so a single code is accepted by GoPay, OVO, DANA, ShopeePay, LinkAja and every bank app — you do not pick a wallet, the buyer does. Confirmation lands within seconds of the scan.

Integration steps

  1. Create the payment request.
  2. Render the QR from payment.qr_string on your page.
  3. Acknowledge the payment — the payment.paid webhook.
  4. Check status when you need certainty: GET /v1/transactions/:id.

1. Create the payment request

curl https://pay.kasera.id/v1/transactions \
  -H "Authorization: Bearer kp_live_..." \
  -H "Idempotency-Key: order-1234" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 150000,
    "external_id": "ORD-1234",
    "payment_methods": ["qris"]
  }'
{
  "id": "payreq_9b2f...",
  "status": "pending",
  "payment_method": "qris",
  "payment": {
    "type": "qr",
    "qr_string": "00020101021226670016COM.KASERA.WWW...6304A1B2"
  },
  "instructions": { "title": "Cara membayar dengan QRIS", "steps": ["..."] },
  "expires_at": "2026-08-27T13:00:00+07:00"
}

2. Display the QR

payment.qr_string is the raw EMV payload. Encode it into an image in the browser — we return the string rather than an image on purpose, so your page owns size, styling and caching. Print the instructions block underneath verbatim; it is written for the buyer, in Indonesian.

// Any QR library works — the payload is a plain string.
// npm install qrcode
import QRCode from "qrcode";

QRCode.toCanvas(document.getElementById("qr"), trx.payment.qr_string);

In the rare case payment is absent, fetch the request again — it is never a placeholder.

3. Acknowledge the payment

The payment.paid webhook is what tells you money arrived. Nothing on the buyer's screen is evidence — only our record is.

4. Check status

GET /v1/transactions/:id when you need certainty — reconciling, or a buyer says they paid and no webhook arrived. Not as a substitute for the webhook.

The QR dies at expires_at — a scan after that fails at the buyer's bank, not at us. Hide it then, and create a new payment request if they still want to pay. Amount and expiry caps are on limits.