-- Block F — Stripe subscriptions. One row per Stripe customer + (optional) -- active subscription. Free-tier hosts never get a row; their tier is -- inferred at read time. CREATE TABLE IF NOT EXISTS subscriptions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, stripe_customer_id TEXT NOT NULL, stripe_subscription_id TEXT, tier TEXT NOT NULL CHECK (tier IN ('free','pro','business')), status TEXT NOT NULL CHECK (status IN ('active','past_due','canceled','incomplete','trialing','unpaid')), current_period_end TIMESTAMPTZ, cancel_at_period_end BOOLEAN NOT NULL DEFAULT FALSE, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ); -- A user may have at most one *granting* subscription at a time. We -- include trialing + past_due because those still convey access (past_due -- is the grace period before Stripe gives up on the card). CREATE UNIQUE INDEX IF NOT EXISTS uniq_subscriptions_active_user ON subscriptions(user_id) WHERE status IN ('active','past_due','trialing'); CREATE INDEX IF NOT EXISTS idx_subscriptions_customer ON subscriptions(stripe_customer_id); CREATE INDEX IF NOT EXISTS idx_subscriptions_subscription ON subscriptions(stripe_subscription_id) WHERE stripe_subscription_id IS NOT NULL;