Context. surface customer · domain account · route /billing · auth signed-in · source apps/oshun/web/src/app/billing/page.tsx
Last walked. 2026-06-29 billing-index direct-route addendum — Playwright real-dev-infra coverage for signed-in /billing render, anonymous proxy redirect, rail hrefs, 44 px tap targets, no horizontal overflow at 390 px, standalone display-mode launch, and real service-worker offline replay of the cached static billing document. Evidence: WALKTHROUGH/results/v1-real-infra-run-2026-06-22.md §202; spec: apps/oshun/web/e2e/billing-index.spec.ts. 2026-06-29 billing-rail telemetry addendum — the same real-dev-infra spec now asserts the static billing card, rail list, crypto/profile/AAA rail links, and billing steward link expose no telemetry hook attributes and dispatch no oshun-analytics:event when activated. Evidence: WALKTHROUGH/results/v1-real-infra-run-2026-06-22.md §216; spec: apps/oshun/web/e2e/billing-index.spec.ts.
Purpose#
The top-level account-billing landing surface — a calm directory that routes the
user to the rail that fits where they came from. Today only the Aje crypto rail
is wired (/billing/crypto); card and bank rails are explicitly "follow in
subsequent releases" per the in-page copy. Users arrive from the
Manage billing affordance in /profile.
Entry points#
- Cross-domain link from
/profilebilling row ("Manage billing" affordance) - Direct URL / bookmark — yes; auth required (
proxy.tsredirects no-session to/welcome;/billingis not inPUBLIC_PATHS/PUBLIC_PREFIXES) - Alias hops:
/settingsand/accountrewrite to/profile, which links here - Upgrade flow that lands a user on a payment decision (
/aaa-upgrade) - Email link from invoice issuance (which more typically deep-links straight to
/billing/crypto?invoice=…)
Layout regions#
page.tsx is a static server component (no data fetch, no LWebShell — it uses
raw Lilith CSS variables --l-bg, --l-paper, --l-ink, --l-accent,
--l-rule, --l-muted directly). A single <main data-billing-index> centers
one bordered card.
- Header: none (no shell header; bare
<main>on--l-bg) - Hero / above-the-fold: the card — eyebrow
Account · billing, serif H1 "Your subscription, kept calm.", and a muted intro paragraph - Main: a
<ul>of three rail/destination links - Footer / sticky bottom: a muted help line with a
mailto:steward link
States#
- Loading — N/A because the page is static (no
await, no data fetch); renders instantly - Empty — N/A because content is hardcoded copy, not data-driven
- Populated (short) — N/A; the three links are fixed
- Populated (long) — N/A; fixed link list
- Error (recoverable) — N/A; no query to fail
- Error (unrecoverable) — no segment
error.tsxexists underapps/oshun/web/src/app/billing; a static render crash would fall through to the nearest parent/global boundary - Offline — static HTML; SW-cached shell renders the links offline; the
mailto:and outbound links still resolve when connectivity returns - Gated / anonymous —
proxy.tsredirects no-session users to/welcomebefore this renders; verified bybilling-index - Standalone PWA — bare
<main>uses100svhand safe-area padding; the 390 px standalone launch is verified for display-mode, containment, 44 px tap targets, and no horizontal overflow
Interactions#
- Pay with crypto (USDC, USDT, ETH, BTC) → (link)
- Function: navigates to
/billing/crypto(the only wired rail) - Keyboard: in document order, first focusable; Enter activates
- Screen reader: announces "Pay with crypto (USDC, USDT, ETH, BTC), link"
- Touch target: verified ≥ 44 px tap height at a 390 px phone viewport
- Mobile (≤ 640 px): full-width card, links stack
- Offline behavior: the billing index itself replays from the real service worker cache after an authenticated online visit; the dynamic crypto target waits for connectivity
- Telemetry: explicitly absent; no hook attributes and no
oshun-analytics:eventdispatch on activation
- Function: navigates to
- Manage your account profile → (link)
- Function: navigates to
/profile - Screen reader: announces "Manage your account profile, link"
- Telemetry: explicitly absent; no hook attributes and no
oshun-analytics:eventdispatch on activation
- Function: navigates to
- Upgrade to the AAA Studio plan → (link)
- Function: navigates to
/aaa-upgrade - Screen reader: announces "Upgrade to the AAA Studio plan, link"
- Telemetry: explicitly absent; no hook attributes and no
oshun-analytics:eventdispatch on activation
- Function: navigates to
- billing@oshun.app (mailto link)
- Function: opens the user's mail client to the billing steward
- Screen reader: announces the address as a link
- Offline behavior: hands off to OS mail client; composition works offline
- Telemetry: explicitly absent; no hook attributes and no
oshun-analytics:eventdispatch on activation
E2E coverage#
apps/oshun/web/e2e/billing-index.spec.ts— signed-in direct-route render, static directory copy, crypto/profile/AAA rail hrefs, billing stewardmailto:, 44 px tap-target floor at 390 px, no horizontal overflow, standalone display-mode launch, real service-worker cached-document offline replay with no generic offline-fallback card, explicit absence of rail/card/steward telemetry hooks and click-dispatched analytics events, and anonymous redirect-before-render.
Data & contracts#
- Reads: none — fully static
- Writes: none
- Realtime: none
- Caching: static (no
dynamic, no fetch);/billingis an exact shell navigation inpublic/sw.js, so an authenticated online navigation writes the document into the static SW cache and offline navigation replays the cached billing page rather than the generic offline fallback - Auth/role check:
proxy.ts— protected by default (not public); no-session →/welcome - Metadata:
title: 'Billing',description: 'Plan, payment method, and invoice history for your OSHUN account.',alternates.canonical: '/billing'
Note the description promises "Plan, payment method, and invoice history," but
the page itself renders none of those — it is a router to /billing/crypto. See
gaps.
Cross-references#
- Feature spec:
V1/features.md#account - Architecture:
V1/ARCHITECTURE.md#account - Sibling routes:
billing-crypto.md— the actual paywallprofile.md— the account hub that links here
- Journeys:
../../journeys/sign-up-and-pay-crypto.md - Upgrade flow:
../13-system/aaa-upgrade.md - Component sources:
apps/oshun/web/src/app/billing/page.tsx(no child components — inline JSX with raw Lilith CSS vars)
Open questions / known gaps#
- The page metadata promises plan / payment-method / invoice-history views,
but none exist here — it is purely a link directory. Confirm whether those
are intended for this route or always delegated to
/billing/crypto. - No telemetry on the rail links — current V1 behavior is explicitly no
billing_rail_selected-style event or hook on the static rail directory. - Card-rail and bank-rail surfaces are unbuilt ("follow in subsequent releases") — confirm they are out of V1 scope.
- Add a segment
error.tsxfor/billing/*if product wants a local unrecoverable render-crash boundary; none exists today.