API payment gateway QRIS: cara kerja, fitur wajib, dan cara integrasinya

Cara kerja API payment gateway QRIS: buat QRIS dinamis per pesanan, terima webhook, uji di sandbox, aturan keamanan, dan biayanya. Dengan contoh kode.

Tim WaroengPay · Diperbarui · 11 menit baca

API payment gateway QRIS: cara kerja, fitur wajib, dan cara integrasinya

API payment gateway QRIS adalah antarmuka HTTP yang membuat servermu bisa membuat tagihan QRIS dinamis untuk setiap pesanan, lalu diberi tahu secara otomatis begitu pesanan itu dibayar. Alurnya selalu sama: servermu memanggil satu endpoint untuk membuat tagihan, pembeli memindai QR yang nominalnya sudah terisi, lalu payment gateway mengirim webhook atau kamu mengecek status lewat API. Panduan ini menjelaskan cara kerjanya, endpoint dan fitur yang wajib ada, contoh kode, cara menguji di sandbox, aturan keamanan, sampai soal biaya, supaya kamu bisa menilai API QRIS mana pun dan memasangnya dengan benar.

Cara kerja API QRIS dalam empat langkah

  1. Buat tagihan. Saat pembeli checkout, servermu mengirim nominal pesanan ke endpoint pembuatan tagihan. Respons berisi id tagihan, string QRIS dinamis, nominal akhir, dan alamat halaman bayar.
  2. Tampilkan pembayaran. Arahkan pembeli ke halaman bayar milik payment gateway, atau buat gambar QR sendiri dari string QRIS dan tampilkan di halaman checkout-mu.
  3. Pembeli membayar. Pembeli memindai QR dari aplikasi m-banking atau e-wallet apa pun. Nominalnya sudah terisi, jadi tidak ada salah ketik.
  4. Pesanan lunas otomatis. Payment gateway mendeteksi pembayaran, mengubah status tagihan menjadi lunas, lalu mengirim webhook ke servermu. Servermu memverifikasi webhook dan memproses pesanan.

Kata kuncinya adalah QRIS dinamis: kode QR yang dibuat per transaksi dan sudah memuat nominal. Tanpa itu, pembeli mengetik nominal sendiri dan pembayaran sulit dicocokkan otomatis. Bedanya dengan QRIS statis dijelaskan di perbedaan QRIS statis dan dinamis.

Bagaimana API tahu tagihan sudah dibayar

Bagian ini yang paling sering luput saat memilih API QRIS. Membuat QR itu mudah; yang sulit adalah mengetahui dengan pasti bahwa pembayaran yang masuk milik tagihan yang mana. Cara pencocokan berbeda antar penyedia, jadi tanyakan atau baca dokumentasinya sebelum memilih.

Di WaroengPay, pencocokan memakai nominal unik. Setiap tagihan mendapat kode unik kecil (default Rp100 sampai Rp250) yang ditambahkan ke harga, sehingga setiap tagihan yang sedang menunggu punya nominal berbeda. Selama ada tagihan terbuka, mutasi rekening merchant dibaca setiap 8 detik. Kredit dengan nominal persis sama dalam jendela waktu tagihan menandai tagihan itu lunas. Satu kredit hanya bisa melunasi satu tagihan, jadi tidak ada konfirmasi ganda. Penjelasan lengkap mekanismenya ada di panduan konfirmasi pembayaran otomatis dan apa itu kode unik transfer.

Konsekuensinya untuk developer: pembeli wajib membayar nominal persis, termasuk kode unik. Tampilkan field amount dari respons apa adanya, jangan dibulatkan.

Fitur yang wajib ada di API payment gateway QRIS

Gunakan tabel ini sebagai daftar periksa saat membandingkan penyedia. Kolom terakhir menunjukkan cara WaroengPay memenuhinya.

KemampuanKenapa pentingDi WaroengPay
Buat tagihan QRIS dinamisNominal terisi, setiap pesanan punya tagihan sendiriPOST /api/payments
Cek status tagihanCadangan kalau webhook terlambat atau servermu sempat matiGET /api/payments/:id
Daftar dan filter tagihanRekonsiliasi harian dan laporanGET /api/payments dengan filter status, tanggal, dan pencarian
Batalkan tagihanPembeli ganti pesanan, stok habisPOST /api/payments/:id/cancel
Webhook bertanda tanganServermu bisa memastikan kiriman benar dari payment gatewayHMAC-SHA256 dengan timestamp, dikirim ulang bila gagal
Idempotency keyTombol checkout terklik dua kali tidak membuat dua tagihanHeader Idempotency-Key
Mode uji (sandbox)Uji alur lengkap tanpa uang sungguhanAPI key wp_test_ dan endpoint simulasi
Kode error yang jelasServermu bisa bereaksi tepat, bukan sekadar "gagal"JSON { error, code } dengan status HTTP standar
Halaman bayar siap pakaiTidak perlu membuat tampilan QR sendiripay_url dengan QR, nominal, dan hitung mundur

Membuat tagihan QRIS dinamis lewat API

Semua request memakai HTTPS dan JSON, dengan API key di header Authorization: Bearer. Nominal dikirim dalam rupiah sebagai bilangan bulat, antara Rp1.000 dan Rp10.000.000. Contoh dalam beberapa bahasa:

curl -X POST https://waroengpay.com/api/payments \
-H "Authorization: Bearer $WAROENGPAY_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: INV-2041" \
-d '{
"amount": 50000,
"reference": "INV-2041",
"description": "Paket Premium 1 bulan",
"callback_url": "https://tokoku.id/webhook/waroengpay",
"redirect_url": "https://tokoku.id/pesanan/INV-2041"
}'

Field opsional yang paling berguna:

  • reference: nomor pesanan di sistemmu. Ikut di webhook, tapi tidak unik, jadi jangan dipakai untuk mencocokkan pembayaran.
  • callback_url: alamat webhook khusus tagihan ini. Kosong berarti memakai URL webhook default akun.
  • redirect_url: pembeli diarahkan ke sini 6 detik setelah lunas.
  • expires_in: masa berlaku dalam menit, 5 sampai 60. Defaultnya 15 menit.

Respons (dipersingkat):

JSON
{
"id": "WRG-7K2M9QX4BD1PZ",
"reference": "INV-2041",
"status": "pending",
"base_amount": 50000,
"unique_code": 137,
"amount": 50137,
"fee": 137,
"net_amount": 50000,
"qris": "000201010212265...6304A1B2",
"pay_url": "https://waroengpay.com/pay/WRG-7K2M9QX4BD1PZ",
"expires_at": "2026-09-24T03:15:00.000Z",
"livemode": true
}
FieldArtinya
idId tagihan (WRG-...). Simpan di pesananmu, karena webhook dicocokkan lewat id ini.
amountNominal yang harus dibayar pembeli, sudah termasuk kode unik.
base_amountHarga yang kamu kirim. Bandingkan dengan total pesanan saat webhook datang.
feeBiaya layanan tagihan ini, dikunci saat tagihan dibuat. net_amount = amount − fee masuk ke saldo.
qrisString QRIS dinamis untuk dibuat gambar QR sendiri.
pay_urlHalaman bayar siap pakai.
livemodefalse untuk tagihan mode uji.

Menampilkan QR ke pembeli

Ada dua pilihan, dan keduanya sama-sama otomatis:

  • Arahkan ke pay_url. Cara tercepat. Halaman bayar sudah berisi QR, nominal persis, hitung mundur, nama dan logo tokomu, lalu berubah sendiri begitu lunas. Setelah lunas, pembeli bisa mengunduh bukti bayar.
  • Render QR sendiri. Buat gambar QR dari string qris dengan pustaka QR apa pun, tampilkan amount persis dan batas waktunya, lalu pantau statusnya. Cocok kalau checkout-mu harus tetap di satu halaman, misalnya di aplikasi mobile atau bot.
Saat QR dipindai, nama merchant yang muncul di aplikasi pembeli adalah WAROENGKU, bukan nama tokomu, karena pembayaran diterima di rekening merchant QRIS yang terhubung dengan WaroengPay. Kalau kamu merender QR sendiri, sebutkan hal ini di dekat QR supaya pembeli tidak ragu.

Mengetahui pembayaran lunas: webhook atau polling

Ada dua cara servermu mengetahui tagihan sudah lunas. Untuk produksi, pakai webhook sebagai jalur utama dan cek status sebagai cadangan.

WebhookPolling status
Cara kerjaWaroengPay mengirim POST ke servermu saat status jadi finalServermu memanggil GET /api/payments/:id berulang
Butuh alamat publikYa, port 80, 443, 8080, atau 8443Tidak
KecepatanSegera setelah lunas terdeteksiTergantung jarak polling, cukup tiap 5 sampai 10 detik
Cocok untukWebsite dan aplikasi dengan backendSkrip sederhana, bot, server tanpa alamat publik

Webhook

Event yang dikirim: payment.paid, payment.expired, payment.cancelled, dan ping dari tombol "Kirim tes" di dashboard. Setiap kiriman membawa header X-WaroengPay-Timestamp dan X-Signature, yaitu HMAC-SHA256 dari "<timestamp>.<body mentah>" dengan secret webhook-mu. Contoh verifikasi lengkap:

import crypto from "node:crypto";
import express from "express";
const app = express();
const SECRET = process.env.WAROENGPAY_WEBHOOK_SECRET; // whsec_...
// pakai body mentah: tanda tangan dihitung dari teks persis yang dikirim
app.post("/webhook/waroengpay", express.raw({ type: "application/json" }), async (req, res) => {
const ts = req.get("x-waroengpay-timestamp") || "";
const sig = req.get("x-signature") || "";
const expected = crypto.createHmac("sha256", SECRET).update(`${ts}.${req.body}`).digest("hex");
const fresh = Math.abs(Date.now() / 1000 - Number(ts)) < 300; // tolak kiriman ulang > 5 menit
if (!fresh || sig.length !== expected.length || !crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
return res.sendStatus(401);
}
const event = JSON.parse(req.body);
if (event.livemode === false) return res.sendStatus(200); // event mode uji: jangan pernah menandai pesanan asli
if (event.event === "payment.paid") {
// cari pesanan lewat id tagihan yang disimpan saat membuat tagihan, BUKAN reference (tidak unik, bebas diisi)
const order = await db.orders.findByWaroengpayId(event.data.id);
if (!order || event.data.base_amount !== order.total) return res.sendStatus(200); // nominal beda: jangan tandai lunas
await db.orders.markPaid(order.id, event.id); // lewati kalau event.id sudah pernah diproses
}
res.sendStatus(200);
});

Balas dengan status 2xx dalam 10 detik dan kerjakan proses berat di belakang. Kalau servermu gagal membalas, kiriman dicoba ulang: langsung, lalu setelah 30 detik, 2 menit, 10 menit, 30 menit, dan 2 jam. Karena itu event yang sama bisa datang lebih dari sekali; simpan id event dan lewati duplikat. Penjelasan setiap langkah verifikasi ada di panduan webhook pembayaran.

Polling

Tanpa webhook, cek status sampai tidak lagi pending:

Node.js
async function tungguLunas(id) {
for (;;) {
const res = await fetch(`https://waroengpay.com/api/payments/${id}`, {
headers: { Authorization: `Bearer ${process.env.WAROENGPAY_API_KEY}` },
});
const tagihan = await res.json();
if (tagihan.status !== "pending") return tagihan; // paid | expired | cancelled
await new Promise((r) => setTimeout(r, 5000)); // cukup tiap 5 sampai 10 detik
}
}

Menguji di sandbox tanpa uang sungguhan

Buat API key mode uji (wp_test_...) di dashboard → Integrasi & API → Kredensial, lalu panggil endpoint yang sama persis. Tagihan uji punya livemode: false, tidak bisa dibayar aplikasi apa pun, dan tidak pernah masuk ke saldo. Kamu melunasinya lewat tombol Simulasikan bayar di halaman bayar atau lewat endpoint simulasi:

curl -X POST https://waroengpay.com/api/payments/WRG-7K2M9QX4BD1PZ/simulate \
-H "Authorization: Bearer $WAROENGPAY_TEST_KEY" \
-H "Content-Type: application/json" \
-d '{"action":"pay"}' # atau "expire"

Tiga hal yang sering membuat bingung saat menguji:

  • Webhook tagihan uji hanya dikirim ke callback_url yang diisi saat membuat tagihan. URL webhook default akun tidak dipakai.
  • Webhook uji ditandatangani dengan secret mode uji (whsec_test_...), bukan secret live.
  • Alamat localhost tidak bisa menerima webhook. Pakai layanan tunnel seperti ngrok atau Cloudflare Tunnel untuk mendapat alamat publik sementara.

Uji minimal tiga skenario: lunas ("action":"pay"), kedaluwarsa ("action":"expire"), dan webhook yang kamu balas dengan error supaya terlihat pengiriman ulangnya.

Aturan keamanan integrasi QRIS

  1. API key hanya di server. Simpan di variabel lingkungan, jangan di kode frontend, aplikasi mobile, atau repository. Curiga bocor? Buat ulang di dashboard; key lama langsung mati. Kartu API key menampilkan kapan dan dari IP mana key terakhir dipakai.
  2. Jangan anggap lunas karena pembeli kembali ke redirect_url. Alamat itu bisa dibuka siapa saja. Status lunas hanya dari webhook atau dari API.
  3. Verifikasi tanda tangan dari body mentah dan tolak kiriman yang timestamp-nya lebih dari 5 menit.
  4. Cocokkan lewat data.id dan nominal. Tanda tangan valid hanya membuktikan event berasal dari WaroengPay, bukan bahwa event itu untuk pesanan yang kamu kira. Pastikan data.base_amount sama dengan total pesanan.
  5. Abaikan livemode: false di server produksi.
  6. Siapkan pembayaran telat. Pembayaran yang masuk setelah tagihan kedaluwarsa bisa dicocokkan admin; kamu lalu menerima payment.paid dengan data.late = true setelah sebelumnya menerima payment.expired. Jangan menolaknya otomatis: proses pesanan atau hubungi pembeli.

Batasan dan kode error

BatasanNilai
Nominal per tagihanRp1.000 sampai Rp10.000.000, ditambah kode unik
Masa berlaku5 sampai 60 menit (default 15), ditambah 5 menit masa tenggang
Tagihan dibuatMaksimal 30 per menit per akun
Tagihan menungguMaksimal 50 per akun, dan 25 dengan nominal yang sama
RequestMaksimal 300 per menit per IP, body JSON maksimal 20 KB
KodeArtinyaYang perlu dilakukan
400 invalid_amountNominal bukan bilangan bulat dalam batasKirim angka rupiah tanpa titik ribuan, misalnya 50000
401 invalid_api_keyAPI key salah atau sudah dibuat ulangPeriksa variabel lingkungan
403 account_not_activeAkun belum disetujui atau nonaktifTunggu persetujuan admin atau hubungi kami
429 rate_limitedTerlalu banyak requestTunggu sesuai header Retry-After
429 too_many_pendingTerlalu banyak tagihan menungguBatalkan tagihan lama atau tunggu selesai
503 amount_busyKode unik untuk nominal itu sedang habisCoba lagi beberapa saat kemudian

Berapa biaya API QRIS?

Banyak orang mencari "API QRIS gratis". Yang perlu dibedakan adalah biaya akses API dan biaya per transaksi. Di WaroengPay, API, webhook, dan mode uji tidak dikenai biaya, begitu juga pendaftaran, biaya bulanan, dan penarikan dana. Per transaksi, kode unik yang dibayar pembeli menjadi biaya layanan, jadi harga barang diterima utuh. Untuk harga di atas Rp500.000 ada tambahan 0,7% dari harga.

ContohHarga Rp50.000Harga Rp600.000
Pembeli membayarRp50.137Rp600.201
Biaya WaroengPayRp137 (kode unik)Rp4.401 (kode unik + 0,7%)
Masuk ke saldomuRp50.000Rp595.800

Angka kode unik di atas hanya contoh; nilai sebenarnya ada di field unique_code setiap tagihan. Rincian lengkap dan jadwal pencairan ada di halaman harga. Untuk membandingkan dengan penyedia lain, baca perbandingan payment gateway QRIS untuk UMKM.

Panduan integrasi per bahasa

  • Integrasi QRIS di PHP dan Laravel: dari checkout, webhook, sampai pengecualian CSRF di Laravel.
  • Webhook pembayaran: verifikasi HMAC, idempotensi, dan pengiriman ulang, dengan contoh Node.js, PHP, dan Python.
  • Dokumentasi API: referensi semua endpoint, termasuk contoh Node.js dan Python. Versi Markdown-nya ada di /docs.md, praktis untuk dibaca asisten AI saat menulis integrasi.

Tidak punya backend atau belum ingin coding? Link pembayaran dibuat dari dashboard tanpa API key dan bisa dibagikan di chat atau bio media sosial. Setiap pembeli tetap mendapat tagihan QRIS sendiri. Gambaran semua pilihan ada di panduan lengkap terima pembayaran QRIS di toko online.

Checklist sebelum go live

  • Alur lengkap sudah diuji di mode uji: lunas, kedaluwarsa, dan webhook yang dikirim ulang.
  • API key diganti ke key live (wp_live_...) dan secret webhook ke secret live.
  • URL webhook default terisi, dan tombol Kirim tes di dashboard mendapat balasan 2xx.
  • Pesanan hanya lunas lewat webhook atau status API, dicocokkan lewat id tagihan dan nominal.
  • payment.paid dengan late: true ditangani.
  • Satu transaksi kecil dengan uang sungguhan berhasil lunas sendiri.

Pertanyaan yang sering muncul

Apakah butuh mesin EDC atau rekening merchant sendiri?

Tidak. Pembayaran diterima di rekening merchant QRIS yang terhubung dengan WaroengPay. Saldo setiap toko dicatat terpisah dan dicairkan ke rekening atas nama pemilik toko.

Bagaimana cara mendapatkan API key?

Daftar akun, tunggu persetujuan admin, lalu buat API key di dashboard → Integrasi & API → Kredensial. Key mode uji bisa dipakai untuk mencoba alur dari awal sampai webhook.

Seberapa cepat status berubah lunas?

Biasanya dalam belasan detik setelah pembeli membayar, karena mutasi dibaca setiap 8 detik selama ada tagihan terbuka. Kecepatannya tetap bergantung pada mutasi bank. Konfirmasi berjalan 24 jam.

Bisakah satu tagihan dibayar dua kali?

Tidak. Satu kredit di mutasi hanya bisa melunasi satu tagihan, dan tagihan yang sudah lunas tidak bisa dilunasi lagi. Kredit kedua dicatat terpisah untuk ditelusuri.

Apakah link pembayaran bisa dibuat lewat API?

Tidak, link pembayaran dibuat di dashboard. Tagihan dari link tetap muncul di GET /api/payments dengan link_id terisi dan mengirim webhook ke URL default akunmu.

Panduan lain

Mulai terima QRIS hari ini

Tanpa biaya bulanan. Buat akun, tunggu persetujuan admin, lalu kirim tagihan pertamamu.