Konsep utama
Satu account USER memiliki satu wallet. Di dalam account itu Anda bisa membuat banyak integrasi. Setiap integrasi mewakili aplikasi atau channel server-side: website checkout, aplikasi mobile melalui backend, POS, Discord, WhatsApp order, kasir internal, atau sistem order.
AccountIdentitas finansial, pemilik wallet, destination payout, VIP, dan session dashboard.
Integration keyAPI key server-side dengan prefix fs_test_ atau fs_live_. Full key hanya tampil sekali.
PaymentIntent pembayaran bernominal Rupiah yang menghasilkan reference dan payment URL.
LedgerSumber saldo immutable. Saldo dihitung dari entry ledger, bukan dari cache UI.
Autentikasi
Ada dua jenis akses: session dashboard untuk operasi browser dan Bearer API key untuk integrasi server-side.
| Jenis | Dipakai untuk | Header wajib |
|---|
| Session cookie | Login, dashboard, buat key, destination, withdrawal, admin | Origin: PUBLIC_ORIGIN untuk POST |
| Bearer API key | Create payment, cek payment, balance, transaksi integrasi | Authorization: Bearer fs_live_... |
| Webhook provider | Settlement payment dari provider | Signature provider, bukan API key integrasi |
Simpan API key hanya di backend. Jangan taruh key di browser, aplikasi mobile client-side, repository publik, log, atau pesan customer.
Quickstart
- Daftar atau login ke dashboard.
- Buka menu Integrasi & API.
- Buat aplikasi/channel, misalnya Website Toko, Mobile App, POS, Backend Order, atau nama produk.
- Terbitkan API key
live untuk production atau test untuk sandbox jika deployment mendukungnya. - Dari backend Anda, buat payment dengan nominal dan
Idempotency-Key unik per order. - Simpan
reference FerdiPay di database Anda. - Arahkan customer ke
payment_url, lalu cek status melalui API atau tunggu webhook provider diproses FerdiPay.
curl -X POST https://domain-anda.com/api/v1/payments \
-H 'Authorization: Bearer fs_live_xxx' \
-H 'Idempotency-Key: order-INV-2026-0001' \
-H 'Content-Type: application/json' \
-d '{"amount_rupiah":10000}'
Ringkasan endpoint
| Endpoint | Akses | Fungsi |
|---|
POST /api/v1/payments | Bearer key | Membuat payment baru. |
GET /api/v1/payments/{reference} | Bearer key | Mengambil status payment milik key tersebut. |
GET /api/v1/balance | Bearer key | Saldo wallet account integrasi. |
GET /api/v1/transactions | Bearer key | Daftar payment milik integrasi, pagination limit/offset. |
GET /payment-methods | Session | Metode payment yang aktif/nonaktif untuk user. |
GET /payout-capabilities | Session | Status provider payout dan daftar bank/e-wallet. |
GET /wallet | Session | Saldo wallet dashboard. |
GET /wallet/ledger | Session | Riwayat ledger account. |
POST /payout-destinations | Session | Mendaftarkan rekening bank atau e-wallet. |
POST /withdrawals | Session | Mengajukan penarikan saldo. |
POST /api/v1/webhooks/{mock|doku} | Provider | Webhook settlement dari provider aktif. |
Payment API
Body create payment hanya menerima amount_rupiah integer positif. Field tambahan ditolak. Nominal maksimum mengikuti konfigurasi deployment.
POST /api/v1/payments
Authorization: Bearer <PAYMENT_API_KEY>
Idempotency-Key: order-123
Content-Type: application/json
{"amount_rupiah":10000}
{
"reference": "fsp_abc123",
"status": "PENDING",
"provider_state": "READY",
"gross_rupiah": 10000,
"provider_fee_rupiah": 70,
"platform_fee_rupiah": 500,
"net_account_rupiah": 9430,
"payment_url": "https://provider.example/pay/fsp_abc123",
"created_at": "2026-09-14T12:00:00Z",
"paid_at": null
}
Gunakan Idempotency-Key yang sama saat retry order yang sama. Jika key sama dikirim dengan nominal berbeda, API mengembalikan 409.
| Status | Arti |
|---|
| PENDING | Payment dibuat dan menunggu pembayaran. |
| PAID | Provider mengonfirmasi sukses dan saldo sudah dikreditkan. |
| FAILED / EXPIRED | Provider menyatakan gagal atau kedaluwarsa. |
| REFUNDED | Refund internal dicatat melalui reversal ledger. |
Metode payment
Pembayaran user hanya tersedia melalui QRIS. Owner dapat mengaktifkan atau menonaktifkan QRIS dari dashboard Owner > Payment. User dapat melihat status metode dari menu Metode Pembayaran atau endpoint session GET /payment-methods.
GET /payment-methods
[
{"method_code":"qris","label":"QRIS DOKU","enabled":true}
]
Jika QRIS dimatikan, request POST /api/v1/payments untuk payment baru akan ditolak. Payment yang sudah dibuat sebelumnya tetap mengikuti status provider masing-masing.
Virtual account, transfer bank, e-wallet langsung, dan kartu tidak tersedia sebagai metode pembayaran user.
Balance dan transaksi
GET /api/v1/balance
Authorization: Bearer <PAYMENT_API_KEY>
{
"wallet_id": "uuid",
"account_id": "uuid",
"kind": "ACCOUNT",
"balance_rupiah": 1250000
}GET /api/v1/transactions?limit=50&offset=0
Authorization: Bearer <PAYMENT_API_KEY>
limit bernilai 1 sampai 100. offset dimulai dari 0.
Payout, destination, dan VIP
Payout dikelola memakai session dashboard agar rekening/e-wallet tujuan dan withdrawal tetap berada di bawah account yang login. POST wajib membawa header Origin sesuai konfigurasi.
Sebelum menampilkan tombol validasi atau tarik saldo, dashboard membaca GET /payout-capabilities. Jika provider payout production belum aktif, UI mengunci validasi dan penarikan agar tidak terlihat seolah dana pasti terkirim.
GET /payout-capabilities
{
"provider": "unavailable",
"available": false,
"institutions": [
{"kind":"BANK","institution_code":"014","label":"Bank BCA"},
{"kind":"BANK","institution_code":"008","label":"Bank Mandiri"},
{"kind":"EWALLET","institution_code":"DANA","label":"DANA"}
]
}
| Endpoint | Body |
|---|
GET /payout-capabilities | Status provider payout dan daftar institusi. |
POST /payout-destinations | kind BANK/EWALLET, institution_code dari daftar resmi, destination_number |
POST /payout-destinations/{id}/validate | Tidak perlu body; hanya berjalan jika provider tersedia. |
POST /withdrawals | destination_id, mode, amount_rupiah jika RECEIVE_EXACT |
GET /withdrawals | 100 penarikan terbaru |
GET /vip | Status VIP, harga, durasi |
POST /vip/purchase | Header Idempotency-Key; body {"payment_method":"WALLET"} untuk potong saldo dan aktivasi langsung, atau {"payment_method":"QR_OWNER_MANUAL"} untuk QR owner dan pemeriksaan manual |
POST /payout-destinations
Origin: https://domain-anda.com
Content-Type: application/json
{"kind":"BANK","institution_code":"014","destination_number":"1234567890"}
{
"destination_id": "uuid",
"mode": "RECEIVE_EXACT",
"amount_rupiah": 50000
}Mode WITHDRAW_ALL tidak menerima amount_rupiah. Server menghitung saldo terbaru, fee, dan total debit. Batas penerimaan Rp50.000–Rp1.000.000 per transaksi berlaku untuk semua user termasuk VIP. WITHDRAW_ALL ditolak jika melebihi maksimum; gunakan RECEIVE_EXACT. Satu akun hanya boleh memiliki satu rekening/e-wallet. Nama pemilik harus terverifikasi oleh provider dan tidak digunakan akun lain sebelum penarikan diterima.
Webhook settlement
Webhook FerdiPay adalah endpoint untuk provider payment aktif, bukan webhook yang dipanggil langsung oleh aplikasi merchant. Provider mengirim event ke /api/v1/webhooks/{provider_name}, FerdiPay memverifikasi signature, lalu mengunci payment dan mencatat ledger secara atomik.
Duplicate event tidak mengkredit saldo dua kali. Event sukses terlambat dapat memperbaiki payment FAILED/EXPIRED jika provider menyatakan sukses. Failure terlambat tidak membatalkan payment yang sudah PAID.
| Provider | Endpoint |
|---|
| Mock | POST /api/v1/webhooks/mock |
| DOKU | POST /api/v1/webhooks/doku |
Owner dan operasional
Endpoint admin hanya untuk account OWNER dan memakai session dashboard. Gunakan untuk audit internal, pengaturan metode payment, pencarian user, dan rekonsiliasi. Endpoint admin bukan untuk integrasi publik.
| Endpoint | Fungsi |
|---|
GET /admin/summary | Ringkasan realtime total transaksi, volume, fee, merchant, dan withdrawal. |
GET /admin/payment-methods | Daftar metode payment dan status aktif/nonaktif. |
POST /admin/payment-methods/{method_code} | Aktif/nonaktifkan metode payment untuk user. |
GET /admin/users | Cari user, filter aktif/tidak aktif, saldo, rekening terdaftar. |
GET /admin/accounts | Daftar account dan saldo. |
GET /admin/payments | Seluruh payment platform. |
GET /admin/transactions | Ledger semua wallet, opsional filter account_id. |
GET /admin/payouts | Daftar withdrawal account. |
POST /admin/reconciliation | Memeriksa kecocokan dana provider, saldo user, dan entitlement developer. |
POST /admin/adjustments | Koreksi transfer antar wallet dengan ledger debit/kredit. |
GET /admin/audit | Audit log operasional. |
Blokir/hapus akun belum diaktifkan karena data finansial dan ledger harus dipertahankan untuk audit. Jika dibutuhkan, gunakan status akun/suspension melalui migration terpisah agar tidak menghapus riwayat transaksi.
Error, keamanan, dan praktik terbaik
| Kode | Umum terjadi saat |
|---|
| 401 | Session tidak valid atau Bearer API key salah/revoked. |
| 403 | Origin tidak sesuai atau akses OWNER diperlukan. |
| 404 | Resource bukan milik account/key pemanggil atau tidak ditemukan. |
| 409 | Konflik idempotency, duplicate destination, saldo tidak cukup, QRIS sedang nonaktif, destination belum valid, atau state konflik. |
| 422 | Payload tidak valid, field tambahan, nominal salah, environment key tidak cocok, atau institution_code tidak tersedia. |
| 429 | Cooldown/kuota validasi destination; baca header Retry-After. |
| 503 | Provider payout belum tersedia atau provider eksternal belum siap. |
Selalu buat Idempotency-Key stabil per order, simpan reference FerdiPay di database Anda, lakukan retry aman pada timeout, dan tampilkan status final hanya setelah PAID, FAILED, EXPIRED, atau REFUNDED.
API key hanya boleh berada di backend. Jangan simpan key di localStorage, aplikasi mobile client-side, repository publik, log, pesan customer, atau frontend JavaScript. Regenerate key jika ada indikasi bocor, lalu revoke key lama dari dashboard.