CREATE TABLE IF NOT EXISTS hwlab_redeem_codes ( id TEXT PRIMARY KEY, code_hash TEXT NOT NULL UNIQUE, code_prefix TEXT NOT NULL, display_name TEXT NOT NULL DEFAULT '', status TEXT NOT NULL DEFAULT 'active', credit_amount BIGINT NOT NULL, reason TEXT NOT NULL DEFAULT 'redeem', max_redemptions INTEGER NOT NULL DEFAULT 1, per_user_limit INTEGER NOT NULL DEFAULT 1, redeemed_count INTEGER NOT NULL DEFAULT 0, expires_at TIMESTAMPTZ, created_by_user_id TEXT, created_by_username TEXT, disabled_at TIMESTAMPTZ, metadata JSONB NOT NULL DEFAULT '{}'::jsonb, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), CHECK (status IN ('active', 'disabled', 'expired')), CHECK (credit_amount > 0), CHECK (max_redemptions > 0), CHECK (per_user_limit > 0), CHECK (redeemed_count >= 0) ); CREATE INDEX IF NOT EXISTS hwlab_redeem_codes_status_created_idx ON hwlab_redeem_codes(status, created_at DESC); CREATE INDEX IF NOT EXISTS hwlab_redeem_codes_prefix_idx ON hwlab_redeem_codes(code_prefix); CREATE TABLE IF NOT EXISTS hwlab_redeem_redemptions ( id TEXT PRIMARY KEY, code_id TEXT NOT NULL REFERENCES hwlab_redeem_codes(id) ON DELETE CASCADE, user_id TEXT NOT NULL REFERENCES hwlab_users(id) ON DELETE CASCADE, ledger_id TEXT REFERENCES hwlab_credit_ledger(id) ON DELETE SET NULL, credit_amount BIGINT NOT NULL, status TEXT NOT NULL DEFAULT 'applied', idempotency_key TEXT UNIQUE, metadata JSONB NOT NULL DEFAULT '{}'::jsonb, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), CHECK (credit_amount > 0), CHECK (status IN ('applied', 'rejected')) ); CREATE INDEX IF NOT EXISTS hwlab_redeem_redemptions_code_created_idx ON hwlab_redeem_redemptions(code_id, created_at DESC); CREATE INDEX IF NOT EXISTS hwlab_redeem_redemptions_user_created_idx ON hwlab_redeem_redemptions(user_id, created_at DESC); CREATE TABLE IF NOT EXISTS hwlab_user_subscriptions ( id TEXT PRIMARY KEY, user_id TEXT NOT NULL REFERENCES hwlab_users(id) ON DELETE CASCADE, plan_id TEXT NOT NULL REFERENCES hwlab_billing_plans(id), status TEXT NOT NULL DEFAULT 'active', source TEXT NOT NULL DEFAULT 'manual', current_period_start TIMESTAMPTZ, current_period_end TIMESTAMPTZ, metadata JSONB NOT NULL DEFAULT '{}'::jsonb, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), CHECK (status IN ('active', 'trialing', 'past_due', 'canceled', 'disabled')), CHECK (source IN ('manual', 'redeem', 'payment_placeholder', 'system')) ); CREATE INDEX IF NOT EXISTS hwlab_user_subscriptions_user_status_idx ON hwlab_user_subscriptions(user_id, status, created_at DESC); CREATE TABLE IF NOT EXISTS hwlab_payment_orders ( id TEXT PRIMARY KEY, user_id TEXT NOT NULL REFERENCES hwlab_users(id) ON DELETE CASCADE, provider TEXT NOT NULL DEFAULT 'unconfigured', status TEXT NOT NULL DEFAULT 'unconfigured', credit_amount BIGINT NOT NULL DEFAULT 0, amount_minor BIGINT NOT NULL DEFAULT 0, currency TEXT NOT NULL DEFAULT 'CNY', reason TEXT NOT NULL DEFAULT '', metadata JSONB NOT NULL DEFAULT '{}'::jsonb, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), CHECK (status IN ('unconfigured', 'created', 'pending', 'paid', 'failed', 'cancelled', 'refunded')), CHECK (credit_amount >= 0), CHECK (amount_minor >= 0) ); CREATE INDEX IF NOT EXISTS hwlab_payment_orders_user_created_idx ON hwlab_payment_orders(user_id, created_at DESC);