- subscriptions 테이블 마이그레이션 (기존 paid orders에서 자동 생성) - GET/PATCH /api/subscription: 구독 조회, 해지, 자동갱신 토글 - 마이페이지 구독 관리 탭: D-day, 해지 버튼, 자동갱신 토글 - 해지 시 만료일까지 서비스 계속 이용 가능 - Vercel Cron: 매일 01:00 KST 만료 구독 자동 처리 + 텔레그램 알림 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
45 lines
2.0 KiB
SQL
45 lines
2.0 KiB
SQL
-- ─── 구독 관리 테이블 ──────────────────────────────────────────────────────────
|
|
CREATE TABLE IF NOT EXISTS public.subscriptions (
|
|
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id uuid NOT NULL REFERENCES public.profiles(id) ON DELETE CASCADE,
|
|
product_id text NOT NULL REFERENCES public.products(id),
|
|
order_id uuid REFERENCES public.orders(id),
|
|
status text NOT NULL DEFAULT 'active', -- 'active' | 'cancelled' | 'expired'
|
|
auto_renew boolean NOT NULL DEFAULT false, -- Toss 빌링키 연동 전까지 false
|
|
started_at timestamptz NOT NULL DEFAULT now(),
|
|
expires_at timestamptz NOT NULL,
|
|
cancelled_at timestamptz,
|
|
billing_key text, -- Toss 자동결제 빌링키 (향후)
|
|
created_at timestamptz NOT NULL DEFAULT now()
|
|
);
|
|
|
|
-- RLS
|
|
ALTER TABLE public.subscriptions ENABLE ROW LEVEL SECURITY;
|
|
|
|
CREATE POLICY "subscriptions_select_own" ON public.subscriptions
|
|
FOR SELECT USING (auth.uid() = user_id);
|
|
|
|
CREATE POLICY "subscriptions_update_own" ON public.subscriptions
|
|
FOR UPDATE USING (auth.uid() = user_id);
|
|
|
|
-- 인덱스
|
|
CREATE INDEX IF NOT EXISTS subscriptions_user_status ON public.subscriptions (user_id, status);
|
|
CREATE INDEX IF NOT EXISTS subscriptions_expires_at ON public.subscriptions (expires_at) WHERE status = 'active';
|
|
|
|
-- ─── 기존 paid lotto orders → subscriptions 마이그레이션 ───────────────────────
|
|
INSERT INTO public.subscriptions (user_id, product_id, order_id, status, started_at, expires_at)
|
|
SELECT
|
|
o.user_id,
|
|
o.product_id,
|
|
o.id,
|
|
CASE
|
|
WHEN (o.created_at + INTERVAL '31 days') < now() THEN 'expired'
|
|
ELSE 'active'
|
|
END,
|
|
o.created_at,
|
|
o.created_at + INTERVAL '31 days'
|
|
FROM public.orders o
|
|
WHERE o.status = 'paid'
|
|
AND o.product_id IN ('lotto_gold', 'lotto_platinum', 'lotto_diamond')
|
|
ON CONFLICT DO NOTHING;
|