hmk run dev

이니시스 & 아임포트 결제 연동 본문

기타

이니시스 & 아임포트 결제 연동

hmk run dev 2021. 11. 11. 17:07

 

결제 시스템에 대해서 공부해보고자 이니시스 결제 샘플 코드를 받아

결제 테스트를 진행해 보았다!

 

아래 링크에서 표준 결제 & 에스크로 결제 샘플코드 다운로드 가능

https://manual.inicis.com/stdpay/

 

KG INICIS MANUAL

 header("Content-Type: text/html; charset=utf-8");  require_once('../libs/INIStdPayUtil.php'); require_once('../libs/HttpClient.php'); require_once('../libs/sha256.inc.php'); require_once('../libs/json_lib.php');  $util = new INIStdPayUtil();

manual.inicis.com

 

build path에서 이니시스에서 제공하는 jar파일 넣어주고

 

나는 jsp로 테스트 해보았다...! 아래는 소스코드 톰캣 넣고 runserver 해주자

https://github.com/hmk1022/inicis_module

 

GitHub - hmk1022/inicis_module

Contribute to hmk1022/inicis_module development by creating an account on GitHub.

github.com

 

처음 샘플코드를 받고 수정할 부분이 몇군데 있는데

로컬 주소를 사용할 경우 아래처럼 주소를 변경해주면 따로 설정할 건 없는것 같다.

String siteDomain = "http://localhost:8099/inicis_test";

 

결제를 위해 필요한 코드들을 간단하게 살펴보자...

 

mid - 가맹점 식별아이디

signKey - 가맹점 식별키

timestamp - 결제시간

oid - 결제 id 로 // mid + 결제시간으로 된 결제 식별자

 

//*** 위변조 방지체크를 signature 생성 ***

oid, price, timestamp 3개의 키와 값을

key=value 형식으로 하여 '&'로 연결한 하여 SHA-256 Hash로 생성 된값

ex) oid=INIpayTest_1432813606995&price=819000&timestamp=2012-02-01 09:19:04.004
				

key기준 알파벳 정렬

timestamp는 반드시 signature생성에 사용한 timestamp 값을 timestamp input에 그대로 사용하여야함


//############################################
// 1.전문 필드 값 설정(***가맹점 개발수정***)
//############################################

// 여기에 설정된 값은 Form 필드에 동일한 값으로 설정
String mid	= "INIpayTest";		// 가맹점 ID(가맹점 수정후 고정)				
	
//인증						  ""
String signKey = "SU5JTElURV9UUklQTEVERVNfS0VZU1RS";	// 가맹점에 제공된 웹 표준 사인키(가맹점 수정후 고정)
String timestamp  = SignatureUtil.getTimestamp();			// util에 의해서 자동생성

String oid = mid+"_"+SignatureUtil.getTimestamp();	// 가맹점 주문번호(가맹점에서 직접 설정)
String price = "1000";													// 상품가격(특수기호 제외, 가맹점에서 직접 설정)

String cardNoInterestQuota	= "11-2:3:,34-5:12,14-6:12:24,12-12:36,06-9:12,01-3:4";		// 카드 무이자 여부 설정(가맹점에서 직접 설정)
String cardQuotaBase		= "2:3:4:5:6:11:12:24:36";		// 가맹점에서 사용할 할부 개월수 설정

//###############################################
// 2. 가맹점 확인을 위한 signKey를 해시값으로 변경 (SHA-256방식 사용)
//###############################################
String mKey = SignatureUtil.hash(signKey, "SHA-256");
	
//###############################################
// 2.signature 생성
//###############################################
Map<String, String> signParam = new HashMap<String, String>();

signParam.put("oid", oid); 					// 필수
signParam.put("price", price);				// 필수
signParam.put("timestamp", timestamp);		// 필수

// signature 데이터 생성 (모듈에서 자동으로 signParam을 알파벳 순으로 정렬후 NVP 방식으로 나열해 hash)
String signature = SignatureUtil.makeSignature(signParam);
	
System.out.println("signParam"+signParam);
/* 기타 */
String siteDomain = "http://localhost:8099/inicis_test"; //가맹점 도메인 입력 // 로컬 테스트시 
// 페이지 URL에서 고정된 부분을 적는다. 
// Ex) returnURL이 http://localhost:8080INIpayStdSample/INIStdPayReturn.jsp 라면
// http://localhost:8080/INIpayStdSample 까지만 기입한다.

 

 

결제요청 후 Response값

- 따로 db에 저장할 값들을 골라 저장한다.

RESPONSE DATA: {
  "buyerTel": "010-1234-5678",
  "parentEmail": "",
  "applDate": "20211111",
  "buyerEmail": "test@inicis.com",
  "p_Sub": "",
  "resultCode": "0000",
  "mid": "INIpayTest",
  "VACT_Date": "20211211",
  "authSignature": "9e63bf56fb70ac332bf3a283d089f636978aea61a1803b7d97546233abe8b55d", // 식별키 암호화?
  "tid": "StdpayVBNKINIpayTest20211111132429617655", // 결제키 + 타임스탬프
  "EventCode": "",
  "VACT_Name": "(주)케이지이니시",
  "VACT_InputName": "김형민",
  "goodName": "테스트",
  "VACT_Time": "235959",
  "TotPrice": "1000",
  "payMethod": "VBank",
  "VACT_BankCode": "11",
  "MOID": "INIpayTest_1636604620704",
  "vactBankName": "농협중앙회",
  "currency": "WON",
  "applTime": "132430",
  "goodsName": "테스트",
  "FlgNotiSendChk": "",
  "buyerName": "홍길동",
  "p_SubCnt": "",
  "resultMsg": "정상처리되었습니다.",
  "custEmail": "hm.kim@iworkerman.com",
  "VACT_Num": "79016258269717", // 가상계좌생성
  "payDevice": "PC"
}

 

결제 플로우

pg(이니시스) 통해 결제 요청 > 가상계좌일 경우 vbank저장 > 아닐 경우 결제진행 >  성공 시 이니시스 필요response 값 선별해 식별key 등과 함께 w_payment에 저장

 

워커맨 결제DB(w_payment)

pay_no // primary key
ord_no // 결제 고유 키? (이니시스 제공?) 
admin_no
estimate_no
approval_no
work_no
member_no
pay_type
total_pay_price
pg_code
pg_err_code
pg_err_msg
imp_uid
paid_amount
paid_date
stat
card_name
card_quota
customer_uid
agency_code
payer_name
sales_confirm_date
sales_confirm_admin_no
create_date
update_date

 

워커맨 가상계좌DB(w_vbank)

vbank_no // 가상계좌 테이블 primary key
pay_no // 결제 foreign key
result_code // 결제 결과
result_msg  // 결과
tid // 이니시스 결제 식별id
auth_date // 인증날짜?
vacct // 가상계좌번호
vacct_name // 계좌이름
vacct_bank_code // 가상계좌 은행코드
valid_date
income_name // 입금자
price // 가격
use_yn // 사용여부
create_date
update_date

 

 

 

 

[API] 아임포트 KG이니시스 가상 결제 API

아임포트 KG이니시스 가상 결제 API

velog.io

 

String mid와 signKey를 실제 회사의 가맹점아이디 & sign키로 변경 그리고 

script태그 안에 src를 두번째줄 처럼 변경후 실제 결제 테스트를 진행할 수도 있다.

<script language="javascript" type="text/javascript" src="https://stdpay.inicis.com/stgstdjs/INIStdPay.js" charset="UTF-8"></script>

<script language="javascript" type="text/javascript" src="https://stdpay.inicis.com/stdjs/INIStdPay.js" charset="UTF-8"></script>

 

 

 

 

 

실제 결제코드 : 아임포트 + 이니시스(billPayMethodForm.jsp) 현재는 이니시스만 사용하지만!

 

아임포트 script

 

아임포트 참고 

https://velog.io/@6441kjy/API-%EC%95%84%EC%9E%84%ED%8F%AC%ED%8A%B8-KG%EC%9D%B4%EB%8B%88%EC%8B%9C%EC%8A%A4-%EA%B0%80%EC%83%81-%EA%B2%B0%EC%A0%9C-API

 

[API] 아임포트 KG이니시스 가상 결제 API

아임포트 KG이니시스 가상 결제 API

velog.io

<script type="text/javascript" src="https://cdn.iamport.kr/js/iamport.payment-1.1.5.js"></script>
function pay(){
	var map={
			bill_no : '${billInfo.bill_no}'
	}
	
	$.ajax({
		url : "/checkBillPayStat", // 현재 결제상태 체크?
		method : 'POST',
		dataType: 'text',
		data : map,
		success : function(str) {
			if(str == "99"){
				alert("실패하였습니다");
		        return;
			}else if(str == "00"){
		
				var domain='<spring:eval expression="@systemProp['work.domain']" />'; // 워커맨 결제도메인 application.properties파일에서 확인가능
				if($('#pay_type').val() == "01"){
					IMP.request_pay({
					    pg : 'inicis', // version 1.1.0부터 지원.
					    pay_method : 'card',
					    merchant_uid : 'work_' + new Date().getTime()+"_"+ ${workInfo.work_no },
					    name : '주문명:청구서',
					    //amount : ${billInfo.final_total_amount},
					    app_scheme : 'workerman',
					    amount : <fmt:parseNumber  value="${billInfo.final_total_amount}" integerOnly="true" />,
					    buyer_email : '',
					    buyer_name : '${workInfo.req_name }',
					    buyer_tel : $('#buyer_tel').val(),
					    buyer_addr : '${workInfo.work_addr1 } ${workInfo.work_addr2 }',
					    buyer_postcode : '${workInfo.work_zip }',
					    m_redirect_url : domain+'/payres?work_no=${workInfo.work_no }&bill_no=${billInfo.bill_no}&paid_amount=${billInfo.final_total_amount}&smsYn=${smsYn}'
					}, function(rsp) {
					    if ( rsp.success ) {
					        var msg = '결제가 완료되었습니다.';
					       // msg += '고유ID : ' + rsp.imp_uid;
					       // msg += '상점 거래ID : ' + rsp.merchant_uid;
					       // msg += '결제 금액 : ' + rsp.paid_amount;
					       // msg += '카드 승인번호 : ' + rsp.apply_num;
					     
					       $('#imp_success').val(true);
					       $('#imp_uid').val(rsp.imp_uid);
					       $('#paid_amount').val(rsp.paid_amount);
					       $('#card_name').val(rsp.card_name);
					       document.payMethodForm.action = "/payres"; // 결제성공시 /payres 컨트롤러에 payMethodForm을 파라미터로 요청을 보냄 
					       document.payMethodForm.submit();
					       alert(msg);
					    } else {
			
					        var msg = '결제에 실패하였습니다.';
					        msg += '에러내용 : ' + rsp.error_msg;
					        alert(msg);
					        
					    }
					   
					});
				}else if($('#pay_type').val() == ""){
					alert("결제수단을 선택해주세요.");
					return;
				}else{
					if($('#buyer_name').val() == ""){
						alert("입금자명을 입력해주세요");
						return;
					}
					
					$('#pay_name').val($('#buyer_name').val());
					
					var data = $("#payMethodForm").serialize();
			
					
					$.ajax({
						url : "/updateBillBank",
						method : 'POST',
						dataType: 'text',
						data : data,
						success : function(str) {
						  			
						  if(str =="00"){
							 //alert("제출 되었습니다");
							 document.payMethodForm.action = "/bankFinish";
							 document.payMethodForm.submit();
							 //location.href="/bankFinish?amount="+$('#pay_amount').val()+"&buyer_name="+$('#buyer_name').val();
						  }else{
							  alert('실패하였습니다');
							  return false;
						  }
			
						},
						error : function() {
							alert('실패하였습니다');
							return false;
						}
					});	
				}
			}else if(str == "10"){
				alert("이미 결제완료하셨습니다");
				return;
			}else if(str == "20"){
				alert("입금대기중입니다");
				return;
			}
		},
		error : function() {
			alert("실패하였습니다");
	        return;
		}
	});
	
}

 

결제 controller

@RequestMapping(value = "/payres")
	public String payres(
			@RequestParam(required = true) Long work_no, 
			@RequestParam(required = false) Long estimate_no, 
			@RequestParam(required = false) Long bill_no, 
			@RequestParam(required = false) String imp_uid, 
			@RequestParam(required = false) String merchant_uid,
			@RequestParam(required = false) String paid_amount, 
			@RequestParam(required = false) String apply_num,
			@RequestParam(required = false) boolean imp_success,
			@RequestParam(required = false) String error_msg,
			@RequestParam(required = false) String smsYn,
			@RequestParam(required = false) String easy_pay_yn,
			@RequestParam(required = false) String req_work_date,
			@RequestParam(required = false) String req_work_date_type,
			Map<String, Object> map, 
			Model model, 
			HttpServletRequest request, 
			Locale locale) throws Exception {
		WorkVO workInfo = null;
		HashMap<String, Object> resultMap = null;
		Gson gson = new Gson();
		Payment iam  = null;
		
		System.out.println("smsYn:::"+smsYn);
		System.out.println("imp_success:::"+imp_success);
		
		//아임포트 결제 실패시
		if(!imp_success) {
			String[] errCode = StringUtil.split(error_msg, ":");
			
			//견적서 선금 결제면 견적서 결제수단 선택 페이지로
			if(estimate_no !=null) {
				return "redirect:/estimatePayMethodForm?work_no="+work_no+"&estimate_no="+estimate_no+"&error_code="+errCode[0]+"&req_work_date="+req_work_date+"&req_work_date_type="+req_work_date_type;
			}
			
			//청구서 결제시
			if(bill_no !=null) {

				return "redirect:/billPayMethodForm?work_no="+work_no+"&final_total_amount="+paid_amount+"&bill_no="+bill_no+"&smsYn="+smsYn+"&error_code="+errCode[0];
			}
		//아임포트 결제 성공시
		}else {
			//주문번호 생성
			
			WorkVO wvo = new WorkVO();
			wvo.setWork_no(work_no);
			workInfo = workService.selectWorkInfo(wvo);	
			String ordno = System.currentTimeMillis()+""+work_no+StringUtil.replaceNull(workInfo.getMember_no()+"","");
			
			
			resultMap = paymentService.processPay(imp_uid, merchant_uid, imp_success);
			String jsonString = gson.toJson(resultMap);
			System.out.println("json:" + jsonString);
			iam = (Payment) resultMap.get("payment");
			
			int chkCnt = paymentService.chckOrderCnt(estimate_no,bill_no,work_no);
			
			System.out.println("chkCnt:" + chkCnt);
			if(chkCnt == 0) {
				
				System.out.println("ordno:::"+ordno);
				System.out.println("work_no:::"+work_no);
				System.out.println("estimate_no:::"+estimate_no);
				System.out.println("bill_no:::"+bill_no);
				System.out.println("paid_amount:::"+paid_amount);
				System.out.println("Double.parseDouble(paid_amount):::"+Double.parseDouble(paid_amount));
				System.out.println("imp_uid:::"+imp_uid);
				System.out.println("iam.getCardName():::"+iam.getCardName());
				System.out.println("iam.getCardQuota():::"+iam.getCardQuota());
				System.out.println("workInfo.getMember_no():::"+workInfo.getMember_no());
				System.out.println("workInfo.getWork_id():::"+workInfo.getWork_id());
				System.out.println("req_work_date_:::"+req_work_date);
				System.out.println("req_work_date_type:::"+req_work_date_type);
				
				
				
				paymentService.paymentSuccess(ordno,work_no, estimate_no, bill_no, Double.parseDouble(paid_amount),
						imp_uid,iam.getCardName(),iam.getCardQuota(),workInfo.getMember_no(),workInfo.getWork_id(),easy_pay_yn,
						workInfo.getWork_confirm_yn(),req_work_date,req_work_date_type);
			}
			
			map.put("workInfo", workInfo);
			map.put("estimate_no", estimate_no);
			
			map.put("paid_amount", paid_amount);
			map.put("iam", iam);
			map.put("smsYn", smsYn);
		}
		
		
		
		return "/payment/paymentCardFinish";

 

결제 성공시 service

public void paymentSuccess(String ordno,Long work_no,Long estimate_no,Long bill_no,Double paid_amount,
			String imp_uid,String card_name,int card_quota,Long member_no,String work_id,String easy_pay_yn,
			String work_confirm_yn,String req_work_date,String req_work_date_type) {
	
		MsgTemplateVO 	tmplInfo 			= null;	
		
		
		System.out.println("----------------w_order 테이블 인서트 시작--------------");
		//주문테이블에 인서트
		OrderVO ord = new OrderVO();
		ord.setEstimate_no(estimate_no);
		ord.setMember_no(member_no);
		ord.setBill_no(bill_no);
		ord.setOrd_no(ordno);
		ord.setOrd_stat(Constants.WORK_ORD_STAT_SUCCESS);
		if(estimate_no != null) {
			ord.setOrd_type(Constants.WORK_ORD_TYPE_PREPAYMENT);
		}else {
			ord.setOrd_type(Constants.WORK_ORD_TYPE_BILLPAYMENT);
		}
		
		ord.setOrd_path(Constants.WORK_ORD_PATH_ONLINE);
		ord.setWork_no(work_no);
		orderMapper.insertOrder(ord);
		
		System.out.println("----------------w_order 테이블 인서트 종료--------------");
		
		System.out.println("----------------w_payment 테이블 인서트 시작--------------");
		//결제 테이블에 인서트
		PaymentVO pay = new PaymentVO();
		pay.setMember_no(member_no);
		pay.setOrd_no(ordno);
		pay.setWork_no(work_no);
		pay.setPaid_amount(paid_amount);
		pay.setStat(Constants.WORK_PAY_STAT_SUCCESS);
		pay.setTotal_pay_price(paid_amount);
		pay.setPay_type(Constants.WORK_PAY_TYPE_CARD);
		pay.setImp_uid(imp_uid);
		pay.setCard_name(card_name);
		pay.setCard_quota(card_quota);
		paymentMapper.insertPayment(pay);
		
		System.out.println("----------------w_payment 테이블 인서트 종료--------------");
		//견적서 선금 카드 결제 완료시
		if(estimate_no != null) {
			EstimateVO estVO = new EstimateVO();
			estVO.setEstimate_no(estimate_no);
			estVO.setPay_type(Constants.WORK_PAY_TYPE_CARD);
			estVO.setPay_stat("10");
			//견적서 테이블의 해당 견적서 업뎃
			estimateMapper.updateEstimateBank(estVO);
			
			//견적서 테이블의 사용자 승인완료 업뎃
			estimateMapper.updateEstimateUserApply(estimate_no);		
			
			//작업 승인 카드 넣기
			MsgTemplateVO tmpVO = new MsgTemplateVO();
			tmpVO.setTmpl_type(Constants.WORK_MSG_TMPL_TYPE_PUSH_CARD);
			tmpVO.setTmpl_gb("est_apply");
			
			tmplInfo = commonMapper.selectMsgTmplInfo(tmpVO);
			
			WorkCardVO card = new WorkCardVO();
			card.setWork_no(work_no);
			card.setContent(tmplInfo.getContent());
			card.setWork_id(work_id);
			
			workMapper.insertWorkCard(card);
			
			
			List<EstimateVO> 	oldEstimateList = null;
			
			EstimateVO schVo = new EstimateVO();
			schVo.setWork_no(work_no);
			schVo.setOld_yn("Y");
			oldEstimateList 	= estimateMapper.listEstimate(schVo);
			//최초견적서이고 작업확정일이 걸려있을시 견적서 선금 완료 하면 워커맨들의 작업예정 일정에서 작업확정일정으로 업데이트
			if(oldEstimateList.size() == 0) {
				if(StringUtil.replaceNull(work_confirm_yn, "").equals("Y")) {
					WorkVO updVO = new WorkVO();
					updVO.setWork_no(work_no);
					updVO.setWork_stat(Constants.WORK_STAT_WORKING);
					workMapper.updateWorkInfo(updVO);
					
					AdminScheduleVO vo = new AdminScheduleVO();
					vo.setWork_no(work_no);
					vo.setSchedule_type(Constants.WORK_SCHEDULE_TYPE_WORKING);
					vo.setChange_schedule_type(Constants.WORK_SCHEDULE_TYPE_WORKING_RESERVE);
					adminMapper.updateAdminScheduleGroup(vo);
					
					adminMapper.updateAdminSchedule(vo);
				}else {
					WorkVO updVO = new WorkVO();
					updVO.setWork_no(work_no);
					updVO.setReq_work_date(req_work_date);
					updVO.setReq_work_date_type(req_work_date_type);
					//작업희망일값 업데이트
					workMapper.updateReqWorkDate(updVO);	
				}
			}
			
		}
		
		if(bill_no != null) {
			BillVO billVO = new BillVO();
			billVO.setBill_no(bill_no);
			billVO.setPay_type(Constants.WORK_PAY_TYPE_CARD);
			billVO.setPay_stat("10");
			
			billMapper.updateBillBank(billVO);
		}
		
		MsgTemplateVO tmpVO = new MsgTemplateVO();
		tmpVO.setTmpl_type(Constants.WORK_MSG_TMPL_TYPE_PUSH_CARD);	
		if(estimate_no != null) {
			tmpVO.setTmpl_gb("est_pay_fin");
		}else {
			tmpVO.setTmpl_gb("bill_pay_fin");
		}
		
		
		//결제완료 메세지 정보 조회
		tmplInfo = commonMapper.selectMsgTmplInfo(tmpVO);
		
		
		DecimalFormat dc = new DecimalFormat("###,###,###,###");  
        String ch = dc.format(paid_amount);

        
        tmplInfo.setContent(tmplInfo.getContent().replace("{pay_amount}", ch));
        tmplInfo.setContent(tmplInfo.getContent().replace("{pay_type}", "카드결제"));
        
        WorkCardVO card = new WorkCardVO();
		card.setWork_no(work_no);
		card.setContent(tmplInfo.getContent());
		card.setWork_id(work_id);
		


		//결제완료 카드정보 인서트
		workMapper.insertWorkCard(card);
		
		//w_work테이블의 작업상태를 50 완료로 업뎃
		if(bill_no != null) {
			WorkVO updVO = new WorkVO();
			updVO.setWork_no(work_no);
			updVO.setWork_stat(Constants.WORK_STAT_FINISH);
			
			workMapper.updateWorkInfo(updVO);
		}
		
		//현장결제 신용카드 결제시  작업상태를 50 완료로 업뎃
		if(StringUtil.replaceNull(easy_pay_yn,"").equals("Y")) {
			WorkVO updVO = new WorkVO();
			updVO.setWork_no(work_no);
			updVO.setWork_stat(Constants.WORK_STAT_FINISH);
			updVO.setPay_stat(Constants.WORK_ORD_STAT_SUCCESS);
			updVO.setPay_card_quota(card_quota+"");
			workMapper.updateWorkInfo(updVO);
		}

 

Comments