본문으로 건너뛰기

빌링 스키마

목적

Lumie에는 소유권 규칙이 다른 두 가지 billing 데이터 계열이 있습니다.

  • platform billing은 Lumie가 academy에 SaaS 구독료를 청구하는 데이터를 기록합니다
  • tuition billing은 academy가 guardian 또는 학생에게 청구하는 데이터를 기록합니다

이 페이지는 해당 테이블, JSON 컬럼, 결제 플로우를 둘러싼 PG audit trail에 대한 reference 문서입니다.

소스 경로

경로역할
lumie-backend/app/src/main/resources/db/migration/public/V28__billing_platform_tables.sql플랫폼 billing 테이블과 초기 PG audit 테이블
lumie-backend/app/src/main/resources/db/migration/public/V29__tuition_tenant_tables.sqltenant 범위 tuition 테이블과 JSON 컬럼 주석
lumie-backend/app/src/main/resources/db/migration/public/V31__billing_add_missing_columns.sqlbilling 컬럼 추가 및 payment_transactions.type 주석 갱신
lumie-backend/app/src/main/resources/db/migration/public/V33__billing_keys_add_customer_key.sqlbilling key에 Toss customer key 추가
lumie-backend/app/src/main/resources/db/migration/public/V34__seed_plans.sql요금제 seed 데이터
lumie-backend/app/src/main/resources/db/migration/public/V35__backfill_free_subscriptions.sql기본 tenant subscription backfill
lumie-backend/app/src/main/resources/db/migration/public/V37__subscription_scheduled_changes.sql예약된 요금제 변경 컬럼
lumie-backend/app/src/main/resources/db/migration/public/V38__create_billing_operation_locks.sqlbilling 작업 lock
lumie-backend/modules/billing/src/main/java/com/lumie/billing/domain/entity/PaymentTransaction.javaPG audit 행의 JPA 매핑
lumie-backend/modules/billing/src/main/java/com/lumie/billing/application/service/PaymentTransactionLogger.javaREQUIRES_NEW 트랜잭션에서의 요청/응답 audit 영속화
lumie-backend/modules/billing/src/main/java/com/lumie/billing/application/service/PaymentService.java일회성 결제 승인 audit payload
lumie-backend/modules/billing/src/main/java/com/lumie/billing/application/service/SubscriptionChargeExecutor.java반복 구독 결제 audit payload
lumie-backend/modules/tuition/src/main/java/com/lumie/tuition/domain/entity/TuitionInvoice.javatuition_invoices의 JPA 매핑
lumie-backend/modules/tuition/src/main/java/com/lumie/tuition/domain/vo/LineItem.javatuition_invoices.items 내부에 저장되는 line item 형태
lumie-backend/modules/tuition/src/main/java/com/lumie/tuition/application/service/TuitionInvoiceCommandService.java총액/VAT 계산과 청구서 발행 성공 경로

플랫폼 빌링

테이블범위목적
plansPlatform요금제 카탈로그와 quota/capability 메타데이터
billing_keysPlatformtenant별 Toss 자동 결제 키 메타데이터
subscriptionsPlatformtenant 구독 상태 및 예약된 플랜 변경
invoicesPlatformLumie에서 tenant로 발행하는 청구서
alimtalk_creditsPlatformtenant 메시지 크레딧 잔액
payment_transactionsPlatformappend-only PG 요청/응답/webhook audit 로그
tax_invoicesPlatform구독 청구서에 연결된 세금계산서 레코드
billing_operation_locksPlatformbilling 워크플로 동시성 가드

platform billing 테이블은 tenants(id)를 참조하지만 RLS 보호는 받지 않습니다. 어떤 사용자가 billing 상태를 조회하거나 변경할 수 있는지는 애플리케이션 권한 부여가 결정합니다.

수업료 빌링

테이블범위목적
guardiansTenant부모 또는 법적 보호자 연락처 데이터
student_guardiansTenant학생-보호자 연계
merchant_profilesTenantToss sub-merchant KYC 프로필
tuition_invoicesTenantacademy에서 guardian/학생에게 발행하는 청구서
tuition_paymentsTenant결제 시도 및 capture/refund 상태
cash_receiptsTenant현금영수증 발행 데이터

tuition 테이블에는 tenant_id, RLS policy, tenant 범위 인덱스가 포함됩니다. user나 class 개념에 대한 일부 참조는 tuition 모듈이 협력하는 모든 모듈과 hard DB 결합을 만들지 않도록 의도적으로 soft reference로 유지됩니다.

JSON 컬럼 예시

tuition_invoices.itemsLineItem 레코드의 JSON 배열을 저장합니다. 최소 저장 형태는 LineItem 레코드와 TuitionInvoice가 사용하는 중첩 Money value object로부터 추론할 수 있습니다.

[
{
"name": "June Math tuition",
"quantity": 1,
"unitPrice": {
"amount": 300000
},
"amount": {
"amount": 300000
}
},
{
"name": "Workbook fee",
"quantity": 2,
"unitPrice": {
"amount": 15000
},
"amount": {
"amount": 30000
}
}
]

그에 대응하는 tuition_invoices 행은 다음을 가져야 합니다.

  • total_amount = 330000
  • status = 'ISSUED' 또는 테이블 check에 정의된 이후 lifecycle 상태
  • 호출자가 중복 발행 방지를 원할 때의 idempotency_key

payment_transactions.pg_payload는 billing 서비스가 PaymentTransactionLogger에 전달하는 원시 요청 또는 응답 맵을 저장합니다. 현재 코드 경로의 최소 예시는 다음과 같습니다.

{
"orderId": "SUB-20260614-001",
"amount": 99000,
"planId": "starter"
}
{
"success": true,
"paymentKey": "pay_1234567890",
"error": ""
}

결제 감사 모델

payment_transactions는 append-only입니다. 이 테이블은 owner 메타데이터와 함께 PG 요청, 응답, webhook을 기록합니다. PaymentTransactionLogger는 이 행들을 별도의 REQUIRES_NEW 트랜잭션에 저장하므로 바깥 비즈니스 트랜잭션이 rollback되더라도 audit trail이 남습니다.

컬럼 그룹의미
transaction_id, idempotency_key재시도 및 외부 PG 호출 상관관계
owner_entity, owner_id청구서, tuition invoice, billing key owner와 연결
type, direction요청/응답/webhook 방향 분류
pg_payload, http_statusaudit용으로 원시 PG payload와 상태 보존

일반 애플리케이션 워크플로에서는 audit 행을 수정하거나 삭제하지 마세요.

성공 신호

워크플로성공 신호
Tuition invoice 발행TuitionInvoiceCommandService가 행을 저장하고 id, student id, 금액과 함께 발행 로그를 남김
일회성 invoice 결제 승인PaymentService가 요청 audit 행과 응답 audit 행을 기록한 뒤 PG 결과가 성공이면 invoice를 paid로 표시
반복 구독 결제SubscriptionChargeExecutor가 요청/응답 audit 행을 기록하고 결제 성공 시 paid invoice 생성

계약 드리프트

payment_transactions.type에는 현재 주의해야 할 불일치가 하나 있습니다.

  • V31__billing_add_missing_columns.sql은 컬럼 주석에 TAX_INVOICE_ISSUE를 문서화합니다.
  • 체크인된 TransactionType enum은 현재 SUBSCRIPTION_CHARGE, SUBSCRIPTION_REFUND, ALIMTALK_RECHARGE, BILLING_KEY_ISSUE, BILLING_KEY_REVOKE를 정의하지만 TAX_INVOICE_ISSUE는 정의하지 않습니다.

billing 모듈 또는 migration 주석이 갱신되기 전까지는 이를 schema-to-code 드리프트로 취급하세요.

검증

cd /path/to/Lumie/lumie-backend
rg -n "create table if not exists (plans|billing_keys|subscriptions|tuition_invoices|tuition_payments|cash_receipts|payment_transactions)" \
app/src/main/resources/db/migration/public

예상 성공 신호: 핵심 플랫폼 billing 및 tuition billing 테이블이 모두 migration 집합 안에 선언되어 있습니다.

cd /path/to/Lumie/lumie-backend
rg -n "LineItem|PaymentTransactionLogger|pg_payload|idempotencyKey|issueInvoice|chargeWithBillingKey|confirmPayment" \
modules/billing modules/tuition

예상 성공 신호: billing 및 tuition 모듈의 검색 결과가 이 페이지에서 설명한 JSON 컬럼 형태, audit logger, 결제 성공 경로를 보여줍니다.

cd /path/to/Lumie/lumie-backend
rg -n "TAX_INVOICE_ISSUE|SUBSCRIPTION_CHARGE|BILLING_KEY_REVOKE" \
app/src/main/resources/db/migration/public/V31__billing_add_missing_columns.sql \
modules/billing/src/main/java/com/lumie/billing/domain/vo/TransactionType.java

예상 성공 신호: 검색 결과가 현재의 주석-대-enum 드리프트를 숨기지 않고 명시적으로 드러냅니다.