Spaces:
Sleeping
Sleeping
Download backend/sql/010_subscriptions.sql from stylevoice/talk: direct link, hf CLI and curl.
- Browser
- Download file 4.46 kB
-
https://huggingface.co/spaces/stylevoice/talk/resolve/main/backend/sql/010_subscriptions.sql
- Command line
-
hf download hf://spaces/stylevoice/talk/backend/sql/010_subscriptions.sql
-
curl -L -o 010_subscriptions.sql https://huggingface.co/spaces/stylevoice/talk/resolve/main/backend/sql/010_subscriptions.sql
4.46 kB
| -- ๊ตฌ๋ + ๊ฒฐ์ ์ด๋ฒคํธ (2026-05-03) | |
| -- | |
| -- CCBill / Epoch ๊ณ์ฝ ์ ์ค์ผ๋ ํค. ๊ณ์ฝ ํ webhook ํตํฉ์ผ๋ก ์ฑ์๋ฃ์. | |
| -- | |
| -- ํ๋ ๊ตฌ์กฐ: | |
| -- free: $0/์ ยท ์ผ 100 ํฌ๋ ๋ง | |
| -- standard: $20/์ ยท ์ผ 1,000 ํฌ๋ ๋ง + ๐ hard ๋ชจ๋ ํ์ฑํ | |
| -- pro: $100/์ ยท ์ผ 5,000 ํฌ๋ ๋ง + ์ฐ์ ์ฒ๋ฆฌ | |
| -- | |
| -- ๊ฒฐ์ ์ฌ (processor): | |
| -- 'ccbill' : ๋ฉ์ธ (์ด๋ํธ ์นด๋ ๊ฒฐ์ ) | |
| -- 'epoch' : ๋ฐฑ์ (์ด๋ํธ ์นด๋ ๊ฒฐ์ ) | |
| -- 'manual' : ๊ด๋ฆฌ์ ์ง์ ๋ถ์ฌ (๋ฒ ํยทํ๋ก๋ชจ์ ) | |
| -- 'coupon' : ์ฟ ํฐ ์ฝ๋๋ก ๋ถ์ฌ๋ ์์ ๊ตฌ๋ | |
| CREATE TABLE IF NOT EXISTS public.subscriptions ( | |
| id UUID PRIMARY KEY DEFAULT gen_random_uuid(), | |
| user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE, | |
| plan VARCHAR(16) NOT NULL CHECK (plan IN ('free', 'standard', 'pro')), | |
| status VARCHAR(16) NOT NULL DEFAULT 'pending' | |
| CHECK (status IN ('pending', 'active', 'past_due', 'canceled', 'expired')), | |
| processor VARCHAR(16) NOT NULL DEFAULT 'manual' | |
| CHECK (processor IN ('ccbill', 'epoch', 'manual', 'coupon')), | |
| processor_subscription_id VARCHAR(128), -- CCBill subscription ID ๋ฑ ์ธ๋ถ ์๋ณ์ | |
| started_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), | |
| current_period_start TIMESTAMPTZ NOT NULL DEFAULT NOW(), | |
| current_period_end TIMESTAMPTZ, -- ๋ค์ ๊ฒฐ์ ์ผ (๊ตฌ๋ ์ข ๋ฃ ์์ ์์ ) | |
| canceled_at TIMESTAMPTZ, -- ์ฌ์ฉ์๊ฐ ์ทจ์ ์์ฒญํ ์์ (period_end ๊น์ง ํ์ฑ) | |
| metadata JSONB NOT NULL DEFAULT '{}', | |
| created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), | |
| updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() | |
| ); | |
| -- ํ ์ฌ์ฉ์์ ๋ํด active ๊ตฌ๋ ์ ์ต๋ 1๊ฐ (DB ๋ ๋ฒจ enforcement) | |
| CREATE UNIQUE INDEX IF NOT EXISTS idx_subscriptions_user_active | |
| ON public.subscriptions(user_id) | |
| WHERE status = 'active'; | |
| -- ์ฌ์ฉ์๋ณ ๊ตฌ๋ ์ด๋ ฅ ์กฐํ ๋น๋ ๋์ | |
| CREATE INDEX IF NOT EXISTS idx_subscriptions_user_created | |
| ON public.subscriptions(user_id, created_at DESC); | |
| -- ๊ฒฐ์ ์ฌ webhook ID ์กฐํ (์ค๋ณต ์ฒ๋ฆฌ ๋ฐฉ์ง์ฉ) | |
| CREATE INDEX IF NOT EXISTS idx_subscriptions_processor_id | |
| ON public.subscriptions(processor, processor_subscription_id) | |
| WHERE processor_subscription_id IS NOT NULL; | |
| ALTER TABLE public.subscriptions ENABLE ROW LEVEL SECURITY; | |
| CREATE POLICY "Users can read own subscriptions" ON public.subscriptions | |
| FOR SELECT USING (auth.uid() = user_id); | |
| -- ๊ฒฐ์ ์ด๋ฒคํธ ๋ก๊ทธ โ webhook ์์ ยท์ฌ์๋ยทrefundยทchargeback ๋ชจ๋ ์ถ์ | |
| CREATE TABLE IF NOT EXISTS public.billing_events ( | |
| id BIGSERIAL PRIMARY KEY, | |
| subscription_id UUID REFERENCES public.subscriptions(id) ON DELETE SET NULL, | |
| user_id UUID REFERENCES auth.users(id) ON DELETE SET NULL, | |
| event_type VARCHAR(32) NOT NULL, | |
| -- 'subscription_created', 'subscription_renewed', 'subscription_canceled', | |
| -- 'payment_success', 'payment_failed', 'refund_issued', 'chargeback', | |
| -- 'webhook_received', 'webhook_processed', 'webhook_failed' | |
| processor VARCHAR(16) NOT NULL DEFAULT 'manual', | |
| processor_event_id VARCHAR(128), -- ์ธ๋ถ ๊ฒฐ์ ์ฌ ์ด๋ฒคํธ ID (idempotency) | |
| amount_usd_cents INTEGER, -- ๊ธ์ก (USD ์ผํธ, ๊ฒฐ์ ์ด๋ฒคํธ๋ง) | |
| payload JSONB NOT NULL DEFAULT '{}', | |
| error_message TEXT, | |
| occurred_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), | |
| created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() | |
| ); | |
| CREATE INDEX IF NOT EXISTS idx_billing_events_user | |
| ON public.billing_events(user_id, occurred_at DESC); | |
| CREATE INDEX IF NOT EXISTS idx_billing_events_subscription | |
| ON public.billing_events(subscription_id, occurred_at DESC); | |
| -- webhook ์ค๋ณต ์์ ์ฐจ๋จ์ฉ | |
| CREATE UNIQUE INDEX IF NOT EXISTS idx_billing_events_processor_event | |
| ON public.billing_events(processor, processor_event_id) | |
| WHERE processor_event_id IS NOT NULL; | |
| ALTER TABLE public.billing_events ENABLE ROW LEVEL SECURITY; | |
| CREATE POLICY "Users can read own billing events" ON public.billing_events | |
| FOR SELECT USING (auth.uid() = user_id); | |
| -- updated_at ์๋ ๊ฐฑ์ | |
| CREATE OR REPLACE FUNCTION public.tg_subscriptions_updated_at() | |
| RETURNS TRIGGER AS $$ | |
| BEGIN | |
| NEW.updated_at = NOW(); | |
| RETURN NEW; | |
| END; | |
| $$ LANGUAGE plpgsql; | |
| DROP TRIGGER IF EXISTS trg_subscriptions_updated_at ON public.subscriptions; | |
| CREATE TRIGGER trg_subscriptions_updated_at | |
| BEFORE UPDATE ON public.subscriptions | |
| FOR EACH ROW EXECUTE FUNCTION public.tg_subscriptions_updated_at(); | |