Files
sam-docs/features/settlement/subscriptions.md
김보곤 8faf5afa8b docs:정산관리 개발문서 추가 (4개 메뉴)
- 영업수수료정산: 입금등록, 수당자동계산, 승인프로세스, 지급추적
- 컨설팅비용정산: 상담시간×시급 정산, 상태관리
- 고객사정산: 월별 매출/수수료/비용 순정산액 관리
- 구독료정산: 플랜별 과금, MRR/ARR 집계, 구독 라이프사이클

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 16:47:22 +09:00

7.5 KiB
Raw Blame History

구독료정산

개요

구독료정산은 고객사별 구독 플랜과 과금을 관리하는 기능입니다. 플랜별 월정액 관리, 과금주기(월/연), MRR/ARR 집계, 사용자 수 추적을 지원합니다.

  • 라우트: GET /finance/subscription
  • UI 기술: React 18 + Babel (브라우저 트랜스파일링)

파일 구조

mng/
├── app/Http/Controllers/Finance/
│   └── SubscriptionController.php         # MNG 컨트롤러 (4개 메서드)
├── app/Models/Finance/
│   └── Subscription.php                   # MNG 구독 모델
└── resources/views/finance/
    └── subscription.blade.php             # React 기반 단일 페이지

api/
├── app/Http/Controllers/Api/V1/
│   └── SubscriptionController.php         # API 컨트롤러 (REST)
├── app/Services/
│   └── SubscriptionService.php            # 비즈니스 로직 서비스
├── app/Models/Tenants/
│   └── Subscription.php                   # API 구독 모델 (상세 구현)
└── database/migrations/
    ├── 2026_02_04_230008_create_subscriptions_table.php
    └── 2025_12_22_222722_add_cancel_columns_to_subscriptions_table.php

라우트

MNG 라우트

// routes/web.php (finance prefix 그룹 내)
GET    /subscription               Blade 페이지 렌더링 (HX-Redirect)

// API 라우트 (subscriptions prefix)
GET    /subscriptions/list         index()     목록 + 통계 (JSON)
POST   /subscriptions/store        store()     등록
PUT    /subscriptions/{id}         update()    수정
DELETE /subscriptions/{id}         destroy()   삭제

API 라우트

// routes/api.php (v1/subscriptions prefix)
GET    /                           index()     목록 (페이지네이션)
GET    /{id}                       show()      상세 조회
POST   /                           store()     생성
PUT    /{id}                       update()    수정
DELETE /{id}                       destroy()   삭제
POST   /{id}/cancel                cancel()    구독 취소
POST   /{id}/suspend               suspend()   일시정지
POST   /{id}/resume                resume()    재개

컨트롤러

SubscriptionController (MNG)

메서드 HTTP 설명
index() GET 목록 + MRR/ARR 통계 (검색, 상태 필터)
store() POST 구독 등록
update() PUT 구독 수정
destroy() DELETE 삭제 (Soft Delete)

index() 응답 구조

{
  "success": true,
  "data": [
    {
      "id": 1,
      "customer": "A사",
      "plan": "Business",
      "monthlyFee": 500000,
      "billingCycle": "monthly",
      "startDate": "2026-01-01",
      "nextBilling": "2026-03-01",
      "status": "active",
      "users": 10,
      "memo": ""
    }
  ],
  "stats": {
    "activeCount": 15,
    "monthlyRecurring": 7500000,
    "yearlyRecurring": 90000000,
    "totalUsers": 150
  }
}

핵심 로직

MRR/ARR 계산

MRR (Monthly Recurring Revenue)
  = SUM(active 구독의 monthly_fee) (billingCycle=monthly)
  + SUM(active 구독의 monthly_fee / 12) (billingCycle=yearly를 월 환산)

ARR (Annual Recurring Revenue)
  = MRR × 12

구독 상태 관리 (API 모델)

pending (대기)
    ↓ activate()
active (활성)
    ├── cancel(reason) → cancelled (취소)
    ├── suspend() → suspended (일시정지)
    │       ↓ resume() → active (재활성)
    └── (만료) → expired

구독 플랜

플랜 설명
Starter 소규모 기본 플랜
Business 중규모 비즈니스 플랜
Enterprise 대규모 기업 플랜

과금 주기

주기 설명
monthly 월간 과금
yearly 연간 과금

모델

Subscription (MNG)

테이블: subscriptions

필드 타입 설명
tenant_id bigint 테넌트 ID
customer string 고객사명
plan string 플랜명 (Starter/Business/Enterprise)
monthly_fee int 월 정액
billing_cycle string monthly / yearly
start_date date 시작일
next_billing date 다음 과금일
status string active / trial / cancelled
users int 사용자 수
memo text 메모
cancelled_at timestamp 취소 일시
cancel_reason string(500) 취소 사유
  • SoftDeletes 적용
  • Casts: monthly_fee, users → integer, start_date, next_billing → date
  • Scope: forTenant($tenantId)

Subscription (API - 상세 구현)

API 프로젝트의 모델은 더 상세한 구독 라이프사이클을 지원합니다:

상태 상수

const STATUS_ACTIVE = 'active';
const STATUS_CANCELLED = 'cancelled';
const STATUS_EXPIRED = 'expired';
const STATUS_PENDING = 'pending';
const STATUS_SUSPENDED = 'suspended';

Relationships

$subscription->tenant     // BelongsTo Tenant
$subscription->plan       // BelongsTo Plan
$subscription->payments   // HasMany Payment

주요 Accessor

$subscription->statusLabel      // 상태 한글 라벨
$subscription->isExpired        // 만료 여부
$subscription->remainingDays    // 잔여 일수
$subscription->isValid          // 유효 여부 (active + 미만료)
$subscription->totalPaid        // 총 결제 금액

주요 메서드

$subscription->activate()           // 활성화
$subscription->renew($newEndDate)   // 갱신
$subscription->cancel($reason)      // 취소
$subscription->suspend()            // 일시정지
$subscription->resume()             // 재개
$subscription->isCancellable()      // 취소 가능 여부

서비스 클래스 (API)

SubscriptionService

메서드 설명
index(params) 페이지네이션 목록 (필터, 정렬)
current() 현재 활성 구독 조회
show(id) 상세 (플랜, 최근 결제 포함)
store(data) 구독 생성 (결제 처리)
update(id, data) 구독 수정
cancel(id, reason) 구독 취소
suspend(id) 일시정지
resume(id) 재개
renew(id, newEndDate) 갱신

뷰 구성 (React)

subscription.blade.php

┌─ 페이지 헤더 ──────────────────────
│  제목: "구독료정산"
│  [CSV 내보내기] [등록] 버튼
│
├─ 통계 카드 (4열) ──────────────────
│  활성 구독 | MRR (월간 반복 수익) | ARR (연간 반복 수익) | 총 사용자
│
├─ 필터 영역 ────────────────────────
│  검색 (고객사명) | 플랜 (Starter/Business/Enterprise) | 상태 (active/trial/cancelled)
│
├─ 구독 목록 테이블 ─────────────────
│  고객사 | 플랜 | 월정액 | 과금주기 | 다음과금일 | 사용자 | 상태 | 작업
│  └─ 플랜: Starter(회색), Business(파랑), Enterprise(보라) 배지
│  └─ 상태: active(초록), trial(파랑), cancelled(빨강) 배지
│  └─ 작업: 수정/삭제 버튼
│
├─ 등록/수정 모달 ───────────────────
│  고객사명, 플랜 선택
│  월정액, 과금주기(월/연)
│  시작일, 다음 과금일
│  상태, 사용자 수, 메모
│  [삭제] [취소] [등록/저장]
│
└─ 비어있을 때: 안내 메시지

HTMX 호환성

  • React 기반 페이지이므로 HX-Redirect 필요
  • @push('scripts') 블록에 React/Babel 스크립트 포함