Top-up koin
Dari memilih paket di aplikasi Android sampai koin bertambah tepat satu kali, lewat backend-mu.
Alur end-to-end
Aplikasi Android tidak memanggil FQris langsung. Backend-mu yang membuat payment, menerima webhook, lalu menambah koin. Aplikasi hanya menampilkan checkout dan membaca saldo dari backend-mu.
- 1Pembeli memilih paket Aplikasi meminta top-up ke backend-mu, misalnya paket 200 koin seharga Rp20.000.
- 2Backend membuat payment Panggil
POST /v1/paymentsdenganamount,reference_idberisi ID order, danmetadataberisiuser_id. Simpan order dengan statusawaiting_payment. - 3Pembeli membayar Aplikasi membuka
checkout_url, atau merenderqris.stringsendiri. Pembeli membayar lewat e-wallet atau m-banking. - 4FQris mengirim webhook Listener mendeteksi uang masuk, payment menjadi
paid, dan webhookpayment.paiddikirim ke backend-mu. - 5Backend mengkredit koin Backend memverifikasi webhook, mengambil ulang payment, lalu menambah koin sekali. Aplikasi menerima kabar "Koin bertambah" lewat push, SSE, atau polling ke backend-mu.
Buat payment dari backend
Isi amount dengan harga paket dalam rupiah bulat dan reference_id dengan ID order, misalnya TOPUP-88812. Kirim Idempotency-Key yang sama agar retry setelah timeout tidak membuat payment ganda. metadata ikut kembali di payment dan webhook, jadi user_id tersedia saat mengkredit. Simpan id payment bersama order.
Kalau FQris menjawab 503 (NO_HEALTHY_SOURCE atau MAINTENANCE), tampilkan pesan "coba lagi" ke pembeli dan ulangi nanti.
fq_live_ di aplikasi Android, termasuk di BuildConfig atau remote config. APK mudah dibongkar, dan pemegang key bisa membuat payment atas namamu.Tampilkan checkout
Pembeli harus membayar final_amount persis, yaitu amount ditambah kode unik acak (rentang bawaan 1 sampai 499). Rp20.000 bisa menjadi Rp20.137. Tampilkan final_amount, bukan amount.
Buka checkout_url di Custom Tab atau WebView. Halaman itu menampilkan QR dan hitung mundur, lalu berganti ke layar sukses begitu pembayaran terdeteksi.
Kalau kamu merender qris.string sendiri, hitung mundur dari expires_at dan pantau status lewat backend-mu, bukan langsung ke FQris.
Kredit koin dari webhook
Kredit koin hanya dari webhook payment.paid yang terverifikasi. Jangan mengkredit dari return_url atau dari klien Android: keduanya bisa dipalsukan.
- 1Verifikasi signature Pakai raw body dan toleransi 5 menit, seperti di panduan Webhook.
- 2Ambil ulang payment Panggil
GET /v1/payments/{id}, pastikanstatusbernilaipaid, lalu cocokkanreference_iddan nominal dengan order. - 3Kredit di dalam transaksi DB Beri kunci unik
payment_idpada catatan kredit, dan satu kunci unik per order (lihat Kasus khusus). Event yang datang dua kali tidak akan mengkredit dua kali. - 4Balas 2xx Balas dalam 10 detik dan kerjakan pekerjaan berat setelahnya.
Kasus khusus
- 1paid_late Pembeli membayar setelah
expires_at, masih dalam masa tenggang (bawaan 30 menit).payment.paiddatang denganpaid_late: true. Ordermu mungkin sudah kamu tandai gagal: kredit atau refund sesuai kebijakanmu. - 2payment.reversed Konfirmasi manual dibatalkan Owner dalam 24 jam. Tarik kembali koin yang sudah dikredit.
- 3payment.overpaid Pembeli membayar dua kali QR yang sama. Status payment tidak berubah otomatis: putuskan sendiri apakah kelebihannya dikredit atau di-refund.
- 4paid_after_cancel Kamu membatalkan payment, tetapi pembeli membayar QR-nya selama masa tenggang. Payment menjadi
paiddenganpaid_after_cancel: truedanpayment.paidterkirim, sama sepertipaid_late: kredit atau refund sesuai kebijakanmu. - 5expired atau cancelled Tandai order gagal hanya setelah masa tenggang habis. Sebelum itu payment
expiredataucancelledmasih bisa menjadipaid, danpayment.paidyang datang kemudian tetap kamu proses (kredit atau refund sesuai kebijakan). - 6Satu order, satu kredit Untuk mencoba lagi, buat payment baru dengan
reference_idbaru: satureference_idhanya untuk satu payment, apa pun statusnya (409 DUPLICATE_REFERENCE). Payment lama dan baru bisa sama-sama lunas, jadi beri kunci kredit unik per order di DB, bukan hanya perpayment_id. Payment kedua yang lunas untuk order yang sudah dikredit kamu refund lewatPOST /v1/payments/{id}/refunds. Kalau kamu hanya membatalkan payment lama sebelum membuat yang baru, itu tidak cukup, karena paymentcancelledpun masih bisa menjadipaidselama masa tenggang.
Uji sebelum live
- 1Pakai key test Buat payment dengan
fq_test_dan daftarkan endpoint webhook mode test. Data test tidak pernah tercampur dengan live. - 2Simulasikan bayar Panggil
POST /v1/sandbox/payments/{id}/simulatedengan{"result":"paid"}. Hasil lain:expireddanpaid_late. Ulangi sampai koin bertambah tepat satu kali. - 3Satu top-up uang nyata Ganti ke key dan endpoint live, lalu lakukan satu top-up kecil dari aplikasi pembayar lain, bukan dari akun DANA Bisnis penerima.