Lewati ke konten

DOKUMENTASI DEVELOPER

Integrasi yang jelas, dari awal.

Panduan integrasi API ScanQRIS. Jalankan integrasi melalui server aplikasi Anda dan simpan kredensial dengan aman.

Kelola kredensial API

01. Mulai integrasi

Panduan integrasi API ScanQRIS. Jalankan integrasi melalui server aplikasi Anda dan simpan kredensial dengan aman.

  1. Buat akun ScanQRIS dan selesaikan verifikasi email.
  2. Buka Kunci API dan konfirmasi kata sandi untuk melihat secret.
  3. Konfigurasikan origin API dan kredensial di server aplikasi Anda.
  4. Buat pembayaran, tampilkan gambar QR, dan gunakan total pembayaran dari respons.
  5. 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.

JavaScript · permintaan bertanda tangan
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.

Contoh respons pembayaran
{
  "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.
Contoh respons detail
{
  "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.

Contoh payload callback
{
  "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.

Contoh error validasi
{
  "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

pending

Belum terkonfirmasi; tampilkan gambar QR selama masih berlaku.

paid

Server telah mengonfirmasi pembayaran; proses pesanan sesuai kebijakan aplikasi Anda.

expired

Masa pembayaran berakhir; jangan gunakan QR ini lagi.

failed

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.