diff --git a/veza-backend-api/migrations/949_subscription_plans_v0121.sql b/veza-backend-api/migrations/949_subscription_plans_v0121.sql new file mode 100644 index 000000000..5b03c1288 --- /dev/null +++ b/veza-backend-api/migrations/949_subscription_plans_v0121.sql @@ -0,0 +1,81 @@ +-- v0.12.1: Plans Premium & Abonnements (F001-F030 business) +-- Subscription plans, user subscriptions, invoices + +-- Subscription plans definition +CREATE TABLE IF NOT EXISTS subscription_plans ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + name VARCHAR(50) NOT NULL UNIQUE, -- 'free', 'creator', 'premium' + display_name VARCHAR(100) NOT NULL, + description TEXT, + price_monthly_cents INTEGER NOT NULL DEFAULT 0, + price_yearly_cents INTEGER NOT NULL DEFAULT 0, + currency VARCHAR(3) NOT NULL DEFAULT 'USD', + upload_limit_monthly INTEGER, -- NULL = unlimited + storage_limit_bytes BIGINT NOT NULL DEFAULT 0, + marketplace_commission_rate NUMERIC(5,4) DEFAULT 0, -- 0.15 = 15% + can_sell_on_marketplace BOOLEAN NOT NULL DEFAULT false, + has_priority_support BOOLEAN NOT NULL DEFAULT false, + has_collaboration_tools BOOLEAN NOT NULL DEFAULT false, + has_distribution BOOLEAN NOT NULL DEFAULT false, + trial_days INTEGER NOT NULL DEFAULT 0, + is_active BOOLEAN NOT NULL DEFAULT true, + sort_order INTEGER NOT NULL DEFAULT 0, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +-- User subscriptions +CREATE TABLE IF NOT EXISTS user_subscriptions ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, + plan_id UUID NOT NULL REFERENCES subscription_plans(id), + status VARCHAR(30) NOT NULL DEFAULT 'active', -- active, trialing, canceled, past_due, expired + billing_cycle VARCHAR(10) NOT NULL DEFAULT 'monthly', -- monthly, yearly + current_period_start TIMESTAMPTZ NOT NULL DEFAULT NOW(), + current_period_end TIMESTAMPTZ NOT NULL, + trial_start TIMESTAMPTZ, + trial_end TIMESTAMPTZ, + canceled_at TIMESTAMPTZ, + cancel_at_period_end BOOLEAN NOT NULL DEFAULT false, + hyperswitch_subscription_id VARCHAR(255), + hyperswitch_customer_id VARCHAR(255), + payment_method_id VARCHAR(255), + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +-- Each user can have only one active subscription at a time +CREATE UNIQUE INDEX IF NOT EXISTS idx_user_subscriptions_active + ON user_subscriptions(user_id) + WHERE status IN ('active', 'trialing'); + +CREATE INDEX IF NOT EXISTS idx_user_subscriptions_user ON user_subscriptions(user_id); +CREATE INDEX IF NOT EXISTS idx_user_subscriptions_status ON user_subscriptions(status); +CREATE INDEX IF NOT EXISTS idx_user_subscriptions_period_end ON user_subscriptions(current_period_end); + +-- Subscription invoices +CREATE TABLE IF NOT EXISTS subscription_invoices ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + subscription_id UUID NOT NULL REFERENCES user_subscriptions(id) ON DELETE CASCADE, + user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, + amount_cents INTEGER NOT NULL, + currency VARCHAR(3) NOT NULL DEFAULT 'USD', + status VARCHAR(30) NOT NULL DEFAULT 'pending', -- pending, paid, failed, refunded + billing_period_start TIMESTAMPTZ NOT NULL, + billing_period_end TIMESTAMPTZ NOT NULL, + hyperswitch_payment_id VARCHAR(255), + paid_at TIMESTAMPTZ, + created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() +); + +CREATE INDEX IF NOT EXISTS idx_subscription_invoices_user ON subscription_invoices(user_id); +CREATE INDEX IF NOT EXISTS idx_subscription_invoices_subscription ON subscription_invoices(subscription_id); +CREATE INDEX IF NOT EXISTS idx_subscription_invoices_status ON subscription_invoices(status); + +-- Insert default plans +INSERT INTO subscription_plans (name, display_name, description, price_monthly_cents, price_yearly_cents, currency, upload_limit_monthly, storage_limit_bytes, marketplace_commission_rate, can_sell_on_marketplace, has_priority_support, has_collaboration_tools, has_distribution, trial_days, sort_order) +VALUES + ('free', 'Free', 'Get started with Veza. Upload up to 5 tracks per month.', 0, 0, 'USD', 5, 1073741824, 0, false, false, false, false, 0, 0), + ('creator', 'Creator', 'For active creators. Unlimited uploads, sell on the marketplace.', 999, 9999, 'USD', NULL, 53687091200, 0.1500, true, false, false, false, 0, 1), + ('premium', 'Premium', 'Full access. Priority support, collaboration tools, and distribution.', 1999, 19999, 'USD', NULL, 214748364800, 0.1000, true, true, true, true, 14, 2) +ON CONFLICT (name) DO NOTHING; diff --git a/veza-backend-api/migrations/949_subscription_plans_v0121_down.sql b/veza-backend-api/migrations/949_subscription_plans_v0121_down.sql new file mode 100644 index 000000000..0df9f05f3 --- /dev/null +++ b/veza-backend-api/migrations/949_subscription_plans_v0121_down.sql @@ -0,0 +1,4 @@ +-- v0.12.1: Rollback subscription plans +DROP TABLE IF EXISTS subscription_invoices; +DROP TABLE IF EXISTS user_subscriptions; +DROP TABLE IF EXISTS subscription_plans;