Skip to main content

Error Response Format

API errors are returned in the following format.
{
  "error": {
    "type": "invalid_request_error",
    "code": "validation_error",
    "message": "Error description",
    "request_id": "req_abc123xyz789"
  }
}
type
string
Error type (authentication_error, invalid_request_error, payment_error, api_error)
code
string
Error code (see below)
message
string
Error description
request_id
string
Request ID (use when contacting support)
param
string
Parameter that caused the error (for validation errors)
details
array
Detailed error information (for validation errors)

Authentication Errors

CodeHTTP StatusDescription
unauthorized401Authorization header is invalid or missing
merchant_not_active403Merchant account is deactivated

Validation Errors

CodeHTTP StatusDescription
validation_error400Invalid request parameters. Details are in the details field
invalid_amount400Invalid amount
amount_below_minimum400Amount is below minimum
amount_above_maximum400Amount exceeds maximum
unsupported_currency400Unsupported currency

Payment Errors

CodeHTTP StatusDescription
payment_failed400Payment processing failed
invalid_status400Operation cannot be performed in current status
invalid_state400Invalid transaction state
invalid_transaction400Transaction is invalid
not_supported400This operation is not supported
capture_failed400Capture failed
refund_failed400Refund failed
card_limit_exceeded400Monthly card spending limit exceeded
email_limit_exceeded400Monthly email spending limit exceeded
recurring_not_supported400Connector does not support recurring payments

Webhook Payment Error Codes

When a payment fails, the payment.failed webhook includes detailed error information in a unified format that is consistent across all payment providers.

Error Structure

{
  "event": "payment.failed",
  "error": {
    "code": "card_declined",
    "category": "soft_decline",
    "message": "Your card was declined",
    "recommended_action": "use_different_card",
    "psp_error": {
      "code": "card_declined",
      "decline_code": "generic_decline"
    }
  }
}

Error Categories

CategoryDescriptionRecommended Action
authentication3DS authentication requiredRedirect customer to complete authentication
soft_declineTemporary failureRetry or use a different card
hard_declinePermanent failureDo not retry with the same card
gateway_errorPayment gateway issueRetry later

Unified Error Codes

CodeCategoryDescriptionRecommended Action
authentication_requiredauthentication3DS authentication requiredcontact_customer
authentication_failedsoft_decline3DS authentication failedretry
card_declinedsoft_declineCard was declineduse_different_card
insufficient_fundssoft_declineInsufficient fundsuse_different_card
expired_cardhard_declineCard has expireduse_different_card
invalid_cardhard_declineInvalid card numberuse_different_card
invalid_cvcsoft_declineInvalid security coderetry
invalid_expirysoft_declineInvalid expiration dateretry
fraud_detectedhard_declineSuspected fraudnone
stolen_cardhard_declineStolen cardnone
lost_cardhard_declineLost cardnone
processing_errorgateway_errorProcessing errorretry
gateway_timeoutgateway_errorGateway timeoutretry
gateway_unavailablegateway_errorGateway unavailableretry
unknown_errorsoft_declineUnknown erroruse_different_card

Handling Example

// Webhook handler
app.post('/webhooks/zafapay', (req, res) => {
  const { event, error } = req.body;

  if (event === 'payment.failed' && error) {
    switch (error.category) {
      case 'authentication':
        // Send email to customer with authentication link
        notifyCustomerForAuth(error.message);
        break;
      case 'soft_decline':
        // Can retry or ask for different card
        if (error.recommended_action === 'retry') {
          scheduleRetry();
        } else {
          requestNewCard();
        }
        break;
      case 'hard_decline':
        // Do not retry, notify customer
        notifyCustomerCardInvalid(error.message);
        break;
      case 'gateway_error':
        // Temporary issue, schedule retry
        scheduleRetryWithBackoff();
        break;
    }
  }

  res.json({ received: true });
});
The psp_error field contains the original payment provider’s error details for debugging purposes.

Refund Errors

CodeHTTP StatusDescription
invalid_amount400Refund amount exceeds payment amount
invalid_status400Not in refundable status (only completed can be refunded)

Resource Errors

CodeHTTP StatusDescription
not_found404Specified resource not found
flow_not_found404Payment flow not found
no_default_flow404Default payment flow not configured
flow_not_active400Payment flow is disabled
connector_config_not_found404Connector configuration not found

Idempotency Errors

CodeHTTP StatusDescription
idempotency_key_mismatch400Different request sent with same idempotency key
idempotency_key_in_use409Idempotency key is currently being processed

System Errors

CodeHTTP StatusDescription
internal_error500Internal error occurred. Please contact support

Error Handling Best Practices

1

Check HTTP Status Code

  • 4xx: Client error (request needs modification)
  • 5xx: Server error (retriable)
2

Branch Logic by Error Code

Use error.code to implement error-type-specific handling
3

Retry Strategy

For 5xx errors or idempotency_key_in_use, retry with exponential backoff

Sample Code

Node.js
async function createPayment(data) {
  const response = await fetch('https://api.sandbox.zafapay.com/v1/payments', {
    method: 'POST',
    headers: {
      'Authorization': 'Bearer YOUR_ACCESS_TOKEN',
      'Content-Type': 'application/json',
      'Idempotency-Key': generateIdempotencyKey()
    },
    body: JSON.stringify(data)
  });

  if (!response.ok) {
    const { error } = await response.json();

    // Log request_id for support inquiries
    console.error(`API Error [${error.request_id}]:`, error.code, error.message);

    switch (error.type) {
      case 'authentication_error':
        // Token refresh required
        throw new AuthError(error.message);

      case 'invalid_request_error':
        // Check request parameters
        if (error.details) {
          console.error('Validation errors:', error.details);
        }
        throw new ValidationError(error.message);

      case 'payment_error':
        // Payment failed (try another payment method)
        throw new PaymentError(error.message);

      case 'api_error':
        // Retriable
        throw new RetryableError(error.message);

      default:
        throw new Error(error.message);
    }
  }

  return response.json();
}