Hosted checkout
Halaman bayar yang dikelola FQris: QR, hitung mundur, dan status realtime, tanpa kamu membangun antarmuka sendiri.
Alur pembeli
- 1Buat payment Backend-mu memanggil
POST /v1/payments. Respons memuatcheckout_url. - 2Arahkan pembeli Redirect ke
checkout_url. Pembeli melihatfinal_amount, rincian nominal + kode unik, QR, dan hitung mundur. - 3Pembeli membayar Begitu pembayaran terdeteksi, halaman berganti sendiri ke sukses dengan struk ringkas. Pembeli tidak perlu memuat ulang.
- 4Pembeli kembali Kalau kamu mengirim
return_url, tombol "Kembali ke {nama project}" muncul dan pembeli diarahkan otomatis setelah 5 detik (bisa dibatalkan).
Di mode live, checkout_url berbentuk …/p/ck_…. Di mode uji, …/test/p/ck_…. Token ck_ acak dan terpisah dari id payment, jadi tidak bisa ditebak dari id.
Yang pembeli lihat
Halaman ini publik, tanpa login, dan tidak diindeks mesin pencari. Isinya: nama project dan logo, deskripsi, final_amount yang besar, rincian nominal + kode unik, QR, hitung mundur, serta tombol Simpan QR dan Salin nominal.
Halaman tidak pernah menampilkan metadata, data customer, atau reference_id.
Hitung mundur dihitung dari expires_at dan server_time dari server, jadi tetap akurat walau jam HP pembeli meleset.
Di HP, pembeli menekan Simpan QR untuk mendapat gambar PNG berisi QR, nominal, dan batas waktu, lalu memilih gambar itu dari galeri di aplikasi pembayarannya. QRIS tidak punya deep link universal, jadi tidak ada tombol yang membuka e-wallet langsung.
Bahasanya Indonesia. Kirim checkout.locale: "en" saat membuat payment, atau tambahkan ?lang=en di URL, untuk bahasa Inggris.
State halaman
- 1Menunggu Payment
pending. QR, nominal, dan hitung mundur tampil. Dua menit terakhir ditandai mendesak. - 2Sukses Payment
paid. Pembeli melihat animasi sukses dan struk. Pembayaran yang masuk setelah batas waktu tetap menjadi sukses, dengan catatan "Dibayar setelah batas waktu — tetap tercatat". - 3Kedaluwarsa Payment
expired. QR disembunyikan. Kalauallow_retryaktif, ada tombol "Buat pembayaran baru". Pembayaran yang sudah terlanjur dilakukan tetap dikonfirmasi selama masa tenggang. - 4Dibatalkan Payment
cancelled. QR disembunyikan dengan pesan agar pembeli tidak membayar QR itu. - 5Dikembalikan Payment
refundedataupartially_refunded. Pembeli melihat bahwa merchant sudah mencatat pengembalian dana. - 6Koneksi terputus Kalau halaman tidak menerima kabar lebih dari 20 detik, banner muncul dan QR tetap tampil.
- 7Tidak ditemukan Token tidak valid atau sudah tidak tersedia: pesan umum tanpa detail pembayaran.
Halaman memakai koneksi realtime (SSE) dengan heartbeat tiap 15 detik. Kalau koneksi itu gagal dua kali, halaman beralih ke polling tiap 4 detik.
return_url dan cancel_url
Kirim return_url dan cancel_url (URL http atau https) saat membuat payment. Saat pembeli kembali, FQris mengalihkannya ke return_url dengan tambahan query payment_id, status, dan reference_id (hanya bila payment punya reference_id).
status adalah status saat pembeli dialihkan. Pembeli yang menekan Kembali sebelum membayar tiba dengan status=pending, dan yang menekannya setelah kedaluwarsa tiba dengan status=expired.
Tombol Batalkan pembayaran hanya muncul bila ada cancel_url. Pembeli dialihkan ke cancel_url dengan query yang sama, tetapi status payment tidak berubah: payment tetap pending sampai kamu membatalkannya lewat API atau dashboard, atau sampai kedaluwarsa.
return_url bisa dibuat siapa saja. Kredit koin atau tandai pesanan lunas hanya dari webhook terverifikasi plus GET /v1/payments/{id}. Lihat panduan Webhook.Buat pembayaran baru
Di state kedaluwarsa, tombol "Buat pembayaran baru" membuat payment baru dengan nominal dasar, deskripsi, metadata, customer, return_url, dan cancel_url yang sama, lalu membuka checkout barunya. Lama expiry disalin dari payment lama (5–60 menit).
id dan kode unik baru, dan tidak membawa reference_id. Kenali pesananmu lewat metadata. Webhook payment.paid datang untuk payment baru itu, bukan untuk yang lama.Retry hanya untuk payment expired (bukan cancelled) dan sekali per payment. Matikan dengan checkout.allow_retry: false saat membuat payment.
expired masih bisa menjadi paid selama masa tenggang, jadi payment lama dan hasil retry bisa sama-sama lunas untuk satu order. Kreditkan order sekali saja, dengan kunci unik per order di DB (kenali order lewat metadata). Payment kedua yang lunas kamu refund lewat POST /v1/payments/{id}/refunds. Kalau kamu hanya membatalkan payment lama sebelum membuat yang baru, itu tidak cukup, karena payment cancelled pun masih bisa menjadi paid selama masa tenggang.