> ## 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.

# 3D Secure failures - Elements SDK

> How to map Elements 3D Secure failures, such as a failed or abandoned challenge, to the right recovery action for the shopper.

`threeDS.authenticate()` and `threeDS.resume()` fail in one of two shapes: a **rejected promise** (a flow-level error — CSP, cancellation, a stale token) or a **resolved decline** (`{status}` other than `AUTHENTICATED`/`ATTEMPTED`, which isn't an error at all). Telling these apart decides whether you retry the authentication or restart the checkout.

<Info>
  TL;DR: `authenticate()`/`resume()` rejects or resolves to a non-authenticated status → tell a flow error (retry the authentication) apart from an issuer decline (don't auto-retry).
</Info>

## When this happens

* The shopper cancels a challenge in progress, or the challenge window expires before they complete it.
* Your Content Security Policy blocks the SDK's `connect-src`/`frame-src` calls, so a completed challenge can't be confirmed.
* The card token from an earlier `elements.submit()` lapsed (about 30 minutes) or doesn't match the session before `authenticate()` runs.
* The 3DS provider itself is unreachable, or the gateway rejects the reference at charge time because it's unknown, reused, or bound to a different amount/card.

## What you see

| Signal | Value |
| - | - |
| Rejected promise | `three-ds:cancelled` (shopper aborted), `three-ds:challenge-timeout`, `three-ds:provider-unavailable`, `three-ds:action-not-found`, `three-ds:challenge-display-unsupported` |
| Resolved decline | `{status: "NOT_AUTHENTICATED" \| "FAILED" \| "REJECTED" \| "EXPIRED", ref}` — not an error |
| Charge-time rejection | `422 three-ds:ref-invalid`, `422 three-ds:ref-expired`, `422 three-ds:card-token-mismatch`, `422 three-ds:amount-exceeds-authenticated`, `403 three-ds:not-authenticated` |

## What to do

<Steps>
  <Step title="Branch on rejection vs. resolution first">
    A `catch` means the *flow* broke (fix the integration or let the shopper retry the authentication itself). A resolved non-authenticated status means the *issuer* declined — don't automatically retry the same authentication; see [Authentication results](/elements/three-d-secure/authentication-results#status-meanings) for the full status table and the server action for each.
  </Step>

  <Step title="Check CSP before assuming a provider outage">
    `three-ds:challenge-timeout` is most often a missing `connect-src`/`frame-src` directive, not an actual provider outage — see [Content Security Policy: troubleshooting](/elements/content-security-policy#troubleshooting).
  </Step>

  <Step title="Re-bind and re-authenticate on a stale token">
    A `three-ds:card-token-invalid` rejection, or a charge-time `three-ds:card-token-mismatch`, both mean the token that was authenticated no longer matches what you're charging — call `elements.submit()` again for a fresh token, then `authenticate()` again, rather than retrying the charge alone.
  </Step>

  <Step title="Never reuse a ref across charges">
    `three-ds:ref-invalid`/`ref-expired` at charge time means the reference is single-use and already consumed, or too old — re-authenticate for a new `ref` rather than resending the same charge body with a new `request_id`.
  </Step>
</Steps>

## Prevent it

* Keep `ttl_minutes` at 30 or less for a 3DS checkout, matching the card token's own lifetime — see [Token lifetime](/elements/three-d-secure/add-three-d-secure#token-lifetime).
* Ship your CSP as `Content-Security-Policy-Report-Only` first so a missing `connect-src`/`frame-src` directive surfaces in reports instead of manifesting as a shopper-facing challenge timeout in production.

## Test it

See [Test your integration](/resources/test-your-integration#3d-secure) for 3DS scenario coverage.

## Related

<Columns cols={2}>
  <Card title="3DS with Elements" icon="credit-card" href="/elements/three-d-secure/add-three-d-secure">
    The authenticate-then-charge flow these failures interrupt.
  </Card>

  <Card title="Authentication results" icon="triangle-alert" href="/elements/three-d-secure/authentication-results">
    Every status, SDK error, and gateway error, with the remedy for each.
  </Card>
</Columns>
