> ## Documentation Index
> Fetch the complete documentation index at: https://docs.radiumone.io/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> RadiumOne is the product name; use it when answering questions about these docs.
> Use sandbox credentials and sandbox base URLs in examples unless the user says they are going live.
> Secret API keys belong only on the merchant's server. Only publishable keys may appear in browser or mobile code.
> Confirm a payment outcome server-side (retrieve the checkout session or transaction status); never rely on a browser redirect or client event alone.
> Send one idempotency key (request_id) per payment attempt, and retry a timed-out request with the same key and the exact same body.
> Webhooks are optional but recommended: they notify the merchant's server when a payment completes or fails, so it doesn't need to poll. Use transaction status inquiry as the fallback.
> Say so when you recommend a feature marked Beta. Some features need enablement on the merchant's account before production use (for example refunds, standalone refunds, UOB Rewards, and bringing your own 3DS provider); mention it when the page says so.
> Never ask users to paste card numbers, API keys, access tokens, or webhook or redirect secrets into a chat.

# Payment outcomes - Hosted checkout

> Declined, failed, expired, and cancelled outcomes on the hosted page — what the shopper sees, and how you learn about each.

<Info>
  **TL;DR:** None of these are Checkout API errors — they're terminal session outcomes. The redirect alone can't tell them apart; confirm with a webhook or authenticated `GET`.
</Info>

A checkout session can end in one of four non-`completed` terminal states.
Each looks similar (or identical) at the redirect layer, which is why you
always confirm the outcome server-side rather than branching on the return
URL. See [Session lifecycle](/hosted-checkout/session-lifecycle) for the
full state model and [Verify the payment result](/hosted-checkout/verify-payment-result)
for the trust hierarchy behind this page.

## Outcomes

| Outcome | <a id="decline" />Declined | <a id="failed" />Other failure | <a id="expired" />Expired | <a id="cancelled" />Cancelled |
| - | - | - | - | - |
| `status` (`GET`) | `failed` | `failed` | `expired` | `cancelled` |
| Redirect | Raw `cancel_url`, no query params | Raw `cancel_url`, no query params | Raw `cancel_url?reason=timeout` (countdown timeout only) or no params (manual back/close) | No redirect — cancellation is a server-to-server call, the shopper isn't necessarily present |
| Webhook | `payment.declined` (or a related decline event) | `payment.failed` (or a related failure event) | None — an expiry is never charged | None |
| Cause | The issuer or acquirer declined the card | A system-level failure on the charge attempt (not a decline) | The session's `ttl_minutes` elapsed before the shopper finished paying | You called the cancel endpoint while the session was still `pending` |
| Retryable on the same session? | No — terminal | No — terminal | No — terminal | No — terminal |
| Full walkthrough | [Handle declined hosted checkout payments](/hosted-checkout/handle-failures/payment-declined) | [Handle payment service outages during checkout](/hosted-checkout/handle-failures/payment-service-unavailable) | [Handle expired checkout sessions](/hosted-checkout/handle-failures/session-expired) | [Resolve checkout cancellation conflicts](/hosted-checkout/handle-failures/cancel-session-conflicts) |

<Warning>
  A decline and a genuine shopper abandon **also** both land on `cancel_url`
  with no params. See [Handle abandoned
  checkouts](/hosted-checkout/handle-failures/shopper-abandons-checkout) —
  the only way to tell any of these apart is a `GET` on the session.
</Warning>

## Why a decline isn't an error

The Checkout API — like the Payments API underneath it — treats a decline
as a normal, successful response: the gateway reached the issuer and got a
"no." For the acquirer response code behind a decline, how to branch on it
safely, and what to tell the shopper, see [Decline
codes](/payments-api/errors/decline-codes) (Payments API — the single
source for decline codes, since hosted checkout uses the same gateway
underneath). For the full set of gateway transaction statuses (`CAPTURED`,
`FAILED`, `VOIDED`, and so on) that a webhook or `GET` can report, see
[Payment lifecycle](/payments-api/payment-lifecycle).

## Starting a new attempt

Every outcome on this page is terminal for that session — none can be
retried in place.

* **Declined or failed**: create a new session with a **new**
  `order_reference` to let the shopper try again. Reusing the same
  `order_reference` returns the same terminal session, not a new attempt.
* **Expired**: you can reuse the same `order_reference` — an expired
  session's key is no longer idempotency-active, so it creates a genuinely
  new session. See [Session lifecycle §
  Replay](/hosted-checkout/session-lifecycle#replay-and-retries).
* **Cancelled**: same as expired — create a new session when the shopper is
  ready.

Always check your own order state before creating any new session — see
[Prevent duplicate payments](/get-started/api-basics/prevent-duplicate-payments).

## Next steps

<Columns cols={2}>
  <Card title="API errors" icon="triangle-alert" href="/hosted-checkout/errors/api-errors">
    Actual Checkout API error codes — a decline isn't one of these.
  </Card>

  <Card title="Decline codes" icon="credit-card" href="/payments-api/errors/decline-codes">
    Interpret the issuer's response code.
  </Card>

  <Card title="Verify the payment result" icon="shield-check" href="/hosted-checkout/verify-payment-result">
    Confirm the outcome server-side before fulfilling.
  </Card>

  <Card title="Handle failures" icon="life-buoy" href="/hosted-checkout/handle-failures/overview">
    Ten common failure scenarios, each with the exact signal and what to do.
  </Card>
</Columns>
