All integrations
- You’ve run the relevant scenarios in Test your integration, including idempotent replay, body mismatch, and timeout recovery.
- Every create-type request path retries a timeout,
5xx, orPENDINGresult with the same idempotency key — never a new one. - Your code branches only on
status(never on the HTTP status code alone, never onresponse_code) — see Problem format and retries. - You confirm the payment result server-side — a status inquiry 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 for the acquirer channel(s) you refund through — unlike most gateways, refunds are disabled by default on RadiumOne. See Refunds require enablement.
- You’ve swapped every
_test_key and sandbox host for its_prod_/ production equivalent — see Moving to production. - Your webhook signature check passes a known-answer test before you point it at a live endpoint — see Verify webhook signatures.
- Your webhook handler dedupes on event
idand doesn’t assume delivery order — see Retries, ordering and duplicates. - You handle the failure scenarios in Handle Payments API failures.
Duplicate payments
- You persist
request_id(oroperation_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_referenceper checkout session attempt, and store the resultingtransaction_idin your own order record — not theorder_referenceitself. - You check your own order state before creating a new checkout session —
order_referenceonly 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
idbefore crediting or fulfilling anything a second time. - You’ve read Prevent duplicate payments and know which of the checks above apply to your integration.
Hosted checkout, no 3D Secure
Hosted checkout, no 3D Secure
- Redirect to hosted checkout works end-to-end in sandbox with approval, decline, and timeout test cards.
- Embedded mode only: your
postMessagehandler covers everyCHECKOUT_*event you rely on, and doesn’t wait forCHECKOUT_CANCELLED(never emitted) — see Embed hosted checkout. - Fail-closed redirect verification: if you’ve configured a redirect
secret, your return-page handler treats a missing or invalid
sigas unverified — never as proof of payment — see Verify the 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_urldomains are registered inallowed_domains. - You handle the failure scenarios in Handle hosted checkout failures.
Elements, no 3D Secure
Elements, no 3D Secure
- Install and load Elements using the npm
loader (with SRI where applicable), not an unpinned CDN
<script>tag. - Accept a card payment works end-to-end in sandbox with approval and decline test cards.
- Your Content Security Policy matches Content Security Policy, rolled out report-only first before you enforce it.
- You handle
ElementsErrorfromsubmit()by showingcustomerMessageand checkingretryAllowed— never the rawmessage. - You handle the failure scenarios in Handle Elements failures.
Elements + RadiumOne 3D Secure
Elements + RadiumOne 3D Secure
- 3DS with Elements works end-to-end in sandbox with frictionless, challenge, and not-authenticated test cards.
- Your server independently verifies the
three_dsref at charge time and never branches on the client-reported status alone — see 3D Secure overview: server policy. - Challenge return pages send
Referrer-Policy: no-referrer, matching Challenge presentation and redirects. - 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.
Elements + your own 3D Secure provider (Beta, gated)
Elements + your own 3D Secure provider (Beta, gated)
- You’ve requested enablement
for this outlet — the gateway rejects
three_dsevidence for outlets that aren’t enabled. - Use your own 3DS 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.
High-risk operations
Only applies if you use standalone refunds.- 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
Test your integration
Run every scenario in this checklist first.
Security and PCI scope
Full key-safety and PCI guidance.
Frequently asked questions
Quick answers on keys, refunds, duplicates, and hosted checkout.