Files
sam-manage/docs/05_PHASE5_REVENUE.md
hskwon 76c8a94e4f docs: MNG 프로젝트 문서 정비
- 개발 단계별 문서 추가 (00_OVERVIEW ~ 06_PHASE)
- 기술 표준 문서 추가 (99_TECHNICAL_STANDARDS)
- 개발 프로세스 및 패턴 문서 추가
  - API_FLOW_TESTER_DESIGN, DEV_PROCESS
  - HTMX_API_PATTERN, LAYOUT_PATTERN
  - SETUP_GUIDE, MNG_PROJECT_PLAN
- 프로젝트 관리 문서 추가 (project-management/)
- INDEX.md, MNG_CRITICAL_RULES.md 업데이트
2025-11-30 21:04:19 +09:00

15 KiB
Raw Blame History

Phase 5: 수익 관리

기간: 2주 우선순위: 높음 (SaaS 비즈니스 수익 관리) 의존성: Phase 1 (테넌트관리)

📋 Phase 개요

SaaS 비즈니스 모델의 수익 관리 시스템을 구축합니다.

포함 기능:

  1. 구독관리 (Subscription Management)
  2. 결제관리 (Payment Management)
  3. 환불관리 (Refund Management)

1 구독관리 (Subscription Management)

기능 목록

1.1 구독 플랜 관리

  • 경로: /mng/subscriptions/plans
  • 기능:
    • 플랜 생성/수정/삭제 (Free, Basic, Pro, Enterprise)
    • 가격, 기능 제한 설정
    • 사용자 수, 저장 용량 한도
    • 월간/연간 결제 옵션
  • 권한: subscriptions.plans.manage

1.2 테넌트 구독 목록

  • 경로: /mng/subscriptions
  • 기능:
    • 전체 테넌트 구독 현황
    • 검색 (테넌트명, 플랜)
    • 필터 (플랜, 상태, 만료 임박)
    • 정렬 (만료일, 가입일)
  • 권한: subscriptions.index

1.3 구독 상세 조회

  • 경로: /mng/subscriptions/{id}
  • 기능:
    • 현재 플랜 정보
    • 구독 이력 (업그레이드/다운그레이드)
    • 사용량 통계 (사용자 수, 저장 용량)
    • 다음 결제 예정일
  • 권한: subscriptions.show

1.4 구독 플랜 변경

  • 경로: /mng/subscriptions/{id}/change-plan
  • 기능:
    • 플랜 업그레이드/다운그레이드
    • 즉시 적용 or 다음 결제일 적용
    • 차액 정산 (프로레이션)
  • 권한: subscriptions.change-plan

1.5 구독 해지

  • 경로: /mng/subscriptions/{id}/cancel
  • 기능:
    • 즉시 해지 or 기간 만료 후 해지
    • 해지 사유 기록
    • 데이터 백업 안내
  • 권한: subscriptions.cancel

DB 스키마

CREATE TABLE subscription_plans (
    id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    name VARCHAR(100) NOT NULL COMMENT '플랜명 (Free, Basic, Pro, Enterprise)',
    slug VARCHAR(50) UNIQUE NOT NULL,
    description TEXT NULL,
    price_monthly DECIMAL(10,2) NOT NULL COMMENT '월 가격',
    price_yearly DECIMAL(10,2) NOT NULL COMMENT '연 가격',
    max_users INT DEFAULT 10 COMMENT '최대 사용자 수',
    storage_limit BIGINT DEFAULT 1073741824 COMMENT '저장 용량 (바이트)',
    features JSON NULL COMMENT '플랜 기능 목록',
    is_active BOOLEAN DEFAULT TRUE,
    sort_order INT DEFAULT 0,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,

    INDEX idx_slug (slug),
    INDEX idx_is_active (is_active)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE subscriptions (
    id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    tenant_id BIGINT UNSIGNED UNIQUE NOT NULL,
    plan_id BIGINT UNSIGNED NOT NULL,
    status ENUM('active', 'past_due', 'cancelled', 'expired') DEFAULT 'active',
    billing_cycle ENUM('monthly', 'yearly') DEFAULT 'monthly',
    current_period_start DATE NOT NULL,
    current_period_end DATE NOT NULL,
    next_billing_date DATE NOT NULL,
    cancelled_at TIMESTAMP NULL,
    cancel_reason TEXT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,

    INDEX idx_tenant_id (tenant_id),
    INDEX idx_plan_id (plan_id),
    INDEX idx_status (status),
    INDEX idx_next_billing_date (next_billing_date),
    FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE,
    FOREIGN KEY (plan_id) REFERENCES subscription_plans(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE subscription_history (
    id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    subscription_id BIGINT UNSIGNED NOT NULL,
    old_plan_id BIGINT UNSIGNED NULL,
    new_plan_id BIGINT UNSIGNED NOT NULL,
    action ENUM('upgrade', 'downgrade', 'renew', 'cancel') NOT NULL,
    reason TEXT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,

    INDEX idx_subscription_id (subscription_id),
    FOREIGN KEY (subscription_id) REFERENCES subscriptions(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

API 엔드포인트

Method Endpoint Description FormRequest
GET /mng/subscriptions/plans 플랜 목록 -
POST /mng/subscriptions/plans 플랜 생성 StorePlanRequest
PUT /mng/subscriptions/plans/{id} 플랜 수정 UpdatePlanRequest
GET /mng/subscriptions 구독 목록 -
GET /mng/subscriptions/{id} 구독 상세 -
POST /mng/subscriptions/{id}/change-plan 플랜 변경 ChangePlanRequest
POST /mng/subscriptions/{id}/cancel 구독 해지 CancelSubscriptionRequest

Service 클래스

// app/Services/SubscriptionService.php
class SubscriptionService
{
    public function listPlans(): Collection;
    public function createPlan(array $data): SubscriptionPlan;
    public function updatePlan(SubscriptionPlan $plan, array $data): SubscriptionPlan;

    public function list(array $filters): LengthAwarePaginator;
    public function find(int $id): Subscription;
    public function create(int $tenantId, int $planId, string $billingCycle): Subscription;
    public function changePlan(Subscription $subscription, int $newPlanId, bool $immediate = false): Subscription;
    public function cancel(Subscription $subscription, string $reason, bool $immediate = false): bool;
    public function renew(Subscription $subscription): bool;

    public function getUsageStats(int $tenantId): array;
    public function checkLimits(int $tenantId): array; // 사용자 수, 저장 용량 체크
    public function calculateProration(Subscription $subscription, int $newPlanId): float;
}

개발 체크리스트

  • SubscriptionPlan, Subscription, SubscriptionHistory 모델 작성
  • SubscriptionService 클래스 작성
  • 프로레이션 계산 로직 구현
  • 사용량 체크 로직 (사용자 수, 저장 용량)
  • 구독 만료 자동 처리 (스케줄러)
  • FormRequest 작성
  • SubscriptionController 작성
  • Blade 템플릿 작성
  • i18n 키 작성
  • 테스트 작성

2 결제관리 (Payment Management)

기능 목록

2.1 결제 내역 조회

  • 경로: /mng/payments
  • 기능:
    • 전체 결제 내역 목록
    • 검색 (테넌트명, 결제 번호)
    • 필터 (상태, 날짜 범위, 플랜)
    • 정렬 (결제일, 금액)
    • 엑셀 내보내기
  • 권한: payments.index

2.2 결제 상세 조회

  • 경로: /mng/payments/{id}
  • 기능:
    • 결제 정보 (금액, 방법, 일시)
    • 구독 정보
    • 영수증 출력
    • PG사 거래 번호
  • 권한: payments.show

2.3 결제 처리

  • 경로: /mng/payments/process
  • 기능:
    • PG 연동 (토스페이먼츠, 이니시스 등)
    • 정기 결제 등록
    • 일회성 결제
    • 결제 실패 처리
  • 권한: payments.process

2.4 결제 실패 관리

  • 경로: /mng/payments/failed
  • 기능:
    • 실패 내역 조회
    • 재시도
    • 알림 발송
  • 권한: payments.failed.manage

DB 스키마

CREATE TABLE payments (
    id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    tenant_id BIGINT UNSIGNED NOT NULL,
    subscription_id BIGINT UNSIGNED NOT NULL,
    payment_number VARCHAR(50) UNIQUE NOT NULL COMMENT '결제 번호 (자동 생성)',
    amount DECIMAL(10,2) NOT NULL COMMENT '결제 금액',
    payment_method ENUM('card', 'bank_transfer', 'virtual_account', 'paypal') DEFAULT 'card',
    status ENUM('pending', 'completed', 'failed', 'refunded') DEFAULT 'pending',
    pg_provider VARCHAR(50) NULL COMMENT 'PG사 (toss, inicis 등)',
    pg_transaction_id VARCHAR(255) NULL COMMENT 'PG사 거래 번호',
    paid_at TIMESTAMP NULL COMMENT '결제 완료 일시',
    failure_reason TEXT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,

    INDEX idx_tenant_id (tenant_id),
    INDEX idx_subscription_id (subscription_id),
    INDEX idx_payment_number (payment_number),
    INDEX idx_status (status),
    INDEX idx_paid_at (paid_at),
    FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE,
    FOREIGN KEY (subscription_id) REFERENCES subscriptions(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE payment_cards (
    id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    tenant_id BIGINT UNSIGNED NOT NULL,
    card_number_masked VARCHAR(20) NOT NULL COMMENT '마스킹된 카드번호 (1234-****-****-5678)',
    card_type VARCHAR(50) NULL COMMENT '카드사',
    expiry_date VARCHAR(7) NOT NULL COMMENT 'MM/YYYY',
    is_default BOOLEAN DEFAULT FALSE,
    billing_key VARCHAR(255) NULL COMMENT 'PG사 빌링키 (정기결제)',
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    deleted_at TIMESTAMP NULL,

    INDEX idx_tenant_id (tenant_id),
    FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

API 엔드포인트

Method Endpoint Description FormRequest
GET /mng/payments 결제 내역 목록 -
GET /mng/payments/{id} 결제 상세 -
POST /mng/payments/process 결제 처리 ProcessPaymentRequest
POST /mng/payments/{id}/retry 결제 재시도 -
GET /mng/payments/{id}/receipt 영수증 출력 -
GET /mng/payments/failed 실패 내역 -

Service 클래스

// app/Services/PaymentService.php
class PaymentService
{
    public function list(array $filters): LengthAwarePaginator;
    public function find(int $id): Payment;
    public function process(int $tenantId, int $subscriptionId, float $amount, string $method): Payment;
    public function complete(Payment $payment, string $pgTransactionId): bool;
    public function fail(Payment $payment, string $reason): bool;
    public function retry(Payment $payment): Payment;
    public function generateReceipt(Payment $payment): string; // PDF 경로

    public function registerCard(int $tenantId, array $cardData): PaymentCard;
    public function getCards(int $tenantId): Collection;
    public function processRecurring(Subscription $subscription): Payment;
}

개발 체크리스트

  • Payment, PaymentCard 모델 작성
  • PaymentService 클래스 작성
  • PG 연동 구현 (토스페이먼츠 우선)
  • 정기 결제 스케줄러
  • 결제 실패 알림 (이메일, SMS)
  • 영수증 PDF 생성
  • 결제 번호 자동 생성
  • FormRequest 작성
  • 테스트 작성 (Mock PG)

3 환불관리 (Refund Management)

기능 목록

3.1 환불 요청 목록

  • 경로: /mng/refunds
  • 기능:
    • 전체 환불 요청 목록
    • 검색 (테넌트명, 결제 번호)
    • 필터 (상태, 날짜)
    • 정렬 (요청일, 금액)
  • 권한: refunds.index

3.2 환불 요청 상세

  • 경로: /mng/refunds/{id}
  • 기능:
    • 환불 요청 정보 (사유, 금액)
    • 원 결제 정보
    • 환불 처리 이력
  • 권한: refunds.show

3.3 환불 승인/반려

  • 경로: /mng/refunds/{id}/approve 또는 /reject
  • 기능:
    • 전액 환불 or 부분 환불
    • 승인/반려 사유 기록
    • PG 환불 API 호출
    • 알림 발송
  • 권한: refunds.approve

3.4 환불 처리

  • 경로: /mng/refunds/{id}/process
  • 기능:
    • PG사 환불 API 연동
    • 환불 완료 처리
    • 구독 상태 업데이트
  • 권한: refunds.process

DB 스키마

CREATE TABLE refunds (
    id BIGINT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
    payment_id BIGINT UNSIGNED NOT NULL,
    tenant_id BIGINT UNSIGNED NOT NULL,
    refund_number VARCHAR(50) UNIQUE NOT NULL COMMENT '환불 번호',
    refund_amount DECIMAL(10,2) NOT NULL COMMENT '환불 금액',
    refund_type ENUM('full', 'partial') DEFAULT 'full',
    reason TEXT NOT NULL COMMENT '환불 사유',
    status ENUM('pending', 'approved', 'rejected', 'completed', 'failed') DEFAULT 'pending',
    approved_by BIGINT UNSIGNED NULL COMMENT '승인자',
    approved_at TIMESTAMP NULL,
    rejection_reason TEXT NULL,
    processed_at TIMESTAMP NULL COMMENT '환불 완료 일시',
    pg_refund_transaction_id VARCHAR(255) NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,

    INDEX idx_payment_id (payment_id),
    INDEX idx_tenant_id (tenant_id),
    INDEX idx_status (status),
    FOREIGN KEY (payment_id) REFERENCES payments(id) ON DELETE CASCADE,
    FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE,
    FOREIGN KEY (approved_by) REFERENCES users(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

API 엔드포인트

Method Endpoint Description FormRequest
GET /mng/refunds 환불 요청 목록 -
GET /mng/refunds/{id} 환불 요청 상세 -
POST /mng/refunds 환불 요청 생성 StoreRefundRequest
POST /mng/refunds/{id}/approve 환불 승인 ApproveRefundRequest
POST /mng/refunds/{id}/reject 환불 반려 RejectRefundRequest
POST /mng/refunds/{id}/process 환불 처리 -

Service 클래스

// app/Services/RefundService.php
class RefundService
{
    public function list(array $filters): LengthAwarePaginator;
    public function find(int $id): Refund;
    public function create(int $paymentId, float $amount, string $reason, string $type = 'full'): Refund;
    public function approve(Refund $refund, int $approverId): bool;
    public function reject(Refund $refund, string $reason): bool;
    public function process(Refund $refund): bool; // PG 환불 API 호출
    public function complete(Refund $refund, string $pgRefundTransactionId): bool;
    public function fail(Refund $refund, string $reason): bool;
}

개발 체크리스트

  • Refund 모델 작성
  • RefundService 클래스 작성
  • PG 환불 API 연동
  • 부분 환불 로직 구현
  • 환불 후 구독 상태 업데이트
  • FormRequest 작성
  • RefundController 작성
  • 환불 알림 발송
  • i18n 키 작성
  • 테스트 작성

🎯 Phase 5 완료 조건

기능 완성도

  • 구독 플랜 관리 완성
  • 결제 처리 (PG 연동) 동작
  • 환불 워크플로우 동작
  • 정기 결제 스케줄러 동작

코드 품질

  • Service-First, FormRequest 준수
  • PG 연동 에러 처리
  • i18n 키 사용
  • Pint, PHPStan 통과

비즈니스 로직

  • 프로레이션 계산 정확
  • 사용량 제한 체크 동작
  • 구독 자동 갱신/만료 처리
  • 결제 실패 재시도 로직

보안

  • PG 통신 암호화
  • 카드 정보 마스킹
  • 빌링키 안전 저장
  • 결제 내역 접근 권한

테스트

  • 구독 변경 시나리오 테스트
  • 결제 처리 Mock 테스트
  • 환불 워크플로우 테스트

최종 업데이트: 2025-11-21 작성자: Claude Code 버전: 1.0.0