파트너 구독 쿠폰 발행 및 적용 통합

파트너 구독 쿠폰 발행 및 적용 통합

ADR-280 파트너 구독 쿠폰 발행 및 적용 통합

1. Metadata

  • ADR ID: ADR-280
  • Status: draft
  • Date: 2026-03-10
  • Owner: YSY
  • Related ADRs: ADR-220, ADR-250, ADR-270

2. Domain Decision

  1. 구독 쿠폰은 plan/add-on 카탈로그와 분리된 독립 엔터티로 관리한다.
  2. 쿠폰 혜택은 순차 세그먼트(order)로 구성하고 각 세그먼트는 plan_code, duration_months, addon_patch를 포함한다.
  3. 쿠폰은 적용 시점에 entitlement_schedule로 고정되며 원본 쿠폰 템플릿 변경의 영향을 받지 않는다.
  4. 쿠폰 적용 중 유효 권한은 coupon_entitlement가 기본 구독 권한보다 우선한다.
  5. 쿠폰 스케줄이 종료되면 권한은 기본 구독(plan + add-on)으로 자동 복귀한다.
  6. 파트너당 동시 활성 쿠폰은 1개로 제한하고 추가 쿠폰은 대기열(queued)로 보관한다.

3. Product Decision

  1. MVP 쿠폰 유형은 구독 기간 부여형(bundle)으로 시작한다.
  2. 예시 번들 쿠폰: basic 2개월 + plus 1개월.
  3. 쿠폰 적용 단위는 월 주기(billing_cycle=monthly)로 고정한다.
  4. 쿠폰 번호는 6자리 혜택식별자-8자리 배치일자-2자리 일련번호 형식의 고정 길이 대문자 하이픈 문자열(xxxxxx-xxxxxxxx-xx)로 통일하며 예시는 PLUS1M-20260310-01이다.
  5. QR로 배포하는 쿠폰도 별도 식별체계를 만들지 않고 동일한 coupon_code를 인코딩한다.
  6. 쿠폰은 발급 조건(대상, 만료일, 최대 사용 수)을 명시해야 한다.
  7. 쿠폰 적용 중 파트너 청구 금액은 쿠폰 권한 범위에서 0원으로 처리한다.
  8. 쿠폰 종료 후에는 직전 기본 구독 과금이 다음 결제일부터 자동 재개된다.

4. UX Decision

  1. 쿠폰 적용 화면은 적용 전 미리보기 타임라인(월별 플랜 변동)을 반드시 제공한다.
  2. 쿠폰 적용 화면은 수동 입력과 QR 스캔 입력을 모두 제공하며, QR 스캔 성공 시 정규화된 coupon_code를 동일 입력창에 채워 즉시 미리보기를 조회한다.
  3. QR 인식 실패 또는 지원하지 않는 형식이면 재시도와 수동 입력 전환을 즉시 제공한다.
  4. 적용 성공 화면은 적용 시작일, 세그먼트별 종료일, 기본 구독 복귀 예정일을 함께 표시한다.
  5. 이미 활성 쿠폰이 있으면 즉시 적용 대신 대기열 등록 여부를 선택하게 한다.
  6. 쿠폰 코드 오류는 사유별 메시지(만료, 대상 아님, 사용 완료, 지원하지 않는 QR 형식)로 분리한다.

5. Tech Decision

  1. 쿠폰 발행/적용 이벤트는 COUPON_ISSUED, COUPON_REDEEMED, COUPON_SEGMENT_STARTED, COUPON_ENDED로 분리 기록한다.
  2. 쿠폰 적용 API는 멱등키(idempotency_key)를 필수로 받고 중복 적용을 차단한다.
  3. 권한 판정 함수는 coupon_entitlement_active 여부와 effective_plan_code를 함께 반환한다.
  4. 쿠폰 세그먼트 전환은 배치/스케줄러가 아닌 결제 주기 경계 이벤트 기준으로 처리한다.
  5. 쿠폰과 기본 구독 간 상태 불일치가 감지되면 권한 판정은 보수적으로 차단(can_issue=false)한다.

6. Ops Decision

  1. 쿠폰 템플릿 생성/수정/종료는 운영 승인 이력과 함께 관리한다.
  2. 쿠폰 악용 방지를 위해 코드 시도 횟수 제한과 비정상 사용 탐지 알람을 운영한다.
  3. 쿠폰 대기열 적체, 세그먼트 전환 실패, 과금 재개 실패를 일일 점검 항목으로 관리한다.
  4. 쿠폰 정책 변경 시 ADR-280, ADR-270, BUSINESS 문서를 동시에 갱신한다.

7. Implementation Contract (Optional)

7.1 API Contract

  • POST /partner/subscription/coupons/redeemcoupon_code, idempotency_key를 필수로 받는다.
  • 성공 응답은 coupon_redemption_id, entitlement_timeline, resume_billing_at를 반환한다.
  • 활성 쿠폰 존재 시 apply_mode=queue|replace를 명시하지 않으면 COUPON_ACTIVE_ALREADY_EXISTS를 반환한다.

7.2 Data Contract

  • subscription_coupon_templates: coupon_code, segments(jsonb), expires_at, max_redemptions, eligibility_rule.
  • partner_coupon_redemptions: partner_id, coupon_code, status(active|queued|ended|canceled), applied_at.
  • partner_coupon_segments: redemption_id, segment_order, plan_code, addon_patch, starts_at, ends_at.
  • billing_resumption_state: partner_id, resume_plan_code, resume_addon_codes, resume_at.

7.3 Error/Observability Contract

  • 표준 코드: COUPON_INVALID, COUPON_EXPIRED, COUPON_REDEMPTION_LIMIT_REACHED, COUPON_NOT_ELIGIBLE, COUPON_ACTIVE_ALREADY_EXISTS, COUPON_QUEUE_LIMIT_EXCEEDED.
  • 운영 지표: 쿠폰 적용 성공률, 코드 오류율, 세그먼트 전환 성공률, 과금 재개 성공률.

7.4 Test/Acceptance Contract

  • basic 2개월 + plus 1개월 쿠폰 적용 시 3개 세그먼트 타임라인이 정확히 생성되어야 한다.
  • 쿠폰 적용 기간 동안 effective_plan_code가 세그먼트 순서대로 전환되어야 한다.
  • 쿠폰 종료 후 기본 구독(plan + add-on)과 과금이 자동 복귀해야 한다.
  • 동일 idempotency_key 재요청 시 동일 coupon_redemption_id가 반환되어야 한다.

8. Validation

  • Domain/Product/UX/Tech/Ops 결정이 충돌하지 않는다.
  • 구현 기준은 SPEC과 정합성을 유지한다.
  • 운영 절차는 RUNBOOK으로 연결된다.
  • ADR-220/270과 역할 경계가 중복되지 않는다.