-- ============================================ -- TEMPLATE-SAAS: Subscriptions -- Schema: billing -- Version: 1.0.0 -- ============================================ CREATE TABLE billing.subscriptions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id UUID NOT NULL REFERENCES tenants.tenants(id) ON DELETE CASCADE, plan_id UUID NOT NULL REFERENCES plans.plans(id), -- Stripe stripe_subscription_id VARCHAR(255) UNIQUE, stripe_customer_id VARCHAR(255), -- Status status tenants.subscription_status DEFAULT 'trialing' NOT NULL, -- Billing interval interval billing.billing_interval DEFAULT 'month', -- Current period current_period_start TIMESTAMPTZ, current_period_end TIMESTAMPTZ, -- Trial trial_start TIMESTAMPTZ, trial_end TIMESTAMPTZ, -- Cancellation cancel_at TIMESTAMPTZ, canceled_at TIMESTAMPTZ, cancel_reason VARCHAR(500), -- Pricing at subscription time price_amount DECIMAL(10, 2), currency VARCHAR(3) DEFAULT 'USD', -- Metadata metadata JSONB DEFAULT '{}'::jsonb, -- Audit created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL, updated_at TIMESTAMPTZ DEFAULT NOW() NOT NULL, -- Constraints CONSTRAINT unique_active_subscription UNIQUE (tenant_id) -- One active sub per tenant ); -- Subscription items (for metered/usage-based) CREATE TABLE billing.subscription_items ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), subscription_id UUID NOT NULL REFERENCES billing.subscriptions(id) ON DELETE CASCADE, -- Stripe stripe_subscription_item_id VARCHAR(255), stripe_price_id VARCHAR(255), -- Item details product_name VARCHAR(200), quantity INT DEFAULT 1, -- Pricing unit_amount DECIMAL(10, 2), -- For metered billing is_metered BOOLEAN DEFAULT FALSE, created_at TIMESTAMPTZ DEFAULT NOW() NOT NULL ); -- Indexes CREATE INDEX idx_subscriptions_tenant ON billing.subscriptions(tenant_id); CREATE INDEX idx_subscriptions_stripe ON billing.subscriptions(stripe_subscription_id); CREATE INDEX idx_subscriptions_status ON billing.subscriptions(status); CREATE INDEX idx_subscription_items_sub ON billing.subscription_items(subscription_id); -- RLS ALTER TABLE billing.subscriptions ENABLE ROW LEVEL SECURITY; ALTER TABLE billing.subscription_items ENABLE ROW LEVEL SECURITY; CREATE POLICY subscriptions_tenant_isolation ON billing.subscriptions USING (tenant_id = current_setting('app.current_tenant_id', true)::UUID); CREATE POLICY subscription_items_tenant_isolation ON billing.subscription_items USING (subscription_id IN ( SELECT id FROM billing.subscriptions WHERE tenant_id = current_setting('app.current_tenant_id', true)::UUID )); -- Trigger CREATE OR REPLACE FUNCTION billing.update_updated_at() RETURNS TRIGGER AS $$ BEGIN NEW.updated_at = NOW(); RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE TRIGGER trg_subscriptions_updated_at BEFORE UPDATE ON billing.subscriptions FOR EACH ROW EXECUTE FUNCTION billing.update_updated_at(); -- Comments COMMENT ON TABLE billing.subscriptions IS 'Active subscriptions per tenant'; COMMENT ON TABLE billing.subscription_items IS 'Line items within a subscription';