FQrisDocs
Referensi

SDK

Library resmi untuk Node, PHP, dan Python: buat payment, verifikasi webhook, dan ambil ulang status dengan nama metode yang sama di ketiganya.

8 menit bacaDiperbarui 2 Nov 2026

Instalasi

Ketiga SDK tanpa dependensi runtime. Node butuh versi 18 atau lebih baru, PHP 8.1 atau lebih baru (dengan ekstensi curl dan json), dan Python 3.9 atau lebih baru. Pilih bahasamu di tab kode.

terminal
1npm install @fqris/node
SDK segera tersedia
Paket @fqris/node sedang disiapkan untuk terbit di npm, fqris/fqris-php di Packagist, dan fqris di PyPI. Sementara itu, pakai contoh cURL: semua endpoint bisa dipanggil langsung lewat HTTP.

Inisialisasi dan buat payment

Buat satu client dengan API key, lalu panggil resource-nya. Key harus diawali fq_live_ atau fq_test_, dan hanya boleh disimpan di backend, bukan di aplikasi Android atau browser.

buat-payment
1import FQris from '@fqris/node';
2const fqris = new FQris(process.env.FQRIS_KEY);
3
4const payment = await fqris.payments.create({
5 amount: 20000,
6 reference_id: 'TOPUP-88812',
7});
8console.log(payment.checkout_url);

Respons berupa objek yang kolomnya dibaca sebagai properti: payment.checkout_url. Di PHP objek itu hanya-baca dan kolom yang tidak ada bernilai null.

Python: kunci yang bentrok dengan dict
Respons Python adalah dict yang juga bisa dibaca lewat atribut. Kunci yang bentrok dengan metode dict, seperti items pada invoice, dibaca lewat invoice["items"], bukan invoice.items.

Verifikasi webhook

Verifikasi signature dengan raw body sebelum body di-parse. Di Express pakai express.raw, bukan express.json(). Di PHP ambil dari php://input. Di Flask pakai request.get_data() dan di Django request.body. Toleransi waktu bawaan 5 menit.

webhook-handler
1app.post('/webhooks/fqris', express.raw({ type: '*/*' }), async (req, res) => {
2 const sig = req.headers['fqris-signature'];
3 // 1. Verifikasi dengan raw body + toleransi 5 menit
4 const event = fqris.webhooks.constructEvent(req.body, sig, process.env.WHSEC);
5 // 2. Ambil ulang status sebelum mengkredit
6 const p = await fqris.payments.retrieveAndAssertPaid(event.data.object.id);
7 await creditCoinsOnce(p.id, p.metadata.user_id);
8 res.sendStatus(200);
9});
Ambil ulang sebelum mengkredit
Jangan mengkredit hanya dari isi event. Panggil retrieveAndAssertPaid (Node dan PHP) atau retrieve_and_assert_paid (Python): metode ini mengambil status terbaru dan hanya lolos untuk status paid. Payment refunded atau partially_refunded melempar error khusus (PaymentNotPaidError di Node dan Python, PaymentNotPaidException di PHP), jadi tidak ikut dikredit. Tangkap error itu dan balas 200 supaya FQris tidak mengirim ulang event yang sama.

Event bisa datang lebih dari sekali dan tidak berurutan. Simpan event.id yang sudah diproses, beri kunci unik per payment dan per order saat mengkredit, dan balas 2xx dalam 10 detik. Panduan Webhook dan Top-up koin membahas aturan ini lengkap.

Sandbox

Pakai key fq_test_, lalu simulasikan hasil pembayaran dengan result berisi paid, expired, atau paid_late. Data mode uji terpisah total dari live.

simulasi-bayar
1import FQris from '@fqris/node';
2const fqris = new FQris(process.env.FQRIS_KEY);
3
4const payment = await fqris.sandbox.simulatePayment('pay_01JA2Z3K8M', {
5 result: 'paid',
6});
7console.log(payment.status);

Paginasi

List mengembalikan data, has_more, dan next_cursor, dan menerima limit (maksimal 100), starting_after, ending_before, serta filter seperti status. Untuk mengiterasi semua data, pakai listAll (Node, PHP) atau list_all (Python). Metode ini ada di payments, customers, paymentLinks, invoices, dan events.

paginasi
1import FQris from '@fqris/node';
2const fqris = new FQris(process.env.FQRIS_KEY);
3
4// Halaman berikutnya diambil otomatis
5for await (const payment of fqris.payments.listAll({ limit: 100, status: 'paid' })) {
6 console.log(payment.id);
7}

Menangani error

Respons non-2xx, setelah retry habis, menjadi FQrisError di Node dan Python, atau FQrisException di PHP. Errornya membawa status, code, pesan, dan requestId (request_id di Python). Sertakan request id itu saat menghubungi dukungan. Kegagalan jaringan memakai status 0 dengan code NETWORK_ERROR atau TIMEOUT.

tangani-error
1import FQris, { FQrisError } from '@fqris/node';
2const fqris = new FQris(process.env.FQRIS_KEY);
3
4try {
5 await fqris.payments.retrieve('pay_tidak_ada');
6} catch (e) {
7 if (!(e instanceof FQrisError)) throw e;
8 console.error(e.status, e.code, e.message, e.requestId);
9}

Retry dan idempotensi

SDK mengulang request yang gagal karena jaringan, timeout, 429, atau 5xx, sampai 2 kali bawaan (maxRetries di Node, max_retries di PHP dan Python), dengan backoff. Header Retry-After dihormati sampai 60 detik.

Setiap POST otomatis membawa header Idempotency-Key. Kuncinya dibuat sekali sebelum percobaan pertama dan sama di semua retry, jadi payment tidak pernah terbuat dua kali. Untuk operasi yang kamu ulang sendiri, misalnya setelah proses backend mati, berikan kunci yang stabil seperti TOPUP-88812.

Jangan pakai ulang kunci
Kunci yang sama dengan isi berbeda ditolak 409 IDEMPOTENCY_KEY_REUSED. Pakai kunci baru untuk body yang baru.

Transport kustom

PHP memakai cURL secara bawaan. Untuk transport lain, misalnya wp_remote_request di WordPress atau tiruan untuk tes, implementasikan \FQris\HttpClient\HttpClientInterface dan berikan lewat opsi http_client saat membuat \FQris\Client. Python menerima http_client= juga: objek dengan metode send(method, url, headers, body, timeout). Node menerima fetch kustom lewat opsi fetch.

Apakah halaman ini membantu?