feat minor billing feat/phase-2g-bundle
AI Agent git-import/068377df · 115 days ago · Apr 2, 2026 · Diff

feat(billing): Phase 16 — Stripe billing integration

Implements the full Phase 16 Stripe billing stack per docs/PHASE16-STRIPE-BILLING-PLAN.md §7 build order.

## What changed

### Step 1 — billing-constants.mjs - Rename `starter` → `plus` (backward-compat alias kept); add `growth` tier (68M tokens, $17/mo) - `pro` monthly token allowance set to `null` (unlimited) - Add `NOTE_CAP_BY_TIER`: free=200, plus=2 000, growth=5 000, pro=null - Add `PACK_TOKENS`: small=20M, medium=60M, large=150M - Add `addonTokensFromPackPriceId()` returning token grant per pack price ID - Add `isSubscriptionPriceId()`, `isPackPriceId()`, `priceIdFromTierShorthand()` helpers - Add `STRIPE_PRICE_GROWTH` env lookup in `tierFromEnvPriceId()`

### Steps 2+3 — server.mjs - `POST /api/v1/billing/checkout`: validates price_id / tier / pack_size, determines mode (subscription vs payment), creates Stripe Checkout Session, returns { url } - `POST /api/v1/billing/portal`: looks up or creates Stripe Customer, creates Billing Portal Session, returns { url }

### Step 4 — billing-stripe.mjs - `checkout.session.completed` payment handler now credits `pack_indexing_tokens_balance` from Stripe price metadata `indexing_tokens` (authoritative) with env-var fallback - Adds `createCheckoutSession()` and `createPortalSession()` exports

### Step 5 — billing-store.mjs - `resetMonthlyTokensIfNeeded(userId)`: resets `monthly_indexing_tokens_used` + advances `period_start/period_end` by one calendar month when period has expired - Called at top of `handleBillingSummary` and `runBillingGate`

### Step 6 — billing-middleware.mjs - Storage cap gate on note CREATE: fetches live note count via `getNoteCount` callback, checks against `NOTE_CAP_BY_TIER[tier]`, returns 402 STORAGE_QUOTA_EXCEEDED when over cap (only when BILLING_ENFORCE=true; shadow-logs when disabled) - `runBillingGate` accepts optional `opts.getNoteCount` async callback - server.mjs wires `getNoteCountForUser` (fetches canister total, fails open)

### Step 7 — Hub UI (hub.js + hub.css + index.html) - Plan badge with tier-coloured pill (free/plus/growth/pro) - Upgrade → button (Free/beta users, Stripe configured) - Manage billing → button (active subscription holders) - Token usage progress bar with warn/over colour states - Note cap display (N max from billing summary) - Pack rollover balance display - Token pack purchase cards (Small $10 / Medium $25 / Large $50) — visible on paid plans - Born Free Communities blurb with bornfree.io link - Period + renewal date display

### billing-http.mjs - Wires `resetMonthlyTokensIfNeeded` before loading summary - Adds `note_cap` and `has_active_subscription` fields to summary response

BILLING_ENFORCE remains false (shadow log mode). Keys/price IDs are set via Netlify env vars.

sha256:29f0377812c07ce287028a495e3553c0209beddb7050b915a3b1628cfeacdfb3 sha
sha256:ffaad790a7b466db82563bb931c468f049212fb816d6a27ccd8a9f0d3d844667 snapshot
← Older Oldest on feat/phase-2g-bundle
All commits
Newer → Latest on feat/phase-2g-bundle

0 comments

No comments yet. Be the first to start the discussion.

To add a comment, use the Muse CLI: muse hub commit comment sha256:29f0377812c07ce287028a495e3553c0209beddb7050b915a3b1628cfeacdfb3 --body "your comment"