V1 Web PWA · Journey

Journey: Sign up and pay with crypto

A source-reconciled journey record for V1 Web PWA — shipped behavior, state boundaries, failure modes, and the automation evidence available today.

automated · 4 specs verdict: partial· 2026-05-29
9sections5 minread

On this page
Journey at a glance
ActorsNew customerSigned-in plan buyerCrypto payerSettlement watcher/operator
Automation4 E2E spec(s) · verdict: partial· 2026-05-29

Journey flow#

Generated from the authored steps below — click a node to jump to that section.

flowchart TD s1["1. Create the account and finish the auth han…"] s2["2. Reach the current billing boundary"] s3["3. Issue an invoice through the BFF seam"] s4["4. Render and pay the pending invoice"] s5["5. Confirm through the signed settlement rece…"] s6["6. Re-enter after settlement"] s1 --> s2 --> s3 --> s4 --> s5 --> s6 click s1 href "#1-create-the-account-and-finish-the-auth-handoff" click s2 href "#2-reach-the-current-billing-boundary" click s3 href "#3-issue-an-invoice-through-the-bff-seam" click s4 href "#4-render-and-pay-the-pending-invoice" click s5 href "#5-confirm-through-the-signed-settlement-receiver" click s6 href "#6-re-enter-after-settlement" s6 --> v(["verdict: partial"]) click v href "../results/sign-up-and-pay-crypto.html" style v stroke:#d6a531,color:#d6a531

Signup, crypto invoice issuance, the paywall, and signed settlement are each real, but they are not one shipped customer funnel. Signup routes through onboarding. /billing links to /billing/crypto without creating an invoice, so it lands on the pending-issuance notice. Tests issue invoices through the BFF API, then prove the paywall and settlement seams directly.

Personas#

  • New customer — creates an account and completes onboarding before any protected billing surface.
  • Signed-in plan buyer — needs a pro or premium invoice bound to the server price book and their user id.
  • Crypto payer — receives an asset-specific non-custodial target and, for issued/less-decentralized rails, acknowledges the disclosure.
  • Settlement watcher/operator — submits a signed on-chain confirmation and expects one plan mutation plus a verifiable receipt.

Pre-conditions#

  • /welcome is public; protected /billing and /billing/crypto routes require an authenticated session.
  • Production quoting requires a bound non-custodial payment runtime. Without one, methods are marked inactive and quote creation fails closed with 503; no address is fabricated.
  • The settlement receiver requires a configured HMAC secret and a bound receipt signer.
  • A plan invoice derives amount/currency from the BFF price book. Client values may only echo that price and mismatches are rejected.

Steps#

1. Create the account and finish the auth handoff#

WelcomeAuthPanel posts the real signup contract through the auth client, tracks viewed/submitted/completed/failed funnel events, and sends new accounts to the onboarding route. A sanitized redirect is preserved through onboarding; signup does not immediately mint a paid entitlement or crypto invoice.

2. Reach the current billing boundary#

/billing states that crypto is the wired rail and links to /billing/crypto. That link carries no invoice id. Missing, malformed, over-length, unknown, expired, cancelled, or confirmed invoices render PendingIssuanceNotice with return/support actions. There is no customer upgrade tile that calls the quote API and redirects with the issued id.

3. Issue an invoice through the BFF seam#

POST /v1/payments/crypto/quote is session-optional, but only an authenticated, plan-bearing quote can grant that purchaser a plan at settlement. It checks crisis suppression, runtime availability, supported asset, tenant, canonical plan price, and the issuance disclosure gate. Only after the provisioner returns a real unique target does the BFF persist the pending invoice and return the web DTO.

Tier-B/Tier-C or centrally issued assets require acknowledgement before quote issuance. The rendered paywall independently asks for its visible legal acknowledgement before revealing the already-issued target.

4. Render and pay the pending invoice#

/billing/crypto?invoice=<id> fetches only a pending DTO, displays fiat and chain amounts, expiry, disclosure version, trust tier, address/QR, copy, and billing support. The asset picker changes visible radio/network choices, but the page provides no onChainSelected callback; it does not re-quote or replace the original invoice target.

The customer sends funds outside the app. The page does not poll invoice status or display confirmation depth.

5. Confirm through the signed settlement receiver#

The deploy-bound watcher posts exact raw JSON with timestamped HMAC, invoice, transaction, block hash, and block height. The BFF rejects missing config, invalid/stale signatures, malformed payloads, unknown invoices, and unavailable receipt signing. A valid pending invoice becomes confirmed, receives an Ed25519-signed receipt, and—when purchaser, plan, currency, and book price all match—updates the persisted customer plan. Replay is acknowledged without re-driving the grant.

This route does not publish a customer-facing payment.invoice.confirmed → payment.invoice.settled event chain. Those bridge contracts exist elsewhere, but the shipped BFF settlement path performs one direct pending-to-confirmed transition.

6. Re-enter after settlement#

A fresh profile read sees the upgraded plan, and a fresh invoice fetch reports confirmed. The paywall loader intentionally refuses non-pending invoices, so reopening the URL shows the generic “could not find invoice” pending notice and never leaks the stale payment address. There is no success receipt view or “Continue to the gated surface” handoff.

Post-conditions#

  • A provisioned invoice is snapshot-durable, purchaser-scoped in member list reads, and never persisted before a real receiving target exists.
  • A valid signed settlement stores chain facts and receipt, confirms the invoice, and conditionally updates the purchaser's plan.
  • Re-delivery is idempotent and a confirmed address is no longer rendered as payable.
  • The end-user signup → issue → pay → success/return journey remains incomplete despite strong API/paywall/settlement coverage.

Failure modes#

  • No invoice from /billing — the primary customer link ends at PendingIssuanceNotice.
  • No payment runtime — quote returns 503 payments_not_configured and methods are inactive.
  • Crisis frame active — quote returns 403 crisis_suppressed before showing a payment prompt.
  • Price/disclosure mismatch — quote fails closed before target issuance.
  • Invalid settlement — status, tx id, receipt, and plan remain unchanged.
  • Below-book settlement — money can confirm, but entitlement is withheld and the invoice is flagged for support.
  • No live status UI — an already-open paywall does not notice settlement; only a new navigation changes what is rendered.
  • Picker ambiguity — changing asset/network controls does not mutate the issued invoice.

E2E coverage#

Per-view files touched#

Cross-references#

  • aaa-upgrade-entitlement-bounce.md — adjacent tier gate/Studio handoff.
  • onboarding-ten-steps.md — mandatory new-account continuation after signup.
  • Sources: WelcomeAuthPanel.tsx, app/billing/page.tsx, app/billing/crypto/CryptoPaywall.tsx, routes/domain-stubs.ts, and payments/settlement-route.ts under apps/oshun.

Open questions#

  • Which customer plan surface should create the quote, handle the issuance disclosure, and redirect to the pending invoice?
  • Should the paywall poll/subscribe to status and expose the signed receipt plus a sanitized return target after confirmation?
  • Should asset/network controls be removed for a fixed invoice or wired to an explicit cancel-and-requote flow?
  • How should bridge-level confirmed/settled events compose with the direct BFF transition without double-granting entitlement?