Skip to content

Product

One payment layer that brings on-chain money into your books ​

This page explains how it works — how an order moves, how callbacks are verified, where the money goes, and who catches it when something breaks. Read it and you'll know whether it fits your system.

The premise

Why cross-border sellers start taking USDT ​

No bank in the loop

The worst part of cross-border collection isn't the fee, it's whether the money arrives at all: rejected account openings, frozen accounts, returned transfers. A stablecoin transfer doesn't ask about your bank or about the relations between two countries.

No FX loss, no intermediary bank

USDT tracks the dollar, so you collect, book and settle in one unit. You drop the conversion spread and the correspondent fees — and those "we're $17 short" moments at reconciliation.

24/7, in minutes

Weekends, holidays and the middle of the night collect as usual. On-chain confirmation is measured in minutes, not in "T+2 business days".

Programmable

Every payment carries a transaction hash and a confirmation count, so you can reconcile and ship automatically — something traditional rails simply can't give you.

The core

The full life of an order ​

The state machine is the backbone of the whole system: only on-chain verification can push an order to Confirmed. The transaction hash a buyer sends back can only push it to Submitted. That line is what makes a forged receipt impossible.

1 · PENDING
Pending
Order created; the checkout link and QR code are live
2 · SUBMITTED
Submitted
Buyer paid and returned a txHash (a lead, not proof)
3 · CONFIRMED
Confirmed
Confirmations met → we call your system
4 · FINISHED
Finished
You confirm delivery; the order closes
5 · EXPIRED
Expired
Unpaid past the window, it voids and the QR code dies
6 · CLAIM
Manual claim
An on-chain payment we can't match is handed to a human

⚠️ One rule for developers: never compare order states by numeric value (say, "status ≥ 3 means paid"). The codes carry no increasing semantics — a state added later will silently fool that check.

Coverage

Chains and tokens ​

Addresses and validation rules are normalized by chain type (chainType), so adding another chain of a type you already support needs no change to settlement logic.

EVM · BSC / Ethereum / Polygon / Arbitrum…TRON · TRC20TONSolana

One address per chain

A merchant uses a separate address on each chain and can enable or disable them individually; the checkout picks the address for whichever chain the buyer chooses.

Addresses normalized per chain

EVM addresses are lowercased, TRON keeps its case — so one address has exactly one stored form. Otherwise the same wallet is recorded twice and reconciliation never lines up.

Only the chains you enable

You tick the chains and tokens you want in the console. Chains you don't collect never appear at checkout, so a buyer can't pay into the wrong place.

Integration

Callbacks: signed, retried, queryable ​

Callbacks fail (your site restarts, the network hiccups, your code throws), so they must retry. And retrying means a duplicate may arrive, so they must be verifiable and idempotent.

Signature (against forgery)

Headers carry a timestamp and an HMAC-SHA256 signature computed over the timestamp plus the request body; the event type is in X-Pay-Event (for example pay.confirmed). A failed verification is dropped every time.

Retries (against lost orders)

Retried on a backoff: 5s → 30s → 2m → 10m → 30m → 1h → 2h → 6h. Return 2xx and we stop.

Idempotency (against double shipping)

Deduplicate on merchantOrderNo — on a repeat callback just return success and don't ship again.

Active queries (against passivity)

Don't trust callbacks alone: poll the query API against your open orders on a schedule. A missed callback tells you nothing; a query is you going to ask.

A real trap

Amounts take different forms on different endpoints: the Open API (server to server) takes a decimal string in token units (like "100" / "100.5"), while the checkout order endpoint takes an integer string in the smallest unit. They are the opposite of each other — copy the wrong one and you are off by 10^decimals. Trust Create order and Error codes.

Security

How the Open API defends itself ​

①
Per-IP fallback limit
Counted before authentication — it stops a flood of scripts hammering your KEY
②
API KEY auth
Only after auth does the context know which merchant is calling
③
Per-merchant limit
One merchant rotating IPs walks straight past an IP-only gate
④
Merchant identity comes only from the KEY
A merchantId in the request has no code path that reads it

Keep the key server-side

An API KEY must never reach a browser — that hands every visitor the right to create orders as you. The front end uses the checkout link only.

Idempotent ordering

merchantOrderNo is the idempotency key: network timeouts, impatient double-clicks and callback retries never turn into two orders.

No secrets in logs

API KEYs, verification codes and SMTP passwords never get logged; a log line keeps the last four characters or a hash.

CORS narrowed by path

The Open API allows any Origin (it authenticates with a header key and sends no cookies, so there is no CSRF surface); every other endpoint adds no CORS header at all.

Architecture

Two sides, deployed apart: platform and merchant ​

The platform side runs operations (merchant approval, chains and tokens, risk rules, reconciliation); the merchant side runs the business (its own console, its own orders and ledger). The two deploy independently, on separate databases — one merchant's growth spurt never slows anyone else down.

Platform side

Configure chains and tokens, approve and manage merchants, and see platform-wide collection reports. It runs on the platform's own domain and database.

Merchant side

Each merchant's console: collection overview, collection addresses, payment orders, reports, settings. The data sits in its own database and is invisible across merchants.

Checkout

The page the buyer opens (/pay/cashier/m/{merchantNo}). Its own domain and deployment, deliberately kept out of search engines.

Cross-database references are IDs only

Plugins never JOIN across databases — those boundaries are hard, so one bad database doesn't drag the others down. The cost: anything that needs a join gets its own endpoint.

Want to run a real payment first?

Register a merchant, add one address on a chain you control, and you can take your first test payment within five minutes.

Read the docs

Updated:

Web3 payment infrastructure for global merchants