강의 장바구니, 결제와 환불 기능 구현
장바구니 기능
관심 있는 강의의 기수를 확인하여 해당 기수를 장바구니에 추가할 수 있습니다. 장바구니 추가 후 장바구니 목록을 확인할 수 있게 해당 페이지로 이동하게 됩니다.
웹 사이트 상단의 장바구니 아이콘을 통해서도 장바구니 목록에 접근 할 수 있습니다.
장바구니 페이지에서는 장바구니 삭제와 결제가 가능합니다.
결제 기능
결제 기능은 토스 페이먼트를 사용하여 진행됩니다.
장바구니에서 결제 요청을 하면 결제가 진행됩니다.
클라이언트의 결제 요청 - 카드사 인증 - 토스 페이먼트 결제 승인 요청 - 백엔드의 승인 결과 처리의 과정을 거쳐 결제 로직이 수행됩니다. 결제 승인의 결과를 토스 페이먼츠에서도 확인 할 수 있습니다.
정상적으로 결제 요청이 처리되면 결제 완료 메시지와 함께 구매한 강의의 목록으로 갈지, 결제 정보 목록으로 갈지 선택할 수 있습니다.
강의 목록에 결제한 강의가 추가 되었음을 알 수 있습니다.
결제 목록에서는 결제 정보 조회와 환불 기능을 제공하며 상세 정보를 통해 결제한 강의의 정보를 볼 수 있습니다.
환불 기능
위에서 볼 수 있듯 결제 목록에서는 각각의 결제 건에 대해 환불을 진행할 수 있습니다.
현재 기준 수업 시작 전 하루 전까지 환불이 가능합니다.
환불 성공 시 해당 결제는 목록에서 제거됩니다.
토스 페이먼츠에서도 정상적으로 환불 처리되었음을 확인할 수 있습니다.
Background Task
•
유효성 검사
◦
장바구니 추가 시 추가할 강의의 유효성, 보유 여부, 모집 기간, 장바구니 추가 여부를 바탕으로 유효성 검사를 합니다.
◦
결제 요청 시 한 번 더 유효성 검사를 거칩니다. 결제할 강의의 유효성, 보유 여부, 모집 기간, 해당 강의의 정원 초과 여부, 결제할 강의 간 or 수강 중인 강의 간의 요일과 시간의 동시성 여부를 바탕으로 유효성 검사와 에러 처리를 합니다.
•
결제 로직
◦
결제는 클라이언트에서의 구매 요청 - 카드사 인증 - 결제 승인 요청 - 카드사 결제 승인 - 결제 결과 DB 반영의 흐름으로 이어집니다.
◦
주문한 품목의 id들을 구매 요청 시 결제 로직에 포함해 결제 결과 처리 시 금액에 대한 유효성 검사를 한 번 더 거쳐 문제 시 결제 취소를 하고, 성공 시 DB에 반영합니다.
◦
결제 승인이 성공적으로 이루어진 후 DB에 반영하는 트랜잭션 과정 도중 문제가 발생하면 결제 취소를 요청하는 로직을 포함했습니다. 이를 통해 서버 문제시 결제는 정상적으로 진행되고 강의는 구매되지 않는 문제를 해결하였습니다.
•
동시성 처리
◦
강의 정원이 정해져 있어 동시에 결제 처리가 되면 DB에 반영될 경우 문제가 발생할 수 있어 결제 과정에서 동시성 처리를 고려해야 했습니다. 동시성 처리를 위해 Redis/BullMQ 라이브러리를 사용했습니다.
◦
Redis/BullMQ 사용 이유
작업 큐 시스템 | BullMQ는 작업 큐 시스템으로, 결제와 관련된 각 작업을 큐에 넣고 순차적으로 처리할 수 있습니다. 이를 통해 여러 결제 요청이 동시에 들어오더라도 작업이 중복되거나 누락되지 않도록 처리할 수 있습니다. |
높은 처리 성능 | BullMQ는 Redis 위에서 동작하기 때문에 빠른 읽기/쓰기 성능을 제공합니다. 이는 대량의 결제 요청이 동시에 발생하는 상황에서도 빠르고 효율적으로 작업을 처리할 수 있게 해줍니다. |
확장성 | BullMQ는 Redis의 분산 시스템을 기반으로 하여 쉽게 확장할 수 있습니다. 추후 서비스가 확장되어 더 많은 트래픽과 동시성을 처리해야 할 때 worker를 추가하여 부하를 분산시킬 수 있습니다. |
간편성 | 다른 메시지 큐 시스템들과 비교하여 쉽게 설정하고 관리할 수 있습니다. Node.js 환경, 서비스의 크기를 생각했을 때 기본적인 기능들을 간편하게 구현할 수 있고, 서비스 확장 시에 추가적인 기능도 사용할 수 있어 BullMQ를 선택했습니다. |
관련 트러블 슈팅
문제 1:
결제 요청 - 카드사 인증 - 결제 승인 요청 - 카드사 결제 승인 - 백엔드의 승인 결과 처리로 이어지는 로직에서 결제는 처리되지만, DB의 결과 처리 과정이 제대로 이루어지지 않는 경우가 발생했습니다.
원인 분석 :
결제 승인이 성공한 후 트랜잭션 처리 중 에러가 발생하는 경우, 결제는 정상 처리되지만, DB에는 해당 결제 데이터가 적절하게 반영되지 않는 것이 원인이었고 이로 인해 토스 결제 부분과 DB의 결제 정보 간의 불일치가 발생했습니다.
해결 :
결제 승인 이후 트랜잭션이 실패하면, 승인된 결제를 자동으로 환불하는 로직을 추가했습니다. 이를 통해 결제 승인과 데이터베이스 업데이트 사이에 문제가 발생할 경우, 결제를 취소하여 시스템 상태를 일관성 있게 유지할 수 있었습니다. 이를 위해 트랜잭션 내에서 승인 요청을 처리하고, 승인 성공 후 발생하는 모든 에러에 대해 일관된 롤백 또는 보정 작업을 수행할 수 있도록 구조를 개선했습니다
문제 2 :
프로젝트의 결제 처리 로직에서 초기에는 결제 요청 전에 직접 orderId를 생성하고, 이 orderId를 기준으로 결제 정보를 미리 DB에 저장하고 결제 요청 시 비교하여 유효성 검사를 수행한 후, 결제 승인 요청을 진행했습니다.
그러나 버블로 프론트엔드를 작업하면서 토스(Toss)에서 제공하는 버블 결제 플러그인을 사용하는 것으로 결정했는데 플러그인을 사용하면 기존의 결제 로직을 그대로 사용할 수 없는 문제가 발생했습니다.
원인 분석
토스에서 제공하는 버블 플러그인은 결제 요청 시에 orderId를 자동으로 생성하도록 설계되어 있어, 개발자가 요청 전에 직접 orderId를 생성할 수 없는 문제가 있었습니다. 이는 기존 로직에서 필수적으로 사용하던 orderId 기반의 유효성 검사 방식, DB에서의 결제 처리 로직과 충돌을 일으켰습니다.
해결 과정 :
•
문제 분석 및 해결 방안 탐색:
처음에는 공식 문서와 다양한 온라인 자료를 통해 문제 해결 방법을 찾으려고 했습니다. 하지만, 버블 플러그인에 대한 정보는 많지 않았고, 문제를 해결할 만한 정보를 얻지는 못했습니다.
•
커뮤니티 및 개발자 소통:
문제 해결에 필요한 정보를 찾는 데 한계가 있었기에, 결국 토스 개발자 커뮤니티에 이슈를 제기하기로 했습니다. 문의한 결과, 토스 개발자로부터 플러그인의 편의성을 위해 orderId는 자동으로 생성되며, 직접 설정할 수 있는 옵션이 따로 제공되지 않는다는 답변을 받았습니다.
•
대체 방안 모색 및 구현:
기존 로직을 그대로 유지할 수 없었기에, 대체 방안을 모색했습니다. 토스 플러그인이 제공하는 기능과 제한 사항을 감안하여, orderId 대신 orderName 필드를 활용하기로 했습니다. 구체적으로는, 요청 전에 클라이언트 측에서 컨트롤할 수 있는 orderName에 결제 품목의 ID를 포함해, 이 값을 기준으로 결제 요청 정보의 유효성을 검사하고, 결제한 품목들을 추적하여 토스 결제 승인 이후에 DB 처리를 할 수 있도록 로직을 변경했습니다.
이를 통해 결제 프로세스의 유효성 검사를 유지하면서도, 토스 플러그인의 구조적인 제한 사항에 맞춘 새로운 로직을 구현할 수 있었습니다.