1차 프로젝트 때 결제 파트를 담당했습니다.
결제는 처음이라 우선 결제 플로우를 이해하는 것에 집중했습니다.
이번 프로젝트에서는 PG사로 토스페이먼츠(Toss Payments)를 선택했고,
공식 개발자 가이드가 상세하고 친절하게 설명이 잘 되어 있어서 구현 과정에서 많은 도움을 받을 수 있었습니다.
https://docs.tosspayments.com/guides/v2/payment-widget/integration
결제 연동 과정에서 고민했던 부분은
- orderId와 amount 값을 언제 서버에 저장해야 할까?
- 결제 요청 이전에 저장하는지
- 결제 승인 이후에 저장하는지
- 카드사 승인 이후 바로 successUrl로 이동하는 것이 맞을까?
- 카드사 승인 완료 이후 무조건 successURL로 이동하게 되어있습니다.
- 하지만 이 시점은 아직 토스 서버에 최종 결제 승인(confirm)을 요청하기 전 단계입니다.
- 이 상태에서 사용자에게 곧바로 "결게 성공" 화면을 보여주는 것이 사용자 경험 측면에서 적절한가에 대한 의문이 들었습니다.
이 2가지 였습니다.
이 고민을 고려하여 다음과 같이 구현했습니다.
- 주문서 페이지 진입 시점에 서버에서 Payment 객체를 미리 생성
- orderId, amount 값을 서버에 선저장
- 클라이언트에서 전달받은 값이 아닌, 서버 기준 데이터를 신뢰
- 카드사 승인 이후 successUrl 대신 preparingUrl로 이동
- 이 단계에서는 아직 "결제 성공"으로 처리하지 않음
- paymentKey를 파라미터로 전달
- preparingUrl에서 토스 서버에 최종 결제 승인 요청
- paymentKey, orderId, amount 를 기준으로 결제 확정
- 승인 성공 시에만 결제 성공 상태로 변경
이러한 구조로 구현했습니다.
이를 기반으로 구현한 내용을 정리해보겠습니다!
전체 플로우

전체적으로 보면 이런 시퀀스다이어그램입니다.
하나씩 뜯어봅시다!
1. 주문서 진입 시 Payment 객체 생성


toss에서 결제 요청과 승인 사이에 데이터 무결성을 확인하기 위해 orderId와 amount를 서버에 임시 저장했습니다.
status pending인 Payment객체를 생성하고 {orderId, amount} 값을 저장했습니다.
2. 결제 위젯 렌더링 및 결제 요청



그 후에 주문서에서 결제 위젯을 렌더링하고, 결제 정보를 입력합니다. (결제수단 선택)
3. 카드사 승인 후 preparingUrl 이동

사용자가 입력한 정보와 카드사 승인을 받으면 preparingUrl로 이동합니다.
preparingUrl에서 paymentKey와 orderId, amount를 쿼리파라미터로 받습니다.
토스 개발자 가이드에서는 카드사 승인 이후 success.html 또는 fail.html로 바로 이동하도록 안내하고 있습니다.
하지만 이 시점은 토스 서버에 최종 결제 승인(confirm)을 요청하기 전 단계입니다. 그럼에도 불구하고 사용자에게 “결제 성공” 화면을 보여주는 플로우가 사용자 경험 측면에서 다소 어색하다고 느꼈습니다.
그래서 카드사 승인과 최종결제 승인을 분리하기 위해 preparingUrl을 추가했습니다.
4. preparingUrl에서 결제 승인 검증

preparingUrl에서는 쿼리파라미터로부터 받은 paymentKey와 orderId, 결제 금액 등을 서버에 보냅니다.
서버에서는 toss한테 결제 승인 api를 보내기 전에 client로부터 받은 amount 값과 payment 객체의 amount 파라미터의값이 같은지 검증합니다.
같으면 toss한테 결제 승인 API를 날리고, 같지 않으면 failUrl로 redirect합니다.
5. 결제 승인 성공 / 실패 처리

서버는 toss한테 승인 API를 요청한 후, 승인이 나면 preparingUrl → successUrl로 redirect하고, payment 객체에

승인 실패가 일어나면 failUrl로 redirect되고, status를 FAILED로 변경합니다.
보통 실패하는 경우는 카드에 돈이 없는 경우나, 네트워크 오류로 인한 실패입니다.
다음 글은 구현하는 글을 가지고오겠습니다.
'Spring Boot' 카테고리의 다른 글
| [Spring Boot] 토스페이먼츠 결제 연동 구현하기 - 2편 (0) | 2026.01.05 |
|---|---|
| DB 동시성 문제 어떤 경우에 발생할까요? DB Lock의 종류 (0) | 2025.11.21 |
| 자주 사용하는 Lombok 생성자 어노테이션 (0) | 2025.10.23 |
| @Transactional(readOnly =true) 하는 이유 (0) | 2025.10.18 |
| Dto와 @Builder에 대하여 (0) | 2025.10.06 |