hmk run dev
이니시스 & 아임포트 결제 연동 본문
결제 시스템에 대해서 공부해보고자 이니시스 결제 샘플 코드를 받아
결제 테스트를 진행해 보았다!
아래 링크에서 표준 결제 & 에스크로 결제 샘플코드 다운로드 가능
https://manual.inicis.com/stdpay/
build path에서 이니시스에서 제공하는 jar파일 넣어주고
나는 jsp로 테스트 해보았다...! 아래는 소스코드 톰캣 넣고 runserver 해주자
https://github.com/hmk1022/inicis_module
처음 샘플코드를 받고 수정할 부분이 몇군데 있는데
로컬 주소를 사용할 경우 아래처럼 주소를 변경해주면 따로 설정할 건 없는것 같다.
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×tamp=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
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
아임포트 참고
<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);
}
'기타' 카테고리의 다른 글
이니시스 모바일 결제 개발하기 (0) | 2022.03.16 |
---|---|
ajax + 개발자 도구로 크롤링하기 (0) | 2021.12.10 |
PL/SQL 계층형 테이블에서 부모 카테고리 번호 구하기 (0) | 2021.12.09 |
SPA 어플리케이션과 SSR프레임워크 & 코드스플리팅 (0) | 2021.12.07 |