synor/docker/economics-service/init.sql
Gulshan Yadav 17f0b4ce4b feat(economics): add Phase 12 - Economics & Billing infrastructure
Complete economics service implementation with:

- Price Oracle with TWAP (Time-Weighted Average Price)
  - Multi-source price aggregation
  - Configurable staleness thresholds
  - Exponential, SMA, and standard TWAP strategies

- Metering Service for L2 usage tracking
  - Storage: GB-months, retrieval bandwidth
  - Hosting: bandwidth, custom domains
  - Database: queries, vector searches
  - Compute: CPU core-hours, GPU hours, memory
  - Network: bandwidth, requests

- Billing Engine
  - Invoice generation with line items
  - Payment processing (crypto/fiat)
  - Credit management with expiration
  - Auto-pay from prepaid balance

- Pricing Tiers: Free, Standard, Premium, Enterprise
  - 0%, 10%, 20%, 30% usage discounts
  - SLA guarantees: 95%, 99%, 99.9%, 99.99%

- Cost Calculator & Estimator
  - Usage projections
  - Tier comparison recommendations
  - ROI analysis

- Docker deployment with PostgreSQL schema

All 61 tests passing.
2026-01-19 21:51:26 +05:30

226 lines
8.5 KiB
PL/PgSQL

-- Synor Economics Database Schema
-- Phase 12: Economics & Billing Infrastructure
-- Enable UUID extension
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- Accounts table
CREATE TABLE IF NOT EXISTS accounts (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
account_id VARCHAR(255) UNIQUE NOT NULL,
tier VARCHAR(50) NOT NULL DEFAULT 'free',
prepaid_balance DECIMAL(38, 18) NOT NULL DEFAULT 0,
credit_balance DECIMAL(38, 18) NOT NULL DEFAULT 0,
billing_cycle_start TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);
-- Price history table
CREATE TABLE IF NOT EXISTS price_history (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
token VARCHAR(20) NOT NULL,
quote VARCHAR(20) NOT NULL,
price DECIMAL(38, 18) NOT NULL,
source VARCHAR(50) NOT NULL,
confidence DECIMAL(5, 4) NOT NULL DEFAULT 1.0,
timestamp TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);
-- Create index for price lookups
CREATE INDEX IF NOT EXISTS idx_price_history_pair_time
ON price_history(token, quote, timestamp DESC);
-- Usage events table
CREATE TABLE IF NOT EXISTS usage_events (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
event_id VARCHAR(100) UNIQUE NOT NULL,
account_id VARCHAR(255) NOT NULL REFERENCES accounts(account_id),
service_id VARCHAR(255),
service_type VARCHAR(50) NOT NULL,
resource_unit VARCHAR(50) NOT NULL,
amount DECIMAL(38, 18) NOT NULL,
cost DECIMAL(38, 18),
timestamp TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
metadata JSONB DEFAULT '{}'
);
-- Create indexes for usage queries
CREATE INDEX IF NOT EXISTS idx_usage_events_account
ON usage_events(account_id, timestamp DESC);
CREATE INDEX IF NOT EXISTS idx_usage_events_service
ON usage_events(service_type, timestamp DESC);
-- Invoices table
CREATE TABLE IF NOT EXISTS invoices (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
invoice_id VARCHAR(100) UNIQUE NOT NULL,
invoice_number VARCHAR(50) NOT NULL,
account_id VARCHAR(255) NOT NULL REFERENCES accounts(account_id),
status VARCHAR(20) NOT NULL DEFAULT 'draft',
period_start TIMESTAMP WITH TIME ZONE NOT NULL,
period_end TIMESTAMP WITH TIME ZONE NOT NULL,
subtotal DECIMAL(38, 18) NOT NULL DEFAULT 0,
discount DECIMAL(38, 18) NOT NULL DEFAULT 0,
discount_description TEXT,
tax DECIMAL(38, 18) NOT NULL DEFAULT 0,
total DECIMAL(38, 18) NOT NULL DEFAULT 0,
due_date TIMESTAMP WITH TIME ZONE NOT NULL,
paid_at TIMESTAMP WITH TIME ZONE,
payment_id VARCHAR(100),
notes TEXT,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);
-- Create indexes for invoice queries
CREATE INDEX IF NOT EXISTS idx_invoices_account
ON invoices(account_id, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_invoices_status
ON invoices(status);
-- Invoice line items table
CREATE TABLE IF NOT EXISTS invoice_line_items (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
invoice_id VARCHAR(100) NOT NULL REFERENCES invoices(invoice_id),
description TEXT NOT NULL,
service_type VARCHAR(50) NOT NULL,
quantity DECIMAL(38, 18) NOT NULL,
unit_price DECIMAL(38, 18) NOT NULL,
amount DECIMAL(38, 18) NOT NULL
);
-- Payments table
CREATE TABLE IF NOT EXISTS payments (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
payment_id VARCHAR(100) UNIQUE NOT NULL,
account_id VARCHAR(255) NOT NULL REFERENCES accounts(account_id),
invoice_id VARCHAR(100) REFERENCES invoices(invoice_id),
amount DECIMAL(38, 18) NOT NULL,
method VARCHAR(50) NOT NULL,
status VARCHAR(20) NOT NULL DEFAULT 'pending',
transaction_hash VARCHAR(100),
block_number BIGINT,
failure_reason TEXT,
metadata JSONB DEFAULT '{}',
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
completed_at TIMESTAMP WITH TIME ZONE
);
-- Create indexes for payment queries
CREATE INDEX IF NOT EXISTS idx_payments_account
ON payments(account_id, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_payments_status
ON payments(status);
-- Credits table
CREATE TABLE IF NOT EXISTS credits (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
credit_id VARCHAR(100) UNIQUE NOT NULL,
account_id VARCHAR(255) NOT NULL REFERENCES accounts(account_id),
original_amount DECIMAL(38, 18) NOT NULL,
remaining_amount DECIMAL(38, 18) NOT NULL,
used_amount DECIMAL(38, 18) NOT NULL DEFAULT 0,
credit_type VARCHAR(50) NOT NULL,
reason TEXT NOT NULL,
reference_id VARCHAR(255),
approved_by VARCHAR(255),
is_active BOOLEAN NOT NULL DEFAULT true,
expires_at TIMESTAMP WITH TIME ZONE,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);
-- Create indexes for credit queries
CREATE INDEX IF NOT EXISTS idx_credits_account
ON credits(account_id, is_active);
-- Discounts table
CREATE TABLE IF NOT EXISTS discounts (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
code VARCHAR(50) UNIQUE NOT NULL,
name VARCHAR(255) NOT NULL,
description TEXT,
discount_type VARCHAR(50) NOT NULL,
value DECIMAL(38, 18) NOT NULL,
min_spend DECIMAL(38, 18),
max_discount DECIMAL(38, 18),
service_types TEXT[],
account_ids TEXT[],
start_date TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
end_date TIMESTAMP WITH TIME ZONE,
max_uses INTEGER,
current_uses INTEGER NOT NULL DEFAULT 0,
is_active BOOLEAN NOT NULL DEFAULT true,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);
-- Pricing tiers table
CREATE TABLE IF NOT EXISTS pricing_tiers (
id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
name VARCHAR(50) UNIQUE NOT NULL,
display_name VARCHAR(100) NOT NULL,
monthly_fee DECIMAL(38, 18) NOT NULL DEFAULT 0,
discount_percentage DECIMAL(5, 2) NOT NULL DEFAULT 0,
priority_support BOOLEAN NOT NULL DEFAULT false,
sla_percentage DECIMAL(5, 2) NOT NULL DEFAULT 95.00,
custom_domain_limit INTEGER NOT NULL DEFAULT 1,
api_rate_limit INTEGER NOT NULL DEFAULT 100,
features TEXT[] NOT NULL DEFAULT '{}',
min_commitment_months INTEGER NOT NULL DEFAULT 0,
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
);
-- Insert default pricing tiers
INSERT INTO pricing_tiers (name, display_name, monthly_fee, discount_percentage, priority_support, sla_percentage, custom_domain_limit, api_rate_limit, features, min_commitment_months)
VALUES
('free', 'Free', 0, 0, false, 95.00, 1, 100, ARRAY['0.5 GB Storage', '1 GB Hosting Bandwidth', '1M Database Queries', '100 CPU Core-Hours', 'Community Support'], 0),
('standard', 'Standard', 10, 10, false, 99.00, 5, 1000, ARRAY['Everything in Free', '10% Usage Discount', '5 Custom Domains', 'Email Support', '99% SLA Guarantee'], 0),
('premium', 'Premium', 50, 20, true, 99.90, 20, 5000, ARRAY['Everything in Standard', '20% Usage Discount', '20 Custom Domains', 'Priority Support', '99.9% SLA Guarantee', 'Advanced Analytics'], 0),
('enterprise', 'Enterprise', 500, 30, true, 99.99, 0, 0, ARRAY['Everything in Premium', '30%+ Usage Discount', 'Unlimited Custom Domains', 'Dedicated Support', '99.99% SLA Guarantee', 'Custom Integrations', 'Volume Pricing', 'Invoice Billing'], 12)
ON CONFLICT (name) DO NOTHING;
-- Aggregated usage view
CREATE OR REPLACE VIEW usage_summary AS
SELECT
account_id,
service_type,
DATE_TRUNC('day', timestamp) as usage_date,
SUM(amount) as total_amount,
SUM(cost) as total_cost,
COUNT(*) as event_count
FROM usage_events
GROUP BY account_id, service_type, DATE_TRUNC('day', timestamp);
-- Outstanding invoices view
CREATE OR REPLACE VIEW outstanding_invoices AS
SELECT
i.*,
a.prepaid_balance,
a.credit_balance,
(a.prepaid_balance + a.credit_balance) >= i.total as can_auto_pay
FROM invoices i
JOIN accounts a ON i.account_id = a.account_id
WHERE i.status IN ('pending', 'overdue')
ORDER BY i.due_date ASC;
-- Function to update updated_at timestamp
CREATE OR REPLACE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ language 'plpgsql';
-- Add triggers for updated_at
CREATE TRIGGER update_accounts_updated_at
BEFORE UPDATE ON accounts
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
CREATE TRIGGER update_invoices_updated_at
BEFORE UPDATE ON invoices
FOR EACH ROW EXECUTE FUNCTION update_updated_at_column();
-- Grant permissions
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO synor;
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO synor;