Skip to content
Last updated

Tutorial: Processor-specific errors

How processor errors reach you

When Paradise Gateway forwards a transaction to a processor, the processor may return its own error or decline code. The API normalizes these into a consistent response, but the raw processor details are available in processor_response.

image

Response layers

Every transaction response contains two layers of information:

LayerFieldsPurpose
Gatewayresponse_status, response_code, response_messageNormalized result you should use for business logic
Processorprocessor_responseRaw response from TSYS or Vericheck — useful for debugging
{
  "id": "TRANSACTION-01KEW32V6YNV11T33XGDR7TGWC",
  "response_status": "declined",
  "response_code": "05",
  "response_message": "Transaction declined by the issuer.",
  "processor_response": {
    "processor_code": "051",
    "processor_message": "INSUFFICIENT FUNDS",
    "auth_response_code": "51"
  },
  "correlation_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890"
}

TSYS error codes (card transactions)

Common TSYS auth response codes and what to do:

CodeMeaningAction
00ApprovedSuccess — fulfill the order
01Refer to issuerDo not retry — the cardholder should call their bank
02Refer to issuer (special)Do not retry — possible fraud alert
05Do not honorDo not retry — ask for a different card
12Invalid transactionCheck your request — wrong transaction type?
13Invalid amountVerify the amount field
14Invalid card numberCard number failed validation at the processor
41Lost card — pick upDo not retry — possible fraud
43Stolen card — pick upDo not retry — possible fraud
51Insufficient fundsAsk the customer for a different card
54Expired cardAsk for an updated card
61Exceeds withdrawal limitAsk for a different card or smaller amount
65Activity limit exceededRetry later or ask for a different card
91Issuer unavailableRetry with exponential backoff

Vericheck error codes (ACH transactions)

ACH errors are typically ACH return codes from the receiving bank:

CodeMeaningAction
R01Insufficient fundsWait and retry, or contact customer
R02Account closedRequest new bank details; delete the token
R03No account / unable to locateRequest corrected account number
R04Invalid account numberValidate and re-enter
R08Payment stoppedContact customer to resolve
R10Customer advises not authorizedDo not retry — investigate
R29Corporate not authorizedDo not retry — contact the business

See ACH returns for the full return handling guide.

Mapping processor errors to actions

Build a mapping function to translate processor codes into your application's actions.

PROCESSOR_ACTION_MAP = {
    "00": "success",
    "01": "refer_to_issuer",
    "05": "decline_no_retry",
    "12": "invalid_request",
    "14": "invalid_card",
    "51": "insufficient_funds",
    "54": "expired_card",
    "91": "retry",
}

NO_RETRY_ACTIONS = {"decline_no_retry", "refer_to_issuer", "invalid_card", "expired_card"}
RETRY_ACTIONS = {"retry"}

def classify_processor_error(processor_response: dict) -> dict:
    code = processor_response.get("auth_response_code", "")
    action = PROCESSOR_ACTION_MAP.get(code, "unknown")

    return {
        "action": action,
        "retryable": action in RETRY_ACTIONS,
        "user_message": get_user_message(action),
        "processor_code": code,
        "processor_message": processor_response.get("processor_message", ""),
    }

def get_user_message(action: str) -> str:
    messages = {
        "success": "Payment successful.",
        "decline_no_retry": "Your card was declined. Please try a different payment method.",
        "refer_to_issuer": "Please contact your bank for assistance.",
        "insufficient_funds": "Insufficient funds. Please try a different card.",
        "expired_card": "Your card has expired. Please use a different card.",
        "invalid_card": "Invalid card number. Please check and re-enter.",
        "invalid_request": "There was an issue with the payment. Please try again.",
        "retry": "Temporary issue. Please try again in a moment.",
    }
    return messages.get(action, "Payment could not be processed. Please try again.")

Best practices

  • Use response_status for flow control — base your approve/decline/retry logic on the normalized gateway fields, not raw processor codes.
  • Use processor_response for diagnostics — log it for debugging, but don't expose it to customers.
  • Keep your mapping table current — as you encounter new processor codes in production, add them to your map.
  • Alert on unknown codes — if classify_processor_error returns unknown, log an alert so you can investigate.

Next steps