Computer Engineering/Server

Springboot RESTAPI 아임포트 가상 결제

말하는호구마 2021. 2. 25. 01:46

Springboot로 REST API를 구현하는 중 가상결제 기능을 구현하고자 했다. 

아임포트를 사용하여 카카오페이 결제 과정을 거쳐 실제 구매처럼 재연했다. 

 

정말 정말 쉬운 과정이지만 RestAPI 구현중임을 간과해서,,,조금 오래 걸렸다.

 

 

이를 진행하는 프로젝트의 클라이언트는 iOS로 구현했다. 

실제 결제화면을 iOS에서 확인하면  좋았겠지만 팀의 사정상 그렇게 하지 못하고 웹에 내가 직접 구현했다.

즉, PC웹 환경에서 결제를 진행할 것이다. 

 

 

일단 아임포트 홈페이지의 대쉬보드에 들어가고 회원가입,로그인을 해야한다!!!!

admin.iamport.kr/users/login

 

로그인 - 아임포트 관리자

가입하신 Email주소로 비밀번호 변경 링크를 발송합니다.

admin.iamport.kr

 

 


 

로그인을 하면 아래와 같은 페이지를 볼 수 있다

[시스템 설정] -> [PG설정(일반결제 및 정기결제)] -> [PG사: 카카오페이 선택]

 

테스트모드 ON을 하면 일반결제용 사이트코드와 정기결제용 사이트코드가 나타난다.

나는 일반 결제를 하기 위해 일반결제용 사이트코드를 가맹점코드(CID)에 입력해준다.

이 과정이 매우매우매우 중요하다.

 

 

 


 

다음은 [내정보]에 들어가준다.

여기에 나오는 가맹점 식별코드를 사용해야하니 복사를 해두자!!!!!!!

 

 

 


 

 

이제 클라이언트 구현을 해보자

나는 html파일을 사용하여 정적으로 구현하였다.

html>
<head>
    <meta charset="UTF-8">
    <title>Insert title here</title>
    <script type="text/javascript" src="https://code.jquery.com/jquery-1.12.4.min.js" ></script>
    <script type="text/javascript" src="https://cdn.iamport.kr/js/iamport.payment-1.1.5.js"></script>

</head>
<body>

<script>

    $(function(){
    
    	//@@@@@@ 1번 @@@@@@@
        var IMP = window.IMP; // 생략가능
        IMP.init('가맹점 식별코드'); //가맹점 식별코드 삽입
        var msg;
        
        //@@@@@@@@ 2번 @@@@@@@@
        //url에서 parameter 가져오기 --> price값 알기 위해서
        var getParam = function(key){
            var _parammap = {};
            document.location.search.replace(/\??(?:([^=]+)=([^&]*)&?)/g, function () {
                function decode(s) {
                    return decodeURIComponent(s.split("+").join(" "));
                }

                _parammap[decode(arguments[1])] = decode(arguments[2]);
            });

            return _parammap[key];
        };
        
		//@@@@@@ 3번 @@@@@@@
            IMP.request_pay({
            pg : 'kakaopay',
            pay_method : 'card',
            merchant_uid : 'merchant_' + new Date().getTime(),
            name : 'vivipayment',
            amount : getParam("price"),
            buyer_email : '이메일 넣기',
            buyer_name : '이름 넣기',
            buyer_tel : '번호 넣기',
            buyer_addr : '주소 넣기',
            buyer_postcode : '123-456',
           // m_redirect_url : '결제 완료후 이동할 페이지'
        }, function(rsp) {
            if ( rsp.success ) {
                //[1] 서버단에서 결제정보 조회를 위해 jQuery ajax로 imp_uid 전달하기
                jQuery.ajax({
                    url: "/payments/complete", //cross-domain error가 발생하지 않도록 주의해주세요
                    type: 'POST',
                    dataType: 'json',
                    contentType: 'application/json',
                    data: JSON.stringify({
                        uid : rsp.imp_uid,
                        price: rsp.paid_amount
                        //기타 필요한 데이터가 있으면 추가 전달
                    })
                }).done(function(data) {
                    //[2] 서버에서 REST API로 결제정보확인 및 서비스루틴이 정상적인 경우
                    if ( everythings_fine ) {
                        msg = '결제가 완료되었습니다.';
                        msg += '\n고유ID : ' + rsp.imp_uid;
                        msg += '\n상점 거래ID : ' + rsp.merchant_uid;
                        msg += '\n결제 금액 : ' + rsp.paid_amount;
                        msg += '카드 승인번호 : ' + rsp.apply_num;

                        alert(msg);
                    } else {
                        //[3] 아직 제대로 결제가 되지 않았습니다.
                        //[4] 결제된 금액이 요청한 금액과 달라 결제를 자동취소처리하였습니다.
                    }
                });
                //성공시 이동할 페이지
                location.href='<%=request.getContextPath()%>/order/paySuccess?msg='+msg;
            } else {
                msg = '결제에 실패하였습니다.';
                msg += '에러내용 : ' + rsp.error_msg;
                //실패시 이동할 페이지
                location.href="<%=request.getContextPath()%>/order/payFail";
                alert(msg);
            }
        });

    });
</script>

</body>
</html>

 

웹은 잘 안하기 때문에 오랜만에 javascript를 했다.

 

 

잠깐 코드 설명을 하자면!!!!!! (@@@@로 나뉜부분을 주목하자)

1번 -->

아임포트에서의 내 정보를 입력해야한다.

아까 기억해뒀던 가맹점 식별코드를 입력해야한다.

가맹점코드(CID)가 아니라 내정보에서의 가맹점 식별코드를 입력해야한다!!!!(헷갈리지 않도록 주의)

 

 

2번 -->

html url부터 parameter을 가져와 결제하고자 하는 금액 알려준다.

클라이언트가 이전부터 차례대로 구현이 되어있다면 다른 방법이 있겠지만 이 페이지만 달랑 만든 것이기 때문에...

정적인 html파일에 결제하고자 하는 값을 알려줘야했다. 

 

그리하여 url에 parameter형식으로 넘겨줬고 이를 parsing하는 과정을 추가했다.

결론적으로 이 클라이언트에 접근할 수 있는 url은 다음과 같다. 

여기서 kakaoPayment.html에 코드를 작성했기 때문에

도메인 주소를 통해 kakaoPayment.html을 호출해주고 파라미터로 price를 준다. 

 

 

여기서 조금 애먹었다.

 

 

현재 서버는 REST API 구현을 위한 것이다.

Springboot 를 사용하고 있기 때문에 다 @RestController어노테이션 설정을 통해 Controller가 구현되어있다. 

@RestController와 @Controller의 리턴 방식은 다르다.

이를 숙지하고 url로 접근해야 html파일로 정상적인 접근이 가능하다. 

 

 

 

단순 정적 페이지에 접근하려면 /src/main/resources/static 디렉토리에 html파일을 생성해야한다.

(static 디렉토리도 만들어야한다)

 

 

3번-->

결제를 하는 메인 로직이다.

ajax를 통해 서버로 결제 결과를 보낸다. (만약 서버로 보낼 정보가 딱히 없다면 안써도 된다)

 

 

url은 내가 결과를 보낼 api이다.

주의해야할 점은 AJAX의 CORS(cross-domain)오류에 주의해야한다.

ajax는 처음해봐서 조금 해맸다. 

CORS를 방지하려면

현재 클라이언트(html파일)의 도메인과 서버의 도메인이 같아야한다.

 

만약 클라이언트 접근 url이 http://abcdef/kakaoPayment.html?price=2000이라면

서버 rest api url이 http://abcdef/payments/complete 여야한다.

CORS를 해결하는 방법이 별도로 존재하는 것 같다.

여러 검색을 해본 결과 개발자들이 엄청 싫어하는 오류인 것 같닼ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ

지긋지긋하다는 글을 제법 많이 봤다. 

 

그래서 나는 이를 맞춰주려고 서버 코드에 클라이언트 페이지를 넣었던 것이다(/src/main/resources/static)

 

 

내가 직면한 오류와 해결 방법은 이러하다. 

100%맞는 것을 확신할 수 없지만 이후에 ajax에 대해 더 공부를 해봐야 제대로 알 것 같다.👩🏻‍💻

 

 

 

 


 

이후에 Springboot api(/payments/complete)에 들어오는 Json을 parsing해서 db에 저장까지 완료하였다.

어려운 문제가 아닌 사소하고 쉬운 오류들을 직면했기에 마음이 아팠다😭

하지만 해결하고 DB저장이 잘되는 것을 확인하니 짜릿하다 ㅎㅎㅎ

 

대단한 기능같지만 구현은 쉬운 카카오 페이 결제 연동 과정이었다. 

아임포트측에서 아주 자세한 레퍼런스들을 많이 제공한다.

이를 잘 보는것도 중요하다.