Checkout
Merchant checkout sessions. The customer pays in crypto; the payment is recorded in your ledger as a stablecoin credit, which can't be spent or withdrawn yet. In sandbox today.
POST /api/v1/checkout/sessions
Bearer apk_ · write:checkout · Idempotent
{
"amountUsdCents": 2500,
"description": "Pro plan · 1 seat",
"merchantReference": "order-1234",
"successUrl": "https://yoursite.com/thanks",
"cancelUrl": "https://yoursite.com/cart",
"acceptedAssets": ["USDC", "USDT"],
"acceptedChains": ["ethereum", "polygon", "arbitrum", "base", "solana"],
"expiresInSeconds": 1800,
"settlementOption": "hold_stablecoin" // "convert_to_fiat" is accepted but does nothing yet
}Returns { session: { id, status, hostedUrl, receivingAddresses, expiresAt, … } }. Send the customer to hostedUrl.
successUrl and cancelUrl are optional http(s) addresses on your site.
After paying, the customer is sent to successUrl; without one the hosted
page shows that the payment was received. cancelUrl is the "Back to …"
button on an expired link, so make it a page for someone who hasn't paid
(your shop or cart, not a thank-you page). expiresInSeconds is 300 to 86400
(default 1800).
acceptedChains is optional. Left out, the session takes those of
ethereum, polygon, arbitrum, base and solana that your
acceptedAssets can be paid on, and for a coin none of them takes (BTC),
the networks it can be paid on. Listed, a network none of your coins can be
paid on is left out (["USDT"] on those five networks takes ethereum,
polygon, arbitrum and solana); the returned acceptedChains is the list
the session kept.
400 when amountUsdCents is over 10000000 ($100,000.00), or when a coin in
acceptedAssets can't be paid on any of the acceptedChains: the message
names the networks the coin works on. SOL is refused on every network for
now (its payments can't be detected yet).
Coins and networks lists every pair.
GET /api/v1/checkout/sessions
Bearer apk_ · read:checkout
Last 50 sessions.
GET /api/v1/checkout/sessions/[id]
Bearer apk_ · read:checkout
Full row, merchant-side. A link you cancelled on Get paid has expiresAt set
to the moment you cancelled it and metadata.sorenpay_cancelled_at (the same
time); the rest of your metadata is kept. Its status turns expired as
for a link that ran out of time: at once when no customer picked a way to pay,
otherwise once the payment watcher has taken a last look on chain (within a
minute). Until then it can't be paid or picked, like any link past its time.
GET /api/v1/checkout/sessions/[id]/status
Public
Customer-safe status poll — used by the hosted page. An unpaid link past its
time answers expired. The chosen coin, network and address are returned only
once a payment is seen on chain: until then each visitor's choice stays their
own. acceptedAssets, acceptedChains and receivingAddresses list only what
can still be paid (a link made before a coin or network pair was dropped keeps
it in its row, not here). completedAt is when a paid link was paid.
cancelled is true for an expired link you cancelled. For an hour after
a link ends (expired or cancelled), a payment sent before it ended still moves
it on to confirming and completed.
POST /api/v1/checkout/sessions/[id]/select
Public
Customer picks (asset, chain) and gets that pair's receiving address. Starts
the on-chain confirmation watcher, which watches every receiving address of
the session: a payment is recorded on the pair whose address it reached, even
if another visitor picked a different pair later. 409 once a payment has
been seen, 410 once the link has ended (cancelled too), 400
pair_not_supported for a pair a link made before the pair was dropped still
lists (USDT on Base, ETH on Polygon, SOL).
{
"asset": "USDC",
"chain": "polygon"
}POST /api/v1/checkout/sessions/[id]/simulate
Bearer apk_ · write:checkout · sandbox only
Completes the session without a real blockchain payment. Refused on a live
deployment or a live workspace. 409 link_expired for an expired or
cancelled link, 422 asset_chain_not_selected before a coin and network
were picked on the hosted page.
State machine
pending → awaiting_payment → confirming (n/3) → completed | underpaid | failed
↓ ↑
expired ──────────────┘
(TTL, or cancelled on Get paid. A payment sent before the link
ended and found within an hour of its end still moves it on. A
payment seen that never confirms ends expired a day after the TTL.)
Outbound webhook
checkout.session.completed fires to every active workspace endpoint subscribed to that event (or *). Carries { id, amountUsdCents, txHash, payerAddress }.