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.
226 lines
8.5 KiB
PL/PgSQL
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;
|