Devme/Project

JAVA / SPRING 프로젝트 삼삼하개(4) 간편결제 API를 이용하여 결제/환불 기능 구현

 

팀프로젝트가 끝나고 되돌아보는 시간 (4) 간편결제 API를 이용하여  결제/환불 기능 구현

 

iamport api를 이용하여 결제시스템을 구현했습니다. 

문서도 예제도 크게 변형할 필요가 없어 프로젝트 사용하면서 가장 활용하기 쉬웠던 api인 것 같습니다.

 

 

이용권 현황 및 구매/환불

아임포트 api 사용법

 

1. 먼저 아임포트 사이트에 가입을 합니다. www.iamport.kr/

2. 회원가입후 시스템설정으로 이동합니다.

3. PG설정(일반결제 및 정기결제) 를 클릭합니다.

4. PG사 카카오페이 선택 후 테스트모드 ON  합니다.

5.전체저장을 누르면 가맹점식별코드(CID)가 발급됩니다. 

6. 아임포트 라이브러리를 추가합니다.

 

import (iamport api)  document🚀

<!-- 제이쿼리 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.min.js" integrity="sha512-bLT0Qm9VnAYZDflyKcBaQ2gg0hSYNQrJ8RilYldYQ1FxQYoCLtUjuuRuZo+fjqhx/qtq/1itJ0C2ejDxltZVFg==" crossorigin="anonymous" type="text/javascript"></script>
<!-- 아임포트 -->
<script src ="https://cdn.iamport.kr/js/iamport.payment-1.1.5.js" type="text/javascript"></script>

 

cominfo_pay.jsp (구매/환불)

<h5>이용권</h5>		
	<table class = pay border="1">
		<tr><td rowspan="2" class="pay_detail">이용권</td><td>월 기본 제공 </td><td><%=bvo.getFree_coupon() %>/5</td></tr>
		<tr><td>남은 구매 이용권 횟수</td><td id = "pay_coupon"><%=bvo.getPay_coupon() %> </td></tr>
	</table>
		
	<div class ="btns">
		<input type="button" id="check1" value="구매">
		<input type="button" id="check2" value="환불">
	</div>

 

button(check1, check2) 클릭 이벤트로 아임포트 api를 이용합니다.

 

<!-- pay js -->
<script type="text/javascript">
$(document).ready(function(){
	   var pay = <%=bvo.getPay_coupon() %>;
	   console.log(pay);
	   	  
		var IMP = window.IMP;
		var code = "imp********"; //가맹점 식별코드
		IMP.init(code);
		
		$("#check1").click(function(e){
			//결제요청
			IMP.request_pay({
				//name과 amout만있어도 결제 진행가능
				//pg : 'kakao', //pg사 선택 (kakao, kakaopay 둘다 가능)
				pay_method: 'card',
				merchant_uid : 'merchant_' + new Date().getTime(),
				name : '결제테스트', // 상품명
				amount : 1,
				buyer_email : '<%=email%>',
				buyer_name : '<%=mvo.getName()%>',
				buyer_tel : '<%= mvo.getPhone()%>',  //필수항목
				//결제완료후 이동할 페이지 kko나 kkopay는 생략 가능
				//m_redirect_url : 'https://localhost:8080/payments/complete'
			}, function(rsp){
				if(rsp.success){//결제 성공시
					var msg = '결제가 완료되었습니다';
					var result = {
					"imp_uid" : rsp.imp_uid,
					"merchant_uid" : rsp.merchant_uid,
					"biz_email" : '<%=email%>',
					"pay_date" : new Date().getTime(),
					"amount" : rsp.paid_amount,
					"card_no" : rsp.apply_num,
					"refund" : 'payed'
					}
					console.log("결제성공 " + msg);
					$.ajax({
						url : '/samsam/insertPayCoupon.do', 
				        type :'POST',
				        data : JSON.stringify(result,
				        		['imp_uid', 'merchant_uid', 'biz_email', 
				        			'pay_date', 'amount', 'card_no', 'refund']),
				        contentType:'application/json;charset=utf-8',
				        dataType: 'json', //서버에서 보내줄 데이터 타입
				        success: function(res){
				        			        	
				          if(res == 1){
							 console.log("추가성공");	
							 pay += 5;
							 $('#pay_coupon').html(pay);			           
				          }else{
				             console.log("Insert Fail!!!");
				          }
				        },
				        error:function(){
				          console.log("Insert ajax 통신 실패!!!");
				        }
					}) //ajax
					
				}
				else{//결제 실패시
					var msg = '결제에 실패했습니다';
					msg += '에러 : ' + rsp.error_msg
				}
				console.log(msg);
			});//pay
		}); //check1 클릭 이벤트
		 
		$("#check2").click(function(e){
		      console.log("남은이용권"+$('#pay_coupon').text());
		      if($('#pay_coupon').text() >= 5){
			$.ajax({
					url: "/samsam/coupon_cancel.do",
					type:"post",
					//datatype:"json",
					contentType : 'application/x-www-form-urlencoded; charset = utf-8',
					data : {
						"biz_email" : '<%=email%>' // 주문번호
						//"cancle_request_amount" : 2000, //환불금액
						//"reason": "테스트 결제 환불", //환불사유
						//"refund_holder": "홍길동", //[가상계좌 환불시 필수입력] 환불 가상계좌 예금주
						//"refund_bank":"88", //[가상계좌 환불시 필수입력] 환불 가상계좌 은행코드(ex Kg이니시스의 경우 신한은행 88)
						//"refund_account": "56211105948400" // [가상계좌 환불시 필수입력] 환불 가상계좌 번호
					}
				}).done(function(result){ //환불 성공
					 pay -= 5;
					 $('#pay_coupon').html(pay);	
					console.log("환불 성공 : "+ result);
				}).fail(function(error){
					console.log("환불 실패 : "+ error);
				});//ajax
			} else{
				console.log("환불 실패 : 남은 결제권 환불 불가");
			}
		}); //check2 클릭
	}); //doc.ready

 

ImportController (구매/환불)

	@RequestMapping(value = "/insertPayCoupon.do")
	@ResponseBody
	public int pay(@RequestBody Payed_listVO pvo) {
		System.out.println("pvo.getMerchant_uid : " + pvo.getMerchant_uid());
		int res = paySV.insert_pay(pvo);
		if(res == 1) {
			Biz_memberVO bvo = memberSV.selectBizMember(pvo.getBiz_email());
			bvo.setPay_coupon(bvo.getPay_coupon()+5);
			System.out.println("paycoupon: " + bvo.getPay_coupon());
			res = paySV.updateBiz_pay(bvo);
			if(res == 1)
				System.out.println("biz_member pay coupon insert complete");
		}
		
		return res;
	}
	@RequestMapping(value = "/coupon_cancel.do")
	@ResponseBody
	public String cancel(@RequestParam(value= "biz_email") String biz_email) {
		System.out.println("biz_email = " + biz_email);
		Payed_listVO pvo = paySV.recentlyPay(biz_email);
					
		PaymentCheck obj = new PaymentCheck();
		String token = obj.getImportToken();
		System.out.println("merchant_uid :" + pvo.getMerchant_uid());
		int res = obj.cancelPayment(token, pvo.getMerchant_uid());
		
		if(res == 1) {
			Biz_memberVO bvo = memberSV.selectBizMember(pvo.getBiz_email());
			bvo.setPay_coupon(bvo.getPay_coupon()-5);
			System.out.println("paycoupon: " + bvo.getPay_coupon());
			res = paySV.updateBiz_refund(bvo);
			if(res ==1) {
				res = paySV.refund_pay(pvo.getMerchant_uid());
				if(res ==1) { return "Success";}
			}else {	return "biz_refund Failure"; }
			return "thanks";
		}
		else 
			return "anyway Failure"; 
	}

문서 내용에 따르면 init은 가장 먼저하는 것이 좋다하여 $(document).ready(function(){} 의 맨 윗부분에 기재했습니다.

 

결제 성공시 결제리스트(테이블)에 데이터를 추가하기위해 key-value 형태의 데이터(변수명 result)를 만들고 JSON.stringify로 JSON 형식으로 변환하여 ajax로 전달합니다.

데이터가 적용완료되면(결제리스트 추가, 사업자 테이블의 이용권 갯수 업데이트) 다시 데이터를 조회하지 않고 현재페이지의 데이터를 변경합니다. (pay += 5; $('#pay_coupon').html(pay); )

 

환불은 이용권 구매시 이용권이 +5 되기 때문에 구매한 이용권의 남은 갯수가 5개 이상이어야만 가능하도록 구현하였습니다.

iamport 환불시 merchant_uid가 필요합니다, 구매를 한번 이상 했을수도 있어 구매시 생성된 merchant_uid가 아닌 환불하지 않은

최신 merchant_uid를 조회하여 환불 진행시 사용했습니다.

 

Mapper (Mybatis)

<!-- 이용권 결제내역 추가 -->
<insert id ="insert_pay" parameterType="Payed_listVO">
 	insert into payed_list
 	values(#{imp_uid},#{merchant_uid}, #{biz_email}, #{pay_date}, #{amount}, #{card_no}, #{refund})
</insert>

<!-- 이용권 결제내역 조회 -->
<select id="recentlyPay" parameterType="String" resultType="Payed_listVO">
	select imp_uid, merchant_uid, biz_email, pay_date, amount, card_no, refund from 
	(select rownum, imp_uid, merchant_uid, biz_email, pay_date, amount, card_no, refund from 
	(select * from payed_list where biz_email = #{biz_email})where refund ='payed' order by pay_date)
	where rownum = 1
</select>

<!-- 이용권 결제 사업자에 반영 -->
<update id="updateBiz_pay" parameterType="Biz_MemberVO">
	update biz_member set pay_coupon = #{pay_coupon} where biz_email = #{biz_email}
</update>

<!-- 이용권 환불 사업자에 반영 -->
<update id="updateBiz_refund" parameterType="Biz_MemberVO">
	update biz_member set pay_coupon = #{pay_coupon} where biz_email = #{biz_email}
</update>

<!-- 이용권 결제내역 환불 -->
<update id="refund_pay" parameterType="String">
	update payed_list set refund = 'refund' where merchant_uid = #{merchant_uid}
</update>