DOKUMENTASI DEVELOPER
Integrasi yang jelas, dari awal.
Panduan integrasi API ScanQRIS. Jalankan integrasi melalui server aplikasi Anda dan simpan kredensial dengan aman.
01. Mulai integrasi
Panduan integrasi API ScanQRIS. Jalankan integrasi melalui server aplikasi Anda dan simpan kredensial dengan aman.
- Buat akun ScanQRIS dan selesaikan verifikasi email.
- Buka Kunci API dan konfirmasi kata sandi untuk melihat secret.
- Konfigurasikan origin API dan kredensial di server aplikasi Anda.
- Buat pembayaran, tampilkan gambar QR, dan gunakan total pembayaran dari respons.
- Pantau status melalui SSE atau periksa detail transaksi sampai server mengonfirmasi status akhir.
BASE URLhttps://pg.kasirleci.com/v1
Mode demo adalah simulasi antarmuka, bukan sandbox API eksternal. Kredensial demo tidak dapat digunakan untuk transaksi nyata.
Kontrak respons telah diperbarui. Field di luar daftar kontrak publik tidak lagi dikirim; perubahan ini tidak kompatibel bagi integrasi yang masih bergantung pada field lama. Sesuaikan pembacaan respons dan callback dengan daftar field saat ini.
02. Autentikasi
Endpoint pembayaran menerima Bearer token atau API key dengan signature. Untuk integrasi server, gunakan API key dengan HMAC.
- X-API-Key
- API key merchant.
- X-Timestamp
- Unix timestamp saat permintaan dibuat, dalam detik.
- X-Signature
- HMAC-SHA256 dalam hex atas
METHOD|PATH|TIMESTAMP|BODY.
PATH adalah pathname lengkap seperti /v1/payments, tanpa query string. BODY harus identik byte-per-byte dengan yang dikirim; untuk GET gunakan string kosong. Gunakan HTTPS dan lakukan penandatanganan di server.
import { createHmac } from 'node:crypto';
const origin = process.env.PAYMENT_API_ORIGIN;
const apiKey = process.env.PAYMENT_API_KEY;
const secret = process.env.PAYMENT_API_SECRET;
if (!origin || !apiKey || !secret) throw new Error('Konfigurasi API belum lengkap');
const method = 'POST';
const path = '/v1/payments';
const timestamp = String(Math.floor(Date.now() / 1000));
// Simpan key unik per operasi logis; gunakan key yang sama saat retry.
const idempotencyKey = 'INV-001:create-payment:v1';
const body = JSON.stringify({
"amount": 50000,
"source": "platform",
"fee_bearer": "customer",
"external_id": "INV-001",
"callback_url": "https://merchant.example/webhook"
});
const signature = createHmac('sha256', secret)
.update([method, path, timestamp, body].join('|'))
.digest('hex');
const response = await fetch(new URL(path, origin), {
method,
headers: {
'Content-Type': 'application/json',
'X-API-Key': apiKey,
'X-Timestamp': timestamp,
'X-Signature': signature,
'Idempotency-Key': idempotencyKey,
},
body,
});
const payload = await response.json();
if (!response.ok || !payload.success) throw new Error('Periksa riwayat sebelum mencoba lagi');
// Gunakan payload.data.final_amount dan payload.data.qris_image_base64 dari server.
// Saat fee_bearer=customer, final_amount mencakup service_fee dan net_amount adalah nilai bersih.Simpan API key, secret, dan origin di environment server. Jangan mengeksposnya ke browser, URL, log, screenshot, atau source control.
03. Buat pembayaran
POST/v1/payments
- amount
- Wajib, rupiah bulat antara 10 dan 10.000.000.
- source
- Opsional; gunakan own atau platform. Default mengikuti akun.
- fee_bearer
- Opsional; gunakan owner atau customer. Default mengikuti akun.
- external_id
- Opsional, ID pesanan milik aplikasi Anda. Tidak menggantikan Idempotency-Key.
- callback_url
- Opsional, URL HTTPS server Anda untuk callback pembayaran.
Kirim header Idempotency-Key unik untuk satu operasi logis dan simpan key tersebut bersama pesanan. Jika retry diperlukan, gunakan key dan body yang sama dengan timestamp serta signature baru. Jangan membuat key baru setelah respons ambigu. external_id adalah ID pesanan dan bukan jaminan idempotensi.
{
"success": true,
"data": {
"trx_id": "PAY-EXAMPLE",
"external_id": "INV-001",
"amount": 50000,
"original_amount": 50000,
"final_amount": 50500,
"nominal": 50500,
"source": "platform",
"purpose": "payment",
"service_fee": 500,
"fee_bearer": "customer",
"net_amount": 50000,
"status": "pending",
"qris_image_base64": "iVBORw0KGgo...",
"created_at": "2026-09-18T12:00:00Z",
"expired_at": "2026-09-18T12:05:00Z",
"paid_at": null,
"updated_at": "2026-09-18T12:00:00Z",
"settled_at": null,
"available_at": null
}
}Gunakan final_amount dari server, bukan rumus lokal. Tampilkan qris_image_base64 sebagai data:image/png;base64,…. Gambar QR dapat dipindai dan didekode oleh aplikasi pembayaran; jangan menjanjikan bahwa isi QR bersifat rahasia mutlak.
04. Detail pembayaran
GET/v1/payments/:trx_id
Endpoint ini mengembalikan transaksi untuk akun yang terautentikasi. Kontrak respons create dan detail sama; field yang tersedia adalah:
- trx_id
- ID transaksi yang diterbitkan layanan.
- external_id
- ID pesanan yang dikirim aplikasi Anda, atau string kosong jika tidak diisi.
- amount
- Nominal permintaan dalam rupiah.
- original_amount
- Alias nominal permintaan dalam rupiah.
- final_amount
- Total yang harus dibayar. Gunakan nilai dari server.
- nominal
- Alias total yang harus dibayar.
- source
- Sumber pembayaran yang digunakan.
- purpose
- Tujuan transaksi.
- service_fee
- Biaya layanan dalam rupiah.
- fee_bearer
- Pihak yang menanggung biaya layanan.
- net_amount
- Nilai bersih dalam rupiah.
- status
- Status transaksi saat respons dibuat.
- qris_image_base64
- Gambar QR dalam base64 untuk ditampilkan sebagai data:image/png;base64,...
- created_at
- Waktu transaksi dibuat.
- expired_at
- Batas waktu pembayaran.
- paid_at
- Waktu pembayaran dikonfirmasi, atau null.
- updated_at
- Waktu terakhir transaksi diperbarui.
- settled_at
- Waktu settlement, jika tersedia.
- available_at
- Waktu dana tersedia, jika tersedia.
{
"success": true,
"data": {
"trx_id": "PAY-EXAMPLE",
"external_id": "INV-001",
"amount": 50000,
"original_amount": 50000,
"final_amount": 50500,
"nominal": 50500,
"source": "platform",
"purpose": "payment",
"service_fee": 500,
"fee_bearer": "customer",
"net_amount": 50000,
"status": "paid",
"qris_image_base64": "iVBORw0KGgo...",
"created_at": "2026-09-18T12:00:00Z",
"expired_at": "2026-09-18T12:05:00Z",
"paid_at": "2026-09-18T12:01:00Z",
"updated_at": "2026-09-18T12:01:00Z",
"settled_at": "2026-09-18T12:06:00Z",
"available_at": "2026-09-18T12:36:00Z"
}
}05. Daftar transaksi
GET/v1/payments
Parameter query: page (mulai 1), limit (maksimal 100), search untuk trx_id atau external_id, status, source, dan purpose. Query string tidak ikut ditandatangani; signature memakai pathname tanpa query.
Respons daftar memakai envelope paginasi: success, data, dan meta untuk informasi halaman.
06. Status realtime
GET/v1/payments/:trx_id/stream
SSE dapat mengirim event status, complete, timeout, revoked, error. Gunakan detail transaksi terautentikasi sebagai sumber kebenaran status akhir.
07. Webhook callback
Isi callback_url ketika membuat pembayaran. Callback menggunakan header X-Webhook-Id, X-Webhook-Timestamp, dan X-Webhook-Signature. Signature adalah HMAC-SHA256 hex dari timestamp.payload menggunakan API secret merchant.
{
"event": "paid",
"event_version": 1,
"event_id": "WH-EXAMPLE",
"data": {
"trx_id": "PAY-EXAMPLE",
"external_id": "INV-001",
"amount": 50000,
"final_amount": 50500,
"service_fee": 500,
"fee_bearer": "customer",
"net_amount": 50000,
"source": "platform",
"purpose": "payment",
"status": "paid",
"paid_at": "2026-09-18T12:01:00Z"
}
}Field envelope callback
- event
- Jenis event callback pembayaran terkonfirmasi.
- event_version
- Versi kontrak payload callback; saat ini 1.
- event_id
- ID unik event untuk deduplikasi.
Field data
- trx_id
- ID transaksi layanan.
- external_id
- ID pesanan aplikasi Anda, atau string kosong jika tidak ada.
- amount
- Nominal permintaan dalam rupiah.
- final_amount
- Total pembayaran dalam rupiah.
- service_fee
- Biaya layanan dalam rupiah.
- fee_bearer
- Pihak yang menanggung biaya layanan. Dapat tidak tersedia pada callback historis; jangan menebak nilainya.
- net_amount
- Nilai bersih dalam rupiah. Dapat tidak tersedia pada callback historis; jangan menebak nilainya.
- source
- Sumber pembayaran.
- purpose
- Tujuan transaksi.
- status
- Status pembayaran yang sudah dikonfirmasi.
- paid_at
- Waktu pembayaran dikonfirmasi.
Verifikasi signature terhadap raw body, periksa event_version, dan deduplikasi berdasarkan event_id sebelum menjalankan efek bisnis. Endpoint Anda harus mengembalikan HTTP 2xx setelah event valid diterima. Konfirmasi detail transaksi melalui GET sebelum memenuhi pesanan.
08. Penanganan error
Periksa status HTTP dan success. Error dapat berupa string atau objek dengan code, message, dan details.field/details.message.
- 400: request atau format tidak valid.
- 401: kredensial atau signature tidak valid.
- 403: akun tidak memiliki akses atau konfigurasi yang diperlukan.
- 404: transaksi atau endpoint tidak tersedia.
- 422: validasi gagal.
- 429: batas request tercapai; tunggu sebelum mencoba lagi.
- 5xx: gangguan layanan; periksa detail transaksi sebelum mengulang create.
Field opsional dalam error.details:
- field
- Field yang memerlukan perbaikan, jika tersedia.
- message
- Petunjuk perbaikan validasi, jika tersedia.
- reason
- Kode alasan; active_transaction_limit_reached berarti slot aktif penuh.
- active_transactions
- Jumlah transaksi aktif saat kapasitas diperiksa.
- active_transaction_limit
- Batas slot aktif paket saat kapasitas diperiksa.
- remaining_transaction_slots
- Slot yang tersisa; 0 saat kapasitas penuh.
Saat slot aktif penuh, respons 403 menjelaskan kapasitas akun. Tunggu transaksi selesai atau kedaluwarsa sebelum membuat pembayaran baru.
{
"success": false,
"error": {
"code": "VALIDATION_ERROR",
"message": "validation failed",
"details": {
"field": "source",
"message": "must be own or platform"
}
}
}Jika respons create tidak jelas, periksa GET menggunakan trx_id bila sudah diketahui. Jika belum, ulangi POST dengan Idempotency-Key dan body yang sama, serta timestamp dan signature baru. Jangan gunakan key baru untuk operasi yang sama.
09. Status pembayaran
Belum terkonfirmasi; tampilkan gambar QR selama masih berlaku.
Server telah mengonfirmasi pembayaran; proses pesanan sesuai kebijakan aplikasi Anda.
Masa pembayaran berakhir; jangan gunakan QR ini lagi.
Pembayaran gagal; periksa rincian sebelum membuat transaksi pengganti.
Status paid, expired, dan failed bersifat terminal. Hitung mundur di browser bukan bukti pembayaran; gunakan status dari server.