Skip to main content
This is the Live “Elements without 3D Secure” path: mount a card field, tokenize the card in the browser, and charge it from your server. It’s the fastest way to accept a card with your own checkout UI.

How it works

  1. Your server creates an access token, then a payment session, and passes session_id, session_secret, and pubkey_jws to the browser.
  2. The browser mounts a card field and the shopper types their card details directly into it.
  3. The browser calls elements.submit(). The card iframe encrypts the card data and tokenizes it with RadiumOne directly — your JavaScript never sees the raw card number.
  4. The browser sends the resulting token to your server.
  5. Your server charges the token with POST /v1/transactions/purchase.
  6. Your server returns the result; your page shows success or a decline message.

Before you begin

Complete Install and load Elements first. You’ll need a sandbox secret key (r1sk_test_…) on your server and a publishable key (r1pk_test_…) in the browser.
Amounts are always integers in the currency’s minor unit. For example, 5000 for SGD means SGD 50.00.

Steps

1

Create an access token on your server

Exchange your secret key for a short-lived access token (API reference). Cache it server-side and reuse it until it expires.
2

Create a payment session on your server

Create a session (API reference) and return session_id, session_secret, and pubkey_jws to the browser as-is — never re-serialize pubkey_jws. If this call fails, see Handle expired sessions and card tokens.
3

Mount a card field in the browser

4

Tokenize the card on submit

Call your server for a session (steps 1–2), pass its response straight into elements.submit(), then charge the result — all under one guard so the Pay button stays disabled for the entire attempt, not just the submit() call. This is the pattern every failure guide on this site assumes; see Prevent double submission for why the guard matters.
submit() throws an ElementsError for validation failures, tokenization failures, and gateway bind errors. Always show customerMessage (never the raw message, which is developer-facing) and check retryAllowed before offering a retry. See Handle tokenization failures and Handle browser network errors for the specific failure paths.
5

Charge the token on your server

Send the token from your server — never from the browser — to POST /v1/transactions/purchase (API reference). If the token has expired, see Handle expired sessions and card tokens.
Create and persist one request_id per payment attempt on your server before calling purchase, and reuse it if you retry — Elements sends no idempotency key of its own, so your server is the only thing standing between a retried charge call and a duplicate payment. See Prevent duplicate payments for the full pattern.

Handle the result

Any 2xx response is a result you must branch on status — never on response_code (that’s the verbatim host/acquirer code; useful for support tickets, not for your app logic).
Balance inquiry also takes a request_id field, but it isn’t an idempotency key — there’s no dedup or replay store. Every call re-queries the rewards host, even with the same request_id.
Keys are 8–64 characters, [a-zA-Z0-9-] only, unique per merchant account. Generate one key per order attempt and persist it to your database before you send the first request — never mint a new key just to retry the same attempt. See Prevent duplicate payments.
PENDING means the outcome isn’t known yet — most often after a processor timeout. Don’t assume success or failure. Recover it one of two ways:
  1. Wait for a webhook (payment.*, authorization.*, refund.* — see Webhook event types).
  2. Call GET /v1/transactions/{id}/status for a live inquiry against the acquirer.
If you don’t have the transaction id yet — a client-side timeout before the first response arrived — replay the same request with the same request_id and body. The replay returns the stored transaction and its id, whatever status it’s reached. Never re-submit with a new idempotency key just because the first attempt is slow — that risks a second charge for the same order. token from elements.submit() is single-use context tied to that session; treat it as opaque and never branch on its shape.

Test your integration

Use a sandbox publishable key and a test card. See Test your integration for sandbox test cards and scenarios.

Go-live notes

Next steps

Add 3D Secure

Authenticate the card with RadiumOne 3D Secure before charging it.

Card fields and events

Split fields, validation states, and events in depth.
Last modified on September 15, 2026