Setiap galat dikembalikan dengan bentuk yang sama, apa pun endpointnya. Kode galat bersifat stabil dan
dapat dijadikan dasar percabangan logika; teks message ditujukan
untuk manusia dan dapat berubah sewaktu-waktu.
Bentuk respons galat
HTTP/1.1 422 Unprocessable Entity
Content-Type: application/json
{
"error": {
"code": "VALIDATION_ERROR",
"message": "amount_minor harus bilangan bulat lebih besar dari nol.",
"request_id": "req_01J9Q4M2T7K3XW",
"details": {
"field": "amount_minor",
"value": 0
}
}
}
code — pengenal stabil dalam huruf besar. Gunakan ini di kode kamu.
message — penjelasan singkat untuk manusia.
request_id — identitas permintaan. Selalu catat nilai ini di log kamu; ia yang membuat penelusuran menjadi mungkin.
details — opsional, isinya bergantung pada jenis galat.
Arti status HTTP
Status
Arti
Ulangi?
400
Permintaan cacat — JSON tidak valid atau header hilang.
Tidak
401
Autentikasi gagal.
Tidak
403
Terautentikasi, tetapi tidak berhak atas tindakan itu.
Tidak
404
Objek tidak ada, atau bukan milik merchant kamu.
Tidak
409
Bentrok keadaan — idempotensi atau status objek.
Bersyarat
422
Bentuknya benar, isinya tidak dapat diproses.
Tidak
429
Kuota permintaan habis.
Ya, setelah Retry-After
500
Galat tak terduga di sisi kami.
Ya, dengan jeda menaik
502 / 503 / 504
Layanan hulu atau penyedia sedang tidak tersedia.
Ya, dengan jeda menaik
Ulangi hanya dengan Idempotency-Key yang sama. Percobaan ulang tanpa
kunci yang sama dapat menghasilkan pembayaran ganda.
Autentikasi & otorisasi
Kode
HTTP
Arti & tindakan
UNAUTHORIZED
401
Header Authorization hilang atau salah bentuk.
CLIENT_NOT_FOUND
401
client_id tidak dikenal atau sudah dicabut.
SIGNATURE_MISSING
401
X-Signature atau X-Timestamp tidak dikirim.
SIGNATURE_INVALID
401
Tanda tangan tidak cocok. Periksa apakah kamu menandatangani byte badan yang persis dikirim.
TIMESTAMP_OUT_OF_WINDOW
401
Selisih jam melebihi 300 detik. Sinkronkan jam server (NTP).
SIGNATURE_REPLAYED
401
Tanda tangan yang sama dikirim ulang. Bentuk stempel waktu dan tanda tangan baru.
FORBIDDEN
403
Kredensial tidak memiliki izin untuk tindakan tersebut.
ENVIRONMENT_MISMATCH
403
Kunci sandbox dipakai untuk objek produksi, atau sebaliknya.
MERCHANT_SUSPENDED
403
Akun merchant sedang dibekukan. Hubungi dukungan.
Validasi
Kode
HTTP
Arti & tindakan
MALFORMED_JSON
400
Badan permintaan bukan JSON yang sah.
VALIDATION_ERROR
422
Satu atau lebih bidang tidak memenuhi aturan. Lihat details.field.
INVALID_AMOUNT
422
Nominal bukan bilangan bulat positif, atau melampaui batas per transaksi.
CURRENCY_NOT_SUPPORTED
422
Mata uang belum diaktifkan untuk merchant kamu.
CURRENCY_MISMATCH
422
Mata uang tidak sama dengan objek yang dirujuk.
PAYMENT_METHOD_UNAVAILABLE
422
Metode tersebut tidak aktif untuk merchant atau mata uang ini.
REFERENCE_ID_DUPLICATE
409
reference_id sudah dipakai pembayaran lain.
Idempotensi
Kode
HTTP
Arti & tindakan
IDEMPOTENCY_KEY_REQUIRED
422
Header Idempotency-Key wajib pada endpoint ini.
IDEMPOTENCY_KEY_INVALID
422
Panjang di luar 8–255 karakter atau memuat karakter non-ASCII.
IDEMPOTENCY_CONFLICT
409
Kunci sama dipakai untuk badan permintaan yang berbeda. Jangan diulang; perbaiki pemanggilnya.
IDEMPOTENCY_IN_PROGRESS
409
Permintaan pertama dengan kunci itu masih berjalan. Coba lagi beberapa detik kemudian dengan kunci yang sama.
Objek & keadaan
Kode
HTTP
Arti & tindakan
PAYMENT_NOT_FOUND
404
Pembayaran tidak ada, atau milik merchant lain.
REFUND_NOT_FOUND
404
Refund tidak ada, atau milik merchant lain.
PAYMENT_NOT_CANCELABLE
409
Hanya pembayaran pending yang dapat dibatalkan.
PAYMENT_NOT_REFUNDABLE
409
Pembayaran belum berhasil, sudah kedaluwarsa, atau sedang disengketakan.
REFUND_EXCEEDS_PAYMENT
422
Total refund melampaui nominal asli. Periksa remaining_refundable_minor.
PAYMENT_EXPIRED
409
Masa berlaku pembayaran sudah habis. Buat pembayaran baru.
DISPUTE_LOCKED
409
Dana terkait sedang ditahan karena sengketa berjalan.
Buku besar & dana
Kode berikut berasal dari inti buku besar dan diteruskan apa adanya. Semuanya berarti tidak ada dana
yang bergerak sama sekali — buku besar tidak pernah menyisakan operasi separuh jalan.
Kode
HTTP
Arti & tindakan
INSUFFICIENT_FUNDS
422
Saldo tersedia tidak cukup. Periksa GET /v1/balances; dana mungkin sedang ditahan.
WALLET_NOT_FOUND
404
Dompet tujuan tidak ada.
WALLET_FROZEN
403
Dompet dibekukan; tidak ada operasi yang diterima.
UNBALANCED_ENTRY
500
Galat internal — jurnal tidak seimbang dan ditolak. Laporkan beserta request_id.
Perutean, risiko, dan laju
Kode
HTTP
Arti & tindakan
NO_ROUTE_AVAILABLE
503
Tidak ada penyedia sehat untuk kombinasi metode dan mata uang ini. Coba lagi kemudian.
GATEWAY_ERROR
502
Penyedia menjawab dengan galat. Aman diulang dengan kunci yang sama.
GATEWAY_TIMEOUT
504
Penyedia tidak menjawab tepat waktu. Jangan menyimpulkan gagal — periksa status pembayaran.
RISK_REJECTED
422
Ditolak mesin deteksi penipuan. Alasan rinci tidak dibuka demi keamanan.
RISK_REVIEW
202
Menunggu tinjauan manual. Hasil akhirnya dikirim lewat webhook.
RATE_LIMITED
429
Kuota habis. Tunggu sesuai Retry-After.
INTERNAL_ERROR
500
Galat tak terduga. Ulangi dengan jeda menaik; bila berulang, laporkan request_id.
RETRYABLE = {429, 500, 502, 503, 504}
def create_payment(payload: dict, idempotency_key: str) -> dict:
for attempt in range(5):
res = post("/payments", payload, idempotency_key=idempotency_key)
if res.status_code < 400:
return res.json()
code = res.json().get("error", {}).get("code")
# Bentrok idempotensi karena permintaan pertama masih berjalan:
# tunggu sebentar, jangan ganti kuncinya.
if code == "IDEMPOTENCY_IN_PROGRESS":
time.sleep(2 ** attempt)
continue
if res.status_code in RETRYABLE:
time.sleep(2 ** attempt)
continue
# Galat 4xx lain: mengulang tidak akan mengubah hasil.
raise PaymentError(code, res.json()["error"]["message"],
res.json()["error"]["request_id"])
# Semua percobaan gagal: JANGAN simpulkan pembayaran gagal.
return lookup_by_reference_id(payload["reference_id"])
Selalu catat request_id bersama
reference_id milikmu di log aplikasi. Dua nilai itu cukup untuk
menelusuri satu permintaan sampai ke entri buku besarnya.