Billing 스키마
개요
Billing 관련 테이블은 두 계층으로 구분됩니다:
| 계층 | 소유 | RLS | 설명 |
|---|---|---|---|
| 플랫폼 빌링 | Lumie ↔ 학원 | 없음 (앱 계층 접근 제어) | plans, subscriptions, invoices, billing_keys 등 |
| 학원 수납 | 학원 ↔ 학부모/학생 | 있음 (tenant_id + RLS) | guardians, tuition_invoices, tuition_payments, cash_receipts 등 |
결제 게이트웨이는 Toss Payments입니다. 직접 수납(direct-Toss tuition payment) 기능은 제거(ff9db43)되었으나 merchant_profiles 테이블은 유지됩니다.
플랫폼 빌링 ERD
플랫폼 빌링 테이블 상세
plans
사전 정의된 구독 플랜 목록입니다. custom_domains 컬럼은 V49에서 제거되었습니다 (모든 플랜에서 커스텀 도메인 지원).
| 플랜 ID | 월 가격 | 학생 수 | OMR/월 |
|---|---|---|---|
| FREE | 0원 | 제한 있음 | 제한 있음 |
| BASIC | 49,000원 | 제한 있음 | 제한 있음 |
| PRO | 149,000원 | 제한 있음 | 제한 있음 |
| MAX | 499,000원 | 제한 있음 | 제한 있음 |
실제 한도값은
plans테이블에서 확인하세요. V51에서 ENTERPRISE → MAX 리네임.
billing_keys
Toss 자동결제 토큰 (billingKey)을 저장합니다. 테넌트당 활성 키는 1개만 허용합니다 (partial unique index: status = 'ACTIVE'). pg_billing_key는 PG사 비밀 값이므로 절대 로깅하지 않습니다.
subscriptions
테넌트 구독 상태입니다. 테넌트당 1개(UNIQUE on tenant_id).
pending_plan_id+scheduled_change_at: 다운그레이드 예약 지원 (V37)status: ACTIVE / PAUSED / PAST_DUE / CANCELLED / EXPIRED
invoices
Lumie가 학원에 발행하는 구독 청구서입니다. invoice_number는 멱등성 키 역할을 합니다.
alimtalk_credits
카카오 알림톡 크레딧 잔액입니다. balance >= 0 CHECK 제약.
payment_transactions
PG 호출 감사 로그입니다. 불변 테이블로 UPDATE/DELETE 를 수행하지 않습니다.
| 컬럼 | 설명 |
|---|---|
type | BILLING_CHARGE / TUITION_PAYMENT / REFUND / BILLING_KEY_ISSUE / MERCHANT_REGISTER |
direction | REQUEST / RESPONSE / WEBHOOK |
owner_entity | INVOICE / TUITION_INVOICE / BILLING_KEY |
pg_payload | 원본 PG 요청/응답 JSONB |
billing_operation_locks
진행 중인 빌링 작업의 테넌트별 뮤텍스입니다 (V38). PK (tenant_id, operation_type)으로 동시 요청을 직렬화합니다. 완료 시 행 삭제, 15분 경과 시 스케줄러가 정리합니다.
tax_invoices
세금계산서 발행 이력입니다. buyer_business_no = 학원 사업자번호.
학원 수납 ERD (테넌트 스코프, RLS 적용)
학원 수납 테이블 상세
guardians / student_guardians
학생의 부모/법정대리인 정보입니다. student_guardians는 학생-보호자 M:N 매핑 테이블로, student_id는 크로스 모듈 경계상 DB FK 없이 소프트 참조합니다.
merchant_profiles
Toss 서브머천트 KYC 레코드입니다. 테넌트당 1개. pg_seller_key는 KYC 승인 후 Toss가 발급합니다. 직접 수납(Direct Toss Tuition) 기능은 제거되었으나 이 테이블은 유지됩니다.
tuition_invoices
학원이 학부모/학생에게 발행하는 수납 청구서입니다. items JSONB는 LineItem {name, quantity, unit_price, amount} 배열입니다.
tuition_payments
수납 청구서에 대한 단일 결제 이벤트입니다. order_id는 Toss orderId 겸 멱등성 키입니다.
cash_receipts
현금영수증 발행 이력입니다. PERSONAL(소득공제) / BUSINESS(지출증빙).
마이그레이션 이력
| 버전 | 내용 |
|---|---|
| V28 | 플랫폼 빌링 테이블 신설 (plans, billing_keys, subscriptions, invoices, alimtalk_credits, payment_transactions, tax_invoices) |
| V29 | 학원 수납 테이블 신설 (guardians, student_guardians, merchant_profiles, tuition_invoices, tuition_payments, cash_receipts) |
| V31 | billing 누락 컬럼 추가 |
| V32 | plans.version 추가 |
| V33 | billing_keys.customer_key 추가 |
| V34 | plans 시드 데이터 |
| V35 | FREE 구독 백필 |
| V36 | billing_keys 카드 메타 nullable |
| V37 | subscriptions 예약 변경 컬럼 추가 (pending_plan_id, scheduled_change_at) |
| V38 | billing_operation_locks 신설 |
| V49 | plans.custom_domains 제거 (모든 플랜 커스텀 도메인 지원) |
| V51 | ENTERPRISE → MAX 리네임 (plans 시드 데이터 업데이트) |