백엔드 카카오페이 결제 기능 구현 (작성중)

김민우·2024년 4월 8일
0

잡동사니

목록 보기
22/22

카카오톡 선물하기 클론 + 펀딩 프로젝트에서 결제 기능을 담당하게 되었다. 결제 수단은 카카오페이다.

결제 플로우

  1. 프론트엔드가 백엔드에게 결제 준비 요청을 보낸다.
  2. 백엔드는 요청값 일부를 캐싱 후 카카오페이 서버에 결제 준비 요청을 보낸다.
  3. 카카오페이로 부터 받은 결제 준비 응답값을 파싱
  4. 3에서 전달받은 값들 중 Redirect URL을 포함한 일부를 프론트엔드에게 반환한다.
  5. 프론트엔드는 전달받은 값 중 Redirect URL로 사용자를 리다이렉트 시킨다. (이 과정에서 사용자가 QR 또는 카카오톡으로 결제)
  6. 사용자가 결제를 진행한다.
  7. (6)에서 결제 결과(성공/실패/취소)에 알맞는 URL로 리다이렉트한다.
  8. 결제 승인 요청 쿼리 파라미터에 있는 PG 토큰값과 (4)에서 전달받은 orderNumber 를 백엔드에 전달한다.
  9. 백엔드는 (8)에서 전달받은 값들을 통해 결제 승인 요청을 수행하여 최종 결제 승인, (2)에서 캐싱한 결제 정보를 가져와 DB에 반영한 후 결제 완료 페이지에 필요한 데이터를 프론트엔드에게 반환한다.
  10. 프론트엔드는 사용자에게 결제 완료 페이지를 응답한다.

문제점 1

결제 과정은 크게 2가지로 나뉜다.

  • 결제 준비
  • 결제 승인

실제로 주문 페이지에서 사용자가 "결제하기" 버튼을 누르면 1 ~ 5가 진행된다. 사용자가 결제를 완료하면 나머지 과정이 진행된다.

문제는 결제 정보(상품, 수량, 가격)등을 결제 준비 과정에서 전달받고 결제 승인 과정에선 전달받지 못한다는 것이다. DB에 결제 관련 정보를 저장해야 되는데 관련 정보는 결제 준비 과정에, 저장해야 되는 시점은 결제 승인 과정이다.

결국 결제 준비 과정에서의 요청값을 결제 승인 과정에서 사용해야 되는 상황이다. 이는 무상태 프로토콜을 위배하는 상황이다.

해결책 1

프론트분들께 이전 API에서 요청값을 다른 API에서 재사용할 수 있냐고 여쭤봤더니 가능하다고 하셨다. 그래서 아래와같이 API 명세서를 만들었다.

결제 준비 응답값

{
    "tid": "T6095a564510678ba960",
    "redirectUrl": "https://online-pay.kakao.com/mockup/v1/1dafd48c55efed61312ac69514a3e7748b84e98e5d841433124aa5f43d889072/info",
    "orderDetails" : [
        {
            "id" : 1,
            "quantity" : 2,
            "totalAmount" : 20000,
            "optionDetailIds" = []
        },
        {
            "id" : 2,
            "quantity" : 1,
            "totalAmount" : 15000,
            "optionDetailIds" = [1, 2]
        }
    ],
    "orderNumber": "61c87107-a"
}

결제 승인 요청값

{
    "tid": "T6095a564510678ba960",
    "pgToken": "XXX",
    "orderDetails" : [
        {
            "id" : 1,
            "quantity" : 2,
            "totalAmount" : 20000,
            "optionDetailIds" = []
        },
        {
            "id" : 2,
            "quantity" : 1,
            "totalAmount" : 15000,
            "optionDetailIds" = [1, 2]
        }
    ],
    "orderNumber": "61c87107-a"
}

그러나, 프론트엔드에 의존하는 플로우가 되버렸다. 백엔드 입장에선 프론트가 데이터를 잘 주겠지 라는 마인드로 무책임하게 구현해버린 꼴이다. 돈과 직결되는 결제인 만큼 더 철저하게 구현을 해야되므로 다른 방법을 고민했다.

해결책 2

결제 준비에서 요청값을 Redis에 캐싱하는 방법이다. 결제 준비 과정에서 결제 정보를 hash로 캐싱 후 key값을 프론트에게 반환했다.

결제 준비 응답

{
    "tid": "T6095a564510678ba960",
    "redirectUrl": "https://online-pay.kakao.com/mockup/v1/1dafd48c55efed61312ac69514a3e7748b84e98e5d841433124aa5f43d889072/info",
    "orderNumber": "61c87107-a"
}

결제 준비 요청

{
    "orderNumber": "a61282ee-cb64-4844-97c2-957a9e96c826",
    "pgToken": "56e4ef8f44c10c3fca22",
    "tid": "T6131ace4510678bd453"
}

결제 준비 요청시 Redis에 orderNumber에 해당하는 값(결제 정보)를 가져와 DB에 반영한다. 이렇게 한다면 데이터 위/변조 가능성이 없어져 더 안정적이라 생각한다.

참고
카카오페이 결제 준비 요청 API에 item_code 라는 필드가 있다. 이 값은 이후 카카오페이 결제 승인 응답에 그대로 포함된다. 때문에 결제 관련 정보를 해당 필드에 저장하는 방식도 있다.

그러나, item_code는 길이가 100으로 제한된다. 현 프로젝트에선 여러 상품을 결제할 수 있으며 상품마다 옵션도 포함되므로 100자로 결제 정보를 저장하기엔 부족하다.

0개의 댓글