프로젝트 진행 중 '결제' 담당을 맡아 처음으로 PG와 연동하여 결제에 도전해보기로 했다.
결제를 위한 방법은 여러가지가 있었고, 그 중에서 Toss Payments로 [가상계좌 + 에스크로]를 선택하였다.
✅ Toss, Virtual Account, Escrow 왜 골랐을까?
먼저 결제 기능을 구현할 때 어떤 순서로 동작하는지 알아보았다.
주 흐름은 아래와 같다.
- 요구 사항 정리
- 결제 서비스 종류 — 카드, 계좌 이체, 간편 결제 등
- 결제 대행 PG 선택 및 연동 방식 이해
- Spring Boot로 BE 기능 구성
- 테스트 결제 구현
PG 종류
- Iamport (portone)
- 다양한 결제 수단 통합 관리 가능
- 기본적인 결제 flow 이해에 도움
- UI 없이 빠르게 연동 가능
- KG Inicis
- 높은 안정성 보유, 전자 결제 시장 점유율 上
- Hash서명, CallBak 처리 등 보안 개념을 익히는 데 큰 도움
- 직접적인 PG 연동 경험 가능
- Toss Payments
- 최신 PG사 연동 경험 보유 가능
- 공식 문서가 깔끔하며 편리한 테스트 환경 제공
- Webhook 설계 스킬 경험 가능
- NHN KCP
- 안정성이 최우선인 대규모 서비스
- 오랜 업력으로 높은 시장 점유율 보유
- NICE Payments
- 온/오프라인 결제 통합이 가능하며 다양한 결제 수단 지원
결제 서비스
- 신용 카드 결제, 간편 결제
- 사용자 인증 - 비밀번호, 생체 인증 등
- 보안을 위한 사용자 디바이스가 필요할 수 있음
- 백 오피스 결제, 관리자 직접 결제
- PG 서버 - 내 서버 통신 처리
- 무통장 입금, 가상계좌, 계좌 이체 등에서 사용
- 비 인증 결제, 자동 결제
- 결제 수단을 미리 저장
- 저장된 결제 token을 이용해 자동 결제 처리
※ 위 내용은 단순 조사 내용으로 실제 시장과 다를 수 있으며, 대부분의 회사에서 다양한 결제 시스템, 문서, 기술 제공 중 입니다.
자세한 기술에 대한 정보는 각 플랫폼 기술 문서 및 자료를 참고해주세요.
선택 이유
우리 팀의 프로젝트는 '실시간 채팅 기반 중고거래 서비스'를 구현하는 것이었고, '중고 거래'라는 특성 상 가장 중요한 건 '사기'의 위험이었다. 중고나라, 당근마켓, 번개장터 등등 각 플랫폼마다 안전하게 거래를 할 수 있는 [안전 거래] 시스템이 존재한다. 당근페이, 번개페이 등등 각 플랫폼에서 인증해주는 전용 페이 시스템도 존재한다.
일반 사업자와 거래보다 더 사기의 위험성이 높은 중고 거래는 그만큼 사용자들에게 안전성을 보장해야 한다고 생각했다. 그래서 우리 플랫폼에서 [결제 시스템]이 도입된다면 사용자들 간 직접 계좌 이체로 발생하는 문제를 최소화 시킬 수 있지 않을까 라는 생각으로 [결제 시스템]을 구현하게 되었다.
가장 최근에 떠오르기 시작한 회사이며 문서 정리가 깔끔하게 잘 되어 있는 토스를 선택했다. 기본적인 flow를 먼저 알기 위해서는 포트원(아임포트), 제대로 보안 로직을 공부하며 구현하기 위해서는 KG이니시스가 나은 선택같지만, 외부 서버에서 정보를 받아오고 REST API 방식으로 간편하게 사용하기 위한 목적이 컸다. 결제 흐름은 구현에 직접 들어가기 전, 그리고 구현을 하며 알아가보기로 하였고, 처음부터 큰 욕심을 내지 않고 webhook에 대해 알아보며 그 흐름을 익히고, CallBack으로 넘어가며 과정에 익숙해지고자 하였다.
위와 같은 이유로 최대한 안전하다는 걸 보장해주기 위한 결제 서비스 중 현재 상황에서 그나마 구현할 수 있다고 판단되는 [가상 계좌]를 선택하게 되었다. 또한 가상계좌 사용 시 [에스크로]를 사용하면 [토스 페이먼츠]에서 에스크로 대금을 맡아주기도 한다고 하였다. 자사 플랫폼에서 에스크로 대금을 보관하는 경우 법적인 여러 조치가 필요하지만, PG사에서 에스크로 대금을 보관해준다면 좀 더 편하게 구현할 수 있다.
예상 시나리오 흐름
- 구매자가 상품을 구매하기로 결정 - 판매자와 대화 후 <채팅>
- 구매자 전용 가상 계좌 발급 <판매자가 가상 계좌 발급 신청>
- 구매자 입금
- PG에서 입금 완료 Webhook 발송
- 결제 상태 업데이트
- 판매자에게 알림
- 거래 완료 후 판매자에게 정산
👉🏻 예상 구조
- 구매자 → 가상 계좌 입금
- PG 입금 확인
- 서버에서 Webhook 수신
- DB 정보 저장
먼저 Order Table을 만들어야 하는가에 대한 의견이 나뉘었다.
Product → 하나의 상품 판매 게시 글
위 Product의 status를 이용하자는 초기 이야기가 있었는데 [결제 시스템] 구현을 위해 알아보는 도중 알게 되었다. 토스 페이먼츠는 orderId로 각 거래를 구분한다는 것이었다. 처음 피드백을 받을 때 "상품 마다 order Id가 다르다"라는 말을 들었는데 잘 이해가 되지 않았다. 그리고 그 말은 결제 시스템에 대해 공부를 하는 도중에 이해가 되었다.
상품 게시 글을 올리면 그건 product Id - 이 게시 글 안에서 여러 개의 상품을 판매할 수 있다.
만약 A라는 구매자가 나타나 이 구매자와 거래를 하게 되는 경우 하나의 주문이 생성된다 order Id
이 주문ID를 이용해 가상 계좌를 발급하고, 그 가상계좌로 구매자에게 요청한다.
참고로 이 부분도 처음에는 [판매자마다 가상 계좌]를 생성하려고 했지만, 나중에 정산을 할 때도 어떤 order의 정산금액인지 구분해야 하고, 주문 당으로 생성되어서 결국엔 판매자 가상계좌가 있어도 주문 가상계좌를 생성해야 하는 것이었다.
가상 계좌 정보
가상 계좌는 처음에 한 번만 응답하여 나중에 조회를 할 수 없다.
가상 계좌를 받으면 그 정보를 RDB에 저장해야 한다.
입금 완료
PG에서 입금 확인 후 PG서버에서 우리 Spring Boot 서버로 POST 요청을 보낸다.
우리 서버에서 POST 요청을 받을 수 있도록 구현이 필요하다.
💻 토스 페이먼츠 연동하기
도움 받은 관련 문서는 접은글 확인
연동하기 | 토스페이먼츠 개발자센터
토스페이먼츠의 간편한 결제 연동 과정을 한눈에 볼 수 있습니다. 각 단계별 설명과 함께 달라지는 UI와 코드를 확인해보세요.
docs.tosspayments.com
API 키 | 토스페이먼츠 개발자센터
토스페이먼츠 클라이언트 키 및 시크릿 키를 발급받고 사용하는 방법을 알아봅니다. 클라이언트 키는 SDK를 초기화할 때 사용하고 시크릿 키는 API를 호출할 때 사용합니다.
docs.tosspayments.com
코어 API | 토스페이먼츠 개발자센터
토스페이먼츠 API 엔드포인트(Endpoint)와 객체 정보, 파라미터, 요청 및 응답 예제를 살펴보세요.
docs.tosspayments.com
웹훅(Webhook) 연결하기 | 토스페이먼츠 개발자센터
토스페이먼츠 결제, 브랜드페이, 지급대행 상태에 변경사항이 있을 때 웹훅으로 실시간 업데이트를 받아보세요.
docs.tosspayments.com
API 키 확인
먼저 토스 페이먼츠 연동을 시작하기 전에 [내 개발정보] - [API 키] 에서 연동 키를 확인하자.
클라이언트 키 + 시크릿 키 하나의 세트로 사용한다.
만약 테스트 키와 라이브 키를 섞어서 사용하는 경우 INVALID_API_KEY
오류가 발생하니 주의.
- Client 키: 브라우저에서 Toss Payments SDK를 초기화할 때 사용.
- 계약 정보, 위젯 설정
- 결제 위젯 클라이언트 키: gck
- API 개별 연동 키: ck
- Secret 키: Client key로 생성한 모든 결제를 승인, 취소, 조회에 사용.
- Base64로 encoding하여 Authorization Basic 헤더로 사용
- 시크릿 키 뒤에 콜론(
:
)을 추가한 상태로 인코딩 - Basic 인증은 {ID:Password} 하나의 쌍으로 관리되는 Base64 인코딩 형식
- 뒤에 콜론을 붙이고 인코딩하게 되는 경우, 시크릿 키는 ID, 비밀번호는 없음
Webhook 연동하기
토스 페이먼츠 웹훅 이벤트 타입 표
PAYMENT_STATUS_CHANGED | 결제 상태 변경 이벤트입니다. 모든 결제수단에 사용 가능합니다. |
DEPOSIT_CALLBACK | 가상계좌 입금 및 입금 취소 이벤트입니다. |
CANCEL_STATUS_CHANGED | 결제 취소 상태입니다. |
METHOD_UPDATED | 브랜드페이 고객 결제수단 변경 이벤트입니다. |
CUSTOMER_STATUS_CHANGED | 브랜드페이 고객 상태 변경 이벤트입니다. |
payout.changed | 지급대행 상태 변경 이벤트입니다. |
seller.changed | 셀러 상태 변경 이벤트입니다. |
[내 개발정보] - [웹훅] 에서 웹훅을 등록할 수 있다.
해당 내용은 위에 있는 표를 참고하여 자세히 알 수 있다.
webhook은 온라인에서 접근할 수 있는 url을 등록해야 한다.
로컬 서버에서 실행하는 spring boot에 접근하기 위해서는 로컬로 받을 수 있는 postman mock servers나 ngrok를 이용할 수 있다. 평소에도 postman으로 api를 확인하고 있어서 mock server를 이용해보려고 했는데 실패했다. 토스에서도 ngrok 연결 방법을 알려주니 편하게 사용해보자.
웹훅 등록 - ngrok 연동
https://ngrok.com/signup?ref=downloads
위 링크에 들어가 ngrok에 회원가입을 했다. 윈도우를 사용하고 있어 설치를 하려고 하니 파일에 바이러스가 있다고 설치가 되지 않았다. 결국 Docker로 실행을 하기로 했다.
ngrok에 가입한 뒤, Docker setup 페이지로 들어가면 그대로 복사해서 사용할 수 있도록 token이 포함된 코드를 제공해준다.
실행을 시키면 이렇게 ngrok와 연결된 모습을 볼 수 있다.
Forwarding에 있는 링크를 webhook url에 작성한다. 위 이미지 기준으로 https://cf8d ~ free.app
으로 전부 넣어야 한다.
❌ ngrok를 docker에서 실행 시 502 발생
하지만 여기서 문제가 발생했다.
계속 웹훅을 받을 때마다 502 Bad Gateway가 떴다. Security 문제인가 싶어 url 설정도 바꾸어봤지만 변하는 건 없었고, spring server port도 8080에서 9090으로 변경해보았다. 네트워크 마저 0.0.0.0으로 변경했지만 계속 실패를 했다.
docker에서 실행하게 되면 그 접근을 한 번 더 검증하는 건가? 싶어 window에서 설치파일을 한번 더 다운했더니 이번엔 문제가 생기지 않고 제대로 다운로드에 성공했다. 그렇게 window에서 ngrok를 설치해서 실행하니 502가 해결 되었다.
해당 문제는 관련 문서 https://ngrok.com/docs/using-ngrok-with/docker/ 를 통해 자세히 확인할 수 있다.
문서에서 알려주는 것처럼 compose파일을 구성하거나, 로컬에 제대로 접근을 요청하는지 port번호, network, url, authentication 등등 모두 확인해보자.
'Framework > SpringBoot' 카테고리의 다른 글
[SpringBoot] Redis 적용 시 @Indexed 사용 문제점 (0) | 2025.03.26 |
---|---|
[SpringBoot] 테스트 코드 작성 시 Mock과 Spy, 그리고 BCryptPasswordEncoder의 encode (0) | 2025.03.26 |
[SpringBoot] Cache 값을 저장하지 못하던 문제 해결 및 @Cacheable과 @CachePut 차이 (0) | 2025.03.06 |
[SpringBoot] access token과 refresh token을 만들어 postman으로 테스트하기 (1) (0) | 2025.02.27 |
[SpringBoot] AOP를 이용해 Log를 남겨보자 -로깅 (0) | 2025.02.27 |