프로젝트 환경
최종적으로 서비스 이용자는 서비스를 이용한 후 후불 결제를 진행하며, 이를 위해 결제 시스템을 도입해야 했습니다.
실제로 PG(Payment Gateway)를 연동하려면 사업자 등록과 보안 인증 등 여러 제약 사항이 있기 때문에, 보다 간편한 토스 페이먼츠를 도입하기로 결정했습니다.
위와 같은 흐름으로 요청/응답이 이루어지게 됩니다. 토스 페이먼츠에서 제공하는 결제창은 웹 브라우저 환경에서 JavaScript SDK를 통해 렌더링되어야 합니다. 따라서 [결제 하기] 웹 페이지를 간단하게 만들었습니다.
문제 상황
토스 페이먼츠에서 제공하는 결제창을 띄우고 결제를 하고나서 최종적으로 승인 API를 토스 페이먼츠에게 요청을 하게 됩니다.
위와 같이 결제 요청을 한다면 결제 알람이 오고, [동의하고 결제하기]를 클릭하면 최종적으로 결제가 이루어집니다. 이 때, 여러 이유로 결제가 거부되면 실패 콜백 URL로 리다이렉트 되고, 결제가 성공하면 성공 콜백 URL로 필요한 파라미터가 담겨서 리다이렉트 됩니다.
위 사진의 URL을 보면 결제가 성공한다면 성공 시 콜백되는 URL로 필요한 파라미터가 담겨서 success로 리다이렉트가 되었고, 이 때 파라미터들을 보면 아래와 같은 파라미터 정보들이 최종적으로 토스페이먼츠에게 최종 결제 승인 요청을 보내게 됩니다.
- paymentKey : 토스페이먼츠에서 정한 결제 구분용 키
- orderId : 서비스에서 정한 주문 고유번호
- amount : 가격 정보
토스페이먼츠에게 최종 결제 승인 요청이 후, 이상이 없으면 토스페이먼츠에서는 승인을 하고, 상품 정보, 결제 정보 등 결제 관련 정보들을 담아서 JSON으로 응답을 해주는데, 이때 위 사진과 같이 500에러가 발생했고 로그를 확인해보면 아래와같이 401 에러가 발생했습니다.
org.springframework.web.client.HttpClientErrorException$Unauthorized: 401 Unauthorized: [no body]
문제 원인
문제의 원인을 찾아보던 중 토스페이먼츠의 인증 관련 자료를 찾아보게 되었고, 위와 같이 인코딩된 값을 Basic 인증 헤더에 사용해야한다는 것을 알았습니다.
HttpHeaders headers = getHeaders();
log.info("@@@" + headers);
[Authorization:"Bearer dGVzdF9za18yNkRsYlhBYVYwS3FqNUFqWHd4NDNxWTUwUTlSOg==", Content-Type:"application/json", Accept:"application/json"]
로그를 찍어서 디버깅을 해보니 위와 같이 Basic 인증 헤더가 아닌, Bearer 인증 헤더가 들어가고 있었습니다.
헤더에는 토스에서 제공해준 시크릿 키를 Basic Authorization 방식으로 base64를 이용하여 인코딩하여 꼭 보내야 합니다.
그러나 Basic이 아닌 Bearer 방식으로 인코딩하여 토스페이먼츠에게 보냈기 때문에 401 인증 실패 에러가 발생한 것이었습니다.
문제 해결
base64('test_sk_testtesttesttesttesttesttest:')
─────────────────┬───────────────── ┬
secretKey :
발급받은 시크릿 키 콜론
위와 같이 Bearer이 아닌 Basic 인증헤더로 설정한다면 아래와같이 토스페이먼츠에서 요구하는 API 인증 조건을 만족하게 됩니다.
[Authorization:"Basic dGVzdF9za18yNkRsYlhBYVYwS3FqNUFqWHd4NDNxWTUwUTlSOg==", Content-Type:"application/json", Accept:"application/json"]
이와 같이 인코딩된 값을 Basic 인증 헤더에 넣고 요청 본문을 추가하여 최종 결제승인을 토스페이먼츠에 요구하게되면 앞서 설명했듯이 결제정보를 JSON형태로 받게 됩니다.
위와 같이 정상적으로 최종 결제가 승인된 것을 확인할 수 있습니다.
ps
원래 알고 있기로는 Basic인증 방식보다 Bearer 인증 방식이 좀 더 보안적으로 좋고, 더 유연하며 확장성이 있다고 알고있습니다. 그러나 왜 토스 페이먼츠에서 제공하는 결제창에서는 Bearer 방식이 아닌 Basic 인증 방식을 채택하고 있을까??
토스페이먼츠에서는 결제, 결제 취소, 현금영수증 발급 등 상점에서 자주 사용하는 코어 API에 Basic 인증 방식을 사용하고 있습니다다. Bearer 인증 방식이 보안적으로 더 강력하고 유연성이 있지만, 토스페이먼츠는 특정 상황에서 Basic 인증을 선택했습니다.
Basic 인증 방식의 가장 큰 장점은 간단함입니다. 사용자 ID와 비밀번호 외에 로그인 페이지나 별도의 인증 정보를 요구하지 않아 구현이 쉽고 직관적입니다. 이는 개발자들이 토스페이먼츠 API를 빠르고 쉽게 연동할 수 있게 해줍니다.
물론 Basic 인증 방식은 Bearer 인증 방식에 비해 덜 안전할 수 있지만, 간단한 API나 개발 환경에서 자주 사용됩니다. 특히 클라이언트가 서버에 직접 자격 증명을 제공해야 하는 경우에 적합합니다.
Basic 인증방식은 HTTPS를 사용하지 않으면 매우 위험하지만 보안을 강화하기 위해 HTTPS/SSL 통신을 강제하고 있습니다.
또한, Basic 인증 방식은 Stateless 방식으로, 매 요청마다 자격 증명을 전송하기 때문에 서버 측에서 세션을 유지할 필요가 없습니다. 이러한 특성 덕분에 토스페이먼츠는 Basic 인증 방식을 선호하는 것입니다.
'Trouble Shooting' 카테고리의 다른 글
[트러블 슈팅] BeanCreationException 빈 충돌 해결을 위한 @ConditionalOnProperty 활용 (0) | 2024.10.11 |
---|---|
[트러블 슈팅] 사용자 직접 결제 취소 시 서버 리다이렉트 문제 해결기 (0) | 2024.10.10 |
[트러블 슈팅] @ModelAttribute의 자동 변환 에러 해결 (0) | 2024.10.07 |
[트러블 슈팅] 게시판 좋아요 동시성 문제 해결 (2) | 2024.10.02 |
[트러블 슈팅] 게시판 조회수 동시성 문제 트러블 슈팅 (0) | 2024.10.01 |