NawaPayNawaPay Docs

Kode galat

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

StatusArtiUlangi?
400Permintaan cacat — JSON tidak valid atau header hilang.Tidak
401Autentikasi gagal.Tidak
403Terautentikasi, tetapi tidak berhak atas tindakan itu.Tidak
404Objek tidak ada, atau bukan milik merchant kamu.Tidak
409Bentrok keadaan — idempotensi atau status objek.Bersyarat
422Bentuknya benar, isinya tidak dapat diproses.Tidak
429Kuota permintaan habis.Ya, setelah Retry-After
500Galat tak terduga di sisi kami.Ya, dengan jeda menaik
502 / 503 / 504Layanan 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

KodeHTTPArti & tindakan
UNAUTHORIZED401Header Authorization hilang atau salah bentuk.
CLIENT_NOT_FOUND401client_id tidak dikenal atau sudah dicabut.
SIGNATURE_MISSING401X-Signature atau X-Timestamp tidak dikirim.
SIGNATURE_INVALID401Tanda tangan tidak cocok. Periksa apakah kamu menandatangani byte badan yang persis dikirim.
TIMESTAMP_OUT_OF_WINDOW401Selisih jam melebihi 300 detik. Sinkronkan jam server (NTP).
SIGNATURE_REPLAYED401Tanda tangan yang sama dikirim ulang. Bentuk stempel waktu dan tanda tangan baru.
FORBIDDEN403Kredensial tidak memiliki izin untuk tindakan tersebut.
ENVIRONMENT_MISMATCH403Kunci sandbox dipakai untuk objek produksi, atau sebaliknya.
MERCHANT_SUSPENDED403Akun merchant sedang dibekukan. Hubungi dukungan.

Validasi

KodeHTTPArti & tindakan
MALFORMED_JSON400Badan permintaan bukan JSON yang sah.
VALIDATION_ERROR422Satu atau lebih bidang tidak memenuhi aturan. Lihat details.field.
INVALID_AMOUNT422Nominal bukan bilangan bulat positif, atau melampaui batas per transaksi.
CURRENCY_NOT_SUPPORTED422Mata uang belum diaktifkan untuk merchant kamu.
CURRENCY_MISMATCH422Mata uang tidak sama dengan objek yang dirujuk.
PAYMENT_METHOD_UNAVAILABLE422Metode tersebut tidak aktif untuk merchant atau mata uang ini.
REFERENCE_ID_DUPLICATE409reference_id sudah dipakai pembayaran lain.

Idempotensi

KodeHTTPArti & tindakan
IDEMPOTENCY_KEY_REQUIRED422Header Idempotency-Key wajib pada endpoint ini.
IDEMPOTENCY_KEY_INVALID422Panjang di luar 8–255 karakter atau memuat karakter non-ASCII.
IDEMPOTENCY_CONFLICT409Kunci sama dipakai untuk badan permintaan yang berbeda. Jangan diulang; perbaiki pemanggilnya.
IDEMPOTENCY_IN_PROGRESS409Permintaan pertama dengan kunci itu masih berjalan. Coba lagi beberapa detik kemudian dengan kunci yang sama.

Objek & keadaan

KodeHTTPArti & tindakan
PAYMENT_NOT_FOUND404Pembayaran tidak ada, atau milik merchant lain.
REFUND_NOT_FOUND404Refund tidak ada, atau milik merchant lain.
PAYMENT_NOT_CANCELABLE409Hanya pembayaran pending yang dapat dibatalkan.
PAYMENT_NOT_REFUNDABLE409Pembayaran belum berhasil, sudah kedaluwarsa, atau sedang disengketakan.
REFUND_EXCEEDS_PAYMENT422Total refund melampaui nominal asli. Periksa remaining_refundable_minor.
PAYMENT_EXPIRED409Masa berlaku pembayaran sudah habis. Buat pembayaran baru.
DISPUTE_LOCKED409Dana 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.

KodeHTTPArti & tindakan
INSUFFICIENT_FUNDS422Saldo tersedia tidak cukup. Periksa GET /v1/balances; dana mungkin sedang ditahan.
WALLET_NOT_FOUND404Dompet tujuan tidak ada.
WALLET_FROZEN403Dompet dibekukan; tidak ada operasi yang diterima.
UNBALANCED_ENTRY500Galat internal — jurnal tidak seimbang dan ditolak. Laporkan beserta request_id.

Perutean, risiko, dan laju

KodeHTTPArti & tindakan
NO_ROUTE_AVAILABLE503Tidak ada penyedia sehat untuk kombinasi metode dan mata uang ini. Coba lagi kemudian.
GATEWAY_ERROR502Penyedia menjawab dengan galat. Aman diulang dengan kunci yang sama.
GATEWAY_TIMEOUT504Penyedia tidak menjawab tepat waktu. Jangan menyimpulkan gagal — periksa status pembayaran.
RISK_REJECTED422Ditolak mesin deteksi penipuan. Alasan rinci tidak dibuka demi keamanan.
RISK_REVIEW202Menunggu tinjauan manual. Hasil akhirnya dikirim lewat webhook.
RATE_LIMITED429Kuota habis. Tunggu sesuai Retry-After.
INTERNAL_ERROR500Galat tak terduga. Ulangi dengan jeda menaik; bila berulang, laporkan request_id.
SERVICE_UNAVAILABLE503Layanan sedang dalam pemeliharaan. Lihat status.nawapay.co.id.

Pola penanganan yang disarankan

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.

NawaPay berada dalam tahap pengembangan. Seluruh contoh pada dokumentasi ini merujuk pada lingkungan sandbox; sistem belum berizin sebagai penyelenggara jasa pembayaran dan belum memproses transaksi uang sungguhan.

© 2026 NawaPay · docs.nawapay.co.id