Skip to content

feat: add checkout widget#727

Draft
tomiiide wants to merge 41 commits into
mainfrom
feat/widget-checkout
Draft

feat: add checkout widget#727
tomiiide wants to merge 41 commits into
mainfrom
feat/widget-checkout

Conversation

@tomiiide
Copy link
Copy Markdown
Contributor

@tomiiide tomiiide commented May 13, 2026

Which Linear task is linked to this PR?

This PR delivers Checkout V1 across the Architecture and three flow parents.
See EMB-360 Checkout V1 project for the full structure.

Architecture (parent: EMB-362)

  • EMB-306LifiWidgetCheckout component
  • EMB-307 — On-ramp provider packages (@lifi/widget-provider-*)
  • EMB-308 — Spike: fund arrival detection (resolved → Model A, client polling)

Pay from wallet (parent: EMB-363)

  • EMB-368 — Connect wallet entry point
  • EMB-369 — Payment flow orchestration

Transfer crypto (parent: EMB-364)

  • EMB-372 — Deposit-address transfer flow
  • EMB-373 — Fund arrival detection wiring
  • EMB-374 — Error states + retry UX

Mesh CEX (parent: EMB-365)

  • EMB-326 — Checkout userId generation
  • EMB-327 — Mesh CEX flow (UI + session)
  • EMB-376 — Mesh post-flow / success states

Transak cash (parent: EMB-366)

  • EMB-301 — Mock Core onramp-session endpoint
  • EMB-302 — Transak modal iframe + lifecycle
  • EMB-303 — Transak UI + session flow
  • EMB-377 — Transak post-flow / success states

Why was it implemented this way?

Checkout V1 is structurally different from the existing swap flow — it's a deposit/funding journey with three entry paths (pay from wallet, transfer crypto, on-ramp via Mesh/Transak) that converge on a single execution step against a frozen quote. The implementation choices:

  • @lifi/widget/checkout subpath export — keeps the standard widget bundle clean of checkout code for partners who don't need it. Marked @experimental until the API stabilizes.
  • Pluggable on-ramp providers (@lifi/widget-provider-{mesh,transak}) — partners opt in by installing only what they need; zero bundle cost otherwise. Routing is by fundingCategory ('cash' | 'exchange'), not by provider id, so a future third provider in the same category doesn't require core changes.
  • Per-instance Zustand stores (router, flow, frozen quote, fiat, on-ramp session registry) — multiple checkout instances on the same page don't share state.
  • Frozen quote on path commit — once the user picks a deposit path, the route is frozen via useFrozenQuote so the in-flight amount can't shift mid-transaction.
  • Client-driven fund arrival detection (Model A from EMB-308) — useCheckoutTransactionStatus polls deposit status; no backend webhook infrastructure required for V1.
  • Restructured transaction execution UI — split TransactionPage into ExecutionProgress + StepActionsList + shared row components. Affects the main widget too, not just checkout; flagged for review attention.

Visual showcase

TODO: screenshots / Loom of the three flows (pay from wallet, transfer crypto, Mesh, Transak).

Notes for review

  • ⚠️ Bundle isolation not yet verified. EMB-307's last two exit criteria — confirming that @lifi/widget (no /checkout) has zero on-ramp provider transitive deps, and that installing only one of @lifi/widget-provider-{mesh,transak} doesn't ship the other — are open. Bundle analysis to follow.
  • ⚠️ Transaction UI refactor (ExecutionProgress / StepActionsList) touches the main widget execution flow, not just checkout. Worth eyes from anyone owning the main swap journey.
  • ⚠️ Temporary Transak native-ETH retry on 5xx for chain 1 in aa9e9e6 — flagged as temporary; remove once Core handles it natively.
  • ✅ Tests added for sessionClient, resolveAdapters, checkoutToWidgetConfig, and navigationRoutes.
  • ✅ Per-instance store isolation is unit-covered.

Checklist before requesting a review

  • I have performed a self-review and testing of my code.
  • This pull request is focused and addresses a single problem. (Checkout V1 is intentionally a large feature; broken down into ~14 sub-tickets, all linked above.)
  • If this PR modifies the Widget API or adds new features that require documentation, I have updated the documentation in the public-docs repository.

tomiiide added 30 commits April 29, 2026 12:39
- Proxy Transak onramp flow with session creation
- Resolve on-chain tokens to Transak crypto currencies
- Handle partner auth with automatic token refresh
- Use correct auth headers and endpoints per Transak docs
- Map mainnet chain IDs to testnet equivalents for staging
- Handle native token address matching and add fallback data
- Add SIMULATE_ERROR env var (timeout/auth_error/config_error modes)
- Add USE_REAL_CORE toggle to proxy requests to real Core API
- Checkout package: LifiWidgetCheckout (modal + ref API), router and pages for
  deposit/funding, MUI layout, theme/config providers, exports
- Playground: @lifi/widget-checkout, swap vs checkout mode, preview, persist v3
- Deps: MUI/Emotion, TanStack Router, zustand
- Add CheckoutWidgetRuntime with checkout→WidgetConfig mapping and default wallet providers
- Wire router for select source, enter amount, progress, funding paths, and widget token/tx routes
- Implement SelectDepositTokenList in checkout using re-exported token-list hooks and components
- Expand @lifi/widget public exports for compositors (pages, providers, token UI building blocks)
- Export wallet-management wallet row helpers; pass playground widget props into checkout
- Add root dev:mock-api script; refresh checkout deps and pnpm-lock.yaml
- Add TransakProvider: POST onramp session, iframe widget, useTransak hook
- Add CheckoutConfig.onrampSessionApiUrl; share OnrampSession types
- Lock destination asset in checkout UI (hidden/disabled to-token controls)
- Merge mock Transak catalog with fallbacks (e.g. mainnet stETH)
- Wire playgrounds: env for onramp API URL; default stETH target in widget
- Remove Transak placeholder dialog and optional @transak/transak-sdk peer meta
- OnRampProvider + optional Transak/Mesh peers; UI SDK embed for Transak.
- Drop funding-methods routes and picker UI; exchange row is coming-soon.
- Merge checkout en.json into widget languageResources for tags.* and checkout.*.
- Wire layout, source page, funding options, and Transak dialog to useTranslation.
- Transak: typed UI errors, stable English onError, wallet CTA via error code.
- Export deepMerge from @lifi/widget.
- Add walletId to WalletMenuOpenArgs
- Skip wallet-list when walletId is provided; useEffect fallback
  for resilience against stale state
- Checkout TopWalletRows passes walletId for multi-connector cards
- Back button closes modal when opened via walletId
- Wrap CheckoutRouter in error boundary with retry UI
- Sync drawer visibility with controlled `open` prop
- Wire onConnected/onConnecting/onError through wallet rows
- Fix CSS property names in styled components
- Extract ProgressPage strings to i18n
- Log warnings for failed on-ramp adapter loads
Changes:
- Move mock-api/ out of src/ to package root
- Co-locate CheckoutDrawerContext with CheckoutDrawer at src/ root
- Dissolve onramp/ into providers/OnRampProvider/ with nested TransakProvider/ and MeshProvider/
- Move SelectSource components into pages/SelectSourcePage/
- Rename CheckoutWidgetContainer to AppProvider
- Remove redundant Checkout prefix from components
  (Container, Header, Stack, ErrorBoundary).
- Rename CheckoutDrawer to CheckoutModal
  (file, context, types, ref API).
- Collapse barrel re-export files into component files.
Changes:
- Move all checkout source to packages/widget/src/checkout/
- Move mock-api server to packages/widget/mock-api/
- Split checkout types into config, modal, results, and theme files
- Inline CheckoutModalContext into CheckoutModal; rename AppProvider
- Update all imports to relative paths within the widget package
- Merge checkout i18n keys into widget en.json
- Update playground to import checkout from @lifi/widget
Changes:
- Remove CheckoutTheme, CheckoutResult, and FundingMethod types
- Remove checkoutThemeToWidgetTheme and defaultCheckoutWalletProviders utilities
- Replace checkout-specific config fields with a single config?: Partial<WidgetConfig> prop
- Remove provider package dependencies no longer needed by checkout
- Update playground to use the new config prop
Changes:
- Add CheckoutResult interface and onSuccess prop to CheckoutConfig
- Wire onSuccess through TransakProvider's order success handler
- Fix config spread order so subvariant/variant can't be overridden
- Implement MeshProvider backed by optional @meshconnect/web-link-sdk peer
- Extract shared OnRampFlowValue interface (Transak and Mesh now share it)
- Add getProvider(id) to useOnRamp for provider-agnostic access
- Activate "Connect Exchange" card in SelectSourceFundingOptions when Mesh SDK is installed
- Add CexSessionRequest/Response types and Mesh i18n error messages
- Annotate forwardRef and createContext exports; inline route literals
  in checkoutNavigationRoutes so .d.ts can emit without cross-file inference.
- Guard router subscribe in TransakCashProvider effect.
- MUI v7 shorthand → sx; drop redundant !! coercions; misc dev tweaks.
- Sectioned funding picker; auto-advance to token select on connect.
- Checkout-only amount/receive/deposit components — no review step.
- CheckoutRoutesPage / CheckoutTransactionPage with optional
  auto-deposit; navigate via checkout-absolute paths.
- Default deposit-to to Ethereum Lido stETH; normalize
  \`contractCalls\` to [] so useRoutes stops gating deposit quotes.
- Scope the Transak cash modal to the widget scrollable container.
- Fix --isolatedDeclarations errors across the checkout dir.
Changes:
- add postCheckoutSession client with x-lifi-api-key/x-lifi-integrator headers
- point Mesh/Transak providers at /v1/checkout/{onramp,cex}/session, require
  widget apiKey, read base URL from onrampSessionApiUrl
- flatten CexSessionRequest to {walletAddress, tokenAddress, chainId}
- add temporary Transak native-ETH retry on 5xx for chain 1
- expose new endpoints in mock-api and proxy CEX sessions to Core
- add VITE_CHECKOUT_* env vars and plumb them through the playground
Cherry-pick from origin/feat-checkout adopting the updated execution flow.

Changes:
- split TransactionPage into smaller cards/sections
- replace Step/StepActions/TransactionDetails with ExecutionProgress + StepActionsList
- swap StepTimer/TimerContent for StepStatusTimer
- move ActionRow/SentToWalletRow/StepActionRow into shared components
- add useRouteExecutionMessage and useClearAmountFields
- Inline token selector + end adornment in CheckoutAmountInput; drop SelectChainAndToken
- Add expandable RouteDetails and inline quote-refresh indicator on CheckoutReceiveCard and Header
- Parse fromAmount with comma tolerance for localized inputs
- Override light background to #f7f8fa; modal uses background.default and drops 720px cap
- Source top wallets from useCombinedWallets with overflow count
- On failed deposit execution, return to EnterAmountPage preserving fromToken/fromAmount
- New CheckoutTransactionStatusPage with watching/executing/completed states and step list
- New CheckoutTransactionDetailsPage with transfer id card
- useCheckoutTransactionStatus hook plus simulation utility
- OnRampFlowOutlet and MeshErrorScreen for provider error handling
- Wire routes, header, and i18n strings for the new flow
tomiiide added 11 commits May 12, 2026 11:15
- Add TransferDepositPage (on-chain deposit with QR + status poll) and
  SelectCashCurrencyPage with fiat currency store/chip
- Introduce useCheckoutFlowQuote/useFrozenQuote and checkout flow store
  to freeze the route once a deposit path is chosen
- Replace CheckoutDepositButton with CheckoutFlowCtaButton dispatching
  to transfer, on-ramp, or CEX flows
- Extend OnRampFlow.openDepositFlow to take depositAddress/amount/
  fiatCurrency and thread them through Mesh/Transak sessions
- Hardcode checkout toChain/toToken to Lido stETH on Ethereum
- Add loading skeleton and Done button to transaction details page
- Add "See details" button alongside Done on transaction page
- Drop quote indicator from header; add header bottom margin
- Soften outlined paper shadow in checkout container
- Adjust spacing on status completed and enter amount pages
- Redesign TransferDepositPage with QR card and collapsible details
- Add DepositErrorPages for low/excess/late/expired/refund states
- Replace SelectCashCurrencyPage with a searchable flag list
- Per-instance router and Zustand stores (flow / fiat / frozen quote)
  so multiple checkout instances no longer share state
- Drop Mesh dev-mode txHash override that faked onSuccess
- Restrict published tarball via package.json "files"
- i18n the checkout ErrorBoundary
- Use route.id (not index) as React key in CheckoutRoutesPage
- Add missing .js ESM extensions in two TransactionPage files
- Mark new compositor exports @experimental
- Replace unsafe error cast in sessionClient with a runtime guard
- Skip transaction-status navigation when an onramp provider is unavailable
- Stabilize Transak SDK init via refs so the iframe no longer rebuilds on parent re-renders
- Read filteredWallets via ref in WalletMenuContent to avoid effect loops
- Gate transactionStatusSimulation fixtures behind NODE_ENV so prod bundles dead-code-eliminate them
- Document why useCheckoutUserId persists across sessions (Mesh reconciliation)
- Add tests for sessionClient, resolveAdapters, checkoutToWidgetConfig, and navigationRoutes
- Drop unused noQuoteAvailable alert + matching i18n key; tighten checkout PageContainer gutters
- Add `./checkout` to package.json exports and tsdown entry; drop the
  checkout and internal composition re-exports from the main entry
- Update playground to import from `@lifi/widget/checkout`
- Convert wallet-management barrel to named exports
- Guard Transak `onSuccess`/`close` handlers with a per-run active flag
  to prevent duplicate fires from the singleton emitter
- Split Mesh and Transak into @lifi/widget-provider-{mesh,transak}; opt
  in via new `onRampProviders` prop.
- Promote shared types, contexts, and session client to
  @lifi/widget-provider/checkout subpath.
- Unify i18n under `checkout.onramp.*` with {{providerName}}.
- Replace per-provider contexts with OnRampSessionsContext; hosts
  register via useRegisterOnRampSession and render as flat siblings
- Route by fundingCategory ('cash' | 'exchange') instead of provider id
- Drop OnRampFlowOutlet; status page reads useActiveOnRampDeposit
- OnRampHostedModals iterates registered metas
Per-instance store via useRef matches existing checkout stores.
Consumers subscribe to a single session slot, so cross-provider
updates no longer re-render unrelated consumers.
- Move onRampProviders from CheckoutConfig to CheckoutProps; it's a
  runtime value and was never read back from config.
- Type checkoutNavigationRoutesValues as CheckoutNavigationRoute[].
@tomiiide tomiiide changed the title Feat/widget checkout feat: add checkout widget May 13, 2026
Comment on lines +5 to +14
tokenEth:
'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/info/logo.png',
tokenUsdc:
'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png',
tokenMatic:
'https://raw.githubusercontent.com/trustwallet/assets/master/blockchains/polygon/info/logo.png',
exchangeCoinbase: 'https://cdn.simpleicons.org/coinbase/0052FF',
exchangeBinance: 'https://cdn.simpleicons.org/binance/F0B90B',
visa: 'https://cdn.simpleicons.org/visa/1A1F71',
mastercard: 'https://cdn.simpleicons.org/mastercard/EB001B',
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please reach out to the design team for proper icons, and we need to put them here after they provide us with assets.

https://github.com/lifinance/types/tree/main/src/assets/icons

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants