Skip to main content
Call the Payments API directly from your server when you’re using RadiumOne Elements (or your own card-on-file flow) instead of hosted checkout. Use purchase to charge a card in one step, or authorize to reserve funds and capture the amount later.

Purchase vs. authorize

An authorization must be captured within your account’s capture window or it lapses to AUTH_EXPIRED. See Payment lifecycle for the full state diagram.
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.
This request requires a valid access token. See Authentication to obtain one with POST /v1/auth/token before you continue.

Request fields

Amounts are always integers in the currency’s minor unit. For example, 5000 for SGD means SGD 50.00.
order_reference accepts up to 128 characters, but issuer statements, receipts, and some reports display far fewer. Put the part a human needs to recognize — your own order number — in the first 20–30 characters.
Fields you don’t recognize in the request body are currently ignored rather than rejected — don’t rely on this; only send documented fields.

Steps

1

Charge the card immediately (purchase)

Use this when you can fulfil the order right away. API reference.
If the call times out or you get no response, see Handle timeouts and unknown outcomes — never mint a new request_id for the same attempt.
2

Or reserve funds for later (authorize)

Use this when you’ll capture a (possibly different, smaller) amount after the fact. API reference.
If the operation is disabled or no terminal is available for your outlet, see Fix operations unavailable for your outlet.

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). A decline is still a successful HTTP call — 201 with data.status: "DECLINED" in the body, not an error response. Always branch on status, never on the HTTP status code alone:
Treat a decline as final for this attempt — don’t retry the same request_id hoping for a different outcome; ask the shopper for another card instead. See Handle declined payments for the full walkthrough.

Idempotency and replay

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.
Persist request_id to your database before you send the first request, so a retry after a timeout reuses the same key and body instead of minting a new one:
See Resolve idempotent replays and conflicts for the full decision flow.

Test your integration

Use the approval, decline, and timeout scenarios in Test your integration before you go live.

Go-live notes

  • Confirm your integration handles PENDING by polling GET /v1/transactions/{id}/status or waiting for a webhook — never by guessing.
  • Never mint a new request_id to “retry faster” after a timeout; it risks a duplicate charge.
  • If you take 3D Secure evidence, verify it server-side per 3D Secure overview before charging.
  • Work through the full go-live checklist.

Next steps

Payment lifecycle

See every status a transaction can reach and how it gets there.

Capture an authorization

Capture a reserved amount within the capture window.

3D Secure overview

Add strong customer authentication to your charges.

Webhooks

Get notified asynchronously instead of polling.
Last modified on September 15, 2026