Skip to content

도메인: Subscription (PRO / MAX)

개요

자동갱신 구독 2티어PRO(₩1,000)·MAX(₩2,000). 결제 시 주간 AI 분석 한도관심종목 슬롯 상한이 티어별로 올라간다. 권한 판정은 서버가 한다(영수증/거래 검증). iOS · Android 동시 제공.

항목PROMAX
상품명(앱 내 표시)PROMAX
가격₩1,000 / 월₩2,000 / 월
product IDdartbrief.pro.monthlydartbrief.max.monthly
  • 과금: 둘 다 자동갱신 구독(auto-renewable). 무료 체험 없음(v1, 추후 intro offer 토글). 플랫폼 iOS(App Store)+Android(Play). product ID 는 iOS/Android 공통 네이밍.

티어별 한도

무료PRO (₩1,000)MAX (₩2,000)
주간 AI 분석(reveal)10회/주 (+광고 보상 +1)60회/주200회/주
관심종목 슬롯3개30개100개
광고한도 소진 시 +1(paid — 한도 도달 시 상위 티어/리셋 안내)동일

한도는 domain-quota 의 주간 데이터(종목당 ~1.3건/주)로 책정 — 평상시 거의 안 걸리고 결산 피크/헤비유저에서 도달해 상위 티어 유도 + 어뷰즈 천장. reveal 원가≈0(캐시 조회)이라 한도는 비용 통제가 아니라 펀넬·어뷰즈 목적(policies/monetization).

엔타이틀먼트 모델

권한의 단일 진실 = 서버. 클라이언트 판정은 신뢰하지 않는다(reveal result 는 백엔드가 게이트).

dartbrief.users 컬럼(또는 동등 저장):

컬럼설명
premium_tiernone / pro / max티어 결정 컬럼. 한도·상한이 이 값으로 갈림
premium_untilTIMESTAMPTZ NULL — 구독 만료 시각. 활성 = premium_until > now()
premium_platformios / android
premium_product_iddartbrief.pro.monthly / dartbrief.max.monthly
premium_tx_refApp Store originalTransactionId / Play purchaseToken — 재검증 키
  • activeTier(userID) = premium_until > now() 이면 premium_tier, 아니면 none. 모든 게이트가 이 헬퍼로 한도를 정한다.
  • Grace period(결제 재시도 유예)도 활성으로 취급 — 스토어가 주는 만료시각에 grace 가 포함됨(premium_until 갱신).
  • 구독은 로그인 필요(엔타이틀먼트가 userID 에 귀속). 비로그인 구매 플로우 없음.

권한 적용 (티어별 한도)

게이트는 우회가 아니라 티어에 따라 한도 값만 바뀐다 (무료도 paid 도 같은 체크, 상수만 다름).

게이트무료PROMAX
AI 분석 reveal — 주간 quota (domain-quota)WeeklyLimit=1060200
관심종목 add — capacity (domain-watchlist)330100

weeklyLimit(tier) / slotCapacity(tier) 단일 매핑으로 결정. used_count 는 티어 무관 계속 기록.

검증 — 웹훅 + verify-on-launch (둘 다, v1)

스토어 서버 API 가 구독 활성의 진실. 백엔드는 premium_until 로 캐시하고, (1) 웹훅으로 즉시 반영 + (2) 구매/앱실행 시 verify 로 보정 한다.

앱(in_app_purchase) ──구매/복원──▶ 스토어 결제
   └ 거래 토큰 → POST /api/v1/subscription/verify
백엔드 ──검증──▶ App Store Server API / Play Developer API (Get Subscription Status)
   → 활성/만료시각 파싱 → premium_until 갱신 → 상태 반환
앱 실행 시 1회 verify refresh → 만료/환불 보정

[웹훅] 스토어 라이프사이클(갱신/만료/환불) → 백엔드 즉시 갱신
   - Apple: ASSN V2 가 POST /internal/appstore-notifications (JWS 서명 검증)
   - Google: Play RTDN → GCP Pub/Sub 토픽 → push 구독 → POST /internal/play-rtdn
  • 웹훅(즉시 반영, v1 포함): 환불/갱신/만료를 스토어가 push → premium_until 즉시 갱신. Apple 은 URL 직접 POST, Google 은 Pub/Sub push 구독(기존 public 백엔드 엔드포인트로 라우팅 — 어렵지 않음).
  • verify-on-launch(보정): 웹훅 누락/지연 대비. 앱 실행 시 재검증으로 상태 동기화.
  • 검증/키: App Store Server API(.p8) + ASSN V2 서명, Play Developer API(서비스 계정) + RTDN. GCP 토픽/권한·Pub/Sub push 구독 설정은 사용자 수동. 시크릿은 ~/.config/cloud-tokens/.
  • 거래 소유권 바인딩(어뷰즈 방지): 스토어 검증은 "거래가 유효함"만 보장하지 "이 사용자 것"은 보장하지 않는다. 타 계정의 유효한 거래 토큰 재사용으로 권한 탈취를 막기 위해, verify 는 거래를 요청 사용자에 귀속시켜야 한다 — appAccountToken(Apple)/obfuscatedExternalAccountId(Google) 대조 또는 premium_tx_ref 최초 소유자 고정(다른 userID 재사용 거부). 실제 verifier 주입(Phase 2) 시 enforce — 골격 단계의 StoreVerifier.Verify 는 이미 userID 를 인자로 받는다.

다운그레이드 (티어 하락 — 만료/환불/MAX→PRO)

티어가 내려가면(만료→none, 환불, MAX→PRO 갱신) 관심종목 상한이 새 티어 값(none=3 / pro=30)으로 내려간다. 현재 등록이 새 상한 초과면:

  1. 즉시 — 초과분은 열람·알림 유지, 단 등록 수가 새 상한 미만 될 때까지 새 종목 add 차단. 하락 후 첫 접속 시 alert: "구독이 변경되어 관심종목을 N개로 정리해야 해요" (사용자가 직접 정리 유도).
  2. 하락 7일 후에도 초과면 — 서버가 자동 정리: 가장 최근 등록 N개만 남기고 오래된 것부터 제거 (V6 "가장 오래된 종목 자동 제거" 규칙과 일치). 정리 잡이 (만료/하락 시각)+7d 경과 + 초과 조건으로 실행.

reveal 주간 한도는 다음 주 reset 부터 새 티어 한도 적용(소급 차감 없음). domain-watchlist 의 capacity 체크 + 자동정리가 이 규칙을 적용. 자동 삭제 전(7일 내)엔 데이터 보존.

구매/복원/환불 플로우 요약

플로우동작
구매페이월에서 PRO/MAX 선택 → 스토어 결제 → verify → premium_tier+premium_until set
업/다운그레이드페이월에서 다른 티어 선택 → 스토어가 proration 처리 → verify 가 premium_tier 갱신
복원(Restore)더보기 > 구독의 "구매 복원"(Apple 필수) → verify 재호출 → 활성이면 티어/만료 복원
갱신다음 주기 자동결제 → verify refresh 에서 만료시각 연장
만료/취소verify refresh 에서 expired → premium_until 과거 → none 으로 강등(다운그레이드 규칙)
환불verify refresh 에서 refunded → 엔타이틀먼트 회수

심사 요구 (구독)

  • 페이월에 가격·기간·"자동갱신" 명시 + 이용약관/개인정보 링크.
  • "구매 복원" 메커니즘 필수 — 더보기 > 구독에 배치(페이월 아님; Apple 은 존재만 요구).
  • 첫 출시 시 구독 상품을 앱 버전과 함께 심사 제출.

관련 문서