Payment API Error Codes Reference
A quick-reference lookup for common error codes and decline reasons from major payment APIs. Covers Stripe decline codes, Adyen result codes, HTTP status mapping, retry-safe classification, and troubleshooting steps for each error type.
Stripe Decline Codes
When a PaymentIntent or Charge fails, Stripe returns an error object with a decline_code (for card declines) or an error.code (for other errors). The error.message provides a human-readable description suitable for logging, while error.type categorizes the error (card_error, api_error, idempotency_error, etc.).
| Code | HTTP | Retry Safe | Description | Troubleshooting |
|---|---|---|---|---|
| card_declined | 402 | Depends | Generic decline. The card issuer declined the transaction. May succeed on retry if a soft decline. | Ask the customer to contact their bank or try a different card. |
| insufficient_funds | 402 | Yes (later) | The card has insufficient funds to complete the purchase. | Customer should add funds and retry. Safe to retry after a delay. |
| expired_card | 402 | No | The card has expired. | Customer must provide a new card. Do not retry. |
| incorrect_cvc | 402 | No | The CVC number provided is incorrect. | Customer should re-enter the CVC. Repeated failures may indicate fraud. |
| incorrect_number | 402 | No | The card number is incorrect. | Customer should verify and re-enter the card number. |
| processing_error | 402 | Yes | An error occurred while processing the card. | Retry the transaction. If persistent, try a different card. |
| authentication_required | 402 | Yes (with 3DS) | The card requires 3D Secure authentication. | Re-attempt with 3DS authentication. Use stripe.handleNextAction() on the client. |
| rate_limit | 429 | Yes | Too many requests sent in a given time period. | Implement exponential backoff. Stripe allows 100 read and 100 write requests per second in live mode. |
| api_connection_error | N/A (client) | Yes | Network connectivity issue between your server and Stripe. | Retry with exponential backoff. Check your network and DNS resolution. |
| idempotency_key_in_use | 409 | No | The idempotency key is being used by another in-progress request. | Wait for the original request to complete, or use a different idempotency key. |
| fraudulent | 402 | No | The charge has been declined as Stripe Radar suspects it is fraudulent. | Do not retry. Review in Stripe Dashboard. Consider adding to a block list. |
| lost_card | 402 | No | The card has been reported lost. | Do not retry. Do not inform the customer the card is reported lost (security). |
| stolen_card | 402 | No | The card has been reported stolen. | Do not retry. Do not inform the customer the card is reported stolen (security). |
Adyen Result Codes
Adyen returns a resultCode in the payment response. For refusals, the refusalReason and refusalReasonCode fields provide more detail. The additionalData.refusalReasonRawfield contains the issuer's raw response code.
| Result Code | Category | Retry Safe | Description | Troubleshooting |
|---|---|---|---|---|
| Authorised | Success | N/A | Payment was successfully authorized. | Proceed with capture (if not auto-captured). |
| Refused | Decline | Depends | Generic refusal. Check refusalReason for details (e.g., DECLINED, CVC_DECLINED, BLOCK_CARD). | Check additionalData.refusalReasonRaw for the issuer-specific code. |
| Error | Error | Yes | A system error occurred during processing. | Retry the transaction. Contact Adyen support if persistent. |
| Cancelled | Terminal | No | The shopper or merchant cancelled the payment before completion. | Create a new payment if the shopper wants to try again. |
| Pending | Async | No (wait) | The payment result is not yet known. Applies to bank transfers, boletos, and other async methods. | Wait for the AUTHORISATION webhook notification with the final result. |
| Received | Async | No (wait) | The payment request was received and is being processed (common for direct debit). | Wait for the final notification. Do not treat as a successful payment. |
| RedirectShopper | Action | N/A | The shopper must be redirected to complete payment (3DS, bank redirect, wallet). | Redirect to the URL in the action object. Handle the return URL callback. |
| IdentifyShopper | Action (3DS2) | N/A | 3DS2 fingerprinting required. Collect device data for frictionless authentication. | Execute the 3DS2 fingerprint action via Drop-in/Components or manually. |
| ChallengeShopper | Action (3DS2) | N/A | 3DS2 challenge required. The issuer needs additional shopper verification. | Present the challenge UI. Submit the result via /payments/details. |
| PresentToShopper | Action | N/A | Present payment details to the shopper (voucher, QR code, bank transfer instructions). | Display the voucher/QR code from the action object. Wait for async confirmation. |
HTTP Status Codes
Payment APIs use standard HTTP status codes, but their meaning can vary by provider. This table covers the common codes you will encounter.
| Status | Meaning | Retryable | Usage in Payment APIs |
|---|---|---|---|
| 200 | OK | N/A | Successful request. Check the response body for business-level status (Stripe: status field, Adyen: resultCode). |
| 400 | Bad Request | No (fix request) | Malformed request parameters. Missing required fields, invalid values, or incorrect types. |
| 401 | Unauthorized | No (fix auth) | Invalid or missing API key. Verify your authentication credentials. |
| 402 | Payment Required | Depends | Stripe-specific: the card was declined. Check the error code for retry eligibility. |
| 403 | Forbidden | No | The API key does not have permission for this operation. Check API key permissions and scopes. |
| 404 | Not Found | No | The requested resource does not exist. Verify the ID and endpoint path. |
| 409 | Conflict | No | Idempotency key conflict (Stripe) or concurrent modification. Wait and retry with a new key if appropriate. |
| 422 | Unprocessable Entity | No (fix request) | Adyen: the request was well-formed but contained invalid parameters for the payment method or configuration. |
| 429 | Too Many Requests | Yes (with backoff) | Rate limit exceeded. Implement exponential backoff. Check Retry-After header if provided. |
| 500 | Internal Server Error | Yes | Server-side error at the provider. Safe to retry with exponential backoff and idempotency key. |
| 502/503 | Bad Gateway / Service Unavailable | Yes | Provider infrastructure issue. Retry with backoff. Typically transient. |
Error Handling Best Practices
- Always use idempotency keys on mutations: Include an idempotency key on every POST request so that retries do not create duplicate charges.
- Categorize errors by retryability: Build your error handler to distinguish between retryable errors (network issues, rate limits, processing errors) and terminal errors (expired card, stolen card, invalid request). Only retry retryable errors.
- Implement exponential backoff: Start with a 1-second delay, doubling each retry, with jitter to prevent thundering herd. Cap at a maximum delay (e.g., 60 seconds) and a maximum retry count (e.g., 5).
- Log raw error responses: Store the full error object (code, message, decline_code, refusalReason) for debugging. Do not expose raw error details to end users -- use friendly messages instead.
- Handle async payment methods: Bank transfers, BNPL, and voucher payments return pending/received states. Do not treat these as failures. Wait for the webhook notification with the final result.
Disclaimer: Error codes, decline reasons, and retry policies are subject to change by each provider. This reference covers common cases but is not exhaustive. Consult the official documentation for Stripe and Adyen for complete and current error code listings.