-- Billing / Razorpay subscriptions. -- Idempotent: safe to re-run (used by debug-migration hot-apply too). -- Subscription lifecycle enum (mirrors Razorpay states). DO $$ BEGIN CREATE TYPE subscription_status_enum AS ENUM ( 'created','authenticated','active','pending', 'halted','cancelled','completed','expired','paused' ); EXCEPTION WHEN duplicate_object THEN NULL; END $$; -- Plans: maps a Razorpay plan_id -> what it grants. CREATE TABLE IF NOT EXISTS subscription_plans ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), razorpay_plan_id text NOT NULL UNIQUE, name text NOT NULL, price_paise integer NOT NULL, storage_bytes bigint NOT NULL, member_limit integer NOT NULL, child_limit integer NOT NULL DEFAULT 3, is_active boolean NOT NULL DEFAULT true, created_at timestamptz NOT NULL DEFAULT now() ); -- One subscription row per family (append on upgrade). CREATE TABLE IF NOT EXISTS family_subscriptions ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), family_id uuid NOT NULL REFERENCES families(id) ON DELETE CASCADE, plan_id uuid NOT NULL REFERENCES subscription_plans(id), razorpay_subscription_id text NOT NULL UNIQUE, razorpay_customer_id text, status subscription_status_enum NOT NULL DEFAULT 'created', current_start timestamptz, current_end timestamptz, cancelled_at timestamptz, ended_at timestamptz, created_at timestamptz NOT NULL DEFAULT now(), updated_at timestamptz NOT NULL DEFAULT now() ); CREATE INDEX IF NOT EXISTS family_subscriptions_family_idx ON family_subscriptions (family_id); -- At most one LIVE (non-terminal) subscription per family. CREATE UNIQUE INDEX IF NOT EXISTS family_live_sub_idx ON family_subscriptions (family_id) WHERE status IN ('created','authenticated','active','pending','halted'); -- Append-only webhook log. razorpay_event_id = idempotency key. CREATE TABLE IF NOT EXISTS razorpay_webhook_events ( id uuid PRIMARY KEY DEFAULT gen_random_uuid(), razorpay_event_id text NOT NULL UNIQUE, event_type text NOT NULL, payload jsonb NOT NULL, received_at timestamptz NOT NULL DEFAULT now() );