# 

## Why transactions are declined

A decline means the card issuer or processor refused the transaction. Common reasons:

| Reason | What happened | What to do |
|  --- | --- | --- |
| Insufficient funds | Customer's balance is too low | Ask the customer to use a different card |
| Invalid card number | PAN failed Luhn check or is not recognized | Prompt the customer to re-enter card details |
| Expired card | The card's expiry date has passed | Ask for an updated card |
| CVV mismatch | CVV does not match the issuer's records | Ask the customer to re-enter CVV |
| AVS mismatch | Billing address does not match | Ask the customer to verify their billing address |
| Fraud suspect | Issuer flagged the transaction | Do not retry — contact the customer |
| Processor error | Temporary issue at the processor | Retry after a delay |


## Recognizing a decline

Check `response_status` and `response_code` on every transaction response.

```json
{
  "id": "TRANSACTION-02LGEHRR1BZSPA6WRY1T3H4CD",
  "type": "sale",
  "amount": 5000,
  "transaction_state": "failed",
  "response_status": "declined",
  "response_code": "05",
  "response_message": "Transaction declined by the issuer.",
  "avs_result_code": "n",
  "cvv_result": "N",
  "correlation_id": "c3d4e5f6-a7b8-9012-cdef-123456789abc"
}
```

## Decision flowchart

![image](https://files.modern-mermaid.live/images/1776277281111-mermaid-diagram-1776277281047.png)

## Code example: Decline handling

Python
```python
import time, requests, os

API_KEY = os.environ["PARADISE_API_KEY"]
BASE = "https://api.dev.paradisegateway.net"

def process_with_retry(payload, max_retries=3):
    for attempt in range(max_retries):
        resp = requests.post(
            f"{BASE}/v1/transactions",
            headers={"Content-Type": "application/json", "APIKEY": API_KEY},
            json=payload,
        )
        result = resp.json()

        if result["response_status"] == "approved":
            return {"success": True, "transaction": result}

        if result["response_status"] == "declined":
            return {
                "success": False,
                "reason": "declined",
                "message": result.get("response_message", "Transaction declined"),
                "avs": result.get("avs_result_code"),
                "cvv": result.get("cvv_result"),
            }

        # error — retry with backoff
        wait = 2 ** attempt
        print(f"Error on attempt {attempt + 1}, retrying in {wait}s...")
        time.sleep(wait)

    return {"success": False, "reason": "error", "message": "Max retries exceeded"}
```

JavaScript
```javascript
const API_KEY = process.env.PARADISE_API_KEY;
const BASE = "https://api.dev.paradisegateway.net";

async function processWithRetry(payload, maxRetries = 3) {
  for (let attempt = 0; attempt < maxRetries; attempt++) {
    const resp = await fetch(`${BASE}/v1/transactions`, {
      method: "POST",
      headers: { "Content-Type": "application/json", "APIKEY": API_KEY },
      body: JSON.stringify(payload),
    });
    const result = await resp.json();

    if (result.response_status === "approved") {
      return { success: true, transaction: result };
    }

    if (result.response_status === "declined") {
      return {
        success: false,
        reason: "declined",
        message: result.response_message || "Transaction declined",
        avs: result.avs_result_code,
        cvv: result.cvv_result,
      };
    }

    // error — retry with backoff
    const wait = 2 ** attempt * 1000;
    console.log(`Error on attempt ${attempt + 1}, retrying in ${wait}ms...`);
    await new Promise((r) => setTimeout(r, wait));
  }
  return { success: false, reason: "error", message: "Max retries exceeded" };
}
```

## What to show the customer

| Scenario | User-facing message |
|  --- | --- |
| Declined — insufficient funds | "Your card was declined. Please try a different payment method." |
| Declined — invalid card | "We couldn't process your card. Please check the number and try again." |
| Declined — AVS mismatch | "Your billing address doesn't match. Please verify and retry." |
| Error — temporary | "Something went wrong. Please try again in a moment." |


Never expose raw error details
Do not show `response_code`, `response_message`, or processor details to the customer. Use friendly messages and log the details server-side.

## Testing declines in sandbox

Use these test card numbers to simulate specific scenarios:

| Card number | Expected result |
|  --- | --- |
| `4000000000000002` | Declined |
| `5200000000000007` | Declined |
| `4111111111111111` | Approved |


See [Quick reference — test cards](/docs/reference/quick-reference#test-card-numbers) for the full list.

## Next steps

* [Handle payment failures](/docs/tutorials/payment-cookbook/handle-payment-failures) — broader error handling patterns
* [Error handling concepts](/docs/concepts/api-fundamentals/error-handling)
* [Fraud prevention](/docs/concepts/security-compliance/fraud-protection)