Hosted Checkout
Redirect customers to a payment page hosted by TRADOM Payment
Hosted Checkout lets you accept stablecoin payments without building the payment UI yourself. Your backend creates an order and redirects the customer to the returned URL.
How it works
Merchant backend ── createOrder ──> TRADOM Payment
│ │
└── redirect to checkoutUrl ──────> Hosted Checkout
│
asset / network selection
QR code and confirmation
│
Merchant backend <── checkoutStatus ───────┘Create a checkout
const checkout = await gatewayClient.gateway.createOrder({
orderId: "order_12345",
amount: 178.25,
currency: "USD",
buyerName: "Taro Yamada",
buyerEmail: "taro@example.com",
country: "JP",
items: [{ name: "T-shirt", category: "apparel" }],
returnUrl: "https://shop.example/thanks",
cancelUrl: "https://shop.example/cart",
mode: "live",
});| Field | Required | Description |
|---|---|---|
orderId | Yes | Your order ID. Creation is idempotent for the same ID |
amount | Yes | A positive finite number in the currency's major unit |
currency | No | JPY (the default when omitted) or USD |
buyerEmail / country / items | Recommended | Used for compliance screening |
buyerName | No | Customer name |
returnUrl | Strongly recommended | Destination after payment |
cancelUrl | No | Destination after cancellation or screening rejection |
mode | No | live (default), testnet, or simulate. On staging (Fireblocks sandbox) live is coerced to testnet; read the effective mode from the response's mode |
Handle the response
if (checkout.status === "ok") {
return redirectTo(checkout.checkoutUrl);
}
// kyc_declined is also a successful API response.
return redirectTo(checkout.checkoutUrl);kyc_declined is not an exception. Only network and validation errors throw a PaymentError.
An ok response echoes amount as a decimal string exactly as submitted and currency as normalized JPY or USD. On save, JPY is rounded to a whole number and USD to two decimals, so checkoutStatus returns the rounded value (e.g. "178" → "178.00"). Send whole yen for JPY and at most two decimals for USD.
currency: "USD" is available in production. JPY orders are quoted with a USD/JPY reference rate; USD orders use the stablecoin's 1:1 USD peg.
Check payment status
const status = await gatewayClient.gateway.checkoutStatus(checkoutToken);checkoutStatus uses the public checkout token and does not send an API key. Do not trust the browser redirect alone. Store the association between your orderId and checkout token on your backend.
| What to check | Field |
|---|---|
| Canonical order amount and fiat currency | status.amount / status.currency |
| Whether the full amount was received | status.confirmed |
| Whether the deposit is short | status.underpaid (amounts in sentAmount / expectedAmount) |
| Deposit tx hash, amount, currency, network | status.txHash / paidAmount / paidCurrency / paidNetwork |
| Current processing stage | status.state.state |
| Whether the state is terminal | status.state.isTerminal |
| Underpayment or overpayment | status.state.matchStatus |
| Message shown to the customer | status.message |
| Execution mode | status.mode |
| Testnet faucets | status.faucets |
confirmed means "the full amount was received". An underpaid deposit yields confirmed: false together with underpaid: true (state.matchStatus: "underpaid"). Use underpaid to tell "no deposit yet" apart from "deposit received but short".
checkoutStatus is the same endpoint the Hosted Checkout page polls. It is not strictly read-only: it may attach an as-yet-unclaimed deposit to the order and record the underpaid classification (it never advances or cancels the order). message is null while the order is awaiting the deposit (M-5 is not shown).
See the status reference for every state.
Session and quote lifetime
- Checkout session: 45 minutes after the first view (extendable once by 5 minutes via
extendSessionfor the wallet check) - Deposit quote: 15 minutes
- Quote refreshes: up to 2
Create a new checkout after the session expires. The session starts when the Hosted Checkout page is first opened; for an order driven purely through the SDK whose page was never opened, sessionExpired stays false.
Checkout controls
For a normal redirect integration, Hosted Checkout performs the following operations. Use the SDK methods for automated smoke tests or when controlling a token-scoped flow from your backend. These calls are authorized by the checkout token and do not send an API key.
const quote = await gatewayClient.gateway.selectAsset(checkoutToken, {
token: "usdc",
network: "ethereum",
});
const refreshed = await gatewayClient.gateway.refreshQuote(checkoutToken);
const accepted = await gatewayClient.gateway.acceptTos(checkoutToken);selectAsset and refreshQuote return the fiat currency, deposit address, token amount, rate, chain metadata, mode, and faucets. JPY orders use a USD/JPY reference rate; USD orders use the stablecoin's 1:1 USD peg. A checkout can refresh its quote up to two times.
A simulate order can be completed after asset selection without moving funds.
const completed = await gatewayClient.gateway.simulateDeposit(checkoutToken);simulateDeposit rejects live and testnet orders. See Test your integration for the complete flow.
Reference quote and wallet check
The helper endpoints used by the Hosted Checkout configure screen are also available from the SDK.
// "≈ amount" at the current rate (non-binding, cached ~12 s)
const ref = await gatewayClient.gateway.referenceQuote(checkoutToken);
if (ref.status === "ok") {
console.log(ref.quotes.usdc?.amounts.ethereum); // "329.87"
}
// State of the buyer-wallet pre-check (small test transfer)
const verify = await gatewayClient.gateway.verifyStatus(checkoutToken);
// verify.screening: "pending" | "clear" | "received" | "blocked"
// One-time +5 minute session extension for the wallet check
const ext = await gatewayClient.gateway.extendSession(checkoutToken);
// ext.extended: true only on the first call
// Reset a blocked verification so the buyer can retry with another wallet (max 2 per order)
const reset = await gatewayClient.gateway.resetVerification(checkoutToken);
// reset.screening: "pending" | "not_required", reset.attemptsRemainingresetVerification throws not_blocked (409) unless the current verdict is blocked and reset_limit_reached (409) once the resets are used up. After the session expires every call throws session_expired (409).
TRON and slippage verdicts
TRON / USDT can be selected when Tron payments are enabled for the merchant.
const verdict = await gatewayClient.gateway.slippageCheck(checkoutToken, {
token: "usdt",
network: "tron",
});
if (verdict.status === "exceeded") {
return showAnotherAsset();
}The verdict is ok, exceeded, skipped, or unavailable. The backend checks the verdict again when the asset is selected.
slippageCheck is available in production. TRON / USDT availability depends on merchant configuration (Tron payments enabled). slippageCheck accepts canonical network names only (ethereum / polygon / tron); the aliases selectAsset accepts (eth / pol / trx) return unsupported_asset.
Authentication
The SDK uses X-Api-Key by default. The REST API accepts either form:
X-Api-Key: gw_live_...
Authorization: Bearer gw_live_...