Checkout
Use StableOps Checkout to create one-time sessions and reusable payment links, customize branding, analyze conversion, and fulfill finalized payments safely.
Checkout is for flows where you want less frontend integration work. StableOps hosts the payment page and displays the chain, asset, exact amount, receiving address, and order status. You can create a one-time Checkout Session from your backend for each business order, or create a reusable Payment Link in the Dashboard. Both entry points generate an independent Payment Order for each payment and report its lifecycle through the same Webhooks.
Choose a payment entry point
| Entry point | Best for | Public URL | What happens for each payment |
|---|---|---|---|
| One-time session | E-commerce orders, invoices, and known internal IDs | /c/{sessionId}?client_secret=... | Your backend creates the Payment Order and gives its private URL to the payer |
| Reusable Payment Link | Fixed-price products, registrations, tips, and QR codes | /p/{slug} without an API key or clientSecret | Opening the link creates a new Checkout Session and independent order |
A one-time session binds merchantOrderId and metadata directly to an existing business object, so use it when a specific order must remain traceable. A Payment Link stores reusable payment configuration; it is not one Payment Order that can be paid repeatedly. Continue using one-time sessions when each payer needs a different amount, customer context, or internal order ID.
One-time Checkout Sessions
- Create a Checkout Session on your backend: call
stableops.checkoutSessions.createwith your API key, merchant order ID, amount, accepted assets, display title, success URL, cancel URL, and optional WalletConnect project ID. - Redirect the payer to Checkout: after creation, issue a 303 redirect to
checkout.url, or return the URL to your frontend and callwindow.location.assign(checkout.url). - Let the payer complete the transfer: the hosted page shows the chain, asset, exact amount, and receiving address, and tracks status live. The payer can use a browser wallet, mobile wallet, or manual transfer. The chain scanner applies the same matching rules in every case.
- Complete business logic from webhooks: receive and verify
payment.confirmed/payment.finalizedwebhook events, then provision service, post ledger entries, or update your order. Do not rely onsuccessUrlalone; it is a browser return path, not final payment proof.
You can control the hosted Checkout language by adding lang to the URL, for example ?client_secret=...&lang=es. Supported values are en, zh, zh-Hant, es, pt-BR, vi, id, and ja. If lang is omitted, Checkout matches the payer's browser language when possible and falls back to English.
Reusable Payment Links
Expand Checkout in the Dashboard sidebar, open Payment links, and select Create payment link to open the creation dialog. No code is required; provide:
- a name and optional payment description;
- a fixed base amount;
- the
autoorexactamount-allocation mode; - one or more accepted chain and asset pairs, filtered for the current Sandbox or Live environment.
After creation, the Dashboard provides a stable URL such as https://pay.stableops.dev/p/{slug}. You can copy it, open it in a new window, or encode it in an offline QR code. A new link starts active and can be deactivated or reactivated at any time.
Each new attempt gets an independent order
When a payer opens /p/{slug}, the public page creates a Checkout Session and Payment Order from the saved link configuration, then redirects to that session's /c/{sessionId} page. Every payment has its own:
- Payment Order ID, Checkout Session ID, and
clientSecret; - exact payable amount, candidate chains, and receiving addresses;
- expiration, confirmation progress, and terminal state.
Refreshing the same browser tab while its current session is still valid reuses that creation request, so a refresh does not reserve another address. Opening the link again after expiry starts a new payment attempt.
Deactivating a Payment Link only prevents it from creating new sessions. It does not cancel Payment Orders or Checkout Sessions that already exist. If a link spreads beyond its intended audience, a campaign ends, or traffic becomes suspicious, deactivate it and check that every chain's receiving address pool still has enough capacity.
amountMode: 'auto' is useful with shared addresses. StableOps adjusts the base amount by the token's smallest unit only when that amount conflicts on every candidate shared address. exact always keeps the fixed amount. In both modes, the payer must send the returned order.amount exactly—never an underpayment, overpayment, or split transfer.
Configure Checkout
Expand Checkout in the Dashboard sidebar and open Brand settings. This page provides a basic Checkout preview alongside controls for:
| Setting | Checkout behavior |
|---|---|
| Brand name | Replaces the default name in the page header |
| Logo URL | Displays your logo; the payer's browser must be able to load it publicly |
| Primary color | Styles primary buttons, progress, and key interactions |
| Accent color | Styles hover states and supporting brand elements |
| Contact merchant | Opens webpage URLs in a new tab or displays localized text in a dialog matching the checkout language |
Localized contact text currently supports English, Simplified Chinese, Traditional Chinese, Spanish, Brazilian Portuguese, Vietnamese, Indonesian, and Japanese. Checkout falls back to English when the payer's current language has no configured text and hides the contact link only when English is also empty.
Branding is isolated by organization and environment, so configure Sandbox and Live separately. Every session—whether created by your backend or a Payment Link—stores a snapshot of the branding at creation time. Later changes affect only new sessions, not sessions that are already open or in progress. Clear and save a field to restore its default presentation.
Inspect the Checkout conversion funnel
The Payment links page shows a 30-day funnel for the current organization and environment. Each metric deduplicates browser sessions:
- Page viewed: the payer opened a Checkout page.
- Session loaded: the page retrieved its Checkout Session successfully.
- Payment started: the payer began an injected-wallet, WalletConnect, or manual-transfer flow.
- Transaction submitted: a wallet submitted the on-chain transaction.
- Payment completed: the Payment Order reached
finalized. - Payment conversion: completed browser sessions divided by browser sessions that viewed a page.
In the Payment Links list, Sessions is the link's cumulative number of generated sessions, while Conversion uses page-view and completion data from the last 30 days. Use the funnel to locate page-load, wallet-connect, and transaction-submission drop-off. It is not a financial ledger and does not replace Webhooks, Payment Order retrieval, or daily reconciliation.
Supported payment methods
Checkout supports the following three payment methods; users can choose freely based on their environment.
Browser wallet (desktop)
The page auto-detects injected wallet providers and connects on selection. The user confirms the transaction in their wallet to send.
- EVM chains: MetaMask, Rabby, or any wallet injecting
window.ethereum. 12 chains supported. - Solana: Phantom or any wallet injecting
window.phantom.solana. Mainnet + devnet. - TRON: TronLink or any wallet injecting
window.tronLink.tronWeb. Mainnet + Nile.
Mobile wallet (WalletConnect)
Requires walletConnectProjectId when creating the session. Checkout shows a wallet list; the payer picks one and connects via WalletConnect QR code or deep link to sign the transaction. Supports EVM, Solana, and TRON chains — for TRON, the wallet signs the transfer over WalletConnect and Checkout broadcasts it on-chain.
Payment Links created in the current Dashboard do not have a WalletConnect project ID field, so they show only injected browser wallets and manual transfer by default. To offer mobile-wallet payment, create a one-time Checkout Session from your backend and pass walletConnectProjectId.
Compatible wallets: MetaMask, Trust Wallet, Coinbase Wallet, OKX, Binance Wallet, TokenPocket, TronLink, Rainbow, Zerion, Ledger Live, and any WalletConnect-compatible wallet. Checkout filters the list by the order's chain families; TRON orders show the TRON-capable wallets: Trust Wallet, TokenPocket, TronLink.
Solana and TRON wallet payments need an RPC node to build and broadcast the transfer, and Checkout handles this for you: mainnet orders are forwarded through StableOps' backend RPC proxy, while testnet orders (Solana Devnet, TRON Nile) connect directly to public RPC nodes — free but occasionally rate-limited; if a testnet payment fails to build, wait a moment and retry.
Manual transfer
Always available. The payer copies the receiving address from the page and sends from any wallet or exchange. Scanners match the deposit by address.
Create a session on the backend
import { StableOps } from '@stableops/api-sdk'
const stableops = new StableOps({
apiKey: process.env.STABLEOPS_API_KEY!,
})
const merchantOrderId = 'order_123'
const checkout = await stableops.checkoutSessions.create(
{
merchantOrderId,
amount: '49.00',
amountMode: 'auto',
acceptedAssets: [{ chain: 'base-sepolia', asset: 'USDC' }],
expiresAt: new Date(Date.now() + 30 * 60 * 1000).toISOString(),
title: 'StableOps Starter Plan',
successUrl: `https://stableops.dev?result=success&orderId=${merchantOrderId}`,
cancelUrl: `https://stableops.dev?result=canceled&orderId=${merchantOrderId}`,
walletConnectProjectId: process.env.WALLETCONNECT_PROJECT_ID,
},
{ idempotencyKey: 'order_123' },
)
return Response.redirect(checkout.url!, 303)Parameter guidance
merchantOrderId: use your internal order ID, and reuse it as the idempotency key to avoid duplicate sessions.amountMode: 'auto': lets StableOps adjust the amount slightly to reduce collisions on shared receiving addresses.acceptedAssets: start with testnet assets, then switch to the production chains you actually support.successUrl/cancelUrl: browser return paths only. Treat webhooks as the source of truth for payment state.walletConnectProjectId: optional. Register a free project ID in Reown Cloud. When present, Checkout shows a mobile wallet entry; EVM, Solana, and TRON chains all connect via WalletConnect QR code or deep link to sign the transaction:- EVM and Solana chains: MetaMask, Trust Wallet, Coinbase, OKX, Binance Wallet, Rainbow, Zerion, Ledger Live, and generic WalletConnect.
- TRON chains: Trust Wallet, TokenPocket, TronLink. The wallet signs the transfer over WalletConnect and Checkout broadcasts it on-chain.
- When omitted, no mobile wallet entries are shown (EVM / Solana / TRON alike); payers can still use an injected browser wallet or manual transfer.
metadata: include plan, user ID, or internal labels. The public Checkout page does not expose order metadata.
Online testing
The panel below uses your sandbox API key directly in the browser to create a Checkout Session, then redirects to the public Checkout page. You can customize merchant order ID, amount, title, description, return URLs, and order metadata to see how merchant parameters appear in the payment flow.
Use a sandbox key. It stays in your browser and is sent directly to the API. In production, call the API from your backend, not the browser.
When on, a deterministic burner sandbox address is imported for this order before the session is created. Useful when your org has no addresses yet. Turn it off to use only the addresses you manage yourself.
Security notes
- This panel is for sandbox testing only. In production, create Checkout Sessions on your backend and never expose a live API key in the browser.
- The
clientSecretopens the public payment page and should only be sent to the current payer. - The Checkout page reads only the public session. It does not require Clerk sign-in and does not expose order metadata.
- A Payment Link contains no API key or
clientSecret, but it is a public reusable entry point: anyone with the URL can start a new payment attempt. Share it only through intended channels, and monitor address-pool capacity and the conversion funnel. - Deactivating a Payment Link cannot revoke Payment Orders that have already been generated. To stop an existing order, follow that Payment Order's own state and cancellation rules.
Handle the webhook
Checkout settles through the same payment orders as the SDK flow, so payment state always arrives over webhooks — never trust the successUrl redirect alone.
- Webhooks — event types, payload shape, and delivery/retry behavior.
- Verify webhook signatures — validate
X-Product-Signaturebefore parsing the body. - Webhook troubleshooting — debugging missed or failing deliveries.
How is this guide?
Last updated