결제 성공보다 어려운 것: 쿠폰·웹훅·권한 동기화를 상태 머신으로 푼 방법
구독 결제는 결제 API를 붙이는 순간 끝나는 기능이 아니다.
실제 운영에서는 결제 성공보다 실패/중복/지연 이벤트가 더 많이 문제를 만든다.
이 글은 구독·쿠폰·프로모션을 안정적으로 운영하기 위해 결제 흐름을 상태 머신으로 다룬 방법을 정리한다.
문제 정의
운영에서 반복되는 이슈는 아래였다.
- 결제 제공자 이벤트가 지연/중복 도착함
- 쿠폰/프로모션이 결제 상태와 충돌함
- 결제 상태와 실제 권한(entitlement)이 불일치할 수 있음
선택: 이벤트 기반 상태 머신
핵심 흐름을 명시적으로 모델링했다.
checkout -> provider event -> webhook/state sync -> entitlement
이렇게 하면 이벤트 순서가 어긋나도 현재 상태를 기준으로 보정 로직을 적용할 수 있다.
상태 전이 표준화
운영에서는 상태명이 다르면 장애 대응이 느려진다.
| 상태 | 의미 | 허용 전이 |
|---|---|---|
PENDING | 결제 대기 | ACTIVE, FAILED, EXPIRED |
ACTIVE | 결제/권한 유효 | PAST_DUE, CANCELED |
PAST_DUE | 결제 실패 후 유예 | ACTIVE, CANCELED |
CANCELED | 해지 완료 | 종결 |
쿠폰 적용도 상태 전이 규칙에 포함시켜, “가격 정책"과 “권한 정책"이 따로 놀지 않게 만들었다.
운영 설계 포인트
- 상태 전이 규칙을 코드로 고정
- 웹훅 idempotency 키로 중복 처리 방지
- 권한 반영은 결제 상태와 분리된 검증 단계로 처리
실패 패턴과 대응
- 중복 웹훅:
provider_event_id중복 저장으로 차단 - 순서 역전 웹훅: 최신 상태 판별 규칙으로 늦은 이벤트 무시
- 권한 오부여: entitlement write를 트랜잭션 단위로 분리
왜 이 구조가 중요한가
결제는 단일 기능이 아니라 신뢰 시스템이다. 정확한 상태 동기화가 안 되면 매출, CS, 신뢰가 동시에 무너진다.
관측 지표
webhook_duplicate_rateentitlement_sync_delaypayment_state_conflict_countmanual_reconciliation_count
결제 장애는 사용자 불만으로 바로 드러나기 때문에, 상태 충돌을 조기에 탐지하는 지표를 필수로 운영해야 한다.
참고 및 인용
참고: Stripe webhook 가이드는 이벤트 서명 검증, 재시도, 순서 비결정성 대응 원칙을 설명한다. Using webhooks
참고: Stripe는 중복 요청 방지를 위해 idempotency key 사용을 권장한다. Idempotent requests
참고: 이벤트 기반 아키텍처에서 상태 전이 모델을 명시하는 방법은 도메인 이벤트 설계의 핵심이다. Event Sourcing