Skip to content

Error Codes

All errors from Gerbang Pay are returned in a consistent JSON format.

Error Response Format

json
{
  "success": false,
  "error": {
    "code": "ERROR_CODE",
    "message": "Human-readable error description"
  }
}

The code field is a constant string that you can use to handle errors programmatically. The message field is descriptive and may change — do not use it for conditionals in your code.


Error Codes Table

HTTP StatusError CodeDescriptionWhen It Occurs
400BAD_REQUESTInvalid requestAmount ≤ 0, currency is not 3 characters, invalid email format
400PAYMENT_METHOD_UNAVAILABLEPayment method not availableMethod is not configured in the tenant's routing rules, or not supported by the provider
401UNAUTHORIZEDAuthentication failedInvalid API key, expired or invalid JWT, missing Authorization header
403FORBIDDENAccess deniedTenant is not a Client role when creating a payment, or accessing another tenant's resources
404NOT_FOUNDResource not foundPayment ID does not exist or belongs to a different tenant
409CONFLICTData conflictData duplication or business rule violation
409IDEMPOTENCY_CONFLICTIdempotency key conflictThe same X-Idempotency-Key was submitted with a different payload
409INVALID_STATE_TRANSITIONInvalid state transitionAttempting to cancel a payment that is already paid or expired
429RATE_LIMIT_EXCEEDEDRate limit exceededToo many requests in a short period
500INTERNAL_ERRORInternal server errorUnexpected internal failure. Transient — safe to retry with exponential backoff
502PROVIDER_ERRORError from external providerWinpay or Midtrans returned an error
503CIRCUIT_BREAKER_OPENCircuit breaker openProvider is failing repeatedly in a short period — wait a few minutes before retrying
504PROVIDER_TIMEOUTProvider timeoutWinpay or Midtrans did not respond within the time limit
504REQUEST_TIMEOUTGlobal request timeoutRequest exceeded the server time limit

💡 Note on 5xx errors: Errors with HTTP statuses 500, 502, 503, and 504 are transient (temporary). Implement a retry with exponential backoff mechanism on your client side for these cases.

Example Error Responses

400 Bad Request — Invalid Amount

json
{
  "success": false,
  "error": {
    "code": "BAD_REQUEST",
    "message": "bad request: Amount must be greater than zero"
  }
}

401 Unauthorized — Invalid API Key

json
{
  "success": false,
  "error": {
    "code": "UNAUTHORIZED",
    "message": "unauthorized: Invalid API Key"
  }
}

403 Forbidden — Non-Client Role

json
{
  "success": false,
  "error": {
    "code": "FORBIDDEN",
    "message": "forbidden: Only client tenants are authorized to process payments"
  }
}

404 Not Found — Payment Not Found

json
{
  "success": false,
  "error": {
    "code": "NOT_FOUND",
    "message": "not found: payment with id 550e8400-e29b-41d4-a716-446655440000"
  }
}

409 Idempotency Conflict

json
{
  "success": false,
  "error": {
    "code": "IDEMPOTENCY_CONFLICT",
    "message": "idempotency conflict: Same key submitted with different payload"
  }
}

409 Invalid State Transition

json
{
  "success": false,
  "error": {
    "code": "INVALID_STATE_TRANSITION",
    "message": "invalid state transition: from paid to cancelled"
  }
}

400 Payment Method Unavailable

json
{
  "success": false,
  "error": {
    "code": "PAYMENT_METHOD_UNAVAILABLE",
    "message": "payment method unavailable: No routing rule configured for e_wallet"
  }
}

429 Rate Limit Exceeded

json
{
  "success": false,
  "error": {
    "code": "RATE_LIMIT_EXCEEDED",
    "message": "Rate limit exceeded. Please slow down and retry."
  }
}

502 Provider Error

json
{
  "success": false,
  "error": {
    "code": "PROVIDER_ERROR",
    "message": "provider error [winpay]: Code: 400 | Status: Bad Request | Body: ..."
  }
}

503 Circuit Breaker Open

json
{
  "success": false,
  "error": {
    "code": "CIRCUIT_BREAKER_OPEN",
    "message": "circuit breaker open for provider: winpay"
  }
}

Payment State Machine

Payments have statuses that follow a strict state machine. Allowed transitions:

pending → paid
pending → failed
pending → expired
pending → cancelled
pending → challenged

challenged → paid
challenged → failed

paid → paid          (duplicate callback, ignored)
paid → refunding
paid → partially_refunded

partially_refunded → partially_refunded
partially_refunded → refunding

refunding → refunded

Attempting a transition outside of those permitted will result in an INVALID_STATE_TRANSITION error (HTTP 409).


Error Handling Tips

  1. Always check the code field for retry/branching logic, not the message field.
  2. 5xx Errors are generally temporary — implement retries with exponential backoff.
  3. PROVIDER_ERROR (502) means the external provider is experiencing issues — try again in a moment.
  4. CIRCUIT_BREAKER_OPEN (503) — the provider is in recovery mode, wait a few minutes.
  5. IDEMPOTENCY_CONFLICT (409) — do not retry with the same key if the payload differs, use a new key.
  6. PAYMENT_METHOD_UNAVAILABLE (400) — ensure routing rules are configured in the dashboard for the method being used.

Gerbang Pay API Documentation