- 결제 옵션: Toss Payments 단일 사용
- 서비스 지역: 대한민국 한정
- 트래픽: 월 약 1,000,000건 (초당 10건 이상)
- 핵심 요구사항:
- 신뢰성(Reliability) 및 장애 허용성(Fault Tolerance)
- 결제 데이터 일관성(Consistency) 보장
- 결제 프로세스: 구매자가 결제를 진행하고, 이커머스 법인 계좌로 이체되는 과정 구현
- 보안 및 사용자 인증: 요구사항에서 생략

Payment Service 는 사용자가 결제를 시작하여 결제가 최종 승인되기까지의 전체 과정을 관리합니다.
주요 역할은 다음과 같습니다.
- 역할
- 결제 시작부터 완료까지 전체 프로세스를 총괄
- 결제 처리, 상태 변경, 최종 완료 처리
- 선택 이유
- 중앙 집중식으로 결제 흐름을 관리하여 전체 과정의 일관성 및 추적 용이
- 사용자 요청에 대해 즉각적인 응답(동기식 처리) 제공
- 역할
- Checkout 과정에서 생성되어 결제 진행의 시작점 역할
- PSP와의 연동(조회, 취소 등)에 필요한 식별 정보 제공
- 주요 필드
id
: Payment Event 식별자order_id
: PSP에서 결제 주문 유일 식별자payment_key
: 결제 승인 후 생성된 식별자is_payment_done
: 결제 완료 여부
- 선택 이유
- 결제 상태 추적 및 PSP 연동 시 고유 식별자 제공을 통해 관리 용이
- 역할
- 실제 결제 대상(상품)에 대한 정보 관리
- 결제 상태, 금액, Ledger 및 Wallet 업데이트 여부 등 세부 정보 관리
- 주요 필드
id
: Payment Order 식별자payment_event_id
: Payment Event와의 관계payment_order_status
: 결제 승인 상태 (NOT_STARTED, EXECUTING, FAILURE, SUCCESS, UNKNOWN 등)amount
: 결제 금액ledger_updated
: Ledger에 기록 여부wallet_updated
: Wallet에 정산 처리 여부buyer_id
: 구매자 식별자seller_id
: 판매자 식별자
- 선택 이유
- 각 결제의 세부 정보를 별도로 관리하여, 후속 보상 처리나 재시도 로직 구현에 용이
참고: 이 프로젝트는 결제 이벤트 생성, 결제 승인, 오류 처리 및 복구 등 결제 전 과정을 관리합니다.
Wallet 및 Ledger 서비스 관련 내용은 포함하지 않습니다.
Payment Service의 핵심 데이터 모델은 Payment Event와 Payment Order로 구성됩니다.
erDiagram
PAYMENT_EVENT {
BIGINT id PK "Payment Event의 기본 키"
STRING order_id UK "PSP에서 유일하게 식별되는 주문 ID"
STRING payment_key "결제 승인 후 Toss Payments에서 생성된 식별자"
BOOLEAN is_payment_done "결제 완료 여부"
}
PAYMENT_ORDER {
BIGINT id PK "Payment Order의 기본 키"
BIGINT payment_event_id FK "Payment Event와 연관"
ENUM payment_order_status "상태: NOT_STARTED, EXECUTING, FAILURE, SUCCESS, UNKNOWN"
BIGDECIMAL amount "결제 금액"
BOOLEAN ledger_updated "Ledger 업데이트 여부 "
BOOLEAN wallet_updated "Wallet 업데이트 여부 "
BIGINT buyer_id "구매자 식별자"
BIGINT seller_id "판매자 식별자"
}
PAYMENT_EVENT ||--o{ PAYMENT_ORDER: "생성"
- 선택 이유
- ACID 트랜잭션과 일관성 보장이 필수인 금융 거래 데이터에 적합
- 데이터베이스의 성숙도와 안정성을 통한 신뢰성 확보
- 확장성도 제공 가능하지만, 데이터 일관성과 무결성을 최우선으로 고려
- 선택 이유
- Ledger, Wallet 등 개별 서비스 장애가 전체 시스템에 영향을 미치지 않도록 SPOF(단일 실패점)를 제거
- 각 서비스의 독립적 배포와 확장을 통해 신뢰성과 장애 허용성 강화
- 적용 부문
- 사용자의 결제 요청 처리
- 선택 이유
- 즉각적인 응답이 필요하므로 동기식 호출로 사용자 경험 보장
- 적용 부문
- PSP 결제 승인 후, 후속 처리(장부 기록, 정산 처리 등)
- 선택 이유
- 사용자에게는 이미 응답이 완료된 후 내부 처리를 비동기식으로 처리하여, 결합도 낮추고 성능 및 확장성 향상
- 적용 부문
- Payment Service에서 Ledger, Wallet Service와의 통신
- 선택 이유
- 높은 TPS 처리에 적합한 논블로킹 방식으로 시스템 효율성 증대
- 상황
- PSP 결제 승인 요청 시 타임아웃이나 네트워크 지연으로 재시도 발생 가능
- 해결 방법
- Request Header에
idempotency-key
를 추가하여, 동일 요청이 중복 처리되지 않도록 보장
- Request Header에
- 선택 이유
- 중복 결제 승인으로 인한 금액 이중 처리 및 데이터 일관성 훼손 방지
- 문제점
- 결제 승인 후 DB 업데이트와 메시지 큐 전송을 동시에 원자적으로 처리해야 함
- 해결 방법: Transactional Outbox Pattern
- 결제 승인 정보를 DB에 저장하는 동시에 메시지 이벤트를 같은 트랜잭션 내에 기록
- 별도 프로세스가 DB에 기록된 이벤트 메시지를 읽어 메시지 큐로 전송
- 선택 이유
- DB와 메시지 큐의 상태를 일관되게 유지하여, 하나만 성공하거나 실패하는 상황을 방지
- 이벤트 메시지 전달 보장
- 방법: Wallet과 Ledger의 처리 완료 후 완료 이벤트 메시지 발행
- 추가: Kafka 내부 트랜잭션 기능을 사용해 메시지 전송과 커밋을 원자적으로 처리
- 선택 이유
- 결제 승인 후 후속 처리의 성공 여부를 확실히 하고, 전체 결제 완료 상태 업데이트 보장
-
사용자 요청:
사용자가 Checkout API를 호출하면 고유orderId
를 생성하고, 이에 따라 결제 이벤트와 주문이 생성됩니다. -
데이터 저장:
생성된 결제 이벤트 및 주문 정보는 RDBMS에 저장되어 데이터 일관성을 보장합니다. -
클라이언트 응답:
Checkout 결과(예:orderId
, 결제 금액 등)를 클라이언트에 반환하여 결제 위젯을 초기화합니다.
sequenceDiagram
participant 사용자 as User
participant 결제서비스 as PaymentService
participant CheckoutUseCase
participant 상품조회포트 as LoadProductPort
participant 결제저장포트 as SavePaymentPort
participant 결제위젯 as PaymentWidget
사용자->>결제서비스: 결제 페이지 요청 (Checkout API 호출)
결제서비스->>CheckoutUseCase: Checkout 요청 위임
CheckoutUseCase->>상품조회포트: 상품 정보 조회
CheckoutUseCase->>CheckoutUseCase: 결제 이벤트 및 주문 생성
CheckoutUseCase->>결제저장포트: DB에 결제 데이터 저장
CheckoutUseCase->>사용자: Checkout 결과 반환
사용자->>결제위젯: 결제 위젯 초기화 (orderId, 금액 등)
-
사용자 입력:
사용자가 결제 정보를 입력한 후 결제 승인 요청을 보냅니다. -
상태 업데이트 및 검증:
- 결제 상태를
NOT_STARTED
에서EXECUTING
으로 업데이트 - 결제 유효성(예: 금액 등) 검증
- Toss Payments에 결제 승인 요청 (헤더에
idempotency-key
포함) - PSP 응답에 따라 결제 상태를
SUCCESS
또는FAILURE
로 업데이트
- 결제 상태를
-
오류 처리:
일시적 오류 발생 시 지수 백오프와 지터를 적용한 재시도 메커니즘을 사용합니다.
-
주기적 실행:
일정 주기로EXECUTING
또는UNKNOWN
상태의 미완료 결제 이벤트를 조회합니다. -
재검증 및 재시도:
미완료 결제에 대해 유효성을 재검증한 후 Toss Payments로 재승인 요청을 수행합니다. -
재시도 횟수 초과:
재시도 횟수 초과 시 Error처리 후 수동으로 대응하는 전략을 채택했습니다.
- 프로그래밍 언어: Kotlin
- 프레임워크: Spring Boot (Webflux 기반 비동기, 논블로킹 I/O)
- 데이터베이스: RDBMS (트랜잭션 및 데이터 일관성 보장)
- 메시징: Kafka
- 멱등성 처리: HTTP 요청 헤더의
idempotency-key
사용