Skip to content
API DocsDocs

Troubleshooting

Solutions for common integration issues

4 min readUpdated Jul 31, 2026

Troubleshooting

Common issues and how to fix them.


#3D Secure Always Fails

The most common cause is a missing or incomplete device object. All fields below are required for 3D Secure authentication to succeed.

Collect these values client-side from the browser:

const device = {
  ip: "collect-server-side",
  userAgent: navigator.userAgent,
  accept: "text/html,application/xhtml+xml",
  acceptLanguage: navigator.language,
  javaEnabled: navigator.javaEnabled(),
  javaScriptEnabled: true,
  deviceLanguage: navigator.language.split('-')[0],
  colorDepth: String(screen.colorDepth),
  screenHeight: String(screen.height),
  screenWidth: String(screen.width),
  deviceTimezone: Intl.DateTimeFormat().resolvedOptions().timeZone
};

Important: The ip field must be collected server-side from the incoming request headers (e.g., X-Forwarded-For). Do not attempt to collect it in the browser.


#Webhook Callback Not Arriving

Checklist:

  • Callback URL must be publicly accessible (not localhost or a private IP).
  • Your endpoint must return HTTP 200 within 10 seconds. Slow responses are treated as failures.
  • Check firewall and security group rules — Exirom's servers must be able to reach your URL.

Retry behavior: Exirom retries failed callbacks 5 times with exponential backoff: 2, 4, 8, 16, 32 minutes. After all retries (~62 minutes total), the callback is abandoned.

Fallback: If callbacks are unreliable, poll the info endpoint instead (returns status and declineCode on failure):

  • Cards: GET /api/v1/payments/card/info/{paymentId}
  • APMs: GET /api/v1/payments/apm/info/{paymentId}

See Callback Retry Policy for full details. For production-grade webhook handling patterns, see Webhook Best Practices.


#APM Checksum Mismatch

Common causes:

  1. Wrong callback formula — APM payment uses accountId|orderAmount|orderCurrency|transactionId; APM payout uses transactionId|requestId|transactionStatus.
  2. APM-payment amount mismatch — Use the exact callback orderAmount representation, not the original request amount.
  3. Encoding or field order — Join fields with pipes in the documented order and use standard Base64, not hexadecimal or URL-safe Base64.

See Checksum Guide for the full field order and code examples in JavaScript, Python, Go, and Kotlin.


#Transaction Stuck in PENDING or CUSTOMER_VERIFICATION

A transaction in PENDING is usually waiting for customer action or an external provider response. A transaction in CUSTOMER_VERIFICATION is waiting for the customer to complete 3DS or an APM redirect/challenge.

  • For CUSTOMER_VERIFICATION, redirect the customer to the returned challengeUrl, challengeUrlIframe, or APM redirect URL.
  • Poll the status endpoint to check for updates if the webhook has not arrived.
  • If the transaction has been PENDING or CUSTOMER_VERIFICATION for more than 30 minutes with no customer action, it will eventually time out to FAILED.
  • Do not create a new transaction for the same order. Use the same requestId to ensure idempotency. Duplicate transactions for the same order can result in double charges.

For detailed decision trees on handling stuck, failed, and ambiguous transactions, see Error Recovery Patterns.


#Authentication Token Expired

Tokens are valid for 30 days. Cache the token and reuse it across requests. Calling auth before every request is a common mistake — see Authenticating Per Request for the full explanation.

Recommended recovery pattern when a 401 is received:

  1. Send the API request with the cached token.
  2. If you receive a 401 Unauthorized response, call POST /api/v1/auth to get a new token.
  3. Retry the original request once with the new token.

Do not authenticate before every API call — this adds unnecessary latency.

See Authentication Guide for full details and Going to Production for token caching patterns.


#CORS Errors in API Playground

All Exirom API calls are server-to-server. Calling the API from client-side JavaScript will fail due to CORS restrictions.

If you hit CORS errors in the API Playground:

  • Use the Copy as cURL button and run the request from your terminal.
  • In production, always proxy API calls through your backend. Never call the Exirom API directly from the browser.

#See Also

Was this helpful?