it0/packages/shared/database/src/migrations/005-create-billing-tables.sql

142 lines
7.0 KiB
SQL

-- 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;