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

# Go-live checklist - Resources

> Confirm your integration handles retries, server-side verification, and key safety correctly before you switch to production keys.

Work through **All integrations** below, then the checklist for your
integration row, then **High-risk operations** if it applies to you — before
you request production keys. Every item links back to the guide that covers
it in full.

## All integrations

* [ ] You've run the relevant scenarios in [Test your
  integration](/resources/test-your-integration), including idempotent replay,
  body mismatch, and timeout recovery.
* [ ] Every create-type request path retries a timeout, `5xx`, or `PENDING`
  result with the **same** idempotency key — never a new one.
* [ ] Your code branches only on `status` (never on the HTTP status code
  alone, never on `response_code`) — see [Problem format and
  retries](/payments-api/errors/problem-format-and-retries).
* [ ] You confirm the payment result server-side — a [status
  inquiry](/payments-api/check-transaction-status) or a verified webhook —
  before fulfilling an order. Never from a client redirect or the browser
  alone.
* [ ] If you refund payments, you've [requested
  enablement](/resources/support#request-enablement) for the acquirer
  channel(s) you refund through — unlike most gateways, refunds are
  **disabled by default** on RadiumOne. See [Refunds require
  enablement](/payments-api/refund#refunds-require-enablement).
* [ ] You've swapped every `_test_` key and sandbox host for its `_prod_` /
  production equivalent — see [Moving to
  production](/get-started/sandbox-and-api-keys#moving-to-production).
* [ ] Your webhook signature check passes a known-answer test before you
  point it at a live endpoint — see [Verify webhook
  signatures](/payments-api/webhooks/verify-signatures).
* [ ] Your webhook handler dedupes on event `id` and doesn't assume delivery
  order — see [Retries, ordering and
  duplicates](/payments-api/webhooks/retries-and-ordering).
* [ ] You handle the failure scenarios in [Handle Payments API
  failures](/payments-api/handle-failures/timeouts-and-unknown-outcomes).

## Duplicate payments

* [ ] You persist `request_id` (or `operation_id`) to your database **before**
  sending the first request, and reuse that same key on every retry of the
  same order attempt — never a new one just because a call was slow.
* [ ] You generate one `order_reference` per checkout session **attempt**, and
  store the resulting `transaction_id` in your own order record — not the
  `order_reference` itself.
* [ ] You check your own order state before creating a new checkout session —
  `order_reference` only dedupes within the session's TTL; after it expires, a
  retry creates a **new** session even for an already-paid order.
* [ ] Your Elements integration disables the Pay button while a submission is
  in flight — the SDK's submit throttle isn't a substitute for this.
* [ ] Your webhook handler dedupes on the event `id` before crediting or
  fulfilling anything a second time.
* [ ] You've read [Prevent duplicate payments](/get-started/api-basics/prevent-duplicate-payments)
  and know which of the checks above apply to your integration.

<AccordionGroup>
  <Accordion title="Hosted checkout, no 3D Secure">
    - [ ] [Redirect to hosted checkout](/hosted-checkout/redirect-integration) works
      end-to-end in sandbox with approval, decline, and timeout test cards.
    - [ ] Embedded mode only: your `postMessage` handler covers every
      `CHECKOUT_*` event you rely on, and doesn't wait for `CHECKOUT_CANCELLED`
      (never emitted) — see [Embed hosted checkout](/hosted-checkout/embedded-integration).
    - [ ] **Fail-closed redirect verification**: if you've configured a redirect
      secret, your return-page handler treats a missing or invalid `sig` as
      unverified — never as proof of payment — see [Verify the payment
      result](/hosted-checkout/verify-payment-result).
    - [ ] Your return pages send `Referrer-Policy: no-referrer` (or stricter) so
      redirect query params aren't leaked to third-party resources.
    - [ ] Your production `success_url`/`cancel_url` domains are registered in
      `allowed_domains` .
    - [ ] You handle the failure scenarios in [Handle hosted checkout
      failures](/hosted-checkout/handle-failures/session-expired).
  </Accordion>

  <Accordion title="Elements, no 3D Secure">
    * [ ] [Install and load Elements](/elements/install-and-load) using the npm
      loader (with SRI where applicable), not an unpinned CDN `<script>` tag.
    * [ ] [Accept a card payment](/elements/accept-a-card-payment) works
      end-to-end in sandbox with approval and decline test cards.
    * [ ] Your Content Security Policy matches [Content Security
      Policy](/elements/content-security-policy), rolled out **report-only
      first** before you enforce it.
    * [ ] You handle `ElementsError` from `submit()` by showing
      `customerMessage` and checking `retryAllowed` — never the raw `message`.
    * [ ] You handle the failure scenarios in [Handle Elements
      failures](/elements/handle-failures/sdk-fails-to-load).
  </Accordion>

  <Accordion title="Elements + RadiumOne 3D Secure">
    * [ ] [3DS with Elements](/elements/three-d-secure/add-three-d-secure) works end-to-end in
      sandbox with frictionless, challenge, and not-authenticated test cards.
    * [ ] Your server independently verifies the `three_ds` ref at charge time
      and never branches on the client-reported status alone — see [3D Secure
      overview: server policy](/get-started/three-d-secure#server-policy).
    * [ ] Challenge return pages send `Referrer-Policy: no-referrer`, matching
      [Challenge presentation and redirects](/elements/three-d-secure/challenge-presentation).
    * [ ] Your CSP allows the 3DS challenge iframe/redirect hosts, including
      your API origin in `connect-src`, scoped to checkout/return routes only.
    * [ ] You've requested enablement for RadiumOne 3D Secure on this account
      before testing this row.
  </Accordion>

  <Accordion title="Elements + your own 3D Secure provider (Beta, gated)">
    * [ ] You've [requested enablement](/resources/support#request-enablement)
      for this outlet — the gateway rejects `three_ds` evidence for outlets that
      aren't enabled.
    * [ ] [Use your own 3DS provider](/payments-api/three-d-secure/use-your-own-provider) works
      end-to-end in sandbox, including the case where your provider is
      unreachable.
    * [ ] Your PCI scope assessment accounts for your 3DS provider handling card
      data outside Elements  .
    * [ ] Your server still independently verifies the evidence the gateway
      accepted — never trust your own provider's client-side result alone.
  </Accordion>
</AccordionGroup>

## High-risk operations

Only applies if you use [standalone refunds](/payments-api/standalone-refunds).

<Warning>
  This is a high-risk operation. Follow these practices:

  * **Least privilege**: request a secret key scoped to only the operations it needs, not a key with every scope.
  * **Key storage**: store secret keys in a server-side secrets manager, never in client code, source control, or logs.
  * **Emergency revocation**: if a key is compromised, rotate or revoke it immediately — see [Emergency key revocation](/resources/support#emergency-key-revocation).
  * **Monitoring**: monitor refunds and settlements for unexpected activity and alert on anomalies.
</Warning>

* [ ] The key used for standalone refunds is scoped to **only** that
  operation — not a general-purpose key.
* [ ] Secret keys live in a server-side secrets manager, never in client
  code, source control, or logs.
* [ ] Only the specific server-side service that needs it can call the
  standalone-refund endpoint.
* [ ] You monitor refund and settlement volume for anomalies and alert on
  unexpected spikes.
* [ ] You know who to contact for emergency key revocation if a key leaks
  .
* [ ] You've run a rotation drill — rotating a key end-to-end with no
  downtime — at least once before go-live.

## Next steps

<Columns cols={2}>
  <Card title="Test your integration" icon="flask-conical" href="/resources/test-your-integration">
    Run every scenario in this checklist first.
  </Card>

  <Card title="Security and PCI scope" icon="shield-check" href="/resources/security-and-pci">
    Full key-safety and PCI guidance.
  </Card>

  <Card title="Frequently asked questions" icon="circle-question-mark" href="/resources/faq">
    Quick answers on keys, refunds, duplicates, and hosted checkout.
  </Card>
</Columns>
