Skip to content

feat(funding): useFundingMethods + useOnramp; buy flow on the session engine - #315

Open
joalavedra wants to merge 8 commits into
feat/funding-method-pickerfrom
funding/v2-hooks
Open

feat(funding): useFundingMethods + useOnramp; buy flow on the session engine#315
joalavedra wants to merge 8 commits into
feat/funding-method-pickerfrom
funding/v2-hooks

Conversation

@joalavedra

Copy link
Copy Markdown
Collaborator

Stacks on #311. Puts the fiat rails on the funding-session state machine and exposes them headless.

  • useFundingMethods(session) and useOnramp(session, method) — the contract hooks. Server-resolved rows (provider hidden); an angle-agnostic open() that commits the onramp payment method and follows the SESSION status (not the popup) to a terminal state.
  • Buy flow reworked onto sessions: the amount screen mints a session and prices it with a session-scoped quote; BuyProcessing commits and tracks. The per-provider stripeApi/coinbaseApi/onrampQuoteApi/providers files are removed; BuyFormState carries { method, session } instead of a provider id.
  • Deposit hub: fiat rows render only once resolved for the buyer's region — no static fallback, no hardcoded fees; Apple Pay gates on ApplePaySession.canMakePayments() (desktop Safari included).
  • Funding domain (target, onramp API clients, sources) moved into hooks/openfort so hooks no longer import from components — the funding engine is now an extractable module.

242 unit tests. Depends on the api onramp endpoints (openfort-xyz/api#1086) and the openfort-js onramp client.

🤖 Generated with Claude Code

joalavedra and others added 2 commits July 2, 2026 18:49
…at rows

Headless fiat per docs/web2-onramp.contract.ts §5, on the funding
session state machine:

- useFundingMethods(session): server-resolved method rows
  (GET /v2/funding/sessions/{id}/methods) — provider hidden, region
  routed. On failure the rows are EMPTY, never a static fallback.
- useOnramp(session, method): open() commits
  setPaymentMethod({type:'onramp'}), presents the checkout
  (popup/redirect/manual) and polls the session to a terminal status —
  the popup is never the source of truth. quote() prices the route with
  the exact provider a commit would use.
- PaymentMethod becomes a crypto | onramp union (+ cryptoPaymentMethod
  narrowing helper); FundingClient gains sessions.methods/quote;
  useFundingClient extracted so every funding hook resolves the same
  client. All exported.

Deposit hub semantics fixed (no dead or illegal rows):
- fiat rows render only once resolved for the buyer's region — no
  static fallback while loading or on failure (compliance, not UX)
- the 'default to Stripe when unresolved' fallback is gone; MoonPay is
  no longer treated as executable client-side
- hardcoded fee/limit subtitle claims removed — real numbers come from
  the live quote
- uiConfig.funding.country override (QA + apps that know the region +
  local dev without a CDN geo header)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…components

The fiat buy now runs on the funding-session state machine end to end:

- Buy (amount screen) mints a fresh funding session per mount and
  prices the entered amount with the session-scoped quote — the exact
  provider a commit would resolve. Continue is provider-less.
- BuyProcessing commits setPaymentMethod({type:'onramp'}) via
  useOnramp: the server resolves the provider, the hosted checkout
  opens, and the SESSION status (webhook-driven) decides the outcome —
  the popup.closed/URL-sniffing machinery is gone.
- stripeApi/coinbaseApi/onrampQuoteApi/providers.ts deleted — no
  provider-specific code remains in the React SDK; BuyFormState carries
  {method, session} instead of a providerId.
- Apple Pay gates on ApplePaySession.canMakePayments() (desktop Safari
  included); the mobile-only wallet-pay gate is gone. Google Pay stays
  UA-gated until isReadyToPay is wired.
- Buy screen inline styles replaced with styled-components.
- Layering fixed: useFundingTarget, onrampMethodsApi, onrampEndpoints
  and fundingSources move to hooks/openfort — hooks no longer import
  from components, so the funding engine is extractable as a module.
- OnrampPaymentMethod carries providerClientSecret (Stripe embedded
  onramp element enabler).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 7, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
playground Ready Ready Preview, Comment Jul 21, 2026 4:38pm

Request Review

@pkg-pr-new

pkg-pr-new Bot commented Jul 7, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@openfort/react@315

commit: 5f7fc6f

open() now accepts the OTP-verified walletPay identity (email, US phone,
verification + Guest-Checkout agreement timestamps) and forwards it, so
apple_pay/google_pay commit a Coinbase native order. The hook presents only the
iframe angle; a native commit's url is the in-page Pay-button link, surfaced via
url + the new angle field for the caller to mount — never auto-opened.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Build the in-modal flow for the native angle so apple_pay/google_pay work
end to end:

- Amount step (Buy): shows Coinbase's Guest-Checkout consent for wallet pay,
  gates Continue on it, and stamps agreementAcceptedAt. Routes to contact
  capture, or straight to commit when the buyer's identity is already complete.
- WalletPayContact: gathers email (if missing) + US phone and OTP-verifies the
  phone via Openfort's OWN phone OTP, stamping phoneNumberVerifiedAt on success
  (no server verifiedAt exists).
- BuyProcessing: passes the verified walletPay identity to open() and mounts
  Coinbase's in-page Pay button (iframe, allow="payment") for the native angle
  instead of the hosted-popup path; the manual-advance fallback is suppressed
  while the buyer is in the Pay sheet.
- New themed Checkbox primitive (none existed) and a pure walletPay helper
  module (isWalletPayMethod / isCompleteWalletPay / needsWalletPayCapture),
  unit-tested. New buyWalletPayContact route + WalletPayDraft on the buy form.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Coinbase's headless onramp docs require sandbox="allow-scripts allow-same-origin"
and referrerpolicy="no-referrer" (alongside allow="payment") for the in-page
Apple/Google Pay sheet to run. Add them to the native Pay-button frame.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@joalavedra

Copy link
Copy Markdown
Collaborator Author

Funding v2 — full PR stack (5 PRs / 4 repos)

Funding v2 = web2 onramps (Apple/Google Pay, Card, Bank) + server-side region+asset routing + the execute path. It spans:

  1. api · openfort-xyz/api#1086 · feat/funding-web2-onramp-routingmain — region routing (OnrampRoutingService/coverage/region), GET /v1/onramp/methods, FundingProjectConfig persistence + GET/PUT /v2/funding/config, onramp-as-a-session-payment-method + POST /v2/funding/sessions/{id}/quotes, Stripe (EU) + MoonPay (ROW) rails, auto-bridge chaining + reconciliation cron, Coinbase native Apple/Google Pay, onramp completion webhooks. Owns the endpoints — merge first.
  2. openfort-js · feat(funding): onramp client — session methods/quote + payment-method union openfort-js#327 · feat/funding-onramp-clientmain — onramp payment method + session methods/quote client (SDK execute path).
  3. openfort-react · feat(funding): method picker + web2 (fiat) onramps #311 · feat/funding-method-pickermain — Deposit-hub method picker + fiat rows, provider hidden.
  4. openfort-react · feat(funding): useFundingMethods + useOnramp; buy flow on the session engine #315 · funding/v2-hooksfeat(funding): method picker + web2 (fiat) onramps #311useFundingMethods/useOnramp hooks + native Coinbase wallet-pay widget. Stacked on feat(funding): method picker + web2 (fiat) onramps #311 → retarget to main after feat(funding): method picker + web2 (fiat) onramps #311 merges.
  5. dash · openfort-xyz/dash#212 · feat/funding-dashboard-uimain — Funding config section (Money movement).

Suggested order: #1086 → activation steps → js#327 → react#311 → react#315 → dash#212. The SDK/dash degrade gracefully until the api endpoints are live.

Activation after api merges: pnpm db:prisma-migrate-deploy + prisma generate · tsoa spec-and-routes · dash pnpm generate:api · set FUNDING_ENC_SECRET.

Still open (next sprint): cross-repo FUNDING_SESSION_UPDATED topic in @openfort-xyz/buffers + Pigeon (outbound webhook delivery won't fire until registered) · SwappedOnrampService (needs API key; MoonPay is interim) · web2-onramp docs.

(auto-posted to cross-link the stack)

Wallet-pay contact capture previously only format-checked an email it
collected; now it sends and verifies an Openfort email OTP, mirroring the
phone step. Skipped entirely when auth already collected the email. When
the phone is already verified, a successful email verification completes
the identity and hands off to commit directly.

Adds verifyEmailOtp (session-less verification) to useEmailOtpAuth.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Apple/Google Pay no longer imply the native Coinbase order client-side:
the amount step resolves the session's methods and only requires the
Guest-Checkout consent + email/phone OTP capture when the server resolved
the method to the native sheet. Wallet pay resolved to the hosted
checkout (rest-of-world, or no project CDP creds) commits like a card.
The resolved angle rides on buyForm so the commit screen's stale-state
guard applies the same rule; unknown angle still assumes native.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@joalavedra

Copy link
Copy Markdown
Collaborator Author

Two additions:

  • c407bc3 — the wallet-pay capture screen now OTP-verifies a newly collected email via Openfort's email OTP (verifyEmailOtp added to useEmailOtpAuth), mirroring the phone step; skipped when auth already collected the email.
  • 2ae2aeb — consent + identity capture are gated on the server-resolved angle, not the method name: wallet pay resolved to the hosted checkout (rest-of-world / no project CDP creds, see api#1086) commits like a card with no capture. Unknown angle still assumes native.

Biome + first-party tsc clean, 248 tests, rollup green.

🤖 Generated with Claude Code

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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.

1 participant