카카오톡 선물하기 클론 + 펀딩 프로젝트에서 결제 기능을 담당하게 되었다. 결제 수단은 카카오페이다.
orderNumber
를 백엔드에 전달한다.결제 과정은 크게 2가지로 나뉜다.
실제로 주문 페이지에서 사용자가 "결제하기" 버튼을 누르면 1 ~ 5가 진행된다. 사용자가 결제를 완료하면 나머지 과정이 진행된다.
문제는 결제 정보(상품, 수량, 가격)등을 결제 준비 과정에서 전달받고 결제 승인 과정에선 전달받지 못한다는 것이다. DB에 결제 관련 정보를 저장해야 되는데 관련 정보는 결제 준비 과정에, 저장해야 되는 시점은 결제 승인 과정이다.
결국 결제 준비 과정에서의 요청값을 결제 승인 과정에서 사용해야 되는 상황이다. 이는 무상태 프로토콜을 위배하는 상황이다.
프론트분들께 이전 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"
}
그러나, 프론트엔드에 의존하는 플로우가 되버렸다. 백엔드 입장에선 프론트가 데이터를 잘 주겠지 라는 마인드로 무책임하게 구현해버린 꼴이다. 돈과 직결되는 결제인 만큼 더 철저하게 구현을 해야되므로 다른 방법을 고민했다.
결제 준비에서 요청값을 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자로 결제 정보를 저장하기엔 부족하다.