구독 계약 생성부터 회차 자동 결제·조정·해지까지 전체 시퀀스를 이해해요.
개별 API는 구독 계약 / 가입, 구독 > 운영 / 회차 에서 확인해요. 이 문서는 라이프사이클을 한 장으로 봐요.
구독 구현 전에 블로그 가이드를 봐요.
- 자동결제는 언제 쓰나요? — 자동결제와 일반 결제의 차이
- 구독 상품 유형별 차이 — 콘텐츠·SaaS·정기배송별 운영 정책
- 구독 주기 설계 — 월/연/커스텀 주기 결정
- 결제 실패 재시도 정책 — 재시도 횟수·간격
- 구독 해지 정책 — 잔여 기간·환불·수수료
구독 사이드바는 구독 메뉴 안의 계약 정의·가입·운영 그룹로 나뉘어 있어요. 이 구독 메뉴는 이 문서를 라이프사이클 단일 소스로 참조해요.
전체 시퀀스
참여자: 고객 | 가맹점 프론트 | 가맹점 서버 | Bootpay | PG사
| 단계 | 흐름 | 유형 | 비고 |
|---|---|---|---|
| 1 | 고객 -> 가맹점 프론트: 구독 상품 선택 | 실선 | |
| 2 | 가맹점 프론트 -> Bootpay: 빌링키 발급 SDK 호출 | 실선 | 카드 수집은 SDK ↔ PG 직접 |
| 3 | Bootpay -> PG사: 빌링키 발급 요청 | 실선 | |
| 4 | PG사 -> Bootpay: 빌링키 반환 | 점선 | |
| 5 | Bootpay -> 가맹점 프론트: billing_key 반환 |
점선 | |
| 6 | 가맹점 프론트 -> 가맹점 서버: billing_key + 구독 상품 정보 전달 |
실선 | |
| 7 | 가맹점 서버 -> Bootpay: 구독 계약 생성 (subscribe) |
실선 | user × plan × billing_key |
| 8 | 가맹점 서버 -> Bootpay: 계약 승인 (approve) |
실선 | 첫 회차 예약 |
| 9 | Bootpay: 회차 스케줄러에 등록 | 실선 | status = 구독 중(1) |
| 10 | Bootpay -> PG사: 매 회차 빌링키 결제 | 실선 | 자동 |
| 11 | Bootpay -> 가맹점 서버: 웹훅 (subscription.billed / subscription.failed) |
실선 | |
| 12 | 가맹점 서버: 서비스 접근 제어 | 실선 | active/inactive 토글 |
구독 라이프사이클 상태 전이
구독 하나는 대기 → 구독 중 → (일시정지) → 해지/만료 흐름을 따라요. 상태 코드는 API 응답·웹훅에서 동일하게 쓰여요.
상태
| ID | 라벨 | 위치 | 타입 |
|---|---|---|---|
| pending | 대기 (0) | col 0, row 0 | initial |
| active | 구독 중 (1) | col 1, row 0 | active |
| paused | 일시정지 (2) | col 2, row 0 | warning |
| terminated | 해지 (3) | col 2, row 1 | end |
| expired | 만료 (4) | col 3, row 0 | end |
| blocked | 거절 (-11) | col 0, row 1 | end |
전이 pending -> active : 승인 pending -> blocked : 거절 active -> paused : 일시정지 paused -> active : 재개 active -> terminated : 해지 요청 paused -> terminated : 해지 요청 active -> expired : 기간 종료
| 현재 | 다음 | 주체 | API |
|---|---|---|---|
| 대기 (0) | 구독 중 (1) | 관리자 | 신규 승인 |
| 대기 (0) | 거절 (-11) | 관리자 | 구독 거절 |
| 구독 중 (1) | 일시정지 (2) | 고객·관리자 | 일시정지 |
| 일시정지 (2) | 구독 중 (1) | 고객 | 재개 |
| 구독 중 (1) | 해지 (3) | 고객·관리자 | 해지 |
| 일시정지 (2) | 해지 (3) | 고객 | 해지 |
| 구독 중 (1) | 만료 (4) | 자동 | 기간 종료 |
회차(빌링 사이클) 상태
구독이 승인되면 주기에 맞춰 회차가 생성돼요. 회차는 구독과 독립 상태를 가져요.
| 회차 상태 | 코드 | 의미 | 다음 액션 |
|---|---|---|---|
| 예정 | 0 | 결제 예정 (스케줄러 등록됨) | 대기 |
| 성공 | 1 | 결제 완료 | 웹훅 subscription.billed |
| 실패 | 2 | 결제 실패 | 재시도 스케줄 |
| 취소 | 3 | 회차 취소 (환불·관리자 취소) | 웹훅 subscription.refunded |
회차 조회·수정 API는 회차 조회 · 회차 금액 조정 참고.
자동 과금 재시도 흐름
결제 실패 시 Bootpay는 정책에 따라 재시도해요. 가맹점은 웹훅을 받아 서비스 접근만 제어하면 돼요.
흐름 스케줄 -> 결제 시도 결제 시도 -> 성공 결제 시도 -> 실패 실패 -> 재시도 대기 재시도 대기 -> 결제 시도 실패 -> 최종 실패
| 단계 | 주체 | 설명 |
|---|---|---|
| 결제 시도 | Bootpay | 회차 도래 시 빌링키로 자동 결제 |
| 성공 | Bootpay | 웹훅 subscription.billed 발송, 다음 회차 예약 |
| 실패 | Bootpay | 웹훅 subscription.failed 발송, 재시도 대기로 전환 |
| 재시도 대기 | Bootpay | 재시도 주기(기본 24h) 경과 후 다시 결제 시도 |
| 최종 실패 | Bootpay | 재시도 횟수 초과 시 구독을 일시정지(2)로 전환, subscription.paused 웹훅 |
재시도 횟수·간격·최종 실패 후 동작(일시정지 vs 해지)은 콘솔에서 설정해요. 블로그의 결제 실패 재시도 정책 을 먼저 정리한 뒤 적용해요.
웹훅 이벤트 매핑
가맹점 서버는 아래 이벤트를 받아 서비스 상태와 DB를 동기화해요.
| 이벤트 | 발생 시점 | 가맹점이 할 일 |
|---|---|---|
subscription.approved |
계약 승인 완료 | service_active = true |
subscription.billed |
회차 결제 성공 | 다음 회차까지 서비스 유지 |
subscription.failed |
회차 결제 실패 | 고객에게 결제수단 변경 안내 |
subscription.paused |
일시정지 (고객·관리자·재시도 초과) | 서비스 접근 일시 차단 |
subscription.resumed |
일시정지 해제 | service_active = true |
subscription.terminated |
해지 완료 | service_active = false, 잔여 기간 정책 적용 |
subscription.refunded |
회차 환불 | 정산·회계 반영 |
웹훅 엔드포인트 구현은 웹훅 처리 가이드 참고.
유예 기간(Grace Period) 설계
결제 실패 ~ 최종 실패 사이 구간을 유예 기간으로 두면 이탈률이 줄어들어요.
| 구간 | 서비스 상태 | 고객 커뮤니케이션 |
|---|---|---|
| 회차 실패 직후 | 유지 (grace) | 카드 업데이트 링크 이메일 |
| 재시도 1회 실패 후 | 유지 (grace) | 2차 알림 + 앱 내 배너 |
| 재시도 N회 실패 후 | 일시정지 | 최종 안내, 수동 재결제 안내 |
유예 기간 길이는 서비스 특성에 따라 달라요 (SaaS 3~7일, 콘텐츠 1~3일이 일반적).
고객 변경 요청 흐름
고객이 해지·인수·이전 같은 변경을 요청하면 관리자 승인 루프를 거쳐요.
흐름 고객 요청 -> 대기 (0) 대기 (0) -> 확인 대기 (1) : 승계/인수만 상대방 확인 확인 대기 (1) -> 승인 (2) 대기 (0) -> 승인 (2) 승인 (2) -> 완료 대기 (0) -> 거절 (-1) 대기 (0) -> 철회 (-2)
| 요청 상태 | 코드 | 다음 |
|---|---|---|
| 대기 | 0 | 관리자 승인/거절 |
| 확인 대기 | 1 | 상대 고객 확인 (승계·인수 한정) |
| 승인 | 2 | 실제 상태 변경 실행 |
| 거절 | -1 | 요청 종료 |
| 철회 | -2 | 고객 철회로 종료 |
흔한 실수
1웹훅만으로 서비스 활성화 판단
// ❌ 위험 — 웹훅이 지연되면 고객이 결제했는데 서비스 못 씀javascript결제 완료는 API 응답과 웹훅 둘 다로 확인해요. 동기 응답으로 1차 활성화 후, 웹훅으로 최종 동기화.
2구독 상태와 회차 상태 혼동
구독 상태(대기/구독 중/일시정지/해지)와 회차 상태(예정/성공/실패/취소)는 분리된 축이에요. 구독이 '구독 중(1)'이어도 해당 월의 회차가 '실패(2)' 일 수 있어요. DB에도 두 축을 분리해서 저장해요.
3재시도 중인 회차를 '실패'로 즉시 취급
첫 결제 실패 후 바로 서비스를 차단하면 일시적 카드사 오류로 이탈이 발생해요. subscription.paused 가 올 때까지는 유예 기간으로 두는 쪽이 안전해요.
4해지 시점을 '즉시' 로 고정
"기간 종료까지 서비스 유지 vs 즉시 차단" 은 정책 결정이에요. 구독 해지 정책 을 먼저 정하고 코드에 반영해요.
