1. 결제 도메인 설계 방향
결제 도메인은 단순히 “결제 성공 여부”만 관리하는 것이 아니라, 상태 변화와 외부 PG 연동까지 포함하는 핵심 도메인이다.
이번 프로젝트에서는 다음 기준으로 설계했다.
- 결제 상태는 명확한 상태값으로 관리한다
- 상태 변경 이력은 별도로 관리한다
- 외부 PG(토스)와의 통신은 서버에서 검증한다
- 서비스 간 통신은 이벤트 기반으로 분리한다
2. Payment / PaymentHistory 구조
결제 도메인은 하나의 애그리거트로 구성된다.
- Payment: 현재 상태를 관리하는 Aggregate Root
- PaymentHistory: 상태 변경 이력을 기록하는 엔티티
역할 구분
Payment는 “현재 상태”를 의미한다.
PaymentHistory는 상태가 변경될 때마다 기록이 쌓인다.
예를 들어 다음과 같은 흐름이 발생한다.
이 경우:
- Payment.status = SUCCESS
- PaymentHistory에는 PENDING, SUCCESS 두 개의 기록이 존재
이 구조를 통해 결제 흐름을 추적할 수 있고, 장애 발생 시 원인 분석이 가능하다.
3. 테이블 설계
Payment
────────────────────────────
PK id UUID
FK booking_id UUID
FK user_id UUID
order_id VARCHAR(100) UNIQUE
payment_key VARCHAR(200)
original_amount BIGINT
discount_amount BIGINT
final_amount BIGINT
status VARCHAR(20)
// PENDING, SUCCESS, FAILED, REFUNDED
requested_at TIMESTAMP
approved_at TIMESTAMP
created_at TIMESTAMP
updated_at TIMESTAMP
PaymentHistory
────────────────────────────
PK id BIGINT
FK payment_id UUID
status VARCHAR(20)
reason_code VARCHAR(50)
reason VARCHAR(255)
created_at TIMESTAMP
4. order_id 설계 이유
order_id는 내부에서 생성하는 결제 식별자다.
이 값은 다음 목적을 가진다.
- 중복 결제 방지 (Idempotency)
- 결제 요청과 승인 매칭
- 외부 PG와의 데이터 검증 기준
사용자가 결제 버튼을 여러 번 누르거나 네트워크 재시도 상황에서도 동일한 order_id를 사용하면 중복 결제를 방지할 수 있다.
5. status와 reason 분리
결제 상태는 enum으로 관리하고, 실패 사유는 별도로 관리한다.
- status: 시스템 로직에서 사용하는 상태값
- reason: 사람이 확인하는 메시지
- reason_code: PG에서 내려주는 코드
예시:
reason_code = INSUFFICIENT_BALANCE
reason = 잔액 부족
토스 결제 API에서도 실패 코드와 메시지를 제공하기 때문에 이를 그대로 저장하도록 설계했다.
6. API 구조 설계
API는 외부 API와 내부 API로 분리했다.
외부 API
- 클라이언트가 호출
- 사용자 인증 필요
- 사용자 기능 제공
내부 API
- 서비스 간 호출
- 외부 접근 불가
- 시스템 처리용
이 구조를 통해 보안과 책임을 명확하게 분리했다.
7. 결제 API 명세
결제 요청
- 결제 요청 생성
- Payment 상태를 PENDING으로 저장
결제 승인 (토스 confirm)
- paymentKey, orderId, amount 전달
- 서버에서 토스 API 호출 후 검증
- 성공 시 SUCCESS 처리 및 이벤트 발행
결제 조회
단일 조회 (구매자)
- 본인 결제만 조회 가능
내 결제 목록 조회
관리자 전체 조회
호스트 프로그램별 조회
- 해당 프로그램의 결제 내역 조회
- 프로그램 소유자 검증 필요
환불
- 결제 취소 및 REFUNDED 상태 변경
8. 내부 API
결제 생성
- 예매 서비스에서 호출
- Payment 생성 (PENDING)
결제 상태 조회
- 다른 서비스에서 결제 상태 확인
상태 변경 처리
- 이벤트 기반 상태 변경
9. 결제 흐름
성공 흐름
→ 토스 결제 진행
→ confirm 호출
→ SUCCESS
→ PaymentHistory 기록
→ Kafka 이벤트 발행
→ 예매 확정
실패 흐름
→ FAILED
→ PaymentHistory 기록
→ 좌석 해제 이벤트
10. 정리
결제 도메인은 단순 CRUD가 아니라 상태 흐름과 외부 시스템 연동을 포함하는 중요한 영역이다.
이번 설계에서 중점적으로 고려한 부분은 다음과 같다.
- Payment와 PaymentHistory를 분리하여 상태와 이력 관리
- order_id를 통한 중복 결제 방지
- status와 reason을 분리하여 시스템과 사용자 관점 구분
- 외부 API와 내부 API 분리를 통한 책임 분리
- Kafka 이벤트를 통한 서비스 간 결합도 감소
이 구조를 기반으로 구현하면 확장성과 안정성을 모두 확보할 수 있다.
'Spring 개발일지' 카테고리의 다른 글
| [팀프로젝트] MSA 기반 티켓팅 프로그램 - 결제 도메인 설계 2편 Kafka 기반으로 전면 수정 + 인프라 설계까지 (1) | 2026.04.22 |
|---|---|
| [팀프로젝트] MSA 기반 티켓팅 프로그램 - 결제 도메인 설계 및 산출물 도출 (0) | 2026.04.21 |
| [팀프로젝트] MSA 기반 티켓팅 프로그램 - 결제 도메인 (1) | 2026.04.18 |
| [팀 프로젝트]MSA 기반 티켓팅 서비스 설계 및 도메인 분리 (프로젝트 기획 정리) (2) | 2026.04.17 |
| [개인 프로젝트] 성능테스트 준비 (1) | 2026.04.16 |