# 

A declined payment returns `200`. The request was well formed, it reached the issuer, and the issuer said no. Nothing is wrong with your integration.

That is the distinction your code has to make:

|  | HTTP status | What it means | What to do |
|  --- | --- | --- | --- |
| Approved | `200`, `result` is `APPROVED` | The issuer approved it | Fulfill |
| Declined | `200`, `result` is `DECLINED` | The issuer refused it | Ask for another payment method |
| Error | `200`, `result` is `ERROR` | The transaction could not be attempted | Investigate; do not retry blindly |
| Failed request | `4xx` or `5xx` | The request never became a transaction | See [Errors and retries](/docs/get-started/errors) |


**Branch on `result`, not on the status code.** Treating any `200` as success is the most common defect in a first integration, and it ships goods for payments that never happened.

## What a decline looks like

```json
{
  "id": "TRANSACTION-01KEW36C9TQ5N1P7YRFGZ0BVW4",
  "type": "SALE",
  "result": "DECLINED",
  "response_code": "51",
  "response_description": "Insufficient funds",
  "batch_id": null,
  "amount": 10000
}
```

`response_code` and `response_description` come from the processor. `batch_id` is null, because a declined transaction never joins a batch.

**A decline issues no payment token.** `payment_method.payment_token` comes back as an empty string rather than `null`, so do not store it. A later read of the transaction reports it as `null`.

The transaction still exists and still has an `id`. A decline is recorded, not discarded, so you can read it back later from [the reporting endpoints](/docs/after-the-payment/find-and-report-on-transactions).

## Retry, or do not

The useful split is between declines that might succeed later and declines that never will.

* **Do not retry the same card immediately.** Card networks penalize repeated attempts on a refused card, and issuers may treat a burst as suspicious.
* **A soft decline** — insufficient funds, a velocity limit — may succeed on a different day. For a recurring charge, retry on a schedule measured in days, not seconds.
* **A hard decline** — closed account, stolen card, invalid number — will not succeed. Ask for a different payment method.
* **Never retry with a slightly different amount** to get around a decline. It looks like probing, and it is.


The gateway does not classify a decline as soft or hard for you. The processor's `response_code` carries that meaning, and the mapping is processor-specific.

## What to tell the customer

Do not show `response_code`, `response_description`, or anything under `source`. "Do not honor" and `51` mean nothing to a cardholder, and some of them imply things about their account that you should not be relaying on the issuer's behalf.

Say what they can do instead:

> That card was declined. Try another card, or contact your bank.


Keep the codes in your logs, where they are worth having. Log the `x-request-id` header with them — it is what support needs to trace one specific attempt.

## ACH is different

An ACH debit can fail at two different times. VeriCheck can decline it when it is submitted, and that decline comes back like a card decline: `200`, with `result` of `DECLINED`. Or it can be approved and then **returned days later** by the customer's bank, long after you saw `APPROVED`.

So handle the decline in the response as you would for a card, and also watch `is_returned` on the transaction for the return that may follow. [Accept an ACH payment](/docs/payments/accept-an-ach-payment) covers the return fields and the trap that `return_code` is omitted rather than null.

## Testing a decline

No card number triggers a decline against the real gateway. To exercise this path in the API reference, send the header `x-redocly-response-body-example: saleDeclined` and the mock returns a declined body. See [Environments and test data](/docs/get-started/environments).

## Next steps

* [Errors and retries](/docs/get-started/errors)
* [Recurring payments](/docs/payments/recurring-payments)
* [AVS and CVV](/docs/concepts/avs-and-cvv)