The paid tier. Built + hard-verified the Stripe money spine, server-side free-tier enforcement, the upgrade UI, and two flagship premium features. Still TEST mode.
0004_phase3_money_spine.sql + 0005_harden_phase3_triggers.sql (applied live):is_premium(uid) — expiry-aware effective-premium check (the read-path downgrade).apply_subscription(...) — the ONLY writer of premium; atomic upsert-to-state (idempotent). SECURITY DEFINER, revoked from anon/authenticated.enforce_compound_limit — free = ONE active compound, enforced in the DB (not just UI). enforce_stacks_premium — stacks are premium-only, DB-enforced.entitlements.stripe_customer_id, profiles.is_admin, stacks + stack_items tables (owner-only RLS).create-checkout (JWT-auth'd; 14-day trial; reuses/creates Stripe customer; returns checkout URL — never grants premium) and stripe-webhook (verify_jwt=false; verifies Stripe signature via constructEventAsync; maps event→user; calls apply_subscription; active/trialing→premium, deleted/past→revoke).useAuth now loads entitlement + exposes isPremium (expiry-aware, mirrors DB). pages/Upgrade.tsx (paywall: perks, yearly/monthly, 14-day trial, "CSV always free" trust line). lib/checkout.ts. Free-tier gate on Compounds (2nd compound → /upgrade, + server-trigger fallback). components/PremiumLock.tsx. pages/Settings.tsx shows plan + polls entitlement after ?upgrade=success.pages/HalfLife.tsx + lib/halflife.ts — estimated medication-level curve per compound from the user's own dose events × published half-lives (overridable), Recharts area chart, "personal reference, not medical guidance" label. pages/Stacks.tsx — named protocols grouping compounds. Both premium-gated (UI + DB)./upgrade, /halflife, /stacks; More shows a Go-Premium banner for free users).npx tsc -b clean · npm run build clean · dev server serves 200.pg_net in the public schema (it doesn't support SET SCHEMA; moving it means drop/recreate which would disrupt the live reminder cron — deferred to launch).subscription.deleted revokes. Test user cascade-deleted; DB confirmed clean (0 rows).4242 4242 4242 4242 (any future date/CVC) → Settings should flip to Premium within a few seconds.profiles.is_admin column already exists.)Landing page, legal pages, support form, empty-state/microcopy polish, Lighthouse gate, /app-safety-check, then flip Stripe TEST→live (products/webhook/keys), email-confirm ON, and domain cutover to getpeptidepal.com. Cancel Emergent (Sondra's manual step).
types.ts still hand-written (now includes Entitlement/Stack/StackItem); regenerate from live DB at launch.Generated for the CEO Dashboard · source: PROJECTS/Peptide Pal/handoffs/v2-phase3-money-spine-2026-07-07.md 🤍