Spaces:
Running
Running
| -- 0024_subscriptions.sql | |
| -- Subscription & Recurring Billing Engine for Rtix | |
| -- Supports SaaS merchants offering recurring payment products. | |
| CREATE TABLE IF NOT EXISTS subscription_plans ( | |
| id VARCHAR(36) PRIMARY KEY DEFAULT gen_random_uuid()::text, | |
| merchant_id VARCHAR(36) NOT NULL REFERENCES merchants(merchant_id) ON DELETE CASCADE, | |
| name VARCHAR(255) NOT NULL, | |
| description TEXT, | |
| price_inr NUMERIC(12, 2) NOT NULL CHECK (price_inr > 0), | |
| interval_days INT NOT NULL CHECK (interval_days > 0), -- e.g. 30 = monthly, 365 = annual | |
| trial_days INT 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() | |
| ); | |
| CREATE TABLE IF NOT EXISTS subscriptions ( | |
| id VARCHAR(36) PRIMARY KEY DEFAULT gen_random_uuid()::text, | |
| merchant_id VARCHAR(36) NOT NULL REFERENCES merchants(merchant_id) ON DELETE CASCADE, | |
| plan_id VARCHAR(36) NOT NULL REFERENCES subscription_plans(id), | |
| subscriber_email VARCHAR(255) NOT NULL, | |
| subscriber_phone VARCHAR(20), | |
| subscriber_name VARCHAR(255) NOT NULL, | |
| status VARCHAR(20) NOT NULL DEFAULT 'TRIAL' | |
| CHECK (status IN ('TRIAL', 'ACTIVE', 'PAST_DUE', 'CANCELLED', 'EXPIRED')), | |
| current_period_start TIMESTAMPTZ NOT NULL DEFAULT NOW(), | |
| current_period_end TIMESTAMPTZ NOT NULL, | |
| cancelled_at TIMESTAMPTZ, | |
| cancel_reason TEXT, | |
| metadata JSONB DEFAULT '{}', | |
| created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), | |
| updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() | |
| ); | |
| CREATE TABLE IF NOT EXISTS subscription_billing_events ( | |
| id VARCHAR(36) PRIMARY KEY DEFAULT gen_random_uuid()::text, | |
| subscription_id VARCHAR(36) NOT NULL REFERENCES subscriptions(id) ON DELETE CASCADE, | |
| merchant_id VARCHAR(36) NOT NULL, | |
| amount_inr NUMERIC(12, 2) NOT NULL, | |
| status VARCHAR(20) NOT NULL DEFAULT 'PENDING' | |
| CHECK (status IN ('PENDING', 'SUCCESS', 'FAILED', 'REFUNDED')), | |
| transaction_id VARCHAR(36), -- links to orders.transaction_id on success | |
| attempt_count INT NOT NULL DEFAULT 1, | |
| billed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), | |
| settled_at TIMESTAMPTZ, | |
| failure_reason TEXT | |
| ); | |
| CREATE INDEX IF NOT EXISTS idx_subscriptions_merchant ON subscriptions(merchant_id); | |
| CREATE INDEX IF NOT EXISTS idx_subscriptions_status ON subscriptions(status); | |
| CREATE INDEX IF NOT EXISTS idx_subscriptions_period_end ON subscriptions(current_period_end) WHERE status = 'ACTIVE'; | |
| CREATE INDEX IF NOT EXISTS idx_billing_events_subscription ON subscription_billing_events(subscription_id); | |
| CREATE INDEX IF NOT EXISTS idx_billing_events_merchant ON subscription_billing_events(merchant_id); | |
| CREATE INDEX IF NOT EXISTS idx_subscription_plans_merchant ON subscription_plans(merchant_id); | |