Orders
Create and inspect orders directly via API — the building block under Checkout sessions.
A Checkout Session is a thin wrapper around an Order. If you want full control of the buyer-side UX (e.g. you embed wallet connection in your own front-end), you can skip Checkout sessions and create orders directly.
Create an order
curl -X POST https://api.0fra.dev/v1/orders \
-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",
"metadata": { "customer_id": "cus_42" }
}'The response includes the pay_to (PaymentRouter address) and a fully-formed eip712 message + signature. Hand both to the buyer's wallet — wagmi's writeContract expects the message struct + signature as the two arguments of payOrder().
{
"id": "...",
"state": "pending_payment",
"pay_to": "0x79abe8c3f1e0c444b10d5cbba918578d70283f74",
"amount": "100.00",
"computed_split": {
"service_fee": "1000000",
"platform_fee": "10000000",
"merchant_gross": "89000000",
"reserve_hold": "4450000",
"merchant_available": "84550000"
},
"eip712": {
"domain": { "name": "0fra", "version": "1", "chainId": 84532, "verifyingContract": "0x..." },
"primary_type": "PayOrder",
"types": { /* ABI types */ },
"message": { /* full PayOrder struct */ }
},
"signature": "0x..."
}computed_split values are in the token's smallest unit (USDC = 6 decimals).
On-chain submission (browser)
import { writeContract } from 'wagmi/actions';
import PaymentRouterAbi from './PaymentRouter.abi.json';
const order = await fetch('/v1/orders', { /* ... */ }).then(r => r.json());
// 1) approve USDC once per buyer
await writeContract({
address: order.eip712.message.token,
abi: erc20Abi,
functionName: 'approve',
args: [order.pay_to, maxUint256],
});
// 2) pay
await writeContract({
address: order.pay_to,
abi: PaymentRouterAbi,
functionName: 'payOrder',
args: [order.eip712.message, order.signature],
});List & retrieve
GET /v1/orders?state=confirmed&limit=50
GET /v1/orders/:id
GET /v1/orders/summary/summary gives you total, pending, confirmed, and gross_24h for dashboards.
State machine
pending_payment ──► confirmed ──► partially_refunded ──► refunded
├─► failed
└─► expiredconfirmedmeans 0fra's chain indexer sawOrderPaidwith the agreed amount and confirmation depthfailedonly sets if you explicitly call our internal failure endpoint or the buyer disputes (rare)expiredhappens automatically whendeadline_atpasses without confirmation
Idempotency hard rules
- Same
order_nofor the same platform → 409 conflict - Same
Idempotency-Key+ same body → cached response replayed - Same key + different body → 409
duplicate_idempotency_key