229 lines
7.8 KiB
PL/PgSQL
229 lines
7.8 KiB
PL/PgSQL
-- ============================================
|
|
-- Migration: V20260120_002
|
|
-- Description: Migrate billing.subscriptions structure
|
|
-- Changes:
|
|
-- - Add stripe_subscription_id, stripe_customer_id columns
|
|
-- - Add interval column (ENUM: month, year)
|
|
-- - Add trial_start, cancel_at, cancel_reason columns
|
|
-- - Add price_amount, currency columns
|
|
-- - Rename cancelled_at -> canceled_at
|
|
-- - Change billing.subscription_status enum value: 'trial' -> 'trialing'
|
|
-- ============================================
|
|
|
|
-- UP Migration
|
|
BEGIN;
|
|
|
|
-- ============================================
|
|
-- 1. Ensure billing_interval ENUM exists
|
|
-- ============================================
|
|
|
|
DO $$
|
|
BEGIN
|
|
IF NOT EXISTS (
|
|
SELECT 1 FROM pg_type
|
|
WHERE typname = 'billing_interval'
|
|
AND typnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'billing')
|
|
) THEN
|
|
CREATE TYPE billing.billing_interval AS ENUM ('month', 'year');
|
|
RAISE NOTICE 'Created billing.billing_interval enum';
|
|
END IF;
|
|
END $$;
|
|
|
|
-- ============================================
|
|
-- 2. Fix billing.subscription_status enum (trial -> trialing)
|
|
-- ============================================
|
|
|
|
DO $$
|
|
DECLARE
|
|
has_trial_value BOOLEAN;
|
|
BEGIN
|
|
-- Check if 'trial' exists in the enum (needs migration)
|
|
SELECT EXISTS (
|
|
SELECT 1
|
|
FROM pg_enum e
|
|
JOIN pg_type t ON e.enumtypid = t.oid
|
|
JOIN pg_namespace n ON t.typnamespace = n.oid
|
|
WHERE n.nspname = 'billing'
|
|
AND t.typname = 'subscription_status'
|
|
AND e.enumlabel = 'trial'
|
|
) INTO has_trial_value;
|
|
|
|
IF has_trial_value THEN
|
|
-- PostgreSQL doesn't allow renaming enum values directly
|
|
-- We need to create a new type and migrate
|
|
|
|
-- Step 1: Create new enum type
|
|
CREATE TYPE billing.subscription_status_new AS ENUM ('trialing', 'active', 'past_due', 'cancelled', 'expired');
|
|
|
|
-- Step 2: Update columns using the old enum
|
|
-- First, change column to use text temporarily
|
|
ALTER TABLE billing.subscriptions
|
|
ALTER COLUMN status TYPE VARCHAR(50) USING status::VARCHAR(50);
|
|
|
|
-- Step 3: Update 'trial' to 'trialing'
|
|
UPDATE billing.subscriptions
|
|
SET status = 'trialing'
|
|
WHERE status = 'trial';
|
|
|
|
-- Step 4: Drop old type
|
|
DROP TYPE IF EXISTS billing.subscription_status;
|
|
|
|
-- Step 5: Rename new type
|
|
ALTER TYPE billing.subscription_status_new RENAME TO subscription_status;
|
|
|
|
-- Step 6: Convert column back to enum
|
|
ALTER TABLE billing.subscriptions
|
|
ALTER COLUMN status TYPE billing.subscription_status
|
|
USING status::billing.subscription_status;
|
|
|
|
-- Step 7: Restore default
|
|
ALTER TABLE billing.subscriptions
|
|
ALTER COLUMN status SET DEFAULT 'trialing'::billing.subscription_status;
|
|
|
|
RAISE NOTICE 'Migrated subscription_status enum: trial -> trialing';
|
|
END IF;
|
|
END $$;
|
|
|
|
-- ============================================
|
|
-- 3. Add Stripe columns if they don't exist
|
|
-- ============================================
|
|
|
|
-- stripe_subscription_id
|
|
DO $$
|
|
BEGIN
|
|
IF NOT EXISTS (
|
|
SELECT 1 FROM information_schema.columns
|
|
WHERE table_schema = 'billing' AND table_name = 'subscriptions' AND column_name = 'stripe_subscription_id'
|
|
) THEN
|
|
ALTER TABLE billing.subscriptions ADD COLUMN stripe_subscription_id VARCHAR(255) UNIQUE;
|
|
CREATE INDEX idx_subscriptions_stripe ON billing.subscriptions(stripe_subscription_id);
|
|
RAISE NOTICE 'Added stripe_subscription_id column';
|
|
END IF;
|
|
END $$;
|
|
|
|
-- stripe_customer_id
|
|
DO $$
|
|
BEGIN
|
|
IF NOT EXISTS (
|
|
SELECT 1 FROM information_schema.columns
|
|
WHERE table_schema = 'billing' AND table_name = 'subscriptions' AND column_name = 'stripe_customer_id'
|
|
) THEN
|
|
ALTER TABLE billing.subscriptions ADD COLUMN stripe_customer_id VARCHAR(255);
|
|
RAISE NOTICE 'Added stripe_customer_id column';
|
|
END IF;
|
|
END $$;
|
|
|
|
-- ============================================
|
|
-- 4. Add interval column
|
|
-- ============================================
|
|
|
|
DO $$
|
|
BEGIN
|
|
IF NOT EXISTS (
|
|
SELECT 1 FROM information_schema.columns
|
|
WHERE table_schema = 'billing' AND table_name = 'subscriptions' AND column_name = 'interval'
|
|
) THEN
|
|
ALTER TABLE billing.subscriptions ADD COLUMN "interval" billing.billing_interval DEFAULT 'month';
|
|
RAISE NOTICE 'Added interval column';
|
|
END IF;
|
|
END $$;
|
|
|
|
-- ============================================
|
|
-- 5. Add trial and cancellation columns
|
|
-- ============================================
|
|
|
|
-- trial_start
|
|
DO $$
|
|
BEGIN
|
|
IF NOT EXISTS (
|
|
SELECT 1 FROM information_schema.columns
|
|
WHERE table_schema = 'billing' AND table_name = 'subscriptions' AND column_name = 'trial_start'
|
|
) THEN
|
|
ALTER TABLE billing.subscriptions ADD COLUMN trial_start TIMESTAMPTZ;
|
|
RAISE NOTICE 'Added trial_start column';
|
|
END IF;
|
|
END $$;
|
|
|
|
-- cancel_at (scheduled cancellation date)
|
|
DO $$
|
|
BEGIN
|
|
IF NOT EXISTS (
|
|
SELECT 1 FROM information_schema.columns
|
|
WHERE table_schema = 'billing' AND table_name = 'subscriptions' AND column_name = 'cancel_at'
|
|
) THEN
|
|
ALTER TABLE billing.subscriptions ADD COLUMN cancel_at TIMESTAMPTZ;
|
|
RAISE NOTICE 'Added cancel_at column';
|
|
END IF;
|
|
END $$;
|
|
|
|
-- cancel_reason
|
|
DO $$
|
|
BEGIN
|
|
IF NOT EXISTS (
|
|
SELECT 1 FROM information_schema.columns
|
|
WHERE table_schema = 'billing' AND table_name = 'subscriptions' AND column_name = 'cancel_reason'
|
|
) THEN
|
|
ALTER TABLE billing.subscriptions ADD COLUMN cancel_reason VARCHAR(500);
|
|
RAISE NOTICE 'Added cancel_reason column';
|
|
END IF;
|
|
END $$;
|
|
|
|
-- ============================================
|
|
-- 6. Add pricing columns
|
|
-- ============================================
|
|
|
|
-- price_amount
|
|
DO $$
|
|
BEGIN
|
|
IF NOT EXISTS (
|
|
SELECT 1 FROM information_schema.columns
|
|
WHERE table_schema = 'billing' AND table_name = 'subscriptions' AND column_name = 'price_amount'
|
|
) THEN
|
|
ALTER TABLE billing.subscriptions ADD COLUMN price_amount DECIMAL(10, 2);
|
|
RAISE NOTICE 'Added price_amount column';
|
|
END IF;
|
|
END $$;
|
|
|
|
-- currency
|
|
DO $$
|
|
BEGIN
|
|
IF NOT EXISTS (
|
|
SELECT 1 FROM information_schema.columns
|
|
WHERE table_schema = 'billing' AND table_name = 'subscriptions' AND column_name = 'currency'
|
|
) THEN
|
|
ALTER TABLE billing.subscriptions ADD COLUMN currency VARCHAR(3) DEFAULT 'USD';
|
|
RAISE NOTICE 'Added currency column';
|
|
END IF;
|
|
END $$;
|
|
|
|
-- ============================================
|
|
-- 7. Rename cancelled_at -> canceled_at
|
|
-- ============================================
|
|
|
|
DO $$
|
|
BEGIN
|
|
-- Check if cancelled_at exists (British spelling)
|
|
IF EXISTS (
|
|
SELECT 1 FROM information_schema.columns
|
|
WHERE table_schema = 'billing' AND table_name = 'subscriptions' AND column_name = 'cancelled_at'
|
|
) THEN
|
|
ALTER TABLE billing.subscriptions RENAME COLUMN cancelled_at TO canceled_at;
|
|
RAISE NOTICE 'Renamed cancelled_at to canceled_at';
|
|
END IF;
|
|
END $$;
|
|
|
|
-- ============================================
|
|
-- 8. Update comments
|
|
-- ============================================
|
|
|
|
COMMENT ON COLUMN billing.subscriptions.stripe_subscription_id IS 'Stripe subscription ID for payment tracking';
|
|
COMMENT ON COLUMN billing.subscriptions.stripe_customer_id IS 'Stripe customer ID';
|
|
COMMENT ON COLUMN billing.subscriptions."interval" IS 'Billing interval: month or year';
|
|
COMMENT ON COLUMN billing.subscriptions.trial_start IS 'When the trial period started';
|
|
COMMENT ON COLUMN billing.subscriptions.cancel_at IS 'Scheduled cancellation date (end of period)';
|
|
COMMENT ON COLUMN billing.subscriptions.cancel_reason IS 'Reason for cancellation';
|
|
COMMENT ON COLUMN billing.subscriptions.price_amount IS 'Price amount at subscription time';
|
|
COMMENT ON COLUMN billing.subscriptions.currency IS 'Currency code (ISO 4217)';
|
|
|
|
COMMIT;
|