-- IT0 Billing Tables (public schema) -- Phase 2: Subscription management + invoicing -- Invoice number sequence CREATE SEQUENCE IF NOT EXISTS billing_invoice_number_seq START 1000; -- Plans CREATE TABLE IF NOT EXISTS billing_plans ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name VARCHAR(50) NOT NULL UNIQUE, -- free, pro, enterprise display_name VARCHAR(100) NOT NULL, monthly_price_usd_cents INTEGER NOT NULL DEFAULT 0, monthly_price_cny INTEGER NOT NULL DEFAULT 0, -- in fen (1/100 yuan) included_tokens_per_month BIGINT NOT NULL DEFAULT 100000, overage_rate_cents_per_m_token INTEGER NOT NULL DEFAULT 0, -- per million tokens max_servers INTEGER NOT NULL DEFAULT 5, -- -1 = unlimited max_users INTEGER NOT NULL DEFAULT 3, max_standing_orders INTEGER NOT NULL DEFAULT 10, hard_limit_percent INTEGER NOT NULL DEFAULT 100, -- 0 = no hard limit trial_days INTEGER NOT NULL DEFAULT 0, is_active BOOLEAN NOT NULL DEFAULT TRUE, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); -- Subscriptions CREATE TABLE IF NOT EXISTS billing_subscriptions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id VARCHAR(20) NOT NULL REFERENCES it0_shared.tenants(id), plan_id UUID NOT NULL REFERENCES billing_plans(id), status VARCHAR(20) NOT NULL DEFAULT 'trialing', current_period_start TIMESTAMPTZ NOT NULL, current_period_end TIMESTAMPTZ NOT NULL, trial_ends_at TIMESTAMPTZ, cancel_at_period_end BOOLEAN NOT NULL DEFAULT FALSE, cancelled_at TIMESTAMPTZ, next_plan_id UUID REFERENCES billing_plans(id), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_billing_subs_tenant ON billing_subscriptions(tenant_id); CREATE INDEX IF NOT EXISTS idx_billing_subs_status ON billing_subscriptions(status); CREATE INDEX IF NOT EXISTS idx_billing_subs_period_end ON billing_subscriptions(current_period_end); -- Invoices CREATE TABLE IF NOT EXISTS billing_invoices ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id VARCHAR(20) NOT NULL REFERENCES it0_shared.tenants(id), subscription_id UUID REFERENCES billing_subscriptions(id), invoice_number VARCHAR(50) NOT NULL UNIQUE, status VARCHAR(20) NOT NULL DEFAULT 'open', -- open, paid, void, past_due, uncollectible currency VARCHAR(3) NOT NULL DEFAULT 'USD', subtotal_cents INTEGER NOT NULL DEFAULT 0, tax_cents INTEGER NOT NULL DEFAULT 0, total_cents INTEGER NOT NULL DEFAULT 0, amount_due_cents INTEGER NOT NULL DEFAULT 0, period_start TIMESTAMPTZ NOT NULL, period_end TIMESTAMPTZ NOT NULL, due_date TIMESTAMPTZ NOT NULL, paid_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_billing_invoices_tenant ON billing_invoices(tenant_id); CREATE INDEX IF NOT EXISTS idx_billing_invoices_status ON billing_invoices(status); -- Invoice Items CREATE TABLE IF NOT EXISTS billing_invoice_items ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), invoice_id UUID NOT NULL REFERENCES billing_invoices(id) ON DELETE CASCADE, item_type VARCHAR(30) NOT NULL, -- subscription, overage, credit, adjustment description TEXT NOT NULL, quantity BIGINT NOT NULL DEFAULT 1, unit_price INTEGER NOT NULL, -- cents amount INTEGER NOT NULL, -- cents currency VARCHAR(3) NOT NULL DEFAULT 'USD', created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_billing_items_invoice ON billing_invoice_items(invoice_id); -- Payments CREATE TABLE IF NOT EXISTS billing_payments ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id VARCHAR(20) NOT NULL REFERENCES it0_shared.tenants(id), invoice_id UUID NOT NULL REFERENCES billing_invoices(id), provider VARCHAR(20) NOT NULL, -- stripe, alipay, wechat_pay, crypto provider_payment_id VARCHAR(255) NOT NULL UNIQUE, amount_cents INTEGER NOT NULL, currency VARCHAR(3) NOT NULL, status VARCHAR(20) NOT NULL DEFAULT 'pending', -- pending, succeeded, failed, refunded paid_at TIMESTAMPTZ, metadata JSONB, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_billing_payments_tenant ON billing_payments(tenant_id); CREATE INDEX IF NOT EXISTS idx_billing_payments_provider_id ON billing_payments(provider_payment_id); -- Payment Methods CREATE TABLE IF NOT EXISTS billing_payment_methods ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id VARCHAR(20) NOT NULL REFERENCES it0_shared.tenants(id), provider VARCHAR(20) NOT NULL, display_name VARCHAR(200) NOT NULL, provider_customer_id VARCHAR(255), details JSONB, -- masked card info, alipay account, etc. NO raw card numbers is_default BOOLEAN NOT NULL DEFAULT FALSE, expires_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_billing_methods_tenant ON billing_payment_methods(tenant_id); -- Monthly Usage Aggregates CREATE TABLE IF NOT EXISTS billing_usage_aggregates ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), tenant_id VARCHAR(20) NOT NULL REFERENCES it0_shared.tenants(id), year INTEGER NOT NULL, month INTEGER NOT NULL, -- 1-12 period_start TIMESTAMPTZ NOT NULL, period_end TIMESTAMPTZ NOT NULL, total_input_tokens BIGINT NOT NULL DEFAULT 0, total_output_tokens BIGINT NOT NULL DEFAULT 0, total_tokens BIGINT NOT NULL DEFAULT 0, total_cost_usd NUMERIC(12, 6) NOT NULL DEFAULT 0, task_count INTEGER NOT NULL DEFAULT 0, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), UNIQUE(tenant_id, year, month) ); CREATE INDEX IF NOT EXISTS idx_billing_usage_tenant_period ON billing_usage_aggregates(tenant_id, year, month); -- Add max_standing_orders column to tenants if missing ALTER TABLE it0_shared.tenants ADD COLUMN IF NOT EXISTS max_standing_orders INTEGER NOT NULL DEFAULT 10; ALTER TABLE it0_shared.tenants ADD COLUMN IF NOT EXISTS max_agent_tokens_per_month BIGINT NOT NULL DEFAULT 100000;