- exe는 제품 로직 곳곳에서 결제 API를 직접 호출하는 대신, 상태 변화를 청구 가능 사실(billable facts) 로 기록하고 확정된 상태를 Stripe와 조정함
- 기존 구조에서는 데이터베이스 트랜잭션과 결제 API 호출이 얽혀 부분 실패, 비정상 구독 상태, 결제 거절 같은 예외가 제품 흐름까지 흔들었음
- 팀 좌석이 추가되면 상태를 dirty로 표시하고, 후속 워커가 비즈니스 규칙에 따라 수량을 계산해 변경된 경우에만 Stripe 구독 수량을 갱신함
- 결제를 분리하면 신규 팀원 온보딩이 결제 코드에 의존하지 않고, 좌석 계산 규칙을 바꿔도 초대와 가입 흐름에 영향을 주지 않음
- 같은 조정 구조를 활성 VM과 디스크 사용량 같은 종량제 청구와 iOS 인앱 구매에도 적용해, 제품 이벤트는 유지하면서 결제 제공자별 연동만 바꿀 수 있음
제품 흐름에서 결제 로직 분리하기
- 결제 로직이 일반 비즈니스 로직과 뒤섞이면 청구가 필요한 모든 핵심 경로에 관련 코드가 퍼지고, 가격 구조도 취약해져 변경하기 어려움
- exe는 한 사람이 결제 지식을 독점하지 않고 누구나 관련 코드를 변경할 수 있도록 하되, 복잡한 예외는 전문 담당자가 처리하는 방식을 지향함
- 초기 팀 좌석 청구는 초대 수락부터 결제까지 하나의 큰 흐름으로 묶여 있었음
- 사용자가 초대를 수락하고 계정을 확인한 뒤 팀에 가입함
- 공유 VM 접근 권한과 요금제에 따른 컴퓨팅 자원을 할당받음
- 이 과정에서 결제 API까지 호출함
- 이렇게 데이터베이스 변경과 외부 API 호출을 결합하면 한쪽만 성공하는 부분 실패가 발생할 수 있음
- 팀 구독 상태가 비정상적일 수 있음
- 추가 좌석에 대한 결제가 거절될 수 있음
- 예외가 쌓일수록 전체 구조가 취약해짐
청구 가능 사실과 사후 조정
- 청구 가능 사실은 특정 상태가 바뀌었음을 나타내는 원자적 작업임
- 제품 로직을 먼저 실행해 자원의 새 상태를 확정함
- 이후 확정된 사실을 바탕으로 결제 제공자의 상태를 조정함
- Stripe에는 그 상태에 도달한 과정이 아니라 최종 수량만 필요함
-
팀 좌석 조정
- 초대가 수락되면 팀 좌석 상태를 dirty로 표시함
- 후속 워커가 dirty 상태를 감지하고 비즈니스 규칙에 따라 좌석 증감분을 계산함
- 수량이 달라진 경우에만 Stripe의 구독 수량을 갱신함
- 팀원 추가와 결제 코드가 분리되므로 초대 흐름을 다시 작성해도 청구가 함께 깨지지 않으며, 좌석 계산 방식도 독립적으로 바꿀 수 있음
-
종량제 청구와 인앱 구매
- 같은 조정 과정은 모든 종량제 청구에 적용됨
- 시스템이 활성 VM과 디스크 사용량에 관한 사실을 기록함
- 계량 워커가 이를 결제 제공자의 상태와 조정함
- 새로운 청구 방식을 추가해도 사실은 그대로 유지되고, 각 API와 조정하는 방법만 달라짐
- iOS 앱 역시 누군가 인앱 구매로 구독했다는 사실만 전달하고, 실제 결제 상태는 이후 조정함
- 결제 구조가 다른 구성원도 다룰 수 있는 영역이 되었으며, 초대 흐름 같은 제품 기능 변경이 청구 시스템을 손상시킬 가능성도 낮아짐
- 같은 조정 과정은 모든 종량제 청구에 적용됨