Refund a payment
Last updated:
The refundPayment method reverses a previously settled payment, in full or in part. Refunds are server-to-server only — never expose the secret key to client-side code.
When you can refund
- The payment must be in
paidstatus. Pending or failed payments cannot be refunded. - One refund per payment. After a partial refund is issued, that payment can no longer be refunded again — the remaining amount stays with the customer's order.
- The refund window depends on the payment method (typically 180 days for cards, shorter for some VA and QRIS rails). The cabinet rejects refunds outside the window.
Endpoint
Send a request to https://api.unitpay.net/api. POST over TLS is recommended for production traffic; GET is accepted for compatibility but exposes the secret key in URL logs and proxy buffers — do not use it.
Required parameters
| Parameter | Type | Description |
|---|---|---|
method | string | Always refundPayment. |
params[paymentId] | integer | UnitPay payment identifier returned by initPayment and echoed in callbacks as unitpayId. |
params[secretKey] | string | Project secret key. Send it only over TLS in a POST body, never in a GET query string. |
Optional parameters
| Parameter | Type | Description |
|---|---|---|
params[sum] | number | Refund amount in major currency units. Omit for a full refund. Must be less than or equal to the original payment sum. |
Example request
POST https://api.unitpay.net/api
Content-Type: application/x-www-form-urlencoded
method=refundPayment
¶ms[paymentId]=1234512345
¶ms[secretKey]=<your-project-secret-key>
¶ms[sum]=50000.00
The example above issues a partial refund of IDR 50,000 against payment 1234512345. Omitting params[sum] would refund the full original amount.
Successful response
{
"result": {
"message": "Refund issued."
}
}
UnitPay returns HTTP 200 once the refund is queued. Funds reach the customer within the timeframe of the underlying payment rail (cards: same business day to several business days; QRIS and VA: typically faster). The refund also flows back through your callback handler as a balance adjustment, so you can reconcile by listening to those events.
Error response
{
"error": {
"message": "Invalid secretKey.",
"code": -32000
}
}
Common causes:
| code | Meaning |
|---|---|
-32000 | Invalid secret key. Rotate from Settings → Project → Secret key and update your application secret store. |
-32001 | Payment not found. Verify the paymentId matches a payment on the project that owns the secret key. |
-32002 | Payment not in a refundable state — pending, failed, or already refunded. |
-32003 | Refund amount exceeds the original payment sum or the remaining refundable balance. |
-32004 | Refund window expired for this payment method. |
Treat any error response as a hard failure: do not retry the same request blindly. Investigate, fix the input, and resubmit.
Important restrictions
- One refund per payment. If you issue a partial refund and later need to refund more, that is not supported — size partial refunds carefully.
- No client-side duplicate suppression. Two near-simultaneous partial refunds for the same amount on the same payment are not blocked by the API. Add idempotency on your side: keep the refund request behind a database-locked operation that records "refund attempted" before calling UnitPay, and never retry without first checking the recorded outcome.
- Test mode. Test-mode payments use the test secret key and a separate
refundPaymentsandbox; refunds in test mode never move real funds. Validate your refund flow in test mode before enabling live refunds.
Refunds via the merchant cabinet
Operators can also issue refunds without writing code: open Statistics → Payment details for the payment in the merchant cabinet and click Issue refund. The cabinet wraps the same API call and is the recommended path for one-off, manually-approved refunds (chargebacks, disputed orders, customer-service exceptions).
Next steps
- Create a payment via API — obtain the
paymentIdyou will later refund. - Callback handler — reconcile refund events on your server.
- Refund & Chargeback Policy — merchant-facing policy for refund timing and chargeback handling.
Metode refundPayment membalik pembayaran yang sudah terselesaikan, secara penuh atau sebagian. Pengembalian dana hanya dilakukan server-ke-server — jangan mengekspos kunci rahasia pada kode sisi klien.
Kapan pengembalian dana dapat dilakukan
- Pembayaran harus berstatus
paid. Pembayaran tertunda atau gagal tidak dapat dikembalikan. - Satu pengembalian per pembayaran. Setelah pengembalian sebagian diterbitkan, pembayaran tersebut tidak dapat dikembalikan lagi — sisa jumlahnya tetap pada pesanan pelanggan.
- Jendela pengembalian bergantung pada metode pembayaran (umumnya 180 hari untuk kartu, lebih singkat untuk sebagian rel VA dan QRIS). Kabinet menolak pengembalian di luar jendela tersebut.
Endpoint
Kirim permintaan ke https://api.unitpay.net/api. POST melalui TLS direkomendasikan untuk trafik produksi; GET diterima untuk kompatibilitas tetapi membuka kunci rahasia di log URL dan buffer proxy — jangan gunakan.
Parameter wajib
| Parameter | Tipe | Deskripsi |
|---|---|---|
method | string | Selalu refundPayment. |
params[paymentId] | integer | Identifikasi pembayaran UnitPay yang dikembalikan oleh initPayment dan dikutip dalam callback sebagai unitpayId. |
params[secretKey] | string | Kunci rahasia proyek. Kirim hanya melalui TLS dalam body POST, jangan dalam query string GET. |
Parameter opsional
| Parameter | Tipe | Deskripsi |
|---|---|---|
params[sum] | number | Jumlah pengembalian dalam satuan mata uang utama. Kosongkan untuk pengembalian penuh. Harus kurang dari atau sama dengan jumlah pembayaran asli. |
Contoh permintaan
POST https://api.unitpay.net/api
Content-Type: application/x-www-form-urlencoded
method=refundPayment
¶ms[paymentId]=1234512345
¶ms[secretKey]=<your-project-secret-key>
¶ms[sum]=50000.00
Contoh di atas menerbitkan pengembalian sebagian sebesar IDR 50.000 atas pembayaran 1234512345. Mengosongkan params[sum] akan mengembalikan jumlah asli secara penuh.
Respons sukses
{
"result": {
"message": "Refund issued."
}
}
UnitPay membalas dengan HTTP 200 begitu pengembalian masuk antrian. Dana akan sampai ke pelanggan dalam jangka waktu rel pembayaran yang mendasarinya (kartu: hari kerja yang sama hingga beberapa hari kerja; QRIS dan VA: umumnya lebih cepat). Pengembalian juga mengalir kembali melalui callback handler Anda sebagai penyesuaian saldo, sehingga Anda dapat merekonsiliasi dengan mendengarkan event tersebut.
Respons error
{
"error": {
"message": "Invalid secretKey.",
"code": -32000
}
}
Penyebab umum:
| code | Arti |
|---|---|
-32000 | Kunci rahasia tidak valid. Lakukan rotasi dari Pengaturan → Proyek → Kunci rahasia dan perbarui penyimpanan rahasia aplikasi Anda. |
-32001 | Pembayaran tidak ditemukan. Verifikasi paymentId cocok dengan pembayaran pada proyek yang memiliki kunci rahasia tersebut. |
-32002 | Pembayaran tidak berada dalam status yang dapat dikembalikan — tertunda, gagal, atau sudah dikembalikan. |
-32003 | Jumlah pengembalian melebihi jumlah pembayaran asli atau saldo refundable yang tersisa. |
-32004 | Jendela pengembalian untuk metode pembayaran ini telah berakhir. |
Anggap respons error apa pun sebagai kegagalan keras: jangan mengulang permintaan yang sama tanpa pemeriksaan. Selidiki, perbaiki input, lalu kirim ulang.
Batasan penting
- Satu pengembalian per pembayaran. Jika Anda menerbitkan pengembalian sebagian dan kemudian perlu mengembalikan lebih banyak, itu tidak didukung — tentukan jumlah pengembalian sebagian dengan hati-hati.
- Tidak ada penekanan duplikat sisi klien. Dua pengembalian sebagian yang dikirim hampir bersamaan untuk jumlah yang sama pada pembayaran yang sama tidak diblokir oleh API. Tambahkan idempotensi di sisi Anda: simpan permintaan pengembalian di balik operasi yang dikunci database yang mencatat "refund attempted" sebelum memanggil UnitPay, dan jangan mencoba ulang tanpa memeriksa hasil yang tercatat terlebih dahulu.
- Mode uji. Pembayaran mode uji menggunakan kunci rahasia uji dan sandbox
refundPaymentterpisah; pengembalian dalam mode uji tidak pernah memindahkan dana sungguhan. Validasi alur pengembalian dalam mode uji sebelum mengaktifkan pengembalian live.
Pengembalian melalui kabinet merchant
Operator juga dapat menerbitkan pengembalian tanpa menulis kode: buka Statistik → Detail pembayaran untuk pembayaran tersebut di kabinet merchant dan klik Terbitkan pengembalian. Kabinet membungkus panggilan API yang sama dan merupakan jalur yang disarankan untuk pengembalian sekali pakai yang disetujui manual (chargeback, pesanan yang disengketakan, pengecualian layanan pelanggan).
Langkah selanjutnya
- Membuat pembayaran melalui API — dapatkan
paymentIdyang nanti akan Anda kembalikan. - Penanganan callback — rekonsiliasi event pengembalian pada server Anda.
- Kebijakan Pengembalian Dana & Chargeback — kebijakan menghadap merchant untuk waktu pengembalian dan penanganan chargeback.