Files
warmbly/internal/infrastructure/db/migrations/000003_subscriptions.up.sql
T
Máté Mészáros (Laptop) 41624a6f79 Analytics & Tracking
2026-01-29 05:59:04 +01:00

121 lines
3.7 KiB
SQL

CREATE TABLE durations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
title VARCHAR(100) NOT NULL
);
CREATE TABLE plans (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name VARCHAR(100),
max_contacts BIGINT NOT NULL,
daily_emails INT NOT NULL,
ai_generation BOOLEAN NOT NULL,
account_limit INT NOT NULL,
price DECIMAL(10, 2) NOT NULL,
discounted_price DECIMAL(10, 2) NOT NULL,
duration_id UUID NOT NULL REFERENCES durations(id) ON DELETE CASCADE,
savings SMALLINT,
public BOOLEAN NOT NULL DEFAULT false,
-- Stripe integration
stripe_price_id VARCHAR(255),
stripe_product_id VARCHAR(255),
-- Organization limits
max_campaigns INT,
max_active_campaigns INT,
max_team_members INT,
max_email_accounts INT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT valid_savings CHECK (savings BETWEEN 0 AND 100)
);
CREATE INDEX idx_plans_stripe_price ON plans(stripe_price_id);
CREATE TABLE offers (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
title VARCHAR(255) NOT NULL,
description TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE offer_options (
offer_id UUID NOT NULL REFERENCES offers(id) ON DELETE CASCADE,
plan_id UUID NOT NULL REFERENCES plans(id) ON DELETE CASCADE,
title VARCHAR(255) NOT NULL,
PRIMARY KEY (offer_id, plan_id)
);
CREATE TABLE secret_plans (
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
plan_id UUID NOT NULL REFERENCES plans(id) ON DELETE CASCADE,
PRIMARY KEY (user_id, plan_id)
);
-- Subscription status enum
CREATE TYPE subscription_status AS ENUM (
'trialing',
'active',
'past_due',
'canceled',
'unpaid',
'incomplete',
'incomplete_expired',
'paused'
);
CREATE TABLE subscriptions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
organization_id UUID NOT NULL REFERENCES organizations(id) ON DELETE CASCADE,
plan_id UUID NOT NULL REFERENCES plans(id) ON DELETE RESTRICT,
-- Stripe identifiers
stripe_customer_id VARCHAR(255) NOT NULL,
stripe_subscription_id VARCHAR(255) UNIQUE,
stripe_price_id VARCHAR(255),
-- Subscription state
status subscription_status NOT NULL DEFAULT 'incomplete',
-- Billing period
current_period_start TIMESTAMPTZ,
current_period_end TIMESTAMPTZ,
cancel_at_period_end BOOLEAN NOT NULL DEFAULT FALSE,
canceled_at TIMESTAMPTZ,
-- Trial info
trial_start TIMESTAMPTZ,
trial_end TIMESTAMPTZ,
-- Enterprise flag - when true, uses user_rate_limits for custom limits
is_enterprise BOOLEAN NOT NULL DEFAULT FALSE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
CONSTRAINT unique_org_subscription UNIQUE (organization_id)
);
CREATE INDEX idx_subscriptions_stripe_customer ON subscriptions(stripe_customer_id);
CREATE INDEX idx_subscriptions_stripe_subscription ON subscriptions(stripe_subscription_id);
CREATE INDEX idx_subscriptions_status ON subscriptions(status);
CREATE INDEX idx_subscriptions_period_end ON subscriptions(current_period_end);
CREATE INDEX idx_subscriptions_org ON subscriptions(organization_id);
-- Stripe webhook events log for idempotency
CREATE TABLE stripe_webhook_events (
id VARCHAR(255) PRIMARY KEY,
event_type VARCHAR(100) NOT NULL,
processed_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
payload JSONB
);
CREATE INDEX idx_stripe_events_type ON stripe_webhook_events(event_type);
CREATE INDEX idx_stripe_events_processed ON stripe_webhook_events(processed_at);