0fra

Hosted Checkout

Send a URL, take payment. 0fra handles the wallet UX so you don't have to.

A Checkout Session is a single-use URL where your customer pays. 0fra hosts the page, prompts the customer for their wallet, and walks them through approve + payOrder on-chain. You only need three moving pieces:

  1. Create the session via API → get a url
  2. Redirect the buyer to that URL
  3. Handle the success/cancel return + order.confirmed webhook

Create a session

curl -X POST https://api.0fra.dev/v1/checkout/sessions \
  -H "Authorization: Bearer sk_live_..." \
  -H "Idempotency-Key: $(uuidgen)" \
  -H "Content-Type: application/json" \
  -d '{
    "merchant_id": "vendor_2001",
    "order_no": "ORDER-1001",
    "chain_id": 84532,
    "token": "USDC",
    "amount": "100.00",
    "success_url": "https://acme.example/success?order=ORDER-1001",
    "cancel_url":  "https://acme.example/cart",
    "metadata": { "ticket_type": "VIP" }
  }'
{
  "id": "...",
  "session_token": "cs_live_4f3...",
  "url": "https://dash.0fra.dev/checkout/cs_live_4f3...",
  "state": "open",
  "order_id": "...",
  "expires_at": "2026-04-25T18:30:00Z"
}
FieldRequiredNotes
merchant_idyesThe external_id you assigned when onboarding
order_noyesUnique within your platform — your own order number
chain_idyes84532 (Sepolia) or 8453 (mainnet, beta)
tokenyesUSDC for now
amountyesDecimal string in human units ("100.00", not 100000000)
success_urlnoWhere to send the buyer after success
cancel_urlnoWhere to send them if they bail
payer_addressnoLock the session to a specific wallet address
expires_in_secondsnoDefault 30 min; max 24 h
metadatanoUp to 5 KB of JSON — echoed back in webhooks

What the buyer sees

The hosted page lays out:

  • The price and the breakdown of fees (so the buyer knows what's going to whom)
  • The merchant's display name
  • A wallet connect button
  • Two-step flow:
    • Step 1 — Approve USDC to PaymentRouter (one click; one-time per token)
    • Step 2 — Sign and broadcast payOrder() (one click)
  • A live confirmation badge once the tx is mined

Polling vs webhooks

Don't poll the session for state from your backend. Instead:

  • Backend: subscribe to order.confirmed / order.failed webhooks
  • Front-end after redirect: read the session via the public endpoint
GET /v1/checkout/sessions/:token  (no auth)

Returns state (open / completed / expired) and the underlying order. Suitable for "show pending..." spinners on your success page.

Expiry

Sessions auto-expire when expires_at passes. The page renders a friendly "expired" state and the underlying order goes to expired.

You can't extend an expired session — generate a new one (with the same order_no if your business logic allows).

Embedding without a redirect

Today the only flow is full-page redirect. SDK / iframe embeds are on the roadmap.

On this page