ZePay Merchant API
31 endpoint dalam 6 kelompok. Setiap request ditandatangani HMAC-SHA256 dengan kunci API merchant; data selalu dibatasi ke merchant pemilik kunci. Mulai dari konsep dasar di bawah, lalu pilih endpoint.
https://payment.envfri.com/api/v2Produksi (mode live/uji ditentukan oleh awalan kunci API)Konsep dasar
Setiap merchant berdiri sendiri: punya kunci API, saldo, rekening terdaftar, pencairan, dan webhook sendiri.
Kunci API terikat ke satu merchant, jadi semua data otomatis dibatasi ke merchant pemilik kunci —
tidak ada parameter merchant_id. Panduan langkah demi langkah: Panduan integrasi.
Merchant hanya lewat API#
Merchant tidak punya login web. Semua kebutuhan merchant tersedia di API ini:
| Kebutuhan | Endpoint |
|---|---|
| Info merchant, tarif & aturan pencairan | GET /merchant |
| Saldo, mutasi, ringkasan periode | GET /balance, GET /balance/transactions, GET /balance/summary |
| Transaksi: buat, cari, cek status, batalkan, simulasi (mode uji) | POST/GET /payments, GET /payments/{id}, POST /payments/{id}/cancel, POST /payments/{id}/simulate |
| Bank & e-money, cek & daftar rekening | GET /banks, POST /bank-accounts/inquiry, /bank-accounts… |
| Pencairan | POST /payouts/quote, POST/GET /payouts, GET /payouts/{id}, POST /payouts/{id}/cancel |
| Webhook & riwayat event | /webhook-endpoints…, GET /events, GET /events/{id}, POST /events/{id}/resend |
Konsol web ZePay khusus admin ZePay. Yang hanya bisa dilakukan admin (ajukan ke tim ZePay): membuat/merotasi
kunci API, membatasi scope & IP kunci, mengubah limit & ambang persetujuan, nama pemilik rekening (KYC), meninjau
rekening pending_review, menyetujui pencairan besar, pencairan gabungan, serta mengatur kanal & tarif.
Biaya#
Ada dua jenis biaya ZePay (GET /merchant → fees.description): biaya transaksi per kanal pembayaran (tarif kanal
pembayaran) dan biaya transfer per pencairan (tarif transfer ke bank/e-money tujuan).
| Biaya | Nilai yang dipakai | Estimasi ditampilkan di |
|---|---|---|
| Transaksi (pembayaran) | Ditampilkan saat tagihan dibuat (fee, fee_bearer, net_amount); nilai final saat dibayar |
GET /merchant → payment_methods[].channels[] |
| Pencairan (transfer) | Dipastikan lewat pengecekan ke bank tujuan saat pratinjau & saat pencairan dibuat, lalu ditahan bersama nominal; nilai final setelah transfer selesai | GET /merchant → payout.fee, GET /balance → payout_fee, GET /banks → payout_fee, POST /bank-accounts/inquiry → payout_fee |
- Tarif di
GET /merchant,GET /balance, danGET /bankshanya estimasi untuk tampilan (payout.fee_is_estimate = true,payout_fee_source = estimate). Saat ini biaya transfer Rp2.500 per pencairan ke bank dan Rp2.000 ke e-money; bila tarif berubah, angka di API ikut berubah. - Pembayaran:
fee= biaya transaksi kanal pembayaran. Sebelum dibayar nilainya ditetapkan saat tagihan dibuat; setelahSUCCESSnilainya final.fee_bearermengikuti aturan kanal:merchant→net_amount = amount − fee;customer→ biaya dibayar pelanggan,net_amount = amount. - Pencairan: biaya dipotong dari saldo di luar nominal: rekening menerima
amountutuh, saldo tersedia berkurangtotal_debited = amount + fee. Contoh: cairkan Rp100.000 dengan biaya transfer Rp2.500 → rekening menerima Rp100.000, saldo terpotong Rp102.500. POST /payouts/quotememastikan biaya dengan pengecekan ke bank tujuan untuk tujuan & nominal itu (fee_source = confirmed). Bila pengecekan sementara tidak tersedia, pratinjau memakai estimasi tarif transfer (fee_source = estimate) dan biaya dihitung ulang saat pencairan dibuat.POST /payoutsselalu mengecek ulang biaya ke bank tujuan; bila bank tidak merespons, pencairan tidak dibuat (503 provider_unavailable).feepada responsPOST /payoutsadalah biaya yang ditahan dan menjadi batas atas. Biaya final ditetapkan setelah transfer selesai: bila lebih kecil, selisihnya kembali ke saldo tersedia (baris mutasipayout_release); bila lebih besar, selisihnya ditanggung ZePay. Merchant tidak pernah membayar lebih dari biaya yang ditahan.- Biaya tampil di setiap respons & webhook pencairan dan sebagai baris mutasi
payout_feedi/balance/transactions. Bila pencairan gagal, ditolak, atau dibatalkan, nominal dan biaya kembali ke saldo tersedia.
Autentikasi (HMAC-SHA256)#
Kirim tiga header di setiap request:
| Header | Isi |
|---|---|
X-ZePay-Key-Id |
zpk_live_… atau zpk_test_… (mode uji) |
X-ZePay-Timestamp |
Unix detik saat ini; selisih maks. 300 detik dari jam server ZePay |
X-ZePay-Signature |
hex(HMAC_SHA256(secret, timestamp + "\n" + METHOD + "\n" + path_dan_query + "\n" + hex(SHA256(body)) [+ "\n" + Idempotency-Key])) |
path_dan_query persis seperti yang dikirim (termasuk prefix dan encoding), mis. /api/v2/payouts?limit=20. Body
kosong (GET, DELETE, atau POST tanpa body) di-hash sebagai string kosong. Bila header Idempotency-Key dikirim,
nilainya ditambahkan sebagai baris terakhir string yang ditandatangani — request yang tersadap tidak bisa dikirim ulang
dengan kunci lain untuk menggandakan tagihan atau pencairan. Secret (zsk_live_… / zsk_test_…) hanya
diserahkan sekali saat kunci dibuat; rotasi dilakukan admin dengan masa transisi kunci lama (langsung, 24 jam, atau
7 hari). Kunci tidak dikenal, dicabut, atau kedaluwarsa → 401 authentication_failed; tanda tangan salah →
401 signature_invalid; jam meleset → 401 timestamp_out_of_range (details.server_time = jam server).
Scope & IP#
Admin bisa membatasi kunci API ke scope tertentu dan ke daftar IP (CIDR). Kunci baru mendapat semua scope:
| Scope | Endpoint |
|---|---|
| — (semua kunci valid) | GET /merchant, GET /banks |
balance:read |
GET /balance, GET /balance/transactions, GET /balance/summary |
payments:read |
GET /payments, GET /payments/{id} |
payments:write |
POST /payments, POST /payments/{id}/cancel, POST /payments/{id}/simulate |
bank_accounts:read |
GET /bank-accounts, GET /bank-accounts/{id} |
bank_accounts:write |
POST /bank-accounts/inquiry, POST /bank-accounts, PATCH /bank-accounts/{id}, DELETE /bank-accounts/{id} |
payouts:read |
GET /payouts, GET /payouts/{id} |
payouts:write |
POST /payouts/quote, POST /payouts, POST /payouts/{id}/cancel |
payouts:any_destination (izin khusus, tidak otomatis; wajib IP publik spesifik, bersama payouts:write & bank_accounts:write) |
POST /payouts/quote & POST /payouts dengan inquiry_id — pencairan ke rekening tanpa daftar |
webhooks:read |
GET /webhook-endpoints, GET /webhook-endpoints/{id}, GET /events, GET /events/{id} |
webhooks:write |
POST /webhook-endpoints, PATCH /webhook-endpoints/{id}, DELETE /webhook-endpoints/{id}, POST /webhook-endpoints/{id}/rotate-secret, POST /webhook-endpoints/{id}/test, POST /events/{id}/resend |
Kunci tanpa scope yang dibutuhkan, atau request dari IP di luar daftar IP kunci → 403 permission_denied. Scope tiap
operasi juga tercantum di security operasi tersebut.
Idempotensi#
Header Idempotency-Key (16–64 karakter: huruf, angka, _, ., :, -; mis. UUID) wajib untuk tujuh POST
berikut — tanpa header atau format salah → 400 invalid_request:
POST /payments, POST /payments/{id}/cancel, POST /bank-accounts, POST /payouts, POST /payouts/{id}/cancel,
POST /webhook-endpoints, POST /webhook-endpoints/{id}/rotate-secret.
- Kirim ulang dengan kunci dan request yang sama (metode, path+query, body) → respons pertama dikembalikan apa adanya
(status sama) dengan header
Idempotent-Replayed: true. Respons4xxikut disimpan; respons5xxtidak disimpan sehingga request bisa diulang dengan kunci yang sama. - Kunci sama dengan request berbeda, atau request pertama masih diproses →
409 idempotency_conflict. - Kunci disimpan 24 jam per merchant (mode live & uji memakai ruang kunci yang sama — jangan pakai ulang antar-mode).
Untuk
POST /payouts, kunci yang sama dari kunci API yang sama tidak pernah membuat pencairan kedua, juga setelah 24 jam. POST /bank-accounts/inquiry,POST /payouts/quote,POST /payments/{id}/simulate,POST /webhook-endpoints/{id}/test, danPOST /events/{id}/resendtidak memakaiIdempotency-Key.
Batas request & ukuran#
- Bawaan 60 request/menit per kunci API (jendela per menit; nilainya bisa diatur ZePay per lingkungan lewat
konfigurasi
API_RATE_LIMIT_PER_MINUTE). Melebihi batas →429 rate_limiteddengan headerRetry-After(detik). - Cek rekening: maks. 30/menit dan 300 per 24 jam per merchant (data pribadi; live & uji dihitung bersama).
- Body request berupa JSON, maks. 64 KB; lebih besar →
400 invalid_request. Field yang tidak dikenal di body ditolak (400 invalid_request, nama field disebut di pesan).
Respons & error#
Setiap respons membawa header X-Request-Id (req_…) dan Cache-Control: no-store. Error selalu berbentuk
{ "error": { "code", "message", "request_id", "details"? } } — sertakan request_id saat menghubungi tim ZePay.
Gunakan code untuk logika program; message berbahasa Indonesia untuk developer.
| HTTP | Kode |
|---|---|
| 400 | invalid_request |
| 401 | authentication_failed, signature_invalid, timestamp_out_of_range |
| 403 | permission_denied |
| 404 | not_found, inquiry_not_found, bank_account_not_found |
| 409 | idempotency_conflict, bank_account_exists, payout_not_cancellable, payment_not_cancellable, webhook_endpoint_limit, no_webhook_endpoint |
| 422 | merchant_inactive, bank_unavailable, account_not_found, inquiry_expired, inquiry_used, bank_account_rejected, bank_account_not_verified, amount_below_minimum, amount_above_maximum, payout_limit_exceeded, insufficient_balance, payment_method_unavailable |
| 429 | rate_limited |
| 500 | internal_error |
| 503 | provider_unavailable |
Path yang tidak ada → 404 not_found; metode HTTP yang tidak didukung untuk path yang ada → 400 invalid_request.
Uang & waktu#
Nominal selalu bilangan bulat rupiah (tanpa desimal). Waktu di API dalam ISO 8601 dengan zona +07:00 (WIB).
Pengecualian: waktu di dalam data event (mis. paid_at) dan di deliveries[] pada GET /events/{id} ditulis
server dalam ISO 8601 dengan offset lain (mis. +00:00 atau Z) — selalu parse sebagai ISO 8601 lengkap.
Saldo#
pending— sudah dibayar pelanggan, menunggu settlement; waktunya bergantung kanal pembayaran (QRIS & e-wallet umumnya 2–4 hari, Alfamart H+3, Indomaret H+2). Pembayaran VA langsung tersedia — VA dikreditkan secara realtime.available— saldo tersedia.reserved— ditahan untuk pencairan yang sedang diproses / menunggu persetujuan (nominal + biaya transfer).withdrawable— nominal maksimum yang bisa dicairkan sekarang:available − payout_fee(estimasi biaya transfer), dibatasi sisa limit harian.
Rekening wajib terdaftar#
Pencairan hanya bisa ke rekening/e-money terdaftar berstatus verified milik merchant. Alurnya:
GET /banks— daftar bank & e-money yang didukung, format nomor, minimum/maksimum, estimasi biaya.POST /bank-accounts/inquiry— cek nomor rekening/e-money ke bank/e-money tujuan; mengembalikan nama pemilik.POST /bank-accountsdenganinquiry_id— daftarkan hasil cek (nama pemilik diambil dari bank, bukan input). Bila nama pemilik sama dengan nama pemilik terdaftar merchant (GET /merchant→account_holder_name), status langsungverified; selain itupending_reviewsampai ditinjau admin ZePay (webhookbank_account.verified/rejected).POST /payouts/quotelaluPOST /payoutsdenganbank_account_id.
Saat pencairan diproses, ZePay mengecek ulang rekening ke bank; bila tidak ditemukan atau nama pemilik berbeda dari yang terdaftar, pencairan gagal dan dana kembali.
Pencairan langsung tanpa daftar (izin khusus)#
Untuk merchant yang perlu mengirim dana ke rekening atas nama siapa pun (mis. pembayaran ke vendor/mitra) tanpa
mendaftarkan rekening & tanpa menunggu tinjauan admin. Hanya untuk kunci API dengan scope khusus
payouts:any_destination, yang wajib dibatasi IP publik spesifik server merchant (IPv4 /24–/32, IPv6 /48–/128) —
kunci yang bocor tidak bisa dipakai dari luar server merchant. Scope ini tidak pernah diberikan otomatis; minta ke tim ZePay. GET /merchant →
payout.any_destination menunjukkan apakah kunci yang dipakai berizin.
POST /bank-accounts/inquiry— wajib: cek rekening tujuan; tampilkanaccount_nameke pengguna untuk dikonfirmasi.POST /payouts/quotelaluPOST /payoutsdenganinquiry_id(bukanbank_account_id).
Hasil cek berlaku 15 menit dan sekali pakai (satu hasil cek = satu pencairan; berikutnya 422 inquiry_used,
kedaluwarsa 422 inquiry_expired — cek ulang). Rekening tujuan tidak didaftarkan dan tidak muncul di
GET /bank-accounts; pencairan menampilkan destination.type = inquiry_account dengan inquiry_id. Minimum,
maksimum, limit harian, dan cek ulang rekening saat diproses tetap berlaku sama; ambang persetujuan dihitung
kumulatif per hari untuk pencairan langsung (total hari ini + nominal baru > payout.approval_threshold →
PENDING_APPROVAL). Respons hilang → ulangi dengan Idempotency-Key yang sama: pencairan yang sama dikembalikan.
Paginasi#
Daftar memakai kursor: ?limit=20&starting_after=<id> (limit 1–100, bawaan 20). Respons berisi data, has_more,
dan next_cursor (id item terakhir bila masih ada halaman berikutnya, selain itu null). Urutan terbaru dulu.
Filter tanggal created_from (inklusif) dan created_to (eksklusif) memakai ISO 8601, mis.
2026-10-01T00:00:00+07:00 (tulis + sebagai %2B di query string).
Mode uji (simulator)#
Kunci zpk_test_… memakai saldo, rekening, tagihan, pencairan, webhook, dan event yang terpisah dari live, dan
tidak pernah menghubungi bank atau kanal pembayaran sungguhan — semua jawabannya disimulasikan secara deterministik:
| Skenario | Cara |
|---|---|
Cek rekening: nama cocok → langsung verified |
nomor apa pun selain akhiran di bawah (nama pemilik = account_holder_name merchant) |
Cek rekening: nama berbeda (BUDI SANTOSO) → pending_review |
nomor berakhiran 1111 |
Cek rekening: tidak ditemukan → 422 account_not_found |
nomor berakhiran 0000 |
Cek rekening: bank gangguan → 503 provider_unavailable |
nomor berakhiran 9999 |
| Pembayaran sukses / gagal / kedaluwarsa | POST /payments/{id}/simulate dengan status SUCCESS / FAILED / EXPIRED |
Settlement (tertunda → tersedia, balance.settled) |
otomatis ±1 menit setelah simulasi SUCCESS |
| Pencairan gagal (nominal + biaya kembali) | amount berakhiran 13, mis. 10013 |
| Pencairan sukses | nominal lain; hasil ±10 detik setelah transfer dikirim worker |
Biaya di mode uji memakai estimasi: fee pencairan = payout_fee bank di GET /banks (biaya final = biaya yang
ditahan); fee tagihan = fee_fixed + ceil(amount × fee_percent / 100) dari kanal di GET /merchant. QRIS,
nomor VA, tautan e-wallet, dan kode bayar retail di mode uji hanya contoh dan tidak bisa dibayar sungguhan. Tagihan
tetap PENDING sampai disimulasikan atau melewati expires_at (lalu ditutup EXPIRED oleh worker).
Merchant
Profil merchant pemilik kunci API, keterangan biaya, kanal pembayaran, dan aturan pencairannya.
Info merchant, tarif & aturan pencairan
Profil & status merchant, keterangan biaya (fees.description), metode & kanal pembayaran aktif dengan
estimasi tarif per kanal, aturan pencairan (estimasi biaya transfer, minimum, limit & sisa limit harian, ambang
persetujuan), webhook aktif, dan ringkasan rekening terdaftar. Bisa dipanggil dengan kunci apa pun (tanpa scope
khusus).
Respons
- 200
OK
Field respons (41)
Field Tipe Keterangan idwajibstring (uuid) codewajibstring Prefix invoice
namewajibstring statuswajibstring Selain
active, merchant tidak bisa membuat tagihan, cek rekening, atau mencairkan (422 merchant_inactive); data tetap bisa dibaca.activeinactivesuspendedlivemodewajibboolean account_holder_namewajibstring | null Nama pemilik rekening sesuai data usaha (KYC). Rekening dengan nama pemilik sama langsung terverifikasi.
contact_emailwajibstring (email) | null feeswajibobject Keterangan biaya. Tarif per kanal di
payment_methods[].channels[]; estimasi biaya transfer dipayout.fee.fees.descriptionwajibstring payment_methodswajibarray<PaymentMethodInfo> Per metode;
enabled= ada minimal satu kanal aktif,channelshanya berisi kanal aktif.payment_methods[].methodwajibstring QRISVAEWALLETRETAILpayment_methods[].enabledwajibboolean payment_methods[].channelswajibarray<PaymentChannel> payment_methods[].channels[].channelwajibstring Nilai
channeluntukPOST /payments—QRIS, kode bank VA (014,002, …),DANA/SHOPEEPAY/LINKAJA/OVO, atauALFAMART/INDOMARET.payment_methods[].channels[].namewajibstring payment_methods[].channels[].fee_fixedwajibinteger Estimasi bagian tetap biaya transaksi (rupiah).
(min 0)
payment_methods[].channels[].fee_percentwajibnumber Estimasi persentase biaya transaksi dari nominal.
(min 0)
payment_methods[].channels[].fee_paid_bywajibstring Penanggung biaya transaksi menurut aturan kanal (sama dengan
fee_bearerdi objek Payment).merchantcustomerpayment_methods[].channels[].min_amountwajibinteger Rupiah, bilangan bulat.
(min 0)
payment_methods[].channels[].max_amountwajibinteger Rupiah, bilangan bulat.
(min 0)
payment_methods[].channels[].expiry_minuteswajibinteger Masa berlaku bawaan tagihan (menit) bila
expires_in_minutestidak diisi.payoutwajibPayoutRules payout.feewajibinteger Estimasi biaya transfer per pencairan: tarif transfer ke bank/e-money rekening utama, atau tarif tertinggi yang aktif bila belum ada rekening utama. Biaya pasti dicek ke bank tujuan saat pratinjau & pencairan (lihat
POST /payouts/quote).(min 0)
payout.fee_typewajib"per_transfer" Biaya dikenakan sekali per pencairan menurut tarif transfer ke bank/e-money tujuan.
payout.fee_is_estimatewajibtrue feedi sini estimasi untuk tampilan; biaya pasti dariPOST /payouts/quotedanPOST /payouts.payout.fee_charged_towajib"balance" Dipotong dari saldo tersedia di luar nominal yang diterima rekening.
payout.minwajibinteger Minimum pencairan merchant (minimum bank di
GET /banksjuga berlaku).(min 0)
payout.daily_limitwajibinteger Rupiah, bilangan bulat.
(min 0)
payout.daily_limit_remainingwajibinteger Sisa limit hari ini (WIB, mulai lagi 00.00), dihitung dari nominal pencairan yang tidak gagal/dibatalkan/ditolak.
(min 0)
payout.approval_thresholdwajibinteger Di atas nilai ini pencairan menunggu persetujuan admin ZePay (
PENDING_APPROVAL).(min 0)
payout.any_destinationwajibboolean true bila kunci API yang dipakai boleh mencairkan ke rekening tanpa daftar (
inquiry_id): punya scope khususpayouts:any_destinationdan dibatasi daftar IP.settlementwajibobject settlement.schedulewajibstring webhookwajibobject | null Endpoint webhook aktif yang paling dulu dibuat (semua endpoint di
GET /webhook-endpoints).webhook.urlstring (uri) webhook.eventsarray<string> bank_accountswajibobject bank_accounts.verifiedwajibinteger bank_accounts.pending_reviewwajibinteger bank_accounts.default_idwajibstring (uuid) | null Rekening utama
created_atwajibstring (date-time) - 401
authentication_failed,signature_invalid, atautimestamp_out_of_range— kunci, tanda tangan, atau jam tidak valid - 403
permission_denied— kunci tidak punya scope yang dibutuhkan operasi ini (termasukinquiry_idtanpa izinpayouts:any_destination), atau IP pemanggil tidak ada di daftar IP kunci - 429
rate_limited— terlalu banyak request; ulangi setelahRetry-Afterdetik
Contoh request
curl -X GET 'https://payment.envfri.com/api/v2/merchant' \
-H 'X-ZePay-Key-Id: zpk_test_…' \
-H 'X-ZePay-Timestamp: 1793446912' \
-H 'X-ZePay-Signature: <hex HMAC-SHA256>'Contoh respons 200
{
"id": "c1a7e5d2-9b3f-4e60-8d21-7f4a2b9c0e13",
"code": "SKR",
"name": "Toko Batik Sekar",
"status": "active",
"livemode": true,
"account_holder_name": "Toko Batik Sekar",
"contact_email": "finance@tokobatiksekar.id",
"fees": {
"description": "Biaya transaksi mengikuti tarif kanal pembayaran; biaya pencairan mengikuti tarif transfer ke bank/e-money tujuan. Angka pasti tampil saat tagihan dibuat dan saat pratinjau pencairan."
},
"payment_methods": [
{
"method": "QRIS",
"enabled": true,
"channels": [
{
"channel": "QRIS",
"name": "QRIS",
"fee_fixed": 0,
"fee_percent": 0.7,
"fee_paid_by": "merchant",
"min_amount": 1000,
"max_amount": 10000000,
"expiry_minutes": 1440
}
]
},
{
"method": "VA",
"enabled": true,
"channels": [
{
"channel": "014",
"name": "BCA Virtual Account",
"fee_fixed": 4000,
"fee_percent": 0,
"fee_paid_by": "merchant",
"min_amount": 10000,
"max_amount": 50000000,
"expiry_minutes": 1440
},
{
"channel": "002",
"name": "BRI Virtual Account",
"fee_fixed": 4000,
"fee_percent": 0,
"fee_paid_by": "merchant",
"min_amount": 10000,
"max_amount": 50000000,
"expiry_minutes": 1440
}
]
},
{
"method": "EWALLET",
"enabled": true,
"channels": [
{
"channel": "DANA",
"name": "DANA",
"fee_fixed": 0,
"fee_percent": 1.5,
"fee_paid_by": "merchant",
"min_amount": 15000,
"max_amount": 2500000,
"expiry_minutes": 60
}
]
},
{
"method": "RETAIL",
"enabled": false,
"channels": []
}
],
"payout": {
"fee": 2500,
"fee_type": "per_transfer",
"fee_is_estimate": true,
"fee_charged_to": "balance",
"min": 10000,
"daily_limit": 50000000,
"daily_limit_remaining": 49950000,
"approval_threshold": 10000000,
"any_destination": false
},
"settlement": {
"schedule": "Saldo tertunda pindah ke tersedia setelah settlement kanal pembayaran: VA langsung tersedia; QRIS & e-wallet ±2–4 hari; Alfamart H+3, Indomaret H+2."
},
"webhook": {
"url": "https://tokobatik.id/hooks/zepay",
"events": [
"payment.*",
"payout.*",
"balance.*",
"bank_account.*"
]
},
"bank_accounts": {
"verified": 1,
"pending_review": 0,
"default_id": "6f1c2a0e-4b7d-4c1a-9a51-2f0b3e7c9d11"
},
"created_at": "2026-03-02T09:15:00+07:00"
}Saldo
Saldo merchant dan mutasinya, dihitung dari ledger.
Saldo merchant & nominal yang bisa dicairkan
balance:readpayout_fee adalah estimasi biaya transfer (payout_fee_source = estimate); biaya pasti dicek ke bank tujuan
saat POST /payouts/quote dan POST /payouts. withdrawable = min(available − payout_fee, daily_limit_remaining).
Respons
- 200
OK
Field respons (12)
Field Tipe Keterangan currencywajib"IDR" pendingwajibinteger Rupiah, bilangan bulat.
(min 0)
availablewajibinteger Rupiah, bilangan bulat.
(min 0)
reservedwajibinteger Ditahan untuk pencairan yang sedang diproses / menunggu persetujuan (nominal + biaya transfer).
(min 0)
withdrawablewajibinteger Maksimum
amountsaat ini = min(available − payout_fee, daily_limit_remaining), minimal 0. Maksimum per bank berlaku saat pencairan.(min 0)
payout_feewajibinteger Estimasi biaya transfer (tarif bank rekening utama, atau tarif tertinggi yang aktif bila belum ada rekening utama).
(min 0)
payout_fee_sourcewajib"estimate" payout_feeadalah estimasi tarif; nilai pasti dicek ke bank tujuan saat pencairan.payout_minwajibinteger Rupiah, bilangan bulat.
(min 0)
daily_limitwajibinteger Rupiah, bilangan bulat.
(min 0)
daily_limit_remainingwajibinteger Rupiah, bilangan bulat.
(min 0)
livemodewajibboolean as_ofwajibstring (date-time) - 401
authentication_failed,signature_invalid, atautimestamp_out_of_range— kunci, tanda tangan, atau jam tidak valid - 403
permission_denied— kunci tidak punya scope yang dibutuhkan operasi ini (termasukinquiry_idtanpa izinpayouts:any_destination), atau IP pemanggil tidak ada di daftar IP kunci - 429
rate_limited— terlalu banyak request; ulangi setelahRetry-Afterdetik
Contoh request
curl -X GET 'https://payment.envfri.com/api/v2/balance' \
-H 'X-ZePay-Key-Id: zpk_test_…' \
-H 'X-ZePay-Timestamp: 1793446912' \
-H 'X-ZePay-Signature: <hex HMAC-SHA256>'Contoh respons 200
{
"currency": "IDR",
"pending": 496000,
"available": 195250,
"reserved": 0,
"withdrawable": 192750,
"payout_fee": 2500,
"payout_fee_source": "estimate",
"payout_min": 10000,
"daily_limit": 50000000,
"daily_limit_remaining": 49950000,
"livemode": true,
"as_of": "2026-10-31T10:42:00+07:00"
}Mutasi saldo
balance:readSetiap perubahan saldo berasal dari jurnal ledger, terbaru dulu. Biaya transfer muncul sebagai baris tersendiri
(category = payout_fee); kelebihan biaya yang dikembalikan muncul sebagai payout_release. Kursor
starting_after memakai id mutasi (btx_…).
Parameter
| Nama | Di | Tipe | Keterangan |
|---|---|---|---|
limit | query | integer | (min 1, maks 100, default 20) |
starting_after | query | string | Kursor dari |
category | query | string |
Nilai: |
created_from | query | string (date-time) | Sejak (inklusif), ISO 8601, mis. |
created_to | query | string (date-time) | Sampai (eksklusif), ISO 8601. |
Respons
- 200
OK
Field respons (11)
Field Tipe Keterangan has_morewajibboolean next_cursorwajibstring | null iditem terakhir bilahas_more; kirim sebagaistarting_after.datawajibarray<BalanceTransaction> data[].idwajibstring btx_<nomor>data[].categorywajibstring paymentdana masuk (tertunda) ·settlementtertunda → tersedia ·payout_holdtersedia → ditahan (nominal + biaya) ·payoutnominal keluar ke rekening ·payout_feebiaya transfer (final) ·payout_releaseditahan → tersedia (pencairan gagal/batal/ditolak, atau kelebihan biaya transfer dikembalikan) ·opening_balancesaldo awal migrasi v1 ·adjustmentkoreksi oleh admin.paymentsettlementpayout_holdpayoutpayout_feepayout_releaseopening_balanceadjustmentdata[].referencewajibstring | null Invoice atau referensi pencairan
data[].amountwajibinteger Rupiah, bilangan bulat.
(min 0)
data[].net_changewajibinteger Perubahan total saldo merchant (+ masuk, − keluar, 0 = pindah antar-bucket)
data[].from_bucketwajibstring | null pendingavailablereserveddata[].to_bucketwajibstring | null pendingavailablereserveddata[].created_atwajibstring (date-time) - 400
invalid_request— body/parameter tidak valid: field wajib kosong, tipe/format salah, field tidak dikenal, nilai di luar batas, JSON rusak, body > 64 KB, atau headerIdempotency-Keytidak ada/salah format. Rincian validasi didetails.errors. - 401
authentication_failed,signature_invalid, atautimestamp_out_of_range— kunci, tanda tangan, atau jam tidak valid - 403
permission_denied— kunci tidak punya scope yang dibutuhkan operasi ini (termasukinquiry_idtanpa izinpayouts:any_destination), atau IP pemanggil tidak ada di daftar IP kunci - 429
rate_limited— terlalu banyak request; ulangi setelahRetry-Afterdetik
Contoh request
curl -X GET 'https://payment.envfri.com/api/v2/balance/transactions' \
-H 'X-ZePay-Key-Id: zpk_test_…' \
-H 'X-ZePay-Timestamp: 1793446912' \
-H 'X-ZePay-Signature: <hex HMAC-SHA256>'Contoh respons 200
{
"data": [
{
"id": "btx_6",
"category": "payout_fee",
"reference": "WD-01JBQ9S2ZK7MX4D8E2R5T0WQ3N",
"amount": 2500,
"net_change": -2500,
"from_bucket": "reserved",
"to_bucket": null,
"created_at": "2026-10-31T10:44:12+07:00"
},
{
"id": "btx_5",
"category": "payout",
"reference": "WD-01JBQ9S2ZK7MX4D8E2R5T0WQ3N",
"amount": 100000,
"net_change": -100000,
"from_bucket": "reserved",
"to_bucket": null,
"created_at": "2026-10-31T10:44:12+07:00"
},
{
"id": "btx_4",
"category": "payout_hold",
"reference": "WD-01JBQ9S2ZK7MX4D8E2R5T0WQ3N",
"amount": 102500,
"net_change": 0,
"from_bucket": "available",
"to_bucket": "reserved",
"created_at": "2026-10-31T10:43:05+07:00"
},
{
"id": "btx_2",
"category": "settlement",
"reference": "SKR-01JBQ7X4M2RKD8T5W9C3HZPN6E",
"amount": 248250,
"net_change": 0,
"from_bucket": "pending",
"to_bucket": "available",
"created_at": "2026-10-31T10:40:02+07:00"
},
{
"id": "btx_1",
"category": "payment",
"reference": "SKR-01JBQ7X4M2RKD8T5W9C3HZPN6E",
"amount": 248250,
"net_change": 248250,
"from_bucket": null,
"to_bucket": "pending",
"created_at": "2026-10-30T10:31:20+07:00"
}
],
"has_more": false,
"next_cursor": null
}Ringkasan periode (rekonsiliasi)
balance:readSaldo awal & akhir, pembayaran masuk, pencairan, biaya transfer, dan penyesuaian dalam periode (maks. 93 hari).
closing_balance = opening_balance + payments_net − payouts − payout_fees + adjustments.
Parameter
| Nama | Di | Tipe | Keterangan |
|---|---|---|---|
fromwajib | query | string (date-time) | Awal periode (inklusif), ISO 8601. |
towajib | query | string (date-time) | Akhir periode (eksklusif), ISO 8601; harus setelah |
Respons
- 200
OK
Field respons (15)
Field Tipe Keterangan periodwajibobject Parameter
from&toseperti yang dikirim.period.fromstring period.tostring opening_balancewajibinteger Total saldo (tertunda + tersedia + ditahan) sebelum
frompayments_netwajibinteger Pembayaran sukses yang masuk saldo (setelah biaya transaksi yang ditanggung merchant)
payoutswajibinteger Nominal pencairan yang terkirim
payout_feeswajibinteger Biaya transfer final atas pencairan tersebut
adjustmentswajibinteger Penyesuaian admin & saldo awal migrasi
closing_balancewajibinteger Total saldo sebelum
topaymentswajibobject Pembayaran
SUCCESSdenganpaid_atdi periode.payments.countinteger payments.grossinteger Rupiah, bilangan bulat.
(min 0)
payments.feesinteger Total biaya transaksi, termasuk yang ditanggung pelanggan.
(min 0)
payments.netinteger Jumlah
net_amount.(min 0)
payouts_countwajibinteger Jumlah pencairan
SUCCESSyang selesai di periode - 400
invalid_request— body/parameter tidak valid: field wajib kosong, tipe/format salah, field tidak dikenal, nilai di luar batas, JSON rusak, body > 64 KB, atau headerIdempotency-Keytidak ada/salah format. Rincian validasi didetails.errors. - 401
authentication_failed,signature_invalid, atautimestamp_out_of_range— kunci, tanda tangan, atau jam tidak valid - 403
permission_denied— kunci tidak punya scope yang dibutuhkan operasi ini (termasukinquiry_idtanpa izinpayouts:any_destination), atau IP pemanggil tidak ada di daftar IP kunci - 429
rate_limited— terlalu banyak request; ulangi setelahRetry-Afterdetik
Contoh request
curl -X GET 'https://payment.envfri.com/api/v2/balance/summary' \
-H 'X-ZePay-Key-Id: zpk_test_…' \
-H 'X-ZePay-Timestamp: 1793446912' \
-H 'X-ZePay-Signature: <hex HMAC-SHA256>'Contoh respons 200
{
"period": {
"from": "2026-10-01T00:00:00+07:00",
"to": "2026-11-01T00:00:00+07:00"
},
"opening_balance": 0,
"payments_net": 248250,
"payouts": 100000,
"payout_fees": 2500,
"adjustments": 0,
"closing_balance": 145750,
"payments": {
"count": 1,
"gross": 250000,
"fees": 1750,
"net": 248250
},
"payouts_count": 1
}Bank & rekening
Daftar bank & e-money, cek rekening, dan rekening terdaftar milik merchant.
Daftar bank & e-money tujuan pencairan
Bank dan e-money yang bisa dicek dan dicairkan, termasuk yang sedang gangguan (available = false), lengkap dengan
minimum, maksimum, format nomor, dan estimasi biaya transfer (payout_fee_source = estimate). Biaya yang dipotong
saat pencairan dipastikan lewat pengecekan ke bank tujuan. Bisa dipanggil dengan kunci apa pun.
Parameter
| Nama | Di | Tipe | Keterangan |
|---|---|---|---|
type | query | string | Saring bank saja atau e-money saja. Nilai: |
Respons
- 200
OK
Field respons (11)
Field Tipe Keterangan datawajibarray<Bank> data[].codewajibstring data[].namewajibstring data[].typewajibstring bankewalletdata[].availablewajibboolean false bila bank/e-money sedang gangguan; cek & pencairan ditolak sementara (
bank_unavailable).data[].payout_feewajibinteger Estimasi biaya transfer ke bank/e-money ini (tarif saat ini Rp2.500 bank, Rp2.000 e-money). Biaya yang dipotong dicek ke bank tujuan saat pencairan.
(min 0)
data[].payout_fee_sourcewajib"estimate" data[].payout_minwajibinteger Rupiah, bilangan bulat.
(min 0)
data[].payout_maxwajibinteger Rupiah, bilangan bulat.
(min 0)
data[].account_number_patternwajibstring Regex format nomor
data[].account_number_hintwajibstring | null - 400
invalid_request— body/parameter tidak valid: field wajib kosong, tipe/format salah, field tidak dikenal, nilai di luar batas, JSON rusak, body > 64 KB, atau headerIdempotency-Keytidak ada/salah format. Rincian validasi didetails.errors. - 401
authentication_failed,signature_invalid, atautimestamp_out_of_range— kunci, tanda tangan, atau jam tidak valid - 403
permission_denied— kunci tidak punya scope yang dibutuhkan operasi ini (termasukinquiry_idtanpa izinpayouts:any_destination), atau IP pemanggil tidak ada di daftar IP kunci - 429
rate_limited— terlalu banyak request; ulangi setelahRetry-Afterdetik
Contoh request
curl -X GET 'https://payment.envfri.com/api/v2/banks' \
-H 'X-ZePay-Key-Id: zpk_test_…' \
-H 'X-ZePay-Timestamp: 1793446912' \
-H 'X-ZePay-Signature: <hex HMAC-SHA256>'Contoh respons 200
{
"data": [
{
"code": "014",
"name": "BCA",
"type": "bank",
"available": true,
"payout_fee": 2500,
"payout_fee_source": "estimate",
"payout_min": 10000,
"payout_max": 50000000,
"account_number_pattern": "^[0-9]{10}$",
"account_number_hint": "10 digit"
},
{
"code": "DANA",
"name": "DANA",
"type": "ewallet",
"available": true,
"payout_fee": 2000,
"payout_fee_source": "estimate",
"payout_min": 20000,
"payout_max": 10000000,
"account_number_pattern": "^08[0-9]{8,11}$",
"account_number_hint": "Nomor HP terdaftar di DANA, awalan 08"
}
]
}Cek rekening atau e-money sebelum didaftarkan
bank_accounts:writeZePay mengecek nomor ke bank/e-money tujuan dan mengembalikan nama pemilik serta perkiraan biaya transfer ke tujuan
itu (payout_fee). Hasilnya berlaku 15 menit dan dipakai sekali untuk POST /bank-accounts.
Cek ini tidak mendaftarkan rekening dan tidak memotong saldo. Spasi, titik, dan tanda hubung di nomor diabaikan.
Dibatasi 30/menit dan 300 per 24 jam per merchant. Tidak memakai Idempotency-Key.
Body JSON
| Field | Tipe | Keterangan |
|---|---|---|
bank_codewajib | string | Kode dari (panjang min 2, panjang maks 20) |
account_numberwajib | string | Nomor rekening, atau nomor HP untuk e-money. Harus cocok dengan (panjang min 5, panjang maks 30) |
Respons
- 200
Rekening/e-money ditemukan
Field respons (10)
Field Tipe Keterangan idwajibstring (uuid) Kirim sebagai
inquiry_idkePOST /bank-accounts.bank_codewajibstring bank_namewajibstring typewajibstring bankewalletaccount_number_maskedwajibstring account_namewajibstring Nama pemilik menurut bank/e-money
name_matches_merchantwajibboolean true → akan langsung
verifiedsaat didaftarkan; false →pending_review(ditinjau admin).payout_feewajibinteger Perkiraan biaya transfer ke tujuan ini: hasil pengecekan ke bank tujuan, atau estimasi tarif transfer bila bank tidak memberikannya. Biaya yang dipotong dicek ulang ke bank tujuan saat pencairan dibuat.
(min 0)
expires_atwajibstring (date-time) Batas waktu pendaftaran (15 menit).
livemodewajibboolean - 400
invalid_request—bank_codetidak dikenal atau format nomor tidak cocok (details.account_number_pattern) - 401
authentication_failed,signature_invalid, atautimestamp_out_of_range— kunci, tanda tangan, atau jam tidak valid - 403
permission_denied— kunci tidak punya scope yang dibutuhkan operasi ini (termasukinquiry_idtanpa izinpayouts:any_destination), atau IP pemanggil tidak ada di daftar IP kunci - 422
account_not_found— nomor tidak ditemukan di bank/e-money tujuan;bank_unavailable— bank sedang tidak tersedia;merchant_inactive— merchant ditangguhkan/nonaktif. - 429
rate_limited— terlalu banyak request; ulangi setelahRetry-Afterdetik - 503
provider_unavailable— bank atau kanal pembayaran sedang tidak merespons; coba lagi. Cek rekening: ulangi cek. Pencairan: tidak dibuat (aman diulang denganIdempotency-Keyyang sama). Tagihan: tagihan ditutupFAILED(details.payment_id,details.reference) — buat tagihan baru.
Contoh request
curl -X POST 'https://payment.envfri.com/api/v2/bank-accounts/inquiry' \
-H 'X-ZePay-Key-Id: zpk_test_…' \
-H 'X-ZePay-Timestamp: 1793446912' \
-H 'X-ZePay-Signature: <hex HMAC-SHA256>' \
-H 'Content-Type: application/json' \
-d '{"bank_code":"014","account_number":"8730124821"}'{
"bank_code": "014",
"account_number": "8730124821"
}{
"bank_code": "DANA",
"account_number": "081234567788"
}Contoh respons 200
{
"id": "3b1d6c2e-8f7a-4c11-9d0e-5a2b7c9e1f30",
"bank_code": "014",
"bank_name": "BCA",
"type": "bank",
"account_number_masked": "•••• 4821",
"account_name": "TOKO BATIK SEKAR",
"name_matches_merchant": true,
"payout_fee": 2500,
"expires_at": "2026-10-31T10:57:00+07:00",
"livemode": true
}Rekening & e-money terdaftar milik merchant
bank_accounts:readRekening utama di urutan pertama, lalu terbaru dulu.
Parameter
| Nama | Di | Tipe | Keterangan |
|---|---|---|---|
limit | query | integer | (min 1, maks 100, default 20) |
starting_after | query | string | Kursor dari |
type | query | string | Nilai: |
status | query | string |
Nilai: |
Respons
- 200
OK
Field respons (15)
Field Tipe Keterangan has_morewajibboolean next_cursorwajibstring | null iditem terakhir bilahas_more; kirim sebagaistarting_after.datawajibarray<BankAccount> data[].idwajibstring (uuid) data[].bank_codewajibstring data[].bank_namewajibstring data[].typewajibstring bankewalletdata[].account_number_maskedwajibstring data[].account_namewajibstring Nama pemilik dari hasil cek rekening
data[].labelwajibstring | null data[].statuswajibstring verifiedbisa menerima pencairan ·pending_reviewmenunggu tinjauan ·rejectedditolak ·disableddinonaktifkanpending_reviewverifiedrejecteddisableddata[].is_defaultwajibboolean data[].review_notewajibstring | null Alasan bila ditolak admin
data[].verified_atwajibstring (date-time) | null data[].created_atwajibstring (date-time) - 400
invalid_request— body/parameter tidak valid: field wajib kosong, tipe/format salah, field tidak dikenal, nilai di luar batas, JSON rusak, body > 64 KB, atau headerIdempotency-Keytidak ada/salah format. Rincian validasi didetails.errors. - 401
authentication_failed,signature_invalid, atautimestamp_out_of_range— kunci, tanda tangan, atau jam tidak valid - 403
permission_denied— kunci tidak punya scope yang dibutuhkan operasi ini (termasukinquiry_idtanpa izinpayouts:any_destination), atau IP pemanggil tidak ada di daftar IP kunci - 429
rate_limited— terlalu banyak request; ulangi setelahRetry-Afterdetik
Contoh request
curl -X GET 'https://payment.envfri.com/api/v2/bank-accounts' \
-H 'X-ZePay-Key-Id: zpk_test_…' \
-H 'X-ZePay-Timestamp: 1793446912' \
-H 'X-ZePay-Signature: <hex HMAC-SHA256>'Contoh respons 200
{
"has_more": true,
"next_cursor": "string",
"data": [
{
"id": "6f1c2a0e-4b7d-4c1a-9a51-2f0b3e7c9d11",
"bank_code": "014",
"bank_name": "BCA",
"type": "bank",
"account_number_masked": "•••• 4821",
"account_name": "TOKO BATIK SEKAR",
"label": "Rekening operasional",
"status": "verified",
"is_default": true,
"review_note": null,
"verified_at": "2026-10-31T10:42:31+07:00",
"created_at": "2026-10-31T10:42:31+07:00"
}
]
}Daftarkan rekening dari hasil cek
bank_accounts:writeWajib Idempotency-KeyWajib memakai inquiry_id dari POST /bank-accounts/inquiry (maks. 15 menit, sekali pakai). Nama pemilik diambil
dari hasil cek. Status verified bila nama sama dengan account_holder_name merchant, selain itu pending_review.
Rekening terverifikasi pertama otomatis menjadi rekening utama. Rekening yang pernah dinonaktifkan bisa didaftarkan
lagi dari hasil cek baru.
Body JSON
| Field | Tipe | Keterangan |
|---|---|---|
inquiry_idwajib | string (uuid) | |
label | string | (panjang maks 60) |
is_default | boolean | Jadikan rekening utama (hanya berlaku bila langsung (default false) |
Respons
- 201
Terdaftar (
verifiedataupending_review)Field respons (12)
Field Tipe Keterangan idwajibstring (uuid) bank_codewajibstring bank_namewajibstring typewajibstring bankewalletaccount_number_maskedwajibstring account_namewajibstring Nama pemilik dari hasil cek rekening
labelwajibstring | null statuswajibstring verifiedbisa menerima pencairan ·pending_reviewmenunggu tinjauan ·rejectedditolak ·disableddinonaktifkanpending_reviewverifiedrejecteddisabledis_defaultwajibboolean review_notewajibstring | null Alasan bila ditolak admin
verified_atwajibstring (date-time) | null created_atwajibstring (date-time) - 400
invalid_request— body/parameter tidak valid: field wajib kosong, tipe/format salah, field tidak dikenal, nilai di luar batas, JSON rusak, body > 64 KB, atau headerIdempotency-Keytidak ada/salah format. Rincian validasi didetails.errors. - 401
authentication_failed,signature_invalid, atautimestamp_out_of_range— kunci, tanda tangan, atau jam tidak valid - 403
permission_denied— kunci tidak punya scope yang dibutuhkan operasi ini (termasukinquiry_idtanpa izinpayouts:any_destination), atau IP pemanggil tidak ada di daftar IP kunci - 404
inquiry_not_found— hasil cek tidak ada, milik merchant lain, atau dari mode lain - 409
bank_account_exists— rekening sudah terdaftar; atauidempotency_conflict - 422
inquiry_expired,inquiry_used,account_not_found, ataubank_account_rejected - 429
rate_limited— terlalu banyak request; ulangi setelahRetry-Afterdetik
Contoh request
curl -X POST 'https://payment.envfri.com/api/v2/bank-accounts' \
-H 'X-ZePay-Key-Id: zpk_test_…' \
-H 'X-ZePay-Timestamp: 1793446912' \
-H 'X-ZePay-Signature: <hex HMAC-SHA256>' \
-H 'Idempotency-Key: 0b9f3c1e-7a52-4d0e-9f4e-2c1d8a6b3e70' \
-H 'Content-Type: application/json' \
-d '{"inquiry_id":"3b1d6c2e-8f7a-4c11-9d0e-5a2b7c9e1f30","label":"Rekening operasional"}'Contoh respons 201
{
"id": "6f1c2a0e-4b7d-4c1a-9a51-2f0b3e7c9d11",
"bank_code": "014",
"bank_name": "BCA",
"type": "bank",
"account_number_masked": "•••• 4821",
"account_name": "TOKO BATIK SEKAR",
"label": "Rekening operasional",
"status": "verified",
"is_default": true,
"review_note": null,
"verified_at": "2026-10-31T10:42:31+07:00",
"created_at": "2026-10-31T10:42:31+07:00"
}Detail rekening terdaftar
bank_accounts:readParameter
| Nama | Di | Tipe | Keterangan |
|---|---|---|---|
idwajib | path | string (uuid) | UUID objek. Bukan UUID atau bukan milik merchant/mode ini → |
Respons
- 200
OK
Field respons (12)
Field Tipe Keterangan idwajibstring (uuid) bank_codewajibstring bank_namewajibstring typewajibstring bankewalletaccount_number_maskedwajibstring account_namewajibstring Nama pemilik dari hasil cek rekening
labelwajibstring | null statuswajibstring verifiedbisa menerima pencairan ·pending_reviewmenunggu tinjauan ·rejectedditolak ·disableddinonaktifkanpending_reviewverifiedrejecteddisabledis_defaultwajibboolean review_notewajibstring | null Alasan bila ditolak admin
verified_atwajibstring (date-time) | null created_atwajibstring (date-time) - 401
authentication_failed,signature_invalid, atautimestamp_out_of_range— kunci, tanda tangan, atau jam tidak valid - 403
permission_denied— kunci tidak punya scope yang dibutuhkan operasi ini (termasukinquiry_idtanpa izinpayouts:any_destination), atau IP pemanggil tidak ada di daftar IP kunci - 404
not_found— tidak ditemukan, bukan milik merchant/mode ini, atauidtidak valid - 429
rate_limited— terlalu banyak request; ulangi setelahRetry-Afterdetik
Contoh request
curl -X GET 'https://payment.envfri.com/api/v2/bank-accounts/{id}' \
-H 'X-ZePay-Key-Id: zpk_test_…' \
-H 'X-ZePay-Timestamp: 1793446912' \
-H 'X-ZePay-Signature: <hex HMAC-SHA256>'Contoh respons 200
{
"id": "6f1c2a0e-4b7d-4c1a-9a51-2f0b3e7c9d11",
"bank_code": "014",
"bank_name": "BCA",
"type": "bank",
"account_number_masked": "•••• 4821",
"account_name": "TOKO BATIK SEKAR",
"label": "Rekening operasional",
"status": "verified",
"is_default": true,
"review_note": null,
"verified_at": "2026-10-31T10:42:31+07:00",
"created_at": "2026-10-31T10:42:31+07:00"
}Ubah label atau jadikan rekening utama
bank_accounts:writeParameter
| Nama | Di | Tipe | Keterangan |
|---|---|---|---|
idwajib | path | string (uuid) | UUID objek. Bukan UUID atau bukan milik merchant/mode ini → |
Body JSON
| Field | Tipe | Keterangan |
|---|---|---|
label | string | null |
(panjang maks 60) |
is_default | true | Rekening utama sebelumnya otomatis dilepas. |
Respons
- 200
Diperbarui
Field respons (12)
Field Tipe Keterangan idwajibstring (uuid) bank_codewajibstring bank_namewajibstring typewajibstring bankewalletaccount_number_maskedwajibstring account_namewajibstring Nama pemilik dari hasil cek rekening
labelwajibstring | null statuswajibstring verifiedbisa menerima pencairan ·pending_reviewmenunggu tinjauan ·rejectedditolak ·disableddinonaktifkanpending_reviewverifiedrejecteddisabledis_defaultwajibboolean review_notewajibstring | null Alasan bila ditolak admin
verified_atwajibstring (date-time) | null created_atwajibstring (date-time) - 400
invalid_request— body/parameter tidak valid: field wajib kosong, tipe/format salah, field tidak dikenal, nilai di luar batas, JSON rusak, body > 64 KB, atau headerIdempotency-Keytidak ada/salah format. Rincian validasi didetails.errors. - 401
authentication_failed,signature_invalid, atautimestamp_out_of_range— kunci, tanda tangan, atau jam tidak valid - 403
permission_denied— kunci tidak punya scope yang dibutuhkan operasi ini (termasukinquiry_idtanpa izinpayouts:any_destination), atau IP pemanggil tidak ada di daftar IP kunci - 404
not_found— tidak ditemukan, bukan milik merchant/mode ini, atauidtidak valid - 422
bank_account_not_verified— hanya rekening terverifikasi yang bisa jadi rekening utama - 429
rate_limited— terlalu banyak request; ulangi setelahRetry-Afterdetik
Contoh request
curl -X PATCH 'https://payment.envfri.com/api/v2/bank-accounts/{id}' \
-H 'X-ZePay-Key-Id: zpk_test_…' \
-H 'X-ZePay-Timestamp: 1793446912' \
-H 'X-ZePay-Signature: <hex HMAC-SHA256>' \
-H 'Content-Type: application/json' \
-d '{"label":"string","is_default":true}'Contoh respons 200
{
"id": "6f1c2a0e-4b7d-4c1a-9a51-2f0b3e7c9d11",
"bank_code": "014",
"bank_name": "BCA",
"type": "bank",
"account_number_masked": "•••• 4821",
"account_name": "TOKO BATIK SEKAR",
"label": "Rekening operasional",
"status": "verified",
"is_default": true,
"review_note": null,
"verified_at": "2026-10-31T10:42:31+07:00",
"created_at": "2026-10-31T10:42:31+07:00"
}Nonaktifkan rekening
bank_accounts:writeRekening verified/pending_review menjadi disabled dan tidak bisa lagi dipakai untuk pencairan baru (rekening
rejected/disabled tidak berubah). Pencairan yang sedang berjalan tidak terpengaruh. Daftarkan ulang lewat cek
rekening bila diperlukan.
Parameter
| Nama | Di | Tipe | Keterangan |
|---|---|---|---|
idwajib | path | string (uuid) | UUID objek. Bukan UUID atau bukan milik merchant/mode ini → |
Respons
- 200
Dinonaktifkan
Field respons (12)
Field Tipe Keterangan idwajibstring (uuid) bank_codewajibstring bank_namewajibstring typewajibstring bankewalletaccount_number_maskedwajibstring account_namewajibstring Nama pemilik dari hasil cek rekening
labelwajibstring | null statuswajibstring verifiedbisa menerima pencairan ·pending_reviewmenunggu tinjauan ·rejectedditolak ·disableddinonaktifkanpending_reviewverifiedrejecteddisabledis_defaultwajibboolean review_notewajibstring | null Alasan bila ditolak admin
verified_atwajibstring (date-time) | null created_atwajibstring (date-time) - 401
authentication_failed,signature_invalid, atautimestamp_out_of_range— kunci, tanda tangan, atau jam tidak valid - 403
permission_denied— kunci tidak punya scope yang dibutuhkan operasi ini (termasukinquiry_idtanpa izinpayouts:any_destination), atau IP pemanggil tidak ada di daftar IP kunci - 404
not_found— tidak ditemukan, bukan milik merchant/mode ini, atauidtidak valid - 429
rate_limited— terlalu banyak request; ulangi setelahRetry-Afterdetik
Contoh request
curl -X DELETE 'https://payment.envfri.com/api/v2/bank-accounts/{id}' \
-H 'X-ZePay-Key-Id: zpk_test_…' \
-H 'X-ZePay-Timestamp: 1793446912' \
-H 'X-ZePay-Signature: <hex HMAC-SHA256>'Contoh respons 200
{
"id": "6f1c2a0e-4b7d-4c1a-9a51-2f0b3e7c9d11",
"bank_code": "014",
"bank_name": "BCA",
"type": "bank",
"account_number_masked": "•••• 4821",
"account_name": "TOKO BATIK SEKAR",
"label": "Rekening operasional",
"status": "verified",
"is_default": true,
"review_note": null,
"verified_at": "2026-10-31T10:42:31+07:00",
"created_at": "2026-10-31T10:42:31+07:00"
}Pencairan
Pencairan dari saldo merchant ke rekening terdaftar; biaya transfer dipotong dari saldo di luar nominal.
Pratinjau pencairan (biaya transfer & total dipotong)
payouts:writeMenghitung nominal diterima, biaya transfer, total dipotong dari saldo, saldo setelah pencairan, dan apakah perlu
persetujuan — tanpa membuat pencairan. ZePay memastikan biaya dengan pengecekan ke bank tujuan untuk tujuan &
nominal ini (fee_source = confirmed); bila pengecekan sementara tidak tersedia, dipakai estimasi tarif transfer
(fee_source = estimate) dan biaya dihitung ulang saat pencairan dibuat. Validasinya sama dengan POST /payouts.
Tampilkan fee dan total_debited ke pengguna sebelum konfirmasi. Tidak memakai Idempotency-Key.
Dengan inquiry_id (kunci berizin payouts:any_destination), pratinjau tidak memakai hasil cek rekening.
Body JSON
| Field | Tipe | Keterangan |
|---|---|---|
bank_account_id | string (uuid) | Rekening terdaftar berstatus |
inquiry_id | string (uuid) | Hasil |
amount | integer | Nominal yang DITERIMA rekening. Minimum = terbesar dari (min 1, maks 1000000000) |
withdraw_all | boolean | Cairkan seluruh saldo yang bisa dicairkan = min(tersedia − biaya transfer, sisa limit harian, maksimum bank). (default false) |
Respons
- 200
Pencairan bisa dibuat dengan rincian berikut
Field respons (19)
Field Tipe Keterangan amountwajibinteger Rupiah, bilangan bulat.
(min 0)
feewajibinteger Biaya transfer untuk tujuan & nominal ini.
(min 0)
fee_sourcewajibstring confirmed= biaya dipastikan lewat pengecekan ke bank tujuan;estimate= pengecekan sementara tidak tersedia, dipakai estimasi tarif transfer — biaya dihitung ulang saat pencairan dibuat.confirmedestimatetotal_debitedwajibinteger amount + fee, dipotong dari saldo tersedia.
(min 0)
available_beforewajibinteger Rupiah, bilangan bulat.
(min 0)
available_afterwajibinteger Rupiah, bilangan bulat.
(min 0)
withdrawablewajibinteger min(tersedia − fee, sisa limit harian, maksimum bank).
(min 0)
daily_limitwajibinteger Rupiah, bilangan bulat.
(min 0)
daily_limit_remainingwajibinteger Rupiah, bilangan bulat.
(min 0)
requires_approvalwajibboolean true bila amount di atas ambang persetujuan.
destinationwajibPayoutDestinationMerchantAccount | PayoutDestinationInquiryAccount destination.type"inquiry_account" destination.bank_account_idnull destination.bank_codestring destination.bank_namestring destination.account_typestring bankewalletdestination.account_namestring destination.account_number_maskedstring destination.inquiry_idstring (uuid) - 400
invalid_request— body/parameter tidak valid: field wajib kosong, tipe/format salah, field tidak dikenal, nilai di luar batas, JSON rusak, body > 64 KB, atau headerIdempotency-Keytidak ada/salah format. Rincian validasi didetails.errors. - 401
authentication_failed,signature_invalid, atautimestamp_out_of_range— kunci, tanda tangan, atau jam tidak valid - 403
permission_denied— kunci tidak punya scope yang dibutuhkan operasi ini (termasukinquiry_idtanpa izinpayouts:any_destination), atau IP pemanggil tidak ada di daftar IP kunci - 404
bank_account_not_found— rekening tidak terdaftar untuk merchant ini; atauinquiry_not_found— hasil cek rekening tidak ditemukan (bukan milik merchant/mode ini) - 422
Pencairan tidak bisa dibuat:
bank_account_not_verified,insufficient_balance(details:amount,fee,total_debited,available; tanpaamount/total_debitedbilawithdraw_all),payout_limit_exceeded,amount_below_minimum,amount_above_maximum,merchant_inactive,bank_unavailable, atauaccount_not_found(rekening tujuan tidak lagi ditemukan saat pengecekan biaya ke bank tujuan, atau hasil cekinquiry_idbukan rekening valid). Denganinquiry_id:inquiry_expired(lewat 15 menit) atauinquiry_used(sudah dipakai) — cek ulang rekening. - 429
rate_limited— terlalu banyak request; ulangi setelahRetry-Afterdetik
Contoh request
curl -X POST 'https://payment.envfri.com/api/v2/payouts/quote' \
-H 'X-ZePay-Key-Id: zpk_test_…' \
-H 'X-ZePay-Timestamp: 1793446912' \
-H 'X-ZePay-Signature: <hex HMAC-SHA256>' \
-H 'Content-Type: application/json' \
-d '{"bank_account_id":"6f1c2a0e-4b7d-4c1a-9a51-2f0b3e7c9d11","amount":100000}'{
"bank_account_id": "6f1c2a0e-4b7d-4c1a-9a51-2f0b3e7c9d11",
"amount": 100000
}{
"bank_account_id": "6f1c2a0e-4b7d-4c1a-9a51-2f0b3e7c9d11",
"withdraw_all": true
}{
"inquiry_id": "3b8e1f52-7c0a-4d9e-b6a1-5f2c8d4e9a03",
"amount": 100000
}Contoh respons 200
{
"amount": 100000,
"fee": 2500,
"fee_source": "confirmed",
"total_debited": 102500,
"available_before": 195250,
"available_after": 92750,
"withdrawable": 192750,
"daily_limit": 50000000,
"daily_limit_remaining": 49950000,
"requires_approval": false,
"destination": {
"type": "merchant_account",
"bank_account_id": "6f1c2a0e-4b7d-4c1a-9a51-2f0b3e7c9d11",
"bank_code": "014",
"bank_name": "BCA",
"account_type": "bank",
"account_name": "TOKO BATIK SEKAR",
"account_number_masked": "•••• 4821"
}
}Riwayat pencairan
payouts:readTermasuk pencairan gabungan yang dibuat admin ZePay dari beberapa merchant (combined = true); yang ditampilkan hanya porsi merchant ini.
Parameter
| Nama | Di | Tipe | Keterangan |
|---|---|---|---|
limit | query | integer | (min 1, maks 100, default 20) |
starting_after | query | string | Kursor dari |
status | query | string |
Nilai: |
created_from | query | string (date-time) | Sejak (inklusif), ISO 8601, mis. |
created_to | query | string (date-time) | Sampai (eksklusif), ISO 8601. |
Respons
- 200
OK
Field respons (24)
Field Tipe Keterangan has_morewajibboolean next_cursorwajibstring | null iditem terakhir bilahas_more; kirim sebagaistarting_after.datawajibarray<Payout> data[].idwajibstring (uuid) data[].referencewajibstring Referensi publik pencairan.
data[].statuswajibstring PENDING_APPROVALmenunggu admin ZePay ·PROCESSINGsedang dikirim ke bank tujuan ·SUCCESSditerima rekening ·FAILEDgagal ·CANCELLEDdibatalkan merchant ·REJECTEDditolak admin.PENDING_APPROVALPROCESSINGSUCCESSFAILEDCANCELLEDREJECTEDdata[].amountwajibinteger Nominal (porsi merchant ini) yang dikirim ke rekening tujuan.
(min 0)
data[].feewajibinteger Biaya transfer yang dibebankan ke saldo merchant ini. Sebelum
SUCCESS= biaya yang ditahan (hasil pengecekan ke bank tujuan, batas atas); setelahSUCCESS= biaya final (bila lebih kecil, selisihnya sudah dikembalikan). Pada pencairan gabungan = porsi biaya merchant ini; 0 bila ditanggung ZePay.(min 0)
data[].total_debitedwajibinteger amount + fee. Kembali utuh ke saldo tersedia bila FAILED/CANCELLED/REJECTED.
(min 0)
data[].combinedwajibboolean true bila pencairan gabungan dari beberapa merchant yang dibuat admin.
data[].destinationwajibPayoutDestinationMerchantAccount | PayoutDestinationInquiryAccount | PayoutDestinationZepayAccount data[].destination.type"zepay_account" data[].destination.bank_account_idnull data[].destination.bank_codestring data[].destination.bank_namestring data[].destination.account_typestring bankewalletdata[].destination.account_namestring data[].destination.account_number_maskedstring data[].destination.inquiry_idstring (uuid) data[].notewajibstring | null data[].failure_reasonwajibstring | null Alasan bila FAILED / CANCELLED / REJECTED.
data[].livemodewajibboolean data[].created_atwajibstring (date-time) data[].completed_atwajibstring (date-time) | null - 400
invalid_request— body/parameter tidak valid: field wajib kosong, tipe/format salah, field tidak dikenal, nilai di luar batas, JSON rusak, body > 64 KB, atau headerIdempotency-Keytidak ada/salah format. Rincian validasi didetails.errors. - 401
authentication_failed,signature_invalid, atautimestamp_out_of_range— kunci, tanda tangan, atau jam tidak valid - 403
permission_denied— kunci tidak punya scope yang dibutuhkan operasi ini (termasukinquiry_idtanpa izinpayouts:any_destination), atau IP pemanggil tidak ada di daftar IP kunci - 429
rate_limited— terlalu banyak request; ulangi setelahRetry-Afterdetik
Contoh request
curl -X GET 'https://payment.envfri.com/api/v2/payouts' \
-H 'X-ZePay-Key-Id: zpk_test_…' \
-H 'X-ZePay-Timestamp: 1793446912' \
-H 'X-ZePay-Signature: <hex HMAC-SHA256>'Contoh respons 200
{
"has_more": true,
"next_cursor": "string",
"data": [
{
"id": "9d0f3c1a-2b7e-4f55-8a19-6c3e2d1b0a77",
"reference": "WD-01JBQ9S2ZK7MX4D8E2R5T0WQ3N",
"status": "PROCESSING",
"amount": 100000,
"fee": 2500,
"total_debited": 102500,
"combined": false,
"destination": {
"type": "merchant_account",
"bank_account_id": "6f1c2a0e-4b7d-4c1a-9a51-2f0b3e7c9d11",
"bank_code": "014",
"bank_name": "BCA",
"account_type": "bank",
"account_name": "TOKO BATIK SEKAR",
"account_number_masked": "•••• 4821"
},
"note": "Tarik mingguan",
"failure_reason": null,
"livemode": true,
"created_at": "2026-10-31T10:43:05+07:00",
"completed_at": null
}
]
}Cairkan saldo ke rekening terdaftar (atau hasil cek rekening)
payouts:writeWajib Idempotency-KeyHanya dari saldo merchant pemilik kunci, ke rekening terdaftar berstatus verified (bank_account_id) — atau,
khusus kunci berizin payouts:any_destination yang dibatasi IP, langsung ke hasil cek rekening (inquiry_id, berlaku
15 menit, sekali pakai; lihat "Pencairan langsung tanpa daftar"). ZePay mengecek biaya
transfer ke bank tujuan — bila bank tidak merespons, pencairan tidak dibuat (503 provider_unavailable, aman
diulang dengan Idempotency-Key yang sama). Biaya transfer dipotong dari saldo di luar amount; nominal + biaya
langsung ditahan. Biaya final ditetapkan setelah transfer selesai: bila lebih kecil, selisihnya dikembalikan; bila
lebih besar, ditanggung ZePay. Di atas payout.approval_threshold status PENDING_APPROVAL sampai disetujui admin ZePay.
Body JSON
| Field | Tipe | Keterangan |
|---|---|---|
bank_account_id | string (uuid) | Rekening terdaftar berstatus |
inquiry_id | string (uuid) | Hasil |
amount | integer | Nominal yang DITERIMA rekening (batas sama dengan (min 1, maks 1000000000) |
withdraw_all | boolean | Cairkan seluruh saldo yang bisa dicairkan, dihitung dengan biaya transfer saat pencairan dibuat — saldo tersedia habis tepat bila tidak dibatasi limit harian/maksimum bank. (default false) |
note | string | Dikirim sebagai keterangan transfer; tampil di mutasi penerima bila didukung bank. (panjang maks 50) |
Respons
- 201
Dibuat. Status
PROCESSING, atauPENDING_APPROVALbila di atas ambang persetujuan.Field respons (21)
Field Tipe Keterangan idwajibstring (uuid) referencewajibstring Referensi publik pencairan.
statuswajibstring PENDING_APPROVALmenunggu admin ZePay ·PROCESSINGsedang dikirim ke bank tujuan ·SUCCESSditerima rekening ·FAILEDgagal ·CANCELLEDdibatalkan merchant ·REJECTEDditolak admin.PENDING_APPROVALPROCESSINGSUCCESSFAILEDCANCELLEDREJECTEDamountwajibinteger Nominal (porsi merchant ini) yang dikirim ke rekening tujuan.
(min 0)
feewajibinteger Biaya transfer yang dibebankan ke saldo merchant ini. Sebelum
SUCCESS= biaya yang ditahan (hasil pengecekan ke bank tujuan, batas atas); setelahSUCCESS= biaya final (bila lebih kecil, selisihnya sudah dikembalikan). Pada pencairan gabungan = porsi biaya merchant ini; 0 bila ditanggung ZePay.(min 0)
total_debitedwajibinteger amount + fee. Kembali utuh ke saldo tersedia bila FAILED/CANCELLED/REJECTED.
(min 0)
combinedwajibboolean true bila pencairan gabungan dari beberapa merchant yang dibuat admin.
destinationwajibPayoutDestinationMerchantAccount | PayoutDestinationInquiryAccount | PayoutDestinationZepayAccount destination.type"zepay_account" destination.bank_account_idnull destination.bank_codestring destination.bank_namestring destination.account_typestring bankewalletdestination.account_namestring destination.account_number_maskedstring destination.inquiry_idstring (uuid) notewajibstring | null failure_reasonwajibstring | null Alasan bila FAILED / CANCELLED / REJECTED.
livemodewajibboolean created_atwajibstring (date-time) completed_atwajibstring (date-time) | null - 400
invalid_request— body/parameter tidak valid: field wajib kosong, tipe/format salah, field tidak dikenal, nilai di luar batas, JSON rusak, body > 64 KB, atau headerIdempotency-Keytidak ada/salah format. Rincian validasi didetails.errors. - 401
authentication_failed,signature_invalid, atautimestamp_out_of_range— kunci, tanda tangan, atau jam tidak valid - 403
permission_denied— kunci tidak punya scope yang dibutuhkan operasi ini (termasukinquiry_idtanpa izinpayouts:any_destination), atau IP pemanggil tidak ada di daftar IP kunci - 404
bank_account_not_found— rekening tidak terdaftar untuk merchant ini; atauinquiry_not_found— hasil cek rekening tidak ditemukan (bukan milik merchant/mode ini) - 409
idempotency_conflict—Idempotency-Keysudah dipakai untuk request berbeda, atau request pertama masih diproses - 422
Pencairan tidak bisa dibuat:
bank_account_not_verified,insufficient_balance(details:amount,fee,total_debited,available; tanpaamount/total_debitedbilawithdraw_all),payout_limit_exceeded,amount_below_minimum,amount_above_maximum,merchant_inactive,bank_unavailable, atauaccount_not_found(rekening tujuan tidak lagi ditemukan saat pengecekan biaya ke bank tujuan, atau hasil cekinquiry_idbukan rekening valid). Denganinquiry_id:inquiry_expired(lewat 15 menit) atauinquiry_used(sudah dipakai) — cek ulang rekening. - 429
rate_limited— terlalu banyak request; ulangi setelahRetry-Afterdetik - 503
provider_unavailable— bank atau kanal pembayaran sedang tidak merespons; coba lagi. Cek rekening: ulangi cek. Pencairan: tidak dibuat (aman diulang denganIdempotency-Keyyang sama). Tagihan: tagihan ditutupFAILED(details.payment_id,details.reference) — buat tagihan baru.
Contoh request
curl -X POST 'https://payment.envfri.com/api/v2/payouts' \
-H 'X-ZePay-Key-Id: zpk_test_…' \
-H 'X-ZePay-Timestamp: 1793446912' \
-H 'X-ZePay-Signature: <hex HMAC-SHA256>' \
-H 'Idempotency-Key: 0b9f3c1e-7a52-4d0e-9f4e-2c1d8a6b3e70' \
-H 'Content-Type: application/json' \
-d '{"bank_account_id":"6f1c2a0e-4b7d-4c1a-9a51-2f0b3e7c9d11","amount":100000,"note":"Tarik mingguan"}'{
"bank_account_id": "6f1c2a0e-4b7d-4c1a-9a51-2f0b3e7c9d11",
"amount": 100000,
"note": "Tarik mingguan"
}{
"bank_account_id": "6f1c2a0e-4b7d-4c1a-9a51-2f0b3e7c9d11",
"withdraw_all": true
}{
"inquiry_id": "3b8e1f52-7c0a-4d9e-b6a1-5f2c8d4e9a03",
"amount": 100000,
"note": "Bayar vendor"
}Contoh respons 201
{
"id": "9d0f3c1a-2b7e-4f55-8a19-6c3e2d1b0a77",
"reference": "WD-01JBQ9S2ZK7MX4D8E2R5T0WQ3N",
"status": "PROCESSING",
"amount": 100000,
"fee": 2500,
"total_debited": 102500,
"combined": false,
"destination": {
"type": "merchant_account",
"bank_account_id": "6f1c2a0e-4b7d-4c1a-9a51-2f0b3e7c9d11",
"bank_code": "014",
"bank_name": "BCA",
"account_type": "bank",
"account_name": "TOKO BATIK SEKAR",
"account_number_masked": "•••• 4821"
},
"note": "Tarik mingguan",
"failure_reason": null,
"livemode": true,
"created_at": "2026-10-31T10:43:05+07:00",
"completed_at": null
}Detail pencairan
payouts:readParameter
| Nama | Di | Tipe | Keterangan |
|---|---|---|---|
idwajib | path | string (uuid) | UUID objek. Bukan UUID atau bukan milik merchant/mode ini → |
Respons
- 200
OK
Field respons (21)
Field Tipe Keterangan idwajibstring (uuid) referencewajibstring Referensi publik pencairan.
statuswajibstring PENDING_APPROVALmenunggu admin ZePay ·PROCESSINGsedang dikirim ke bank tujuan ·SUCCESSditerima rekening ·FAILEDgagal ·CANCELLEDdibatalkan merchant ·REJECTEDditolak admin.PENDING_APPROVALPROCESSINGSUCCESSFAILEDCANCELLEDREJECTEDamountwajibinteger Nominal (porsi merchant ini) yang dikirim ke rekening tujuan.
(min 0)
feewajibinteger Biaya transfer yang dibebankan ke saldo merchant ini. Sebelum
SUCCESS= biaya yang ditahan (hasil pengecekan ke bank tujuan, batas atas); setelahSUCCESS= biaya final (bila lebih kecil, selisihnya sudah dikembalikan). Pada pencairan gabungan = porsi biaya merchant ini; 0 bila ditanggung ZePay.(min 0)
total_debitedwajibinteger amount + fee. Kembali utuh ke saldo tersedia bila FAILED/CANCELLED/REJECTED.
(min 0)
combinedwajibboolean true bila pencairan gabungan dari beberapa merchant yang dibuat admin.
destinationwajibPayoutDestinationMerchantAccount | PayoutDestinationInquiryAccount | PayoutDestinationZepayAccount destination.type"zepay_account" destination.bank_account_idnull destination.bank_codestring destination.bank_namestring destination.account_typestring bankewalletdestination.account_namestring destination.account_number_maskedstring destination.inquiry_idstring (uuid) notewajibstring | null failure_reasonwajibstring | null Alasan bila FAILED / CANCELLED / REJECTED.
livemodewajibboolean created_atwajibstring (date-time) completed_atwajibstring (date-time) | null - 401
authentication_failed,signature_invalid, atautimestamp_out_of_range— kunci, tanda tangan, atau jam tidak valid - 403
permission_denied— kunci tidak punya scope yang dibutuhkan operasi ini (termasukinquiry_idtanpa izinpayouts:any_destination), atau IP pemanggil tidak ada di daftar IP kunci - 404
not_found— tidak ditemukan, bukan milik merchant/mode ini, atauidtidak valid - 429
rate_limited— terlalu banyak request; ulangi setelahRetry-Afterdetik
Contoh request
curl -X GET 'https://payment.envfri.com/api/v2/payouts/{id}' \
-H 'X-ZePay-Key-Id: zpk_test_…' \
-H 'X-ZePay-Timestamp: 1793446912' \
-H 'X-ZePay-Signature: <hex HMAC-SHA256>'Contoh respons 200
{
"id": "9d0f3c1a-2b7e-4f55-8a19-6c3e2d1b0a77",
"reference": "WD-01JBQ9S2ZK7MX4D8E2R5T0WQ3N",
"status": "PROCESSING",
"amount": 100000,
"fee": 2500,
"total_debited": 102500,
"combined": false,
"destination": {
"type": "merchant_account",
"bank_account_id": "6f1c2a0e-4b7d-4c1a-9a51-2f0b3e7c9d11",
"bank_code": "014",
"bank_name": "BCA",
"account_type": "bank",
"account_name": "TOKO BATIK SEKAR",
"account_number_masked": "•••• 4821"
},
"note": "Tarik mingguan",
"failure_reason": null,
"livemode": true,
"created_at": "2026-10-31T10:43:05+07:00",
"completed_at": null
}Batalkan pencairan yang masih menunggu persetujuan
payouts:writeWajib Idempotency-KeyHanya untuk pencairan PENDING_APPROVAL yang dibuat lewat API. Body tidak diperlukan. Webhook payout.cancelled dikirim.
Parameter
| Nama | Di | Tipe | Keterangan |
|---|---|---|---|
idwajib | path | string (uuid) | UUID objek. Bukan UUID atau bukan milik merchant/mode ini → |
Respons
- 200
Dibatalkan (
CANCELLED); nominal dan biaya kembali ke saldo tersediaField respons (21)
Field Tipe Keterangan idwajibstring (uuid) referencewajibstring Referensi publik pencairan.
statuswajibstring PENDING_APPROVALmenunggu admin ZePay ·PROCESSINGsedang dikirim ke bank tujuan ·SUCCESSditerima rekening ·FAILEDgagal ·CANCELLEDdibatalkan merchant ·REJECTEDditolak admin.PENDING_APPROVALPROCESSINGSUCCESSFAILEDCANCELLEDREJECTEDamountwajibinteger Nominal (porsi merchant ini) yang dikirim ke rekening tujuan.
(min 0)
feewajibinteger Biaya transfer yang dibebankan ke saldo merchant ini. Sebelum
SUCCESS= biaya yang ditahan (hasil pengecekan ke bank tujuan, batas atas); setelahSUCCESS= biaya final (bila lebih kecil, selisihnya sudah dikembalikan). Pada pencairan gabungan = porsi biaya merchant ini; 0 bila ditanggung ZePay.(min 0)
total_debitedwajibinteger amount + fee. Kembali utuh ke saldo tersedia bila FAILED/CANCELLED/REJECTED.
(min 0)
combinedwajibboolean true bila pencairan gabungan dari beberapa merchant yang dibuat admin.
destinationwajibPayoutDestinationMerchantAccount | PayoutDestinationInquiryAccount | PayoutDestinationZepayAccount destination.type"zepay_account" destination.bank_account_idnull destination.bank_codestring destination.bank_namestring destination.account_typestring bankewalletdestination.account_namestring destination.account_number_maskedstring destination.inquiry_idstring (uuid) notewajibstring | null failure_reasonwajibstring | null Alasan bila FAILED / CANCELLED / REJECTED.
livemodewajibboolean created_atwajibstring (date-time) completed_atwajibstring (date-time) | null - 400
invalid_request— body/parameter tidak valid: field wajib kosong, tipe/format salah, field tidak dikenal, nilai di luar batas, JSON rusak, body > 64 KB, atau headerIdempotency-Keytidak ada/salah format. Rincian validasi didetails.errors. - 401
authentication_failed,signature_invalid, atautimestamp_out_of_range— kunci, tanda tangan, atau jam tidak valid - 403
permission_denied— kunci tidak punya scope yang dibutuhkan operasi ini (termasukinquiry_idtanpa izinpayouts:any_destination), atau IP pemanggil tidak ada di daftar IP kunci - 404
not_found— tidak ditemukan, bukan milik merchant/mode ini, atauidtidak valid - 409
payout_not_cancellable— status bukanPENDING_APPROVALatau pencairan gabungan dari konsol admin; atauidempotency_conflict - 429
rate_limited— terlalu banyak request; ulangi setelahRetry-Afterdetik
Contoh request
curl -X POST 'https://payment.envfri.com/api/v2/payouts/{id}/cancel' \
-H 'X-ZePay-Key-Id: zpk_test_…' \
-H 'X-ZePay-Timestamp: 1793446912' \
-H 'X-ZePay-Signature: <hex HMAC-SHA256>' \
-H 'Idempotency-Key: 0b9f3c1e-7a52-4d0e-9f4e-2c1d8a6b3e70'Contoh respons 200
{
"id": "9d0f3c1a-2b7e-4f55-8a19-6c3e2d1b0a77",
"reference": "WD-01JBQ9S2ZK7MX4D8E2R5T0WQ3N",
"status": "PROCESSING",
"amount": 100000,
"fee": 2500,
"total_debited": 102500,
"combined": false,
"destination": {
"type": "merchant_account",
"bank_account_id": "6f1c2a0e-4b7d-4c1a-9a51-2f0b3e7c9d11",
"bank_code": "014",
"bank_name": "BCA",
"account_type": "bank",
"account_name": "TOKO BATIK SEKAR",
"account_number_masked": "•••• 4821"
},
"note": "Tarik mingguan",
"failure_reason": null,
"livemode": true,
"created_at": "2026-10-31T10:43:05+07:00",
"completed_at": null
}Transaksi
Tagihan pembayaran QRIS, VA, e-wallet, retail, dan payment link — buat, cari, cek status, batalkan, simulasi.
Daftar pembayaran
payments:readParameter
| Nama | Di | Tipe | Keterangan |
|---|---|---|---|
limit | query | integer | (min 1, maks 100, default 20) |
starting_after | query | string | Kursor dari |
status | query | string |
Nilai: |
method | query | string |
Nilai: |
reference | query | string | Cari satu invoice (persis), mis. |
created_from | query | string (date-time) | Sejak (inklusif), ISO 8601, mis. |
created_to | query | string (date-time) | Sampai (eksklusif), ISO 8601. |
Respons
- 200
OK
Field respons (30)
Field Tipe Keterangan has_morewajibboolean next_cursorwajibstring | null iditem terakhir bilahas_more; kirim sebagaistarting_after.datawajibarray<Payment> data[].idwajibstring (uuid) data[].referencewajibstring Referensi publik tagihan (invoice) = prefix merchant + ULID.
data[].statuswajibstring PENDINGmenunggu pembayaran ·SUCCESSdibayar (dana masuk saldo tertunda, lalu tersedia setelah settlement; VA langsung tersedia) ·FAILEDditolak penerbit atau gagal diterbitkan kanal pembayaran ·EXPIREDlewat batas waktu (ditutup worker ±2 menit setelahexpires_at) ·CANCELLEDdibatalkan merchant.EXPIRED/CANCELLEDbisa menjadiSUCCESSbila dana tetap masuk.PENDINGSUCCESSFAILEDEXPIREDCANCELLEDdata[].amountwajibinteger Rupiah, bilangan bulat.
(min 0)
data[].feewajibinteger | null Biaya transaksi kanal pembayaran. Sebelum
SUCCESS: nilai yang ditetapkan saat instrumen (QRIS / nomor VA / tautan / kode bayar) dibuat (nullbila belum ada, mis. payment link yang belum dipilih metodenya). SetelahSUCCESS: biaya final.(min 0)
data[].fee_bearerwajibstring Penanggung biaya transaksi menurut aturan kanal (
GET /merchant→fee_paid_by). Payment link mengikuti kanal yang dipilih pelanggan.merchantcustomerdata[].net_amountwajibinteger | null Yang masuk saldo merchant —
amount − feebilafee_bearer = merchant,amountbilacustomer;nullbilafeebelum diketahui.data[].methodwajibstring | null null= payment link yang belum dipilih metodenya.CARD/CASH_WITHDRAWALhanya pada data hasil migrasi v1.QRISVAEWALLETRETAILCARDCASH_WITHDRAWALdata[].channelwajibstring | null QRIS, kode bank VA (014),DANA,ALFAMART, …data[].checkout_urlwajibstring (uri) | null Halaman checkout ZePay (hanya
LINK).data[].allowed_methodswajibarray<string> | null data[].qr_stringwajibstring | null Isi QRIS dinamis (render sebagai QR).
data[].va_numberwajibstring | null data[].payment_urlwajibstring (uri) | null Tautan pembayaran e-wallet (bukan untuk OVO push).
data[].payment_codewajibstring | null Kode bayar retail (Alfamart/Indomaret).
data[].descriptionwajibstring | null data[].customerwajibobject data[].customer.namestring data[].customer.emailstring data[].customer.phonestring data[].expires_atwajibstring (date-time) | null data[].paid_atwajibstring (date-time) | null data[].settled_atwajibstring (date-time) | null Terisi setelah settlement (dana pindah ke saldo tersedia).
data[].failure_reasonwajibstring | null Alasan bila EXPIRED / FAILED / CANCELLED
data[].metadatawajibobject data[].livemodewajibboolean data[].created_atwajibstring (date-time) - 400
invalid_request— body/parameter tidak valid: field wajib kosong, tipe/format salah, field tidak dikenal, nilai di luar batas, JSON rusak, body > 64 KB, atau headerIdempotency-Keytidak ada/salah format. Rincian validasi didetails.errors. - 401
authentication_failed,signature_invalid, atautimestamp_out_of_range— kunci, tanda tangan, atau jam tidak valid - 403
permission_denied— kunci tidak punya scope yang dibutuhkan operasi ini (termasukinquiry_idtanpa izinpayouts:any_destination), atau IP pemanggil tidak ada di daftar IP kunci - 429
rate_limited— terlalu banyak request; ulangi setelahRetry-Afterdetik
Contoh request
curl -X GET 'https://payment.envfri.com/api/v2/payments' \
-H 'X-ZePay-Key-Id: zpk_test_…' \
-H 'X-ZePay-Timestamp: 1793446912' \
-H 'X-ZePay-Signature: <hex HMAC-SHA256>'Contoh respons 200
{
"has_more": true,
"next_cursor": "string",
"data": [
{
"id": "6f1c2a0e-4b7d-4c1a-9a51-2f0b3e7c9d11",
"reference": "SKR-01JBQ7X4M2RKD8T5W9C3HZPN6E",
"status": "PENDING",
"amount": 0,
"fee": 0,
"fee_bearer": "merchant",
"net_amount": 0,
"method": "QRIS",
"channel": "string",
"checkout_url": "https://toko.example/kembali",
"allowed_methods": [
"QRIS"
],
"qr_string": "string",
"va_number": "string",
"payment_url": "https://toko.example/kembali",
"payment_code": "string",
"description": "string",
"customer": {
"name": "string",
"email": "string",
"phone": "string"
},
"expires_at": "2026-10-31T10:42:05+07:00",
"paid_at": "2026-10-31T10:42:05+07:00",
"settled_at": "2026-10-31T10:42:05+07:00",
"failure_reason": "Tagihan kedaluwarsa",
"metadata": {},
"livemode": true,
"created_at": "2026-10-31T10:42:05+07:00"
}
]
}Buat tagihan
payments:writeWajib Idempotency-Keymethod = LINK membuat halaman checkout ZePay (pelanggan memilih metode dari allowed_methods yang aktif dan
menerima nominal itu). Metode lain langsung menerbitkan QRIS / nomor VA / tautan e-wallet / kode retail di kanal
pembayaran; fee di respons = biaya transaksi kanal itu. Bila kanal pembayaran menolak atau tidak merespons,
tagihan langsung ditutup FAILED dan details.payment_id / details.reference menunjuk tagihan itu
(422 payment_method_unavailable dengan details.reason_code, atau 503 provider_unavailable).
Body JSON
| Field | Tipe | Keterangan |
|---|---|---|
amountwajib | integer | Nominal tagihan. Batas kanal ( (min 1000, maks 100000000) |
methodwajib | string | LINKQRISVAEWALLETRETAIL |
channel | string | Wajib untuk VA, EWALLET, RETAIL (QRIS: abaikan). VA: kode bank ( (panjang min 2, panjang maks 20) |
allowed_methods | array<string> | Hanya untuk (min 1 item, maks 4 item) |
expires_in_minutes | integer | Bawaan (min 5, maks 10080) |
description | string | (panjang maks 140) |
customer | object | |
customer.name | string | (panjang maks 60) |
customer.email | string (email) | (panjang maks 100) |
customer.phone | string | Pola: |
redirect_url_success | string (uri) | Bawaan dari pengaturan merchant. Pola: (panjang maks 500) |
redirect_url_failed | string (uri) | Bawaan dari pengaturan merchant. Pola: (panjang maks 500) |
metadata | object | Dikembalikan di objek Payment & event pembayaran, mis. |
Respons
- 201
Dibuat
Field respons (27)
Field Tipe Keterangan idwajibstring (uuid) referencewajibstring Referensi publik tagihan (invoice) = prefix merchant + ULID.
statuswajibstring PENDINGmenunggu pembayaran ·SUCCESSdibayar (dana masuk saldo tertunda, lalu tersedia setelah settlement; VA langsung tersedia) ·FAILEDditolak penerbit atau gagal diterbitkan kanal pembayaran ·EXPIREDlewat batas waktu (ditutup worker ±2 menit setelahexpires_at) ·CANCELLEDdibatalkan merchant.EXPIRED/CANCELLEDbisa menjadiSUCCESSbila dana tetap masuk.PENDINGSUCCESSFAILEDEXPIREDCANCELLEDamountwajibinteger Rupiah, bilangan bulat.
(min 0)
feewajibinteger | null Biaya transaksi kanal pembayaran. Sebelum
SUCCESS: nilai yang ditetapkan saat instrumen (QRIS / nomor VA / tautan / kode bayar) dibuat (nullbila belum ada, mis. payment link yang belum dipilih metodenya). SetelahSUCCESS: biaya final.(min 0)
fee_bearerwajibstring Penanggung biaya transaksi menurut aturan kanal (
GET /merchant→fee_paid_by). Payment link mengikuti kanal yang dipilih pelanggan.merchantcustomernet_amountwajibinteger | null Yang masuk saldo merchant —
amount − feebilafee_bearer = merchant,amountbilacustomer;nullbilafeebelum diketahui.methodwajibstring | null null= payment link yang belum dipilih metodenya.CARD/CASH_WITHDRAWALhanya pada data hasil migrasi v1.QRISVAEWALLETRETAILCARDCASH_WITHDRAWALchannelwajibstring | null QRIS, kode bank VA (014),DANA,ALFAMART, …checkout_urlwajibstring (uri) | null Halaman checkout ZePay (hanya
LINK).allowed_methodswajibarray<string> | null qr_stringwajibstring | null Isi QRIS dinamis (render sebagai QR).
va_numberwajibstring | null payment_urlwajibstring (uri) | null Tautan pembayaran e-wallet (bukan untuk OVO push).
payment_codewajibstring | null Kode bayar retail (Alfamart/Indomaret).
descriptionwajibstring | null customerwajibobject customer.namestring customer.emailstring customer.phonestring expires_atwajibstring (date-time) | null paid_atwajibstring (date-time) | null settled_atwajibstring (date-time) | null Terisi setelah settlement (dana pindah ke saldo tersedia).
failure_reasonwajibstring | null Alasan bila EXPIRED / FAILED / CANCELLED
metadatawajibobject livemodewajibboolean created_atwajibstring (date-time) - 400
invalid_request— body/parameter tidak valid: field wajib kosong, tipe/format salah, field tidak dikenal, nilai di luar batas, JSON rusak, body > 64 KB, atau headerIdempotency-Keytidak ada/salah format. Rincian validasi didetails.errors. - 401
authentication_failed,signature_invalid, atautimestamp_out_of_range— kunci, tanda tangan, atau jam tidak valid - 403
permission_denied— kunci tidak punya scope yang dibutuhkan operasi ini (termasukinquiry_idtanpa izinpayouts:any_destination), atau IP pemanggil tidak ada di daftar IP kunci - 409
idempotency_conflict—Idempotency-Keysudah dipakai untuk request berbeda, atau request pertama masih diproses - 422
Tagihan tidak bisa dibuat:
merchant_inactive;payment_method_unavailable(kanal tidak ada/nonaktif, tidak ada metode aktif yang menerima nominal itu untukLINK—details.allowed_methods, atau kanal pembayaran menolak membuat tagihan — tagihan ditutupFAILED,details.payment_id,reference,reason_code);amount_below_minimum(details.min_amount) atauamount_above_maximum(details.max_amount) menurut batas kanal. - 429
rate_limited— terlalu banyak request; ulangi setelahRetry-Afterdetik - 503
provider_unavailable— bank atau kanal pembayaran sedang tidak merespons; coba lagi. Cek rekening: ulangi cek. Pencairan: tidak dibuat (aman diulang denganIdempotency-Keyyang sama). Tagihan: tagihan ditutupFAILED(details.payment_id,details.reference) — buat tagihan baru.
Contoh request
curl -X POST 'https://payment.envfri.com/api/v2/payments' \
-H 'X-ZePay-Key-Id: zpk_test_…' \
-H 'X-ZePay-Timestamp: 1793446912' \
-H 'X-ZePay-Signature: <hex HMAC-SHA256>' \
-H 'Idempotency-Key: 0b9f3c1e-7a52-4d0e-9f4e-2c1d8a6b3e70' \
-H 'Content-Type: application/json' \
-d '{"amount":250000,"method":"QRIS","description":"Pesanan #1042","metadata":{"order_id":"1042"}}'{
"amount": 250000,
"method": "QRIS",
"description": "Pesanan #1042",
"metadata": {
"order_id": "1042"
}
}{
"amount": 150000,
"method": "VA",
"channel": "BCA",
"customer": {
"name": "Budi Santoso",
"phone": "081234567788"
},
"metadata": {
"order_id": "1043"
}
}{
"amount": 50000,
"method": "LINK",
"allowed_methods": [
"QRIS",
"VA"
],
"expires_in_minutes": 1440,
"redirect_url_success": "https://tokobatik.id/terima-kasih"
}Contoh respons 201
{
"id": "2c6e1f0a-7d3b-4a9e-8f21-5b0c9d4e7a13",
"reference": "SKR-01JBQ7X4M2RKD8T5W9C3HZPN6E",
"status": "PENDING",
"amount": 250000,
"fee": 1750,
"fee_bearer": "merchant",
"net_amount": 248250,
"method": "QRIS",
"channel": "QRIS",
"checkout_url": null,
"allowed_methods": null,
"qr_string": "00020101021226650013ID.CO.QRIS.WWW…6304A1B2",
"va_number": null,
"payment_url": null,
"payment_code": null,
"description": "Pesanan #1042",
"customer": {},
"expires_at": "2026-11-01T10:30:00+07:00",
"paid_at": null,
"settled_at": null,
"failure_reason": null,
"metadata": {
"order_id": "1042"
},
"livemode": true,
"created_at": "2026-10-31T10:30:00+07:00"
}{
"id": "7e3b9d21-4c5a-4f80-9b12-3d6e8a0c1f45",
"reference": "SKR-01JBQ7Z9H4N2C5V8D1F6K3M0QT",
"status": "PENDING",
"amount": 50000,
"fee": null,
"fee_bearer": "merchant",
"net_amount": null,
"method": null,
"channel": null,
"checkout_url": "https://payment.envfri.com/pay/EFoxFkkUEsaVYrGkSEa2eBel",
"allowed_methods": [
"QRIS",
"VA"
],
"qr_string": null,
"va_number": null,
"payment_url": null,
"payment_code": null,
"description": null,
"customer": {},
"expires_at": "2026-11-01T10:31:00+07:00",
"paid_at": null,
"settled_at": null,
"failure_reason": null,
"metadata": {},
"livemode": true,
"created_at": "2026-10-31T10:31:00+07:00"
}Detail pembayaran
payments:readCadangan bila webhook terlambat. fee final setelah SUCCESS; settled_at terisi setelah settlement.
Parameter
| Nama | Di | Tipe | Keterangan |
|---|---|---|---|
idwajib | path | string (uuid) | UUID objek. Bukan UUID atau bukan milik merchant/mode ini → |
Respons
- 200
OK
Field respons (27)
Field Tipe Keterangan idwajibstring (uuid) referencewajibstring Referensi publik tagihan (invoice) = prefix merchant + ULID.
statuswajibstring PENDINGmenunggu pembayaran ·SUCCESSdibayar (dana masuk saldo tertunda, lalu tersedia setelah settlement; VA langsung tersedia) ·FAILEDditolak penerbit atau gagal diterbitkan kanal pembayaran ·EXPIREDlewat batas waktu (ditutup worker ±2 menit setelahexpires_at) ·CANCELLEDdibatalkan merchant.EXPIRED/CANCELLEDbisa menjadiSUCCESSbila dana tetap masuk.PENDINGSUCCESSFAILEDEXPIREDCANCELLEDamountwajibinteger Rupiah, bilangan bulat.
(min 0)
feewajibinteger | null Biaya transaksi kanal pembayaran. Sebelum
SUCCESS: nilai yang ditetapkan saat instrumen (QRIS / nomor VA / tautan / kode bayar) dibuat (nullbila belum ada, mis. payment link yang belum dipilih metodenya). SetelahSUCCESS: biaya final.(min 0)
fee_bearerwajibstring Penanggung biaya transaksi menurut aturan kanal (
GET /merchant→fee_paid_by). Payment link mengikuti kanal yang dipilih pelanggan.merchantcustomernet_amountwajibinteger | null Yang masuk saldo merchant —
amount − feebilafee_bearer = merchant,amountbilacustomer;nullbilafeebelum diketahui.methodwajibstring | null null= payment link yang belum dipilih metodenya.CARD/CASH_WITHDRAWALhanya pada data hasil migrasi v1.QRISVAEWALLETRETAILCARDCASH_WITHDRAWALchannelwajibstring | null QRIS, kode bank VA (014),DANA,ALFAMART, …checkout_urlwajibstring (uri) | null Halaman checkout ZePay (hanya
LINK).allowed_methodswajibarray<string> | null qr_stringwajibstring | null Isi QRIS dinamis (render sebagai QR).
va_numberwajibstring | null payment_urlwajibstring (uri) | null Tautan pembayaran e-wallet (bukan untuk OVO push).
payment_codewajibstring | null Kode bayar retail (Alfamart/Indomaret).
descriptionwajibstring | null customerwajibobject customer.namestring customer.emailstring customer.phonestring expires_atwajibstring (date-time) | null paid_atwajibstring (date-time) | null settled_atwajibstring (date-time) | null Terisi setelah settlement (dana pindah ke saldo tersedia).
failure_reasonwajibstring | null Alasan bila EXPIRED / FAILED / CANCELLED
metadatawajibobject livemodewajibboolean created_atwajibstring (date-time) - 401
authentication_failed,signature_invalid, atautimestamp_out_of_range— kunci, tanda tangan, atau jam tidak valid - 403
permission_denied— kunci tidak punya scope yang dibutuhkan operasi ini (termasukinquiry_idtanpa izinpayouts:any_destination), atau IP pemanggil tidak ada di daftar IP kunci - 404
not_found— tidak ditemukan, bukan milik merchant/mode ini, atauidtidak valid - 429
rate_limited— terlalu banyak request; ulangi setelahRetry-Afterdetik
Contoh request
curl -X GET 'https://payment.envfri.com/api/v2/payments/{id}' \
-H 'X-ZePay-Key-Id: zpk_test_…' \
-H 'X-ZePay-Timestamp: 1793446912' \
-H 'X-ZePay-Signature: <hex HMAC-SHA256>'Contoh respons 200
{
"id": "6f1c2a0e-4b7d-4c1a-9a51-2f0b3e7c9d11",
"reference": "SKR-01JBQ7X4M2RKD8T5W9C3HZPN6E",
"status": "PENDING",
"amount": 0,
"fee": 0,
"fee_bearer": "merchant",
"net_amount": 0,
"method": "QRIS",
"channel": "string",
"checkout_url": "https://toko.example/kembali",
"allowed_methods": [
"QRIS"
],
"qr_string": "string",
"va_number": "string",
"payment_url": "https://toko.example/kembali",
"payment_code": "string",
"description": "string",
"customer": {
"name": "string",
"email": "string",
"phone": "string"
},
"expires_at": "2026-10-31T10:42:05+07:00",
"paid_at": "2026-10-31T10:42:05+07:00",
"settled_at": "2026-10-31T10:42:05+07:00",
"failure_reason": "Tagihan kedaluwarsa",
"metadata": {},
"livemode": true,
"created_at": "2026-10-31T10:42:05+07:00"
}Batalkan tagihan yang belum dibayar
payments:writeWajib Idempotency-KeyHanya untuk status PENDING (tagihan yang sudah CANCELLED dikembalikan apa adanya). Webhook payment.cancelled
dikirim. Bila dana ternyata tetap masuk (mis. VA dibayar sebelum nomor VA ditutup bank), pembayaran tetap dicatat
SUCCESS dan payment.succeeded dikirim — dana tidak hilang.
Parameter
| Nama | Di | Tipe | Keterangan |
|---|---|---|---|
idwajib | path | string (uuid) | UUID objek. Bukan UUID atau bukan milik merchant/mode ini → |
Body JSON (opsional)
| Field | Tipe | Keterangan |
|---|---|---|
reason | string | Disimpan di (panjang maks 140) |
Respons
- 200
Dibatalkan
Field respons (27)
Field Tipe Keterangan idwajibstring (uuid) referencewajibstring Referensi publik tagihan (invoice) = prefix merchant + ULID.
statuswajibstring PENDINGmenunggu pembayaran ·SUCCESSdibayar (dana masuk saldo tertunda, lalu tersedia setelah settlement; VA langsung tersedia) ·FAILEDditolak penerbit atau gagal diterbitkan kanal pembayaran ·EXPIREDlewat batas waktu (ditutup worker ±2 menit setelahexpires_at) ·CANCELLEDdibatalkan merchant.EXPIRED/CANCELLEDbisa menjadiSUCCESSbila dana tetap masuk.PENDINGSUCCESSFAILEDEXPIREDCANCELLEDamountwajibinteger Rupiah, bilangan bulat.
(min 0)
feewajibinteger | null Biaya transaksi kanal pembayaran. Sebelum
SUCCESS: nilai yang ditetapkan saat instrumen (QRIS / nomor VA / tautan / kode bayar) dibuat (nullbila belum ada, mis. payment link yang belum dipilih metodenya). SetelahSUCCESS: biaya final.(min 0)
fee_bearerwajibstring Penanggung biaya transaksi menurut aturan kanal (
GET /merchant→fee_paid_by). Payment link mengikuti kanal yang dipilih pelanggan.merchantcustomernet_amountwajibinteger | null Yang masuk saldo merchant —
amount − feebilafee_bearer = merchant,amountbilacustomer;nullbilafeebelum diketahui.methodwajibstring | null null= payment link yang belum dipilih metodenya.CARD/CASH_WITHDRAWALhanya pada data hasil migrasi v1.QRISVAEWALLETRETAILCARDCASH_WITHDRAWALchannelwajibstring | null QRIS, kode bank VA (014),DANA,ALFAMART, …checkout_urlwajibstring (uri) | null Halaman checkout ZePay (hanya
LINK).allowed_methodswajibarray<string> | null qr_stringwajibstring | null Isi QRIS dinamis (render sebagai QR).
va_numberwajibstring | null payment_urlwajibstring (uri) | null Tautan pembayaran e-wallet (bukan untuk OVO push).
payment_codewajibstring | null Kode bayar retail (Alfamart/Indomaret).
descriptionwajibstring | null customerwajibobject customer.namestring customer.emailstring customer.phonestring expires_atwajibstring (date-time) | null paid_atwajibstring (date-time) | null settled_atwajibstring (date-time) | null Terisi setelah settlement (dana pindah ke saldo tersedia).
failure_reasonwajibstring | null Alasan bila EXPIRED / FAILED / CANCELLED
metadatawajibobject livemodewajibboolean created_atwajibstring (date-time) - 400
invalid_request— body/parameter tidak valid: field wajib kosong, tipe/format salah, field tidak dikenal, nilai di luar batas, JSON rusak, body > 64 KB, atau headerIdempotency-Keytidak ada/salah format. Rincian validasi didetails.errors. - 401
authentication_failed,signature_invalid, atautimestamp_out_of_range— kunci, tanda tangan, atau jam tidak valid - 403
permission_denied— kunci tidak punya scope yang dibutuhkan operasi ini (termasukinquiry_idtanpa izinpayouts:any_destination), atau IP pemanggil tidak ada di daftar IP kunci - 404
not_found— tidak ditemukan, bukan milik merchant/mode ini, atauidtidak valid - 409
payment_not_cancellable— status sudah bukanPENDING; atauidempotency_conflict - 429
rate_limited— terlalu banyak request; ulangi setelahRetry-Afterdetik
Contoh request
curl -X POST 'https://payment.envfri.com/api/v2/payments/{id}/cancel' \
-H 'X-ZePay-Key-Id: zpk_test_…' \
-H 'X-ZePay-Timestamp: 1793446912' \
-H 'X-ZePay-Signature: <hex HMAC-SHA256>' \
-H 'Idempotency-Key: 0b9f3c1e-7a52-4d0e-9f4e-2c1d8a6b3e70' \
-H 'Content-Type: application/json' \
-d '{"reason":"Pesanan dibatalkan pembeli"}'Contoh respons 200
{
"id": "6f1c2a0e-4b7d-4c1a-9a51-2f0b3e7c9d11",
"reference": "SKR-01JBQ7X4M2RKD8T5W9C3HZPN6E",
"status": "PENDING",
"amount": 0,
"fee": 0,
"fee_bearer": "merchant",
"net_amount": 0,
"method": "QRIS",
"channel": "string",
"checkout_url": "https://toko.example/kembali",
"allowed_methods": [
"QRIS"
],
"qr_string": "string",
"va_number": "string",
"payment_url": "https://toko.example/kembali",
"payment_code": "string",
"description": "string",
"customer": {
"name": "string",
"email": "string",
"phone": "string"
},
"expires_at": "2026-10-31T10:42:05+07:00",
"paid_at": "2026-10-31T10:42:05+07:00",
"settled_at": "2026-10-31T10:42:05+07:00",
"failure_reason": "Tagihan kedaluwarsa",
"metadata": {},
"livemode": true,
"created_at": "2026-10-31T10:42:05+07:00"
}Simulasikan hasil pembayaran (mode uji)
payments:writeHanya untuk kunci zpk_test_… — kunci live ditolak 403 permission_denied. Meniru hasil pembayaran dari kanal
pembayaran untuk instrumen terakhir tagihan ini, lalu mengembalikan objek Payment terbaru:
SUCCESS→ statusSUCCESS,feefinal = biaya simulasi, dana masuk saldo tertunda, webhookpayment.succeeded. Settlement disimulasikan ±1 menit kemudian (settled_at,balance.settled, tertunda → tersedia). Tagihan yang sudahSUCCESStidak berubah; tagihanEXPIRED/CANCELLEDmenjadiSUCCESS(meniru dana yang tetap masuk).FAILED/EXPIRED→ tagihan langsung ditutup dengan status itu (payment.failed/payment.expired). Pada payment link, hanya percobaan metode itu yang gagal; tagihan tetapPENDINGsehingga pelanggan bisa memilih metode lain.
Payment link yang belum dipilih metodenya di halaman checkout belum punya instrumen → 400 invalid_request.
Tidak memakai Idempotency-Key.
Parameter
| Nama | Di | Tipe | Keterangan |
|---|---|---|---|
idwajib | path | string (uuid) | UUID objek. Bukan UUID atau bukan milik merchant/mode ini → |
Body JSON (opsional)
| Field | Tipe | Keterangan |
|---|---|---|
status | string | (default "SUCCESS") SUCCESSFAILEDEXPIRED |
Respons
- 200
Hasil simulasi diterapkan
Field respons (27)
Field Tipe Keterangan idwajibstring (uuid) referencewajibstring Referensi publik tagihan (invoice) = prefix merchant + ULID.
statuswajibstring PENDINGmenunggu pembayaran ·SUCCESSdibayar (dana masuk saldo tertunda, lalu tersedia setelah settlement; VA langsung tersedia) ·FAILEDditolak penerbit atau gagal diterbitkan kanal pembayaran ·EXPIREDlewat batas waktu (ditutup worker ±2 menit setelahexpires_at) ·CANCELLEDdibatalkan merchant.EXPIRED/CANCELLEDbisa menjadiSUCCESSbila dana tetap masuk.PENDINGSUCCESSFAILEDEXPIREDCANCELLEDamountwajibinteger Rupiah, bilangan bulat.
(min 0)
feewajibinteger | null Biaya transaksi kanal pembayaran. Sebelum
SUCCESS: nilai yang ditetapkan saat instrumen (QRIS / nomor VA / tautan / kode bayar) dibuat (nullbila belum ada, mis. payment link yang belum dipilih metodenya). SetelahSUCCESS: biaya final.(min 0)
fee_bearerwajibstring Penanggung biaya transaksi menurut aturan kanal (
GET /merchant→fee_paid_by). Payment link mengikuti kanal yang dipilih pelanggan.merchantcustomernet_amountwajibinteger | null Yang masuk saldo merchant —
amount − feebilafee_bearer = merchant,amountbilacustomer;nullbilafeebelum diketahui.methodwajibstring | null null= payment link yang belum dipilih metodenya.CARD/CASH_WITHDRAWALhanya pada data hasil migrasi v1.QRISVAEWALLETRETAILCARDCASH_WITHDRAWALchannelwajibstring | null QRIS, kode bank VA (014),DANA,ALFAMART, …checkout_urlwajibstring (uri) | null Halaman checkout ZePay (hanya
LINK).allowed_methodswajibarray<string> | null qr_stringwajibstring | null Isi QRIS dinamis (render sebagai QR).
va_numberwajibstring | null payment_urlwajibstring (uri) | null Tautan pembayaran e-wallet (bukan untuk OVO push).
payment_codewajibstring | null Kode bayar retail (Alfamart/Indomaret).
descriptionwajibstring | null customerwajibobject customer.namestring customer.emailstring customer.phonestring expires_atwajibstring (date-time) | null paid_atwajibstring (date-time) | null settled_atwajibstring (date-time) | null Terisi setelah settlement (dana pindah ke saldo tersedia).
failure_reasonwajibstring | null Alasan bila EXPIRED / FAILED / CANCELLED
metadatawajibobject livemodewajibboolean created_atwajibstring (date-time) - 400
invalid_request— body/parameter tidak valid: field wajib kosong, tipe/format salah, field tidak dikenal, nilai di luar batas, JSON rusak, body > 64 KB, atau headerIdempotency-Keytidak ada/salah format. Rincian validasi didetails.errors. - 401
authentication_failed,signature_invalid, atautimestamp_out_of_range— kunci, tanda tangan, atau jam tidak valid - 403
permission_denied— dipanggil dengan kunci live, kunci tanpa scopepayments:write, atau IP tidak diizinkan - 404
not_found— tidak ditemukan, bukan milik merchant/mode ini, atauidtidak valid - 429
rate_limited— terlalu banyak request; ulangi setelahRetry-Afterdetik
Contoh request
curl -X POST 'https://payment.envfri.com/api/v2/payments/{id}/simulate' \
-H 'X-ZePay-Key-Id: zpk_test_…' \
-H 'X-ZePay-Timestamp: 1793446912' \
-H 'X-ZePay-Signature: <hex HMAC-SHA256>' \
-H 'Content-Type: application/json' \
-d '{"status":"SUCCESS"}'{
"status": "SUCCESS"
}{
"status": "FAILED"
}Contoh respons 200
{
"id": "4a8c2e6f-1b3d-4f57-9a0c-2e4f6a8c0b13",
"reference": "SKR-01JBQA3C6E9G2J5M8P1R4T7W0Y",
"status": "SUCCESS",
"amount": 250000,
"fee": 1750,
"fee_bearer": "merchant",
"net_amount": 248250,
"method": "QRIS",
"channel": "QRIS",
"checkout_url": null,
"allowed_methods": null,
"qr_string": "00020101021226650013ID.ZEPAY.SIM…6304SIMU",
"va_number": null,
"payment_url": null,
"payment_code": null,
"description": null,
"customer": {},
"expires_at": "2026-11-01T10:30:00+07:00",
"paid_at": "2026-10-31T10:31:02+07:00",
"settled_at": null,
"failure_reason": null,
"metadata": {},
"livemode": false,
"created_at": "2026-10-31T10:30:00+07:00"
}Webhook & event
Endpoint webhook milik merchant, rotasi secret, event uji, riwayat event & kirim ulang.
Endpoint webhook milik merchant
webhooks:readSemua endpoint di mode kunci ini (maks. 3), urut waktu dibuat. Tanpa paginasi.
Respons
- 200
OK
Field respons (11)
Field Tipe Keterangan datawajibarray<WebhookEndpoint> data[].idwajibstring (uuid) data[].urlwajibstring (uri) data[].eventswajibarray<string> data[].descriptionwajibstring | null data[].statuswajibstring enableddisableddata[].formatwajibstring v2= event bertanda tangan (lihat skema Event).v1_legacyhanya untuk endpoint hasil migrasi v1 (disetel ZePay, tidak bisa lewat API) selama masa transisi: body lama{partner_reff, status, amount}dan hanya untuk eventpayment.*(event lain dilewati).v2v1_legacydata[].secret_last4wajibstring 4 karakter terakhir secret aktif
data[].previous_secret_expires_atwajibstring (date-time) | null Akhir masa transisi rotasi
data[].livemodewajibboolean data[].created_atwajibstring (date-time) - 401
authentication_failed,signature_invalid, atautimestamp_out_of_range— kunci, tanda tangan, atau jam tidak valid - 403
permission_denied— kunci tidak punya scope yang dibutuhkan operasi ini (termasukinquiry_idtanpa izinpayouts:any_destination), atau IP pemanggil tidak ada di daftar IP kunci - 429
rate_limited— terlalu banyak request; ulangi setelahRetry-Afterdetik
Contoh request
curl -X GET 'https://payment.envfri.com/api/v2/webhook-endpoints' \
-H 'X-ZePay-Key-Id: zpk_test_…' \
-H 'X-ZePay-Timestamp: 1793446912' \
-H 'X-ZePay-Signature: <hex HMAC-SHA256>'Contoh respons 200
{
"data": [
{
"id": "6f1c2a0e-4b7d-4c1a-9a51-2f0b3e7c9d11",
"url": "https://toko.example/kembali",
"events": [
"string"
],
"description": "string",
"status": "enabled",
"format": "v2",
"secret_last4": "string",
"previous_secret_expires_at": "2026-10-31T10:42:05+07:00",
"livemode": true,
"created_at": "2026-10-31T10:42:05+07:00"
}
]
}Tambah endpoint webhook
webhooks:writeWajib Idempotency-KeyMaks. 3 endpoint per mode (live/uji). secret hanya ditampilkan sekali di respons ini (dan pada pengulangan dengan Idempotency-Key yang sama selama 24 jam).
Body JSON
| Field | Tipe | Keterangan |
|---|---|---|
urlwajib | string (uri) | Pola: (panjang maks 500) |
events | array<string> | Nama event persis, pola (min 1 item, maks 20 item, default ["payment.","payout.","balance.","bank_account."]) |
description | string | (panjang maks 100) |
Respons
- 201
Dibuat
Field respons (11)
Field Tipe Keterangan idwajibstring (uuid) urlwajibstring (uri) eventswajibarray<string> descriptionwajibstring | null statuswajibstring enableddisabledformatwajibstring v2= event bertanda tangan (lihat skema Event).v1_legacyhanya untuk endpoint hasil migrasi v1 (disetel ZePay, tidak bisa lewat API) selama masa transisi: body lama{partner_reff, status, amount}dan hanya untuk eventpayment.*(event lain dilewati).v2v1_legacysecret_last4wajibstring 4 karakter terakhir secret aktif
previous_secret_expires_atwajibstring (date-time) | null Akhir masa transisi rotasi
livemodewajibboolean created_atwajibstring (date-time) secretwajibstring Signing secret (
whsec_…). Hanya ditampilkan sekali; simpan di server. - 400
invalid_request— body/parameter tidak valid: field wajib kosong, tipe/format salah, field tidak dikenal, nilai di luar batas, JSON rusak, body > 64 KB, atau headerIdempotency-Keytidak ada/salah format. Rincian validasi didetails.errors. - 401
authentication_failed,signature_invalid, atautimestamp_out_of_range— kunci, tanda tangan, atau jam tidak valid - 403
permission_denied— kunci tidak punya scope yang dibutuhkan operasi ini (termasukinquiry_idtanpa izinpayouts:any_destination), atau IP pemanggil tidak ada di daftar IP kunci - 409
webhook_endpoint_limit— sudah ada 3 endpoint di mode ini; atauidempotency_conflict - 429
rate_limited— terlalu banyak request; ulangi setelahRetry-Afterdetik
Contoh request
curl -X POST 'https://payment.envfri.com/api/v2/webhook-endpoints' \
-H 'X-ZePay-Key-Id: zpk_test_…' \
-H 'X-ZePay-Timestamp: 1793446912' \
-H 'X-ZePay-Signature: <hex HMAC-SHA256>' \
-H 'Idempotency-Key: 0b9f3c1e-7a52-4d0e-9f4e-2c1d8a6b3e70' \
-H 'Content-Type: application/json' \
-d '{"url":"https://tokobatik.id/hooks/zepay","description":"Server produksi"}'Contoh respons 201
{
"id": "6f1c2a0e-4b7d-4c1a-9a51-2f0b3e7c9d11",
"url": "https://toko.example/kembali",
"events": [
"string"
],
"description": "string",
"status": "enabled",
"format": "v2",
"secret_last4": "string",
"previous_secret_expires_at": "2026-10-31T10:42:05+07:00",
"livemode": true,
"created_at": "2026-10-31T10:42:05+07:00",
"secret": "string"
}Detail endpoint webhook
webhooks:readParameter
| Nama | Di | Tipe | Keterangan |
|---|---|---|---|
idwajib | path | string (uuid) | UUID objek. Bukan UUID atau bukan milik merchant/mode ini → |
Respons
- 200
OK
Field respons (10)
Field Tipe Keterangan idwajibstring (uuid) urlwajibstring (uri) eventswajibarray<string> descriptionwajibstring | null statuswajibstring enableddisabledformatwajibstring v2= event bertanda tangan (lihat skema Event).v1_legacyhanya untuk endpoint hasil migrasi v1 (disetel ZePay, tidak bisa lewat API) selama masa transisi: body lama{partner_reff, status, amount}dan hanya untuk eventpayment.*(event lain dilewati).v2v1_legacysecret_last4wajibstring 4 karakter terakhir secret aktif
previous_secret_expires_atwajibstring (date-time) | null Akhir masa transisi rotasi
livemodewajibboolean created_atwajibstring (date-time) - 401
authentication_failed,signature_invalid, atautimestamp_out_of_range— kunci, tanda tangan, atau jam tidak valid - 403
permission_denied— kunci tidak punya scope yang dibutuhkan operasi ini (termasukinquiry_idtanpa izinpayouts:any_destination), atau IP pemanggil tidak ada di daftar IP kunci - 404
not_found— tidak ditemukan, bukan milik merchant/mode ini, atauidtidak valid - 429
rate_limited— terlalu banyak request; ulangi setelahRetry-Afterdetik
Contoh request
curl -X GET 'https://payment.envfri.com/api/v2/webhook-endpoints/{id}' \
-H 'X-ZePay-Key-Id: zpk_test_…' \
-H 'X-ZePay-Timestamp: 1793446912' \
-H 'X-ZePay-Signature: <hex HMAC-SHA256>'Contoh respons 200
{
"id": "6f1c2a0e-4b7d-4c1a-9a51-2f0b3e7c9d11",
"url": "https://toko.example/kembali",
"events": [
"string"
],
"description": "string",
"status": "enabled",
"format": "v2",
"secret_last4": "string",
"previous_secret_expires_at": "2026-10-31T10:42:05+07:00",
"livemode": true,
"created_at": "2026-10-31T10:42:05+07:00"
}Ubah URL, langganan event, deskripsi, atau aktif/nonaktif
webhooks:writeParameter
| Nama | Di | Tipe | Keterangan |
|---|---|---|---|
idwajib | path | string (uuid) | UUID objek. Bukan UUID atau bukan milik merchant/mode ini → |
Body JSON
| Field | Tipe | Keterangan |
|---|---|---|
url | string (uri) | Pola: (panjang maks 500) |
events | array<string> | (min 1 item, maks 20 item) |
description | string | null | (panjang maks 100) |
status | string | enableddisabled |
Respons
- 200
Diperbarui
Field respons (10)
Field Tipe Keterangan idwajibstring (uuid) urlwajibstring (uri) eventswajibarray<string> descriptionwajibstring | null statuswajibstring enableddisabledformatwajibstring v2= event bertanda tangan (lihat skema Event).v1_legacyhanya untuk endpoint hasil migrasi v1 (disetel ZePay, tidak bisa lewat API) selama masa transisi: body lama{partner_reff, status, amount}dan hanya untuk eventpayment.*(event lain dilewati).v2v1_legacysecret_last4wajibstring 4 karakter terakhir secret aktif
previous_secret_expires_atwajibstring (date-time) | null Akhir masa transisi rotasi
livemodewajibboolean created_atwajibstring (date-time) - 400
invalid_request— body/parameter tidak valid: field wajib kosong, tipe/format salah, field tidak dikenal, nilai di luar batas, JSON rusak, body > 64 KB, atau headerIdempotency-Keytidak ada/salah format. Rincian validasi didetails.errors. - 401
authentication_failed,signature_invalid, atautimestamp_out_of_range— kunci, tanda tangan, atau jam tidak valid - 403
permission_denied— kunci tidak punya scope yang dibutuhkan operasi ini (termasukinquiry_idtanpa izinpayouts:any_destination), atau IP pemanggil tidak ada di daftar IP kunci - 404
not_found— tidak ditemukan, bukan milik merchant/mode ini, atauidtidak valid - 429
rate_limited— terlalu banyak request; ulangi setelahRetry-Afterdetik
Contoh request
curl -X PATCH 'https://payment.envfri.com/api/v2/webhook-endpoints/{id}' \
-H 'X-ZePay-Key-Id: zpk_test_…' \
-H 'X-ZePay-Timestamp: 1793446912' \
-H 'X-ZePay-Signature: <hex HMAC-SHA256>' \
-H 'Content-Type: application/json' \
-d '{"status":"disabled"}'{
"status": "disabled"
}{
"events": [
"payment.*",
"payout.*"
]
}Contoh respons 200
{
"id": "6f1c2a0e-4b7d-4c1a-9a51-2f0b3e7c9d11",
"url": "https://toko.example/kembali",
"events": [
"string"
],
"description": "string",
"status": "enabled",
"format": "v2",
"secret_last4": "string",
"previous_secret_expires_at": "2026-10-31T10:42:05+07:00",
"livemode": true,
"created_at": "2026-10-31T10:42:05+07:00"
}Hapus endpoint webhook
webhooks:writePengiriman yang belum terkirim ke endpoint ini ikut dihapus.
Parameter
| Nama | Di | Tipe | Keterangan |
|---|---|---|---|
idwajib | path | string (uuid) | UUID objek. Bukan UUID atau bukan milik merchant/mode ini → |
Respons
- 204
Dihapus (tanpa body)
- 401
authentication_failed,signature_invalid, atautimestamp_out_of_range— kunci, tanda tangan, atau jam tidak valid - 403
permission_denied— kunci tidak punya scope yang dibutuhkan operasi ini (termasukinquiry_idtanpa izinpayouts:any_destination), atau IP pemanggil tidak ada di daftar IP kunci - 404
not_found— tidak ditemukan, bukan milik merchant/mode ini, atauidtidak valid - 429
rate_limited— terlalu banyak request; ulangi setelahRetry-Afterdetik
Contoh request
curl -X DELETE 'https://payment.envfri.com/api/v2/webhook-endpoints/{id}' \
-H 'X-ZePay-Key-Id: zpk_test_…' \
-H 'X-ZePay-Timestamp: 1793446912' \
-H 'X-ZePay-Signature: <hex HMAC-SHA256>'Rotasi signing secret webhook
webhooks:writeWajib Idempotency-KeySecret baru langsung dipakai. Selama masa transisi (previous_secret_valid_hours), setiap event ditandatangani dengan
secret baru dan lama (ZePay-Signature: t=…,v1=<baru>,v1=<lama>), jadi server merchant bisa diperbarui tanpa
kehilangan event. 0 = secret lama langsung tidak dipakai.
Parameter
| Nama | Di | Tipe | Keterangan |
|---|---|---|---|
idwajib | path | string (uuid) | UUID objek. Bukan UUID atau bukan milik merchant/mode ini → |
Body JSON (opsional)
| Field | Tipe | Keterangan |
|---|---|---|
previous_secret_valid_hours | integer | (default 24) 024168 |
Respons
- 200
Secret baru (ditampilkan sekali)
Field respons (11)
Field Tipe Keterangan idwajibstring (uuid) urlwajibstring (uri) eventswajibarray<string> descriptionwajibstring | null statuswajibstring enableddisabledformatwajibstring v2= event bertanda tangan (lihat skema Event).v1_legacyhanya untuk endpoint hasil migrasi v1 (disetel ZePay, tidak bisa lewat API) selama masa transisi: body lama{partner_reff, status, amount}dan hanya untuk eventpayment.*(event lain dilewati).v2v1_legacysecret_last4wajibstring 4 karakter terakhir secret aktif
previous_secret_expires_atwajibstring (date-time) | null Akhir masa transisi rotasi
livemodewajibboolean created_atwajibstring (date-time) secretwajibstring Signing secret (
whsec_…). Hanya ditampilkan sekali; simpan di server. - 400
invalid_request— body/parameter tidak valid: field wajib kosong, tipe/format salah, field tidak dikenal, nilai di luar batas, JSON rusak, body > 64 KB, atau headerIdempotency-Keytidak ada/salah format. Rincian validasi didetails.errors. - 401
authentication_failed,signature_invalid, atautimestamp_out_of_range— kunci, tanda tangan, atau jam tidak valid - 403
permission_denied— kunci tidak punya scope yang dibutuhkan operasi ini (termasukinquiry_idtanpa izinpayouts:any_destination), atau IP pemanggil tidak ada di daftar IP kunci - 404
not_found— tidak ditemukan, bukan milik merchant/mode ini, atauidtidak valid - 409
idempotency_conflict—Idempotency-Keysudah dipakai untuk request berbeda, atau request pertama masih diproses - 429
rate_limited— terlalu banyak request; ulangi setelahRetry-Afterdetik
Contoh request
curl -X POST 'https://payment.envfri.com/api/v2/webhook-endpoints/{id}/rotate-secret' \
-H 'X-ZePay-Key-Id: zpk_test_…' \
-H 'X-ZePay-Timestamp: 1793446912' \
-H 'X-ZePay-Signature: <hex HMAC-SHA256>' \
-H 'Idempotency-Key: 0b9f3c1e-7a52-4d0e-9f4e-2c1d8a6b3e70' \
-H 'Content-Type: application/json' \
-d '{"previous_secret_valid_hours":24}'Contoh respons 200
{
"id": "6f1c2a0e-4b7d-4c1a-9a51-2f0b3e7c9d11",
"url": "https://toko.example/kembali",
"events": [
"string"
],
"description": "string",
"status": "enabled",
"format": "v2",
"secret_last4": "string",
"previous_secret_expires_at": "2026-10-31T10:42:05+07:00",
"livemode": true,
"created_at": "2026-10-31T10:42:05+07:00",
"secret": "string"
}Kirim event uji `webhook.test` ke endpoint ini
webhooks:writeEvent webhook.test dikirim ke endpoint ini saja, juga bila endpoint tidak berlangganan webhook.test. Endpoint
nonaktif → pengiriman langsung dead (last_error: endpoint nonaktif). Tidak memakai Idempotency-Key.
Parameter
| Nama | Di | Tipe | Keterangan |
|---|---|---|---|
idwajib | path | string (uuid) | UUID objek. Bukan UUID atau bukan milik merchant/mode ini → |
Respons
- 202
Dijadwalkan; hasil pengiriman terlihat di
GET /events/{event_id}Field respons (1)
Field Tipe Keterangan event_idwajibstring - 401
authentication_failed,signature_invalid, atautimestamp_out_of_range— kunci, tanda tangan, atau jam tidak valid - 403
permission_denied— kunci tidak punya scope yang dibutuhkan operasi ini (termasukinquiry_idtanpa izinpayouts:any_destination), atau IP pemanggil tidak ada di daftar IP kunci - 404
not_found— tidak ditemukan, bukan milik merchant/mode ini, atauidtidak valid - 429
rate_limited— terlalu banyak request; ulangi setelahRetry-Afterdetik
Contoh request
curl -X POST 'https://payment.envfri.com/api/v2/webhook-endpoints/{id}/test' \
-H 'X-ZePay-Key-Id: zpk_test_…' \
-H 'X-ZePay-Timestamp: 1793446912' \
-H 'X-ZePay-Signature: <hex HMAC-SHA256>'Contoh respons 202
{
"event_id": "evt_9b1d3f5a7c9e4b2d8f0a1c3e5a7b9d2f"
}Riwayat event (untuk mengejar webhook yang terlewat)
webhooks:readEvent 30 hari terakhir, terbaru dulu. Saring webhook yang gagal dengan delivery_status=failed, lalu kirim ulang
dengan POST /events/{id}/resend. Kursor starting_after memakai id event (evt_…).
Parameter
| Nama | Di | Tipe | Keterangan |
|---|---|---|---|
limit | query | integer | (min 1, maks 100, default 20) |
starting_after | query | string | Kursor dari |
type | query | string | Nama persis, pola |
delivery_status | query | string |
Nilai: |
created_from | query | string (date-time) | Sejak (inklusif), ISO 8601, mis. |
created_to | query | string (date-time) | Sampai (eksklusif), ISO 8601. |
Respons
- 200
OK
Field respons (9)
Field Tipe Keterangan has_morewajibboolean next_cursorwajibstring | null iditem terakhir bilahas_more; kirim sebagaistarting_after.datawajibarray<EventSummary> data[].idwajibstring evt_+ 32 heksadesimaldata[].typewajibstring payment.succeededpayment.expiredpayment.failedpayment.cancelledbalance.settledpayout.createdpayout.succeededpayout.failedpayout.cancelledbank_account.createdbank_account.verifiedbank_account.rejectedwebhook.testdata[].createdwajibstring (date-time) data[].livemodewajibboolean data[].datawajibobject data[].delivery_statuswajibstring succeededbila minimal satu endpoint menerima;failedbila semua pengiriman berhenti (dead);no_endpointbila tidak ada endpoint yang berlangganan.pendingsucceededfailedno_endpoint - 400
invalid_request— body/parameter tidak valid: field wajib kosong, tipe/format salah, field tidak dikenal, nilai di luar batas, JSON rusak, body > 64 KB, atau headerIdempotency-Keytidak ada/salah format. Rincian validasi didetails.errors. - 401
authentication_failed,signature_invalid, atautimestamp_out_of_range— kunci, tanda tangan, atau jam tidak valid - 403
permission_denied— kunci tidak punya scope yang dibutuhkan operasi ini (termasukinquiry_idtanpa izinpayouts:any_destination), atau IP pemanggil tidak ada di daftar IP kunci - 429
rate_limited— terlalu banyak request; ulangi setelahRetry-Afterdetik
Contoh request
curl -X GET 'https://payment.envfri.com/api/v2/events' \
-H 'X-ZePay-Key-Id: zpk_test_…' \
-H 'X-ZePay-Timestamp: 1793446912' \
-H 'X-ZePay-Signature: <hex HMAC-SHA256>'Contoh respons 200
{
"has_more": true,
"next_cursor": "string",
"data": [
{
"id": "evt_4f2a9c1e7b3d4e8a9c0b1d2e3f405162",
"type": "payment.succeeded",
"created": "2026-10-31T10:44:12+07:00",
"livemode": true,
"data": {
"payment_id": "2c6e1f0a-7d3b-4a9e-8f21-5b0c9d4e7a13",
"reference": "SKR-01JBQ7X4M2RKD8T5W9C3HZPN6E",
"amount": 250000,
"fee": 1750,
"fee_bearer": "merchant",
"net_amount": 248250,
"method": "QRIS",
"channel": "QRIS",
"paid_at": "2026-10-31T03:44:11.721+00:00",
"metadata": {
"order_id": "1042"
}
}
}
]
}Detail event & percobaan pengirimannya
webhooks:readParameter
| Nama | Di | Tipe | Keterangan |
|---|---|---|---|
idwajib | path | string |
|
Respons
- 200
OK
Field respons (15)
Field Tipe Keterangan idwajibstring evt_+ 32 heksadesimaltypewajibstring payment.succeededpayment.expiredpayment.failedpayment.cancelledbalance.settledpayout.createdpayout.succeededpayout.failedpayout.cancelledbank_account.createdbank_account.verifiedbank_account.rejectedwebhook.testcreatedwajibstring (date-time) livemodewajibboolean datawajibobject delivery_statuswajibstring succeededbila minimal satu endpoint menerima;failedbila semua pengiriman berhenti (dead);no_endpointbila tidak ada endpoint yang berlangganan.pendingsucceededfailedno_endpointdeliverieswajibarray<WebhookDelivery> deliveries[].endpoint_idwajibstring (uuid) deliveries[].attemptwajibinteger Percobaan ke-n (naik setiap gagal).
deliveries[].statuswajibstring pendingmenunggu / dijadwalkan ulang setelah gagal ·succeededditerima (2xx) ·deadberhenti dicoba (±24 jam gagal beruntun atau endpoint nonaktif); kirim ulang denganPOST /events/{id}/resend.pendingsucceededfaileddeaddeliveries[].http_statuswajibinteger | null deliveries[].duration_mswajibinteger | null deliveries[].last_errorwajibstring | null deliveries[].next_attempt_atwajibstring (date-time) | null Hanya saat
pending.deliveries[].created_atwajibstring (date-time) - 401
authentication_failed,signature_invalid, atautimestamp_out_of_range— kunci, tanda tangan, atau jam tidak valid - 403
permission_denied— kunci tidak punya scope yang dibutuhkan operasi ini (termasukinquiry_idtanpa izinpayouts:any_destination), atau IP pemanggil tidak ada di daftar IP kunci - 404
not_found— tidak ditemukan, bukan milik merchant/mode ini, atauidtidak valid - 429
rate_limited— terlalu banyak request; ulangi setelahRetry-Afterdetik
Contoh request
curl -X GET 'https://payment.envfri.com/api/v2/events/{id}' \
-H 'X-ZePay-Key-Id: zpk_test_…' \
-H 'X-ZePay-Timestamp: 1793446912' \
-H 'X-ZePay-Signature: <hex HMAC-SHA256>'Contoh respons 200
{
"id": "evt_4f2a9c1e7b3d4e8a9c0b1d2e3f405162",
"type": "payment.succeeded",
"created": "2026-10-31T10:44:12+07:00",
"livemode": true,
"data": {
"payment_id": "2c6e1f0a-7d3b-4a9e-8f21-5b0c9d4e7a13",
"reference": "SKR-01JBQ7X4M2RKD8T5W9C3HZPN6E",
"amount": 250000,
"fee": 1750,
"fee_bearer": "merchant",
"net_amount": 248250,
"method": "QRIS",
"channel": "QRIS",
"paid_at": "2026-10-31T03:44:11.721+00:00",
"metadata": {
"order_id": "1042"
}
}
}Kirim ulang event ke endpoint aktif yang berlangganan
webhooks:writeMembuat pengiriman baru (percobaan dari awal) ke setiap endpoint aktif yang berlangganan event ini. Tidak memakai Idempotency-Key.
Parameter
| Nama | Di | Tipe | Keterangan |
|---|---|---|---|
idwajib | path | string |
|
Respons
- 202
Dijadwalkan
Field respons (1)
Field Tipe Keterangan scheduledwajibinteger Jumlah endpoint yang akan dikirimi.
- 401
authentication_failed,signature_invalid, atautimestamp_out_of_range— kunci, tanda tangan, atau jam tidak valid - 403
permission_denied— kunci tidak punya scope yang dibutuhkan operasi ini (termasukinquiry_idtanpa izinpayouts:any_destination), atau IP pemanggil tidak ada di daftar IP kunci - 404
not_found— tidak ditemukan, bukan milik merchant/mode ini, atauidtidak valid - 409
no_webhook_endpoint— tidak ada endpoint aktif yang berlangganan event ini - 429
rate_limited— terlalu banyak request; ulangi setelahRetry-Afterdetik
Contoh request
curl -X POST 'https://payment.envfri.com/api/v2/events/{id}/resend' \
-H 'X-ZePay-Key-Id: zpk_test_…' \
-H 'X-ZePay-Timestamp: 1793446912' \
-H 'X-ZePay-Signature: <hex HMAC-SHA256>'Contoh respons 202
{
"scheduled": 0
}