Konek QR

Mulai cepat: tagihan QRIS pertama lewat API

Buat tagihan uji, simulasikan pembayaran, dan terima kabarnya dalam 10 menit.

Dalam 10 menit kamu membuat tagihan uji, menandainya lunas lewat simulasi, dan menerima kabarnya. Semua contoh memakai curl; SDK Node dan Laravel ada di bagian akhir.

Referensi lengkap: openapi.yaml (juga di https://api.konekqr.web.id/openapi.yaml).

Cara kerjanya singkat

  1. Server-mu membuat tagihan dengan nominal, misalnya Rp25.000.
  2. Konek QR menambahkan kode unik Rp1–499 (total misalnya Rp25.193) dan membuat QRIS dinamis dari QRIS statis milikmu.
  3. Pembeli membayar dari aplikasi apa saja. Uangnya langsung masuk ke akun merchant-mu, bukan lewat Konek QR.
  4. Aplikasi Konek QR Listener di HP-mu membaca notifikasi "pembayaran diterima", Konek QR mencocokkan totalnya, lalu tagihan jadi paid.
  5. Server-mu diberi tahu lewat webhook, atau membacanya dari GET /v1/events.

1. Siapkan akun

  1. Daftar di app.konekqr.web.id.
  2. QRIS → tambahkan QRIS statis dari aplikasi merchant-mu (unggah gambar atau tempel teksnya). Untuk mode uji, QRIS belum perlu diverifikasi.
  3. API → buat key mode uji. Key (kq_test_…) hanya ditampilkan sekali; simpan di variabel lingkungan server, jangan di kode atau di browser.
Shell
export KONEKQR_KEY='kq_test_...'

Key uji hanya melihat dan membuat objek mode uji. QR tagihan uji sengaja tidak bisa dibayar (penerbitnya ID.KONEKQR.TEST, merchant KONEK QR TEST), jadi aman dicoba.

2. Buat tagihan

Shell
curl https://api.konekqr.web.id/v1/payments \
  -H "Authorization: Bearer $KONEKQR_KEY" \
  -H "Idempotency-Key: order-1001" \
  -H "Content-Type: application/json" \
  -d '{"amount": 25000, "description": "Kopi susu 2x", "referenceId": "INV-1001", "expiresInMinutes": 15}'

Jawaban 201:

JSON
{
  "payment": {
    "id": "01972d1c-4b8e-7c3a-9d2f-1a2b3c4d5e6f",
    "kind": "invoice",
    "livemode": false,
    "status": "pending",
    "amount": 25193,
    "baseAmount": 25000,
    "uniqueCode": 193,
    "referenceId": "INV-1001",
    "expiresAt": "2026-11-04T09:45:00Z",
    "payUrl": "https://pay.konekqr.web.id/p/01972d1c-4b8e-7c3a-9d2f-1a2b3c4d5e6f",
    "qrString": "00020101021226…6304ABCD",
    "qris": { "id": "…", "label": "Toko Budi", "provider": "shopeepay", "merchantName": "TOKO CONTOH", "nmid": "ID0000000000000" },
    "…": "…"
  }
}

Lalu pilih salah satu:

  • Arahkan pembeli ke payUrl: halaman bayar siap pakai dengan QR, hitung mundur, status otomatis, dan baris "Dibayar ke: nama · NMID · provider". Isi returnUrl supaya ada tombol kembali ke tokomu.
  • Tampilkan QR sendiri dari qrString (library QR apa saja), dan tulis totalnya (amount, Rp25.193) dengan jelas. Pembeli harus membayar tepat sebesar total itu.

Hal yang perlu diingat:

  • Idempotency-Key wajib. Kirim ulang permintaan yang sama dengan key yang sama (misalnya saat timeout) dan kamu mendapat tagihan yang sama, bukan tagihan kedua; jawabannya membawa header Idempotency-Replayed: true. Pakai id pesananmu. Berlaku 24 jam.
  • Tanpa qrisId, tagihan memakai QRIS tertua yang bisa dipakai. Daftar QRIS: GET /v1/qris.
  • referenceId unik per akun dan mode (409 reference_taken).
  • metadata (objek JSON ≤ 4 KB) dikembalikan apa adanya di setiap tagihan dan event.

3. Cek status

Shell
curl https://api.konekqr.web.id/v1/payments/01972d1c-4b8e-7c3a-9d2f-1a2b3c4d5e6f \
  -H "Authorization: Bearer $KONEKQR_KEY"

status: pending, paid, expired (lewat expiresAt), atau cancelled. paidLate: true artinya dibayar setelah kedaluwarsa: pembayaran sampai 24 jam setelah expiresAt tetap dicocokkan, dan kamu yang memutuskan pesanan itu tetap diproses atau tidak.

Daftar tagihan: GET /v1/payments?status=paid&limit=50 (terbaru dulu; halaman berikutnya dengan before=<id terakhir>). Cari per pesanan: GET /v1/payments?referenceId=INV-1001. Batalkan: POST /v1/payments/{id}/cancel.

4. Simulasikan pembayaran (mode uji)

Shell
curl -X POST https://api.konekqr.web.id/v1/payments/01972d1c-4b8e-7c3a-9d2f-1a2b3c4d5e6f/simulate \
  -H "Authorization: Bearer $KONEKQR_KEY"

Tagihan menjadi paid dengan paidVia: "simulation", dan event payment.paid dikirim persis seperti pembayaran sungguhan. Di dashboard, tagihan uji punya tombol "Simulasikan lunas" yang sama.

5. Terima kabar lunas

Ada dua cara; banyak integrasi memakai keduanya.

Webhook (disarankan)

  1. Dashboard → Webhook → tambah endpoint: URL https publik, mode uji (endpoint menerima event live atau uji), event payment.paid (atau semua).
  2. Simpan secret whsec_… yang ditampilkan.
  3. Di server-mu, verifikasi tanda tangan lalu proses event. Contoh lengkap untuk PHP, Node, Python, dan Go: Verifikasi webhook.
  4. Tekan "Kirim event uji", lalu simulasikan tagihan dari langkah 4.

Isi webhook adalah Event:

JSON
{ "id": "evt_01972d1f-…", "type": "payment.paid", "livemode": false, "createdAt": "2026-11-04T09:31:12Z", "data": { "…tagihan…": "…" } }

Jawab 2xx dalam 15 detik. Kalau gagal, Konek QR mencoba lagi selama sekitar 3 hari, dan log pengiriman menampilkan diagnosis Dokter Webhook.

Event API (tanpa URL publik, atau sebagai cadangan)

Shell
curl "https://api.konekqr.web.id/v1/events?types=payment.paid&wait=30" \
  -H "Authorization: Bearer $KONEKQR_KEY"

Jawaban: { "events": [...], "next": "evt_…" }. Simpan next dan kirim sebagai after= pada panggilan berikutnya; next tetap maju walaupun event yang lewat tidak cocok dengan types. Dengan wait=30 permintaan menunggu sampai ada event baru (long-poll), jadi cukup diulang terus. Event disimpan 30 hari: konsumen yang sempat mati tinggal melanjutkan dari cursor terakhirnya.

Event yang ada: payment.paid, payment.expired, payment.cancelled, payment.unmatched (pembayaran yang tidak cocok dengan tagihan mana pun, misalnya pembeli membayar QR statis langsung), device.offline, device.online.

6. Pindah ke live

  1. Verifikasi QRIS: pasang Listener (Memasang Konek QR Listener), lalu di Dashboard → QRIS tekan verifikasi dan bayar nominal kecil yang diminta ke QRIS-mu sendiri. Setelah Listener mendeteksinya, QRIS jadi active. QRIS kedua dan seterusnya menunggu 24 jam setelah diverifikasi.
  2. Buat key live (kq_live_…) dan endpoint webhook live.
  3. Pertimbangkan dua opsi pengaman di POST /v1/payments:
    • "requireDetection": true: tagihan ditolak (409 detection_offline) kalau tidak ada HP Listener yang aktif dalam 7 menit terakhir, supaya kamu tidak menjual barang yang pembayarannya tidak bisa dikonfirmasi.
    • "expectedNmid": "ID10…": tagihan ditolak (409 nmid_mismatch) kalau QRIS-nya bukan NMID ini.
  4. Pantau HP Listener: GET /v1/devices atau event device.offline.

Error

Semua error berformat application/problem+json. Bandingkan code, bukan teksnya:

JSON
{ "type": "https://konekqr.web.id/errors/invalid_api_key", "title": "Invalid API key", "status": 401, "code": "invalid_api_key", "detail": "API key tidak ada, salah, atau sudah dicabut. …" }
StatuscodeArtinya
400idempotency_key_invalidheader Idempotency-Key kosong atau lebih dari 100 karakter
400invalid_jsonbody bukan JSON yang valid, atau ada field yang tidak dikenal
401invalid_api_keykey salah, dicabut, atau tidak dikirim
403insufficient_scopekey tidak punya izin (payments:read, payments:write, events:read)
403browser_not_allowedpermintaan dari browser; API key hanya untuk server
403test_mode_onlysimulasi memakai key live
404not_foundtagihan atau QRIS tidak ada (atau milik mode lain)
409qris_not_ready, too_many_pending, unique_codes_exhausted, reference_taken, nmid_mismatch, detection_offline, payment_not_pending, idempotency_conflict, idempotency_in_progresslihat deskripsi di OpenAPI
422validation_failedfield salah; rinciannya di errors[] (field, code, message)
429rate_limitedlebih dari 120 permintaan per menit per key; tunggu sesuai Retry-After

detail dan message berbahasa Indonesia; kirim Accept-Language: en untuk bahasa Inggris.

SDK

Node.js 20.3+ (npm install @konekqr/node):

JavaScript
import { KonekQR } from '@konekqr/node';

const kq = new KonekQR({ apiKey: process.env.KONEKQR_KEY });
const payment = await kq.createPayment({ amount: 25000, referenceId: 'INV-1001' }, { idempotencyKey: 'order-1001' });
for await (const event of kq.events({ types: ['payment.paid'] })) {
  console.log(event.data.referenceId, 'lunas');
}

Laravel 12/13 (composer require konekqr/laravel):

PHP
use KonekQR\Laravel\Facades\KonekQR;

$payment = KonekQR::createPayment(['amount' => 25000, 'referenceId' => 'INV-1001'], idempotencyKey: 'order-1001');

Detail pemasangan ada di README masing-masing SDK.

Ada yang kurang jelas atau salah? Kirim ke [email protected].