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

# Architecture - Elements SDK

> Where Elements sits between the shopper's browser, your server, and RadiumOne, and the trust boundaries that keep raw card data out of your systems.

export const DataFlow = ({letter, from, to, twoWay, children}) => <div className="r1-flow" role="listitem">
    <span className="r1-flow__badge" aria-hidden="true">{letter}</span>
    <div className="r1-flow__content">
      <div className="r1-flow__title">
        <span className="r1-flow__sr-only">{`Flow ${letter}: `}</span>
        {from}
        <span className="r1-flow__arrow" aria-hidden="true">
          <Icon icon={twoWay ? "arrow-left-right" : "arrow-right"} size={14} />
        </span>
        <span className="r1-flow__sr-only">{twoWay ? " and back to " : " to "}</span>
        {to}
      </div>
      <div className="r1-flow__body">{children}</div>
    </div>
  </div>;

export const DataFlows = ({children}) => <div className="r1-flows" role="list">
    {children}
  </div>;

Elements is three pieces working together: your checkout page (which you build), a RadiumOne-hosted card iframe (which you never touch), and your server (which holds the secret key and moves money). This page maps how those three pieces — plus RadiumOne's CDN and Payments API gateway — fit together, so [Modules and packages](/elements/modules-and-packages) makes sense in context.

## How it works

### Components

* **Your checkout page** (browser) — the page you build; loads the SDK and mounts the card iframe.
* **Your server** — holds your secret key; creates sessions and charges tokens.
* **CDN** — serves the versioned, SRI-pinned SDK script.
* **Card iframe** (browser, RadiumOne-hosted) — the only place that ever sees the raw card number.
* **Payments API gateway** — binds cards to tokens and processes charges.
* **Card networks & issuer** — authorize the payment.
* **Issuer ACS** — runs the 3D Secure challenge.

### Data flows

The letters match the flow tags in the diagram.

<DataFlows>
  <DataFlow letter="A" from="RadiumOne CDN" to="Your checkout page">
    Your page loads the Elements SDK from RadiumOne's CDN; Subresource Integrity (SRI) checks the script hasn't been tampered with.
  </DataFlow>

  <DataFlow letter="B" from="Your server" to="Payments API gateway">
    Your server creates a session, authenticated with your secret key.
  </DataFlow>

  <DataFlow letter="C" from="Your server" to="Your checkout page">
    Your server passes the session details — `session_id`, `session_secret`, and `pubkey_jws` — to your page.
  </DataFlow>

  <DataFlow letter="D" from="Card iframe" to="Payments API gateway">
    The card iframe encrypts the card details and sends them to RadiumOne using your publishable key.
  </DataFlow>

  <DataFlow letter="E" from="Card iframe" to="Your checkout page">
    The iframe hands back a card token, and your page sends it to your server. Your page never sees the card number.
  </DataFlow>

  <DataFlow letter="F" from="Your server" to="Payments API gateway">
    Your server charges the token, with a `request_id` so a retry can't charge twice.
  </DataFlow>

  <DataFlow letter="G" from="Payments API gateway" to="Card networks & issuer">
    The card network and the shopper's bank approve or decline the payment.
  </DataFlow>

  <DataFlow letter="H" from="Payments API gateway" to="Issuer ACS" twoWay>
    When 3D Secure is required, the shopper completes the bank's challenge.
  </DataFlow>
</DataFlows>

See [Modules and packages](/elements/modules-and-packages) for which package loads the SDK (flow A) for your stack, and [Accept a card payment](/elements/accept-a-card-payment) for flows B-F end to end.

## PCI scope: only the iframe sees the card

With Elements, card fields render inside RadiumOne-hosted iframes and tokenize the card before it reaches your server. Your website never touches raw card data, which keeps your PCI DSS scope reduced compared to handling card numbers directly.

The card iframe is the only place in this whole picture that ever holds a raw card number — not your checkout page's JavaScript, not your server, and not RadiumOne's own gateway logs (the gateway receives the card already encrypted as a JWE and only decrypts it inside its tokenization boundary). Compare this to [hosted checkout](/hosted-checkout/architecture), where the card fields render on a RadiumOne-hosted *page* instead of an iframe on yours — the PCI-scope outcome is the same, but Elements is the integration to reach for when you need the fields inside your own checkout UI.

## Data and trust boundaries

Four mechanisms keep the pieces above honest about who can see what:

<Steps>
  <Step title="Publishable vs. secret key">
    The browser only ever holds a publishable key (`r1pk_…`), which can create `Elements`/`ThreeDS` instances and bind sessions — never charge, capture, void, or refund. Your secret key (`r1sk_…`) stays server-side.

    <Danger>
      Never use a secret key (`r1sk_…`) in browser code, mobile apps, or anywhere a shopper can inspect it. Secret keys belong on your server only.
    </Danger>
  </Step>

  <Step title="pubkey_jws signature verification">
    `pubkey_jws` is a compact JWS your server relays byte-for-byte from session creation. The card iframe verifies its signature, key ID, session binding, and expiry before it trusts the encryption key inside — your checkout page's JS only checks the wire shape, never the cryptographic proof.
  </Step>

  <Step title="Subresource Integrity">
    The npm loader bakes in an SRI hash automatically; the CDN `<script>` tag needs one from your release notes. Either way, a tampered script fails to load rather than running silently. See [Install and load Elements](/elements/install-and-load#runtime-attestation).
  </Step>

  <Step title="Content Security Policy">
    `script-src` and `frame-src` scope which origins your page will load the SDK script and card iframe from. Adding 3D Secure needs a few more directives, scoped to just your checkout and return routes. See [Content Security Policy](/elements/content-security-policy).
  </Step>
</Steps>

## Next steps

<Columns cols={2}>
  <Card title="Modules and packages" icon="package" href="/elements/modules-and-packages">
    Every package and runtime object Elements ships, and which one you need.
  </Card>

  <Card title="Install and load Elements" icon="download" href="/elements/install-and-load">
    Add the SDK to your page with npm, CDN, or React.
  </Card>

  <Card title="Accept a card payment" icon="credit-card" href="/elements/accept-a-card-payment">
    Mount a card field and charge your first test payment.
  </Card>

  <Card title="Content Security Policy" icon="shield-check" href="/elements/content-security-policy">
    The CSP directives Elements needs, and why 3D Secure needs a few more.
  </Card>
</Columns>
