Tab Docs

Checkout states

All 16 states the embedded checkout can show, and the real event that drives each transition.

The checkout is a 16-stage state machine (checkout-state.ts in the SDK). Transitions fire on real events — API responses, on-chain reads, settlement reports — never on timers pretending to be progress.

Every stage

StageThe buyer seesEntered whenLeaves when
intent-loadingSkeleton buttonComponent mounts (or retry after error)Signed intent loaded → idle
idleYour Pay button with the amountIntent + context readyBuyer clicks Pay → opening
openingOpening sheetPay clickedPayment opened; live session found → balance-loading, else → email
emailEmail fieldAuth requiredEmail submitted → email-sending
email-sendingSending indicatorEmail submittedCode sent → otp
otp6-digit code inputCode sent (auto-submits on 6th digit)Submitted → otp-verifying; wrong code returns here
otp-verifyingVerifying indicatorCode submittedVerified → balance-loading; rejected → otp
device-approval"Approve from your email" noticeMagic flags a new deviceApproved (automatic) → balance-loading; restart → email
balance-loadingBalance skeletonAuth doneReal on-chain balance read → balance-ready or insufficient
balance-readyBalance + Confirm buttonBalance covers amount and gasConfirm → confirming
insufficientExact shortfall + fix pathBalance below amount, or USDC without gasTestnet: claim sandbox funds → add-funds; Mainnet: manual deposit
add-fundsFunding progress (real faucet legs)Claim startedBalance re-read → balance-ready / insufficient
confirmingPaying indicatorConfirm clicked. Testnet: the buyer's wallet signs a real Base Sepolia USDC transfer; the server re-verifies it on-chain before settlingSettled → success; slow (>20s) → stuck; failure → error
stuck"Still working" honesty noticeConfirmation exceeded the delay notice windowResolves like confirming
successReceipt: amount, ref code, real tx hashServer confirmed settlement (verification rpc)Buyer closes → done
errorWhat failed + retryAny failure eventRetry → opening / intent-loading

Cancel is an event, not a stage: dismissing the sheet returns to idle from anywhere except confirming (once money may be moving, the sheet stays until the outcome is known). Abandoned payments stay honestly pending — never fake-settled, never fake-failed.

What "settled" means here

On Testnet the transfer is real: sandbox USDC moves from the buyer's wallet to your receiving address on Base Sepolia. The SDK reports the transaction hash; the server independently verifies the receipt via RPC (success status, matching Transfer log for payer → receiver at the exact intent amount) before marking the payment settled with verification.method: "rpc" and the real txHash. If the transaction is not yet indexed the report returns 202 TEST_SETTLEMENT_PENDING and the SDK retries on backoff.

Mainnet execution stays blocked until live verification lands — a reported live payment stays honestly pending, never fake-settled.

Design guarantees

  • Codes auto-submit on the 6th digit; wrong codes return to otp with the field cleared, never a dead end.
  • Returning buyers with a live Magic session skip auth entirely (silent resume) — opening goes straight to balance-loading.
  • device-approval is a real state with a real exit, not an error.
  • The insufficiency check includes gas: a wallet holding USDC it cannot move routes to the test funds grant, which repairs both legs independently.
  • Every terminal screen shows evidence: ref code, transaction hash, and a Basescan link on Testnet.

The checkout only says "Nothing has been charged" for failures known to happen before broadcast. After broadcast, it says what it knows and keeps reconciling — an honest "in flight" beats a comforting lie.

On this page