Files
Momento/docs/story-3.7-billing-ux.md
Antigravity bd495be965
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 12s
feat: design system overhaul — sidebar, AI chats, settings, brainstorm, color cleanup
- Sidebar: dynamic brand-accent colors, brainstorm section restyled
- AI chat general: popup panel with expand/collapse, hides when contextual AI open
- AI chat contextual: tabs reordered (Actions first), X close button, height fix
- Settings: all tabs restyled, 6 new color presets (sage, terracotta, iron, etc.)
- Global color cleanup: emerald/orange hardcoded → brand-accent dynamic
- Brainstorm page: orange → brand-accent throughout
- PageEntry animation component added to key pages
- Floating AI button: bg-brand-accent instead of hardcoded black
- i18n: all 15 locales updated with new AI/billing keys
- Billing: freemium quota tracking, BYOK, stripe subscription scaffolding
- Admin: integrated into new design
- AGENTS.md + CLAUDE.md project rules added
2026-05-16 12:59:30 +00:00

10 KiB
Raw Permalink Blame History

Story 3.7: Billing & Subscription UX — Complete User Journey

Epic: 3 — The SaaS Commercial Engine Priority: High Status: ready-for-dev Depends on: 3.1 (quota tracking), 3.6 (Stripe tiers) Blocks: 4.3 (data portability export needs billing status)


Context

The backend billing infrastructure (Stripe checkout, webhook sync, quota tracking via Redis) is fully implemented. The base UI components exist:

  • billing-plans.tsx — Plan selection cards + Stripe embedded checkout
  • usage-meter.tsx — Sidebar quota bar with upgrade modal
  • /settings/billing page — Current plan + upgrade CTA
  • API routes — /billing/status, /billing/create-checkout, /billing/portal, /billing/webhook, /usage/current

However, the user experience has significant gaps that block a production launch:

  1. No detailed usage breakdown — Users see a tiny progress bar in the sidebar but have no dedicated view showing per-feature consumption
  2. No billing history — Users cannot view past invoices or download receipts
  3. No inline paywall — When a quota-exceeded error occurs during an AI action, the user gets a generic error toast instead of being guided to upgrade
  4. No billing cycle overview — Missing period start/end, next billing date, tokens used this cycle
  5. Upgrade flow not triggered from error states — Quota exhaustion in AI features shows a toast but doesn't offer a path to resolution

User Stories

US-1: Detailed Usage Dashboard

As a user on any tier, I want to see a detailed breakdown of my AI feature usage for the current billing period, So that I understand exactly where my quota is being consumed.

Acceptance Criteria:

  • Given I am on the Billing settings page
  • When I scroll below the current plan card
  • Then I see a "Usage this period" section with a per-feature breakdown:
    • Feature name (i18n key)
    • Progress bar: used / limit
    • Numeric count (e.g., "47 / 100")
    • Visual state: normal (violet), warning ≥75% (amber), exhausted 100% (rose)
  • And for unlimited features (Enterprise), the bar is full and shows "Unlimited"
  • And the data refreshes every 30s via the existing useQuery with refetchInterval
  • And the section shows the period dates (e.g., "May 1 May 31, 2026")

US-2: Billing History

As a paying user, I want to see my past invoices and download them, So that I have records for accounting.

Acceptance Criteria:

  • Given I have an active or past paid subscription
  • When I view the Billing settings page
  • Then I see a "Billing History" section below usage
  • And it lists invoices sorted by date (newest first) with:
    • Date
    • Amount (formatted in user currency)
    • Status (Paid, Pending, Failed) with color badge
    • Download PDF link (via Stripe hosted invoice URL)
  • And for BASIC/free users, the section is hidden entirely
  • And the "Open Billing Portal" button remains for payment method changes

US-3: Inline Paywall (Quota Exceeded → Upgrade)

As a free user who just exhausted my AI Discovery Pack, I want to see a clear upgrade prompt instead of a cryptic error, So that I can take action immediately.

Acceptance Criteria:

  • Given I trigger an AI feature (e.g., auto-tag) and my quota is exhausted
  • When the API returns a QuotaExceededError
  • Then the UI shows an inline paywall panel (not just a toast) with:
    • Clear message: "You've used all your [feature] credits this month"
    • Two CTAs:
      1. "Upgrade to Pro" → links to /settings/billing
      2. "Use your own API key" → links to /settings/ai#byok
    • A "Maybe later" dismiss button
  • And the paywall replaces the AI action result area (not a full-page overlay)
  • And the paywall auto-dismisses if the user navigates away or after 10s

US-4: Billing Cycle Overview

As a paying user, I want to see my current billing period details, So that I know when I'll be charged next and how much I've used.

Acceptance Criteria:

  • Given I have an active paid subscription
  • When I view the Billing page
  • Then the Current Plan card also shows:
    • Billing period: "May 1 May 31, 2026"
    • Next billing date (or "Access until [date]" if canceled)
    • A small ring/donut chart showing total AI credits used vs total limit (aggregate across features)

US-5: Upgrade Success Confirmation

As a user who just completed checkout, I want to see a clear confirmation that my plan is now active, So that I have confidence the payment worked.

Acceptance Criteria:

  • Given I return from Stripe checkout (via session_id URL param)
  • When the page loads
  • Then I see a success banner at the top: "Welcome to [Plan Name]! Your subscription is now active."
  • And the banner auto-dismisses after 5s
  • And the usage meter in the sidebar updates immediately
  • And the plan cards section is hidden (replaced with current plan + manage)

Technical Design

Architecture

All changes are frontend-only — no new API routes needed. The existing APIs provide all necessary data:

Data Need Existing API Field
Per-feature quotas GET /api/usage/current quotas[feature].{used,limit,remaining}
Billing status GET /api/billing/status tier, status, currentPeriodEnd, cancelAtPeriodEnd
Invoices Stripe Customer Portal Via /api/billing/portal redirect
Checkout POST /api/billing/create-checkout {clientSecret, url}

Component Structure

settings/billing/page.tsx
├── BillingPlans (existing — current plan card + plan selection)
│   ├── CurrentPlanCard (enhanced with billing cycle dates + mini usage ring)
│   ├── PlanCard[] (existing — unchanged for BASIC)
│   └── ManageBilling (existing — portal button for paid users)
├── UsageBreakdown (NEW — per-feature usage dashboard)
│   ├── PeriodHeader ("Usage for May 131, 2026")
│   └── FeatureRow[] (one per feature: label + progress bar + count)
└── BillingHistory (NEW — for paid users, via Stripe portal link)
components/quota-paywall.tsx (NEW — inline paywall)
├── Message ("You've used all X credits")
├── CTAs (Upgrade link + BYOK link)
└── Dismiss button

Files to Create

File Description
components/settings/usage-breakdown.tsx Detailed per-feature usage table with progress bars
components/settings/billing-history.tsx Invoice list with download links (via Stripe)
components/quota-paywall.tsx Inline paywall component for quota-exceeded errors

Files to Modify

File Change
app/(main)/settings/billing/page.tsx Add UsageBreakdown + BillingHistory sections
components/settings/billing-plans.tsx Enhance CurrentPlanCard with billing period dates + mini usage ring
components/usage-meter.tsx Add click handler to navigate to /settings/billing (not just on exhausted)
Various AI feature components Catch QuotaExceededError and show QuotaPaywall instead of toast

Design Tokens (Existing — No New Colors)

Element Token/Class
Section header text-[11px] font-bold uppercase tracking-[0.15em] text-muted-foreground
Card container rounded-xl border border-border/40 bg-paper p-6 space-y-4
Progress bar track h-1.5 rounded-full bg-secondary/40
Progress bar fill (normal) bg-gradient-to-r from-violet-400 to-purple-400
Progress bar fill (warning ≥75%) bg-amber-400
Progress bar fill (exhausted) bg-rose-400
Feature count text text-[11px] text-muted-ink tabular-nums
Badge (active) bg-emerald-100 text-emerald-700
Badge (past due) bg-amber-100 text-amber-700
CTA primary bg-[#D4A373] text-white hover:bg-[#C49363] (highlighted)
CTA secondary border border-border hover:bg-foreground/5
Paywall panel rounded-xl border border-rose-200 bg-rose-50/50 dark:border-rose-800/40 dark:bg-rose-900/10 p-4

i18n Keys to Add (15 locales)

{
  "billing": {
    "usageThisPeriod": "Usage this period",
    "periodRange": "{start}  {end}",
    "featureUsedLimit": "{used} / {limit}",
    "unlimited": "Unlimited",
    "noUsage": "No AI usage yet this period.",
    "billingHistory": "Billing History",
    "noInvoices": "No invoices yet.",
    "viewInvoices": "View all invoices in Stripe portal",
    "nextBillingDate": "Next billing date",
    "billingPeriod": "Billing period",
    "planSince": "Subscribed since {date}"
  },
  "quotaPaywall": {
    "title": "Monthly limit reached",
    "description": "You've used all your {feature} credits for this month.",
    "upgrade": "Upgrade plan",
    "useOwnKey": "Use your own API key",
    "later": "Maybe later"
  }
}

Out of Scope

  • Stripe Customer Portal customization — uses the default hosted portal (already configured)
  • Downgrade flow — handled via Stripe portal (out of scope for this story)
  • Enterprise self-serve — "Contact Sales" mailto link is sufficient
  • Real-time WebSocket quota updates — polling every 30s is sufficient
  • Admin billing dashboard — deferred to a later epic
  • Currency switching — EUR default, handled by Stripe locale

Testing Notes

  • Unit: billing-sync.test.ts, billing-price-map.test.ts already exist and pass
  • Integration: Test checkout flow with Stripe test mode (sk_test_ keys)
  • Visual: Verify in both light and dark mode
  • i18n: All 15 locale files must have the new keys
  • Responsive: Plan cards grid must collapse to single column on mobile (<768px)
  • Edge cases:
    • User with no subscription (BASIC) should see only usage + upgrade cards
    • User with canceled subscription should see "Access until [date]" + no upgrade cards
    • User with past_due subscription should see warning banner
    • Redis down → usage section shows "Unable to load usage" (fail-open on API, graceful on UI)

Estimated Effort

Task Hours
usage-breakdown.tsx component 2h
billing-history.tsx component 1.5h
quota-paywall.tsx component 1.5h
Enhance billing-plans.tsx with cycle info 1h
Wire paywall into AI feature error handlers 2h
i18n keys (15 locales) 1h
Visual testing + dark mode 1h
Total ~10h