docs QRIS (Publik)
Integrasi pembayaran QRIS untuk bot Telegram, web store, dan backend toko. Setiap pengguna wajib pakai API Key sendiri di env bot/store (supaya transaksi & saldo tidak tabrakan antar toko). Yang tidak perlu di env: QRIS static & token merchant (sudah di server admin).
https://YOUR-DOMAIN
Pengenalan
Gateway ini memakai QRIS merchant admin di server. Setiap penyewa yang punya lisensi aktif dapat membuat pembayaran, mendeteksi lunas, dan menarik saldo lewat portal (Withdraw).
API Key per toko
Setiap pengguna punya key beda. Semua transaksi & saldo terpisah, tidak tabrakan.
Tanpa QRIS di bot
QRIS static & token merchant di server admin. Bot cukup BASE_URL + API_KEY.
Saldo & Withdraw
Pembayaran success masuk saldo akun itu saja → tarik lewat portal Withdraw.
Env bot / web store (wajib per pengguna)
Setiap penyewa mendapat API Key unik di portal.
Taruh key itu di env bot / store masing-masing agar request terikat ke akun mereka:
transaksi, payment_id, dan saldo tidak campur dengan toko lain.
| Variabel | Keterangan | |
|---|---|---|
BASE_URL |
wajib | URL gateway (contoh domain kamu) |
API_KEY |
wajib | API Key unik per pengguna dari portal (Settings). Jangan saling pakai key orang lain. |
Autentikasi
Semua request (kecuali gambar QR) pakai header Bearer API Key dari portal.
Login di Portal untuk menampilkan API Key Anda di sini.
Daftar / Login PortalAuthorization: Bearer js_API_KEY_ANDA
Cara Mulai
- Daftar di /portal (OTP Telegram) → sistem buat API Key unik
- Salin API Key dari Settings → taruh di env bot/store:
API_KEY+BASE_URL - Setiap request pakai header
Authorization: Bearer API_KEY→ transaksi & saldo terpisah per toko - Panggil create QRIS → tampilkan ke pelanggan → cek bayar dengan
payment_id
Buat QRIS (Payment)
Membuat QRIS dinamis memakai QRIS admin di server.
Response berisi payment_id dan nominal yang harus dibayar persis.
/gateaway/qris/create
Request Body
{
"amount": 50000,
"ref": "ORDER-123",
"note": "Top up user"
}
| Param | Tipe | Keterangan | |
|---|---|---|---|
amount | integer | wajib | Nominal order (IDR) yang akan masuk saldo setelah lunas |
ref | string | opsional | Referensi order di sistem kamu |
note | string | opsional | Catatan |
Response
{
"success": true,
"message": "QRIS created successfully (shared PG)",
"data": {
"payment_id": "pay_abc123",
"amount": 50550,
"requested_amount": 50000,
"credit_amount": 50000,
"qr_string": "00020101...",
"qr_url": "https://YOUR-DOMAIN/gateaway/qr/xxxxx",
"expiry_time": "2026-07-23 15:00:00",
"mode": "tenant_shared"
}
}
| Field | Keterangan |
|---|---|
amount | Nominal yang harus dibayar customer di QR — bayar persis |
requested_amount / credit_amount | Nominal order; yang masuk saldo setelah lunas |
payment_id | ID untuk cek status pembayaran |
checkout_url | Halaman checkout siap pakai untuk customer (/pay/:id) lengkap dengan timer, salin nominal, unduh QR, & auto-redirect |
expiry_time | Batas waktu bayar (~15 menit). Lewat → payment otomatis cancelled |
POST /gateway/qris/create:
callback_url— URL webhook server Anda (menerima POST JSON + headerX-SignatureHMAC-SHA256 saat lunas).redirect_url— URL tujuan redirect otomatis setelah pembeli melunasi di halamancheckout_url.- Header
Idempotency-Key— Mencegah pembuatan QR ganda bila bot/server Anda melakukan retry request yang sama.
amount = harga order kamu. Server mengembalikan nominal bayar final di
field amount response — tampilkan itu ke customer.
Setelah lunas, saldo = credit_amount.
expiry_time, status otomatis cancelled.
Buat payment baru bila perlu.
Cek Pembayaran
Cek apakah customer sudah bayar. Jika success, saldo penyewa otomatis di-credit.
/gateaway/payment/check
{ "payment_id": "pay_abc123" }
Response (lunas)
{
"success": true,
"data": {
"paid": true,
"expired": false,
"cancelled": false,
"credit_amount": 50000,
"payment": { "id": "pay_abc123", "amount": 50350, "status": "paid" },
"balance": { "balance": 50000, "locked": 0, "available": 50000 }
}
}
Response (belum bayar)
{
"success": true,
"data": {
"paid": false,
"expired": false,
"cancelled": false,
"payment": { "id": "pay_abc123", "status": "pending", "expires_at": "..." }
}
}
Response (QRIS expired → auto cancelled)
{
"success": true,
"message": "QRIS expired — otomatis dibatalkan",
"data": {
"paid": false,
"expired": true,
"cancelled": true,
"payment": {
"id": "pay_abc123",
"status": "cancelled",
"cancel_reason": "expired",
"expires_at": "..."
}
}
}
paid === true untuk sukses.
Jika expired / cancelled true → hentikan polling, minta user buat QR baru.
Expire juga dijalankan periodik di server (~30 detik), tidak hanya saat /payment/check.
Status pembayaran & auto-cancel expire
Setelah qris/create, payment berstatus pending sampai lunas
atau lewat expiry_time (default ~15 menit, env
QRIS_EXPIRY_MINUTES).
| Status | Arti | Di bot / web store |
|---|---|---|
pending |
QR aktif, menunggu bayar | Tampilkan “Menunggu pembayaran…” · lanjut polling |
paid |
Lunas · saldo di-credit | Order sukses · hentikan polling |
cancelled |
Otomatis dibatalkan karena expire (atau dibatalkan sistem) | Tampilkan “QR kadaluarsa / dibatalkan” · buat payment baru |
status: "cancelled" dengan
cancel_reason: "expired". Tidak perlu cancel manual.
Response payment/check → paid: false,
expired: true, cancelled: true.
Kapan auto-cancel jalan?
- Saat ada yang memanggil
GET/POSTpayment (list / check) lewatfindById - Job periodik server (~setiap 30 detik) membatalkan semua pending yang lewat
expires_at
Setelah cancelled, nominal unik dibebaskan untuk payment baru.
Gambar QR (/gateaway/qr/:id) juga tidak valid lagi setelah expiry store QR.
Riwayat & Saldo
/gateaway/payments
Alias riwayat juga: GET /gateaway/transactions (mode shared PG otomatis).
QR Image
Gambar QR publik (tanpa API Key). ID random & berumur pendek (~15 menit).
/gateaway/qr/:id
| Query | Keterangan |
|---|---|
format=png | Default — image/png |
format=svg | SVG |
format=raw | String QRIS |
format=json | JSON metadata |
Contoh Bot / Web Store
POST /gateaway/qris/create→ dapatpayment_id,amount,qr_url,expiry_time- Tampilkan QR ke pelanggan — bayar persis
amountsebelum expire - Polling
POST /gateaway/payment/checksetiap 3–5 detik - Jika
paid: true→ order sukses (saldo =credit_amount) - Jika
expired/cancelled→ QR kadaluarsa, otomatis dibatalkan; buat payment baru
Node.js
Python
PHP / Laravel (cURL)
Webhook & Verifikasi Signature (HMAC-SHA256)
Selain polling, Anda dapat menyertakan callback_url saat membuat QRIS (atau mengatur Default Callback URL di Portal → Pengaturan).
Begitu pembayaran lunas, server kami mengirim HTTP POST JSON dengan header tanda tangan kriptografi:
| Header | Isi |
|---|---|
X-Signature | sha256=<hex HMAC-SHA256(webhook_secret, timestamp + "." + rawBody)> |
X-Signature-Simple | sha256=<hex HMAC-SHA256(webhook_secret, rawBody)> |
X-Webhook-Timestamp | Unix timestamp detik saat pengiriman |
X-Webhook-Event | payment.success atau binance.payment.success |
const crypto = require('crypto');
app.post('/webhook/janspay', express.raw({ type: 'application/json' }), (req, res) => {
const secret = process.env.WEBHOOK_SECRET; // dari Portal → Pengaturan
const ts = req.headers['x-webhook-timestamp'];
const sig = req.headers['x-signature'];
const expected = 'sha256=' + crypto.createHmac('sha256', secret)
.update(`${ts}.${req.body.toString('utf8')}`)
.digest('hex');
if (sig !== expected) return res.status(401).send('Invalid signature');
const event = JSON.parse(req.body.toString('utf8'));
// Proses order lunas: event.payment_id, event.credit_amount, event.ref
res.json({ ok: true });
});
Integrasi PHP & Laravel
Contoh lengkap integrasi payment gateway di PHP native (cURL) dan framework Laravel (Guzzle / HTTP Client).
1. PHP Native (cURL) — Buat Tagihan & Redirect
2. Laravel Framework (HTTP Client) — Controller & Checkout
3. Verifikasi Webhook Signature (HMAC-SHA256) di PHP
⚡ Interactive API Playground (Coba Langsung)
Live SimulatorUji simulasi pembuatan tagihan QRIS secara langsung dari browser Anda tanpa perlu koding. Jika Anda belum punya API Key, sistem otomatis menggunakan Demo Sandbox publik.
Error Umum
| Pesan / status | Arti | Solusi |
|---|---|---|
| 401 Unauthorized | API Key salah / kosong | Salin ulang dari portal Settings |
| QRIS admin belum di-set | Server admin belum siap | Hubungi admin gateway |
| paid: false | Belum bayar / nominal beda | Pastikan bayar persis amount di response |
| expired / cancelled: true | QRIS lewat expiry_time, auto-cancel | Buat QR baru lewat /qris/create |