Charges
Membuat charge — POST /v1/charges
{
"invoice_id": "INV-20261105-0001",
"method": "qris",
"amount": 15000,
"gateway": "gdc",
"callback_url": "https://api.vogsystem.com/payroute/callback",
"metadata": { "kiosk": "K-12" }
}
| Field | Wajib | Aturan |
|---|---|---|
invoice_id |
ya | 1–64 karakter A-Z a-z 0-9 . _ : -, unik per client, case-sensitive |
method |
ya | qris (metode lain menyusul) |
amount |
ya | rupiah, bilangan bulat ≥ 1 |
gateway |
tidak | tripay / gdc / midtrans (fake di staging). Kosong = default |
callback_url |
ya | https, host harus terdaftar untuk client Anda |
metadata |
tidak | objek JSON ≤ 4 KB, dikembalikan apa adanya |
Respons 201 berisi charge dengan qris.qr_string (render sebagai QR), expires_at, dan gateway yang dipakai.
Idempotensi per invoice
- Selama ada charge pending untuk invoice itu, request yang sama mengembalikan charge yang sama (
200). Aman untuk retry saat timeout. - Lama create: biasanya < 1 detik, tapi Payroute menunggu sampai 10 detik per gateway sebelum failover ke gateway berikutnya, jadi kalau gateway lambat/hang jawaban bisa makan ±30 detik. Kalau timeout di sisi Anda lebih pendek (mis. kiosk), jangan anggap gagal: kirim ulang create dengan
invoice_idyang sama, atauGET /v1/charges?invoice_id=…, sampaiqr_stringterisi. amountberbeda →409 invoice_conflict. Invoice sudah dibayar →409 invoice_paid.- Setelah charge
expiredataufailed, create berikutnya membuat charge baru (QR baru). Charge lama yang QR-nya sudah lewat ditutup otomatis. - Dua create bersamaan untuk invoice yang sama bisa mendapat charge yang belum selesai dibuat (
gateway: null,qr_stringkosong) — panggilGET /v1/charges/{id}sebentar kemudian. 502 gateway_unavailable: semua gateway gagal saat itu. Ulangi dengan invoice yang sama beberapa detik kemudian.
Status
| Status | Arti |
|---|---|
pending |
QR aktif, menunggu bayar |
paid |
dibayar — final, tidak pernah berubah lagi |
expired |
QR lewat waktu tanpa pembayaran |
failed |
gateway menolak / gagal |
Pembayaran yang datang terlambat (setelah expired) tetap membuat charge menjadi paid dan Anda menerima charge.paid. Jangan anggap expired sebagai final untuk pembukuan.
Satu invoice bisa dibayar dua kali
Urutan ini mungkin terjadi: charge A expired → Anda membuat charge B untuk invoice yang sama (QR baru) → pelanggan ternyata sudah membayar QR A terlambat dan membayar QR B. Payroute mencatat keduanya paid (uang sudah diambil gateway) dan mengirim charge.paid untuk masing-masing — dua kali untuk satu invoice_id, dengan charge.id berbeda.
Project wajib menangani ini: kunci pembayaran per charge.id (bukan hanya invoice_id), deteksi invoice yang menerima lebih dari satu charge.paid, dan proses refund / rekonsiliasi lewat tim finance. Payroute belum mendukung refund otomatis.
Mengecek status — GET /v1/charges/{id}
Polling tiap beberapa detik (mis. 5 detik di kiosk) aman: Payroute menjawab dari database-nya dan hanya bertanya ke gateway paling sering sekali per 30 detik per charge (dihitung sejak dibuat). Webhook tetap jalur utama; polling adalah cadangan.
Daftar charge invoice — GET /v1/charges?invoice_id=…
Semua charge Anda untuk invoice itu, terbaru dulu: {"data": [ … ]}.
Kode error
| HTTP | error.code |
Kapan |
|---|---|---|
| 400 | invalid_request |
JSON rusak, field tak dikenal, query wajib kosong, body > 64 KB (413) |
| 400 | unknown_gateway |
gateway bukan kode yang dikenal |
| 401 | invalid_signature |
autentikasi gagal |
| 404 | not_found |
charge tidak ada / milik client lain |
| 409 | invoice_conflict / invoice_paid |
lihat idempotensi |
| 422 | invalid_request |
validasi per field, detail di error.fields |
| 422 | callback_not_allowed |
callback_url bukan https / host tidak terdaftar |
| 502 | gateway_unavailable |
semua gateway gagal, coba lagi |
Setiap error membawa request_id; sertakan saat melapor masalah.