/** * A failed PayMe API call, normalised into one error type. * * A PayMe error is NOT signalled by the HTTP status code alone. PayMe answers * 200 OK with `status_code: 1` for a declined card, and HTTP 500 with a * well-formed error body for an ordinary refusal. Success is `status_code` 0 in * a 2xx response. * * Every error body carries the same fields: * status_code 1 (0 means success) * status_error_code PayMe's stable numeric error code — log and switch on this * status_error_details human-readable message, in `language` if you sent one * status_additional_info usually the offending parameter name, or a value * session request id — quote it to PayMe support * * Map this to a 502 (or your framework's equivalent) at your API boundary: the * upstream provider rejected the call; your caller's request was well-formed. */ export type PayMeErrorKind = /** * PayMe answered with an explicit failure, at any HTTP status. `outcomeUnknown` * says whether that proves nothing happened (codes 991, 20002 and 999 don't). */ | 'rejected' /** HTTP 429, usually code 998. Turned away, but a matching call may still be running. */ | 'rate-limited' /** A non-2xx with no error code in its body. A 5xx may have been processed before it failed. */ | 'http' /** No response within the timeout. The request may well have been processed. */ | 'timeout' /** The connection failed. Only a refused/unresolvable connection is known to be unsent. */ | 'network' /** A 2xx response that was not PayMe's JSON. */ | 'invalid-response'; export class PayMeApiError extends Error { constructor( /** What failed, e.g. "generate-sale" — with ids in the path masked. */ readonly endpoint: string, readonly kind: PayMeErrorKind, /** PayMe's numeric error code (`status_error_code`). */ readonly paymeErrorCode: number | null, /** PayMe's human-readable message (`status_error_details`). */ readonly paymeErrorDetails: string, /** Usually the offending field name (`status_additional_info`). */ readonly additionalInfo: unknown, /** PayMe's request session id — quote this to PayMe support. */ readonly session: string | null, /** Raw response body, redacted, kept for logging. */ readonly raw: unknown, /** * True when PayMe may have acted on the request even though this call * failed: timeouts, dropped connections, a 5xx with no error code, HTTP 429, * and codes 991, 20002 and 999. A 5xx with any other error code is a refusal, * so this is false. NEVER retry a call that moves money (pay-sale, capture, * refund, generate-sale with buyer_key) when this is true — check the sale * with PayMe first. */ readonly outcomeUnknown: boolean, ) { super(paymeErrorDetails || `PayMe call to ${endpoint} failed`); this.name = 'PayMeApiError'; } }