Error Handling
Every error, on every endpoint, has the same shape:
{
"success": false,
"error": {
"code": "INVALID_CALLBACK_URL",
"message": "The callback URL is not registered for this application.",
"details": { "callback_url": ["…"] }
}
}
code— stable, machine-readable. Branch on this, not onmessage.message— human-readable, may change.details— optional; validation errors per field,retry_after, orpayment_idfor idempotency conflicts.
Successful responses always have "success": true and a data object.
Error codes
| Code | HTTP | Retry? | Meaning |
|---|---|---|---|
UNAUTHENTICATED |
401 | no | Authorization header missing |
INVALID_TOKEN |
401 | no | Token unknown, revoked or expired |
APPLICATION_DISABLED |
403 | no | Application disabled by an admin |
FORBIDDEN |
403 | no | Not allowed (e.g. admin IP allowlist) |
RATE_LIMITED |
429 | after details.retry_after |
Request budget exceeded |
VALIDATION_ERROR |
422 | no | Generic validation failure; see details |
INVALID_AMOUNT |
422 | no | Amount not an integer within limits |
INVALID_CURRENCY |
422 | no | Unsupported currency |
INVALID_CALLBACK_URL |
422 | no | Not registered, or not https |
INVALID_IDEMPOTENCY_KEY |
422 | no | Malformed header |
IDEMPOTENCY_CONFLICT |
409 | no | Key reused with a different body |
PAYMENT_NOT_FOUND |
404 | no | Unknown or other application's payment |
PAYMENT_ALREADY_PAID |
409 | no | Operation not applicable to a paid payment |
PAYMENT_EXPIRED |
409 | no | Payment expired |
PAYMENT_NOT_VERIFIABLE |
409 | shortly | Never registered at a gateway, or verification in progress |
INVALID_STATUS_TRANSITION |
409 | no | Internal state guard |
GATEWAY_NOT_CONFIGURED |
502 | no | Application has no usable gateway (admin action needed) |
GATEWAY_DISABLED |
502 | no | Configured gateway is disabled |
GATEWAY_ERROR |
502 | no | Gateway rejected the operation |
GATEWAY_UNAVAILABLE |
503 | yes | Gateway unreachable / 5xx — retry with the same idempotency key |
VERIFICATION_FAILED |
502 | no | Gateway refused verification |
UNSUPPORTED_OPERATION |
422 | no | Gateway lacks the feature |
NOT_FOUND |
404 | no | Unknown route/resource |
METHOD_NOT_ALLOWED |
405 | no | Wrong HTTP method |
INTERNAL_ERROR |
500 | later | Unexpected failure on Cartino Pay; it is logged with a trace |
Handling strategy
$response = Http::withToken($token)->post("$base/api/v1/payments", $payload);
$json = $response->json();
if ($json['success'] ?? false) {
return $json['data'];
}
match ($json['error']['code'] ?? 'INTERNAL_ERROR') {
'GATEWAY_UNAVAILABLE', 'RATE_LIMITED' => retryLater(),
'INVALID_CALLBACK_URL', 'GATEWAY_NOT_CONFIGURED' => alertOps(), // configuration problem
'IDEMPOTENCY_CONFLICT' => useNewKey(),
default => showGenericPaymentError(),
};
Gateway-specific error messages are recorded in Cartino Pay's per-payment trace for admins; only a normalised message reaches your application.