package kr.wisestone.owl.service.impl; import kr.wisestone.owl.constant.Constants; import kr.wisestone.owl.constant.MsgConstants; import kr.wisestone.owl.domain.*; import kr.wisestone.owl.domain.enumType.EmailType; import kr.wisestone.owl.exception.OwlRuntimeException; import kr.wisestone.owl.repository.PaymentRepository; import kr.wisestone.owl.service.*; import kr.wisestone.owl.util.CommonUtil; import kr.wisestone.owl.util.ConvertUtil; import kr.wisestone.owl.util.DateUtil; import kr.wisestone.owl.util.MapUtil; import kr.wisestone.owl.vo.PaymentHistoryVo; import kr.wisestone.owl.vo.PaymentVo; import kr.wisestone.owl.vo.UserVo; import kr.wisestone.owl.web.form.PaymentForm; import org.apache.commons.text.StringEscapeUtils; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.client.HttpClientErrorException; import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URLDecoder; import java.nio.charset.Charset; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; @Service public class PaymentServiceImpl extends AbstractServiceImpl> implements PaymentService { private static final Logger log = LoggerFactory.getLogger(PaymentServiceImpl.class); @Autowired private PaymentRepository paymentRepository; @Autowired private WorkspaceService workspaceService; @Autowired private UserWorkspaceService userWorkspaceService; @Autowired private ReservationDisableUserService reservationDisableUserService; @Autowired private UserService userService; @Autowired private SystemEmailService systemEmailService; @Autowired private PaymentHistoryService paymentHistoryService; @Value("${saas.usdkrw}") private Integer usdKrw; @Value("${payment.cancel.manager.email}") private String paymentCancelManagerEmail; private static final String IAMPORT_SERVER_URL = "https://api.iamport.kr"; private static final String ACCESS_TOKEN_REQUEST_URL = "/users/getToken"; private static final String IMP_KEY = "4101736461182334"; private static final String IMP_SECRET = "HjTiwAyYJD8r0NRDY0wcmQG989bsOTGvczOJamIT0Rl0FdEDdaQz1wJ9GUCuNj00TOK3btWclic0vREI"; private static final String SUBSCRIBE_PAYMENTS_ONETIME = "/subscribe/payments/onetime"; // 최초 결제할때 private static final String SUBSCRIBE_PAYMENTS_AGAIN = "/subscribe/payments/again"; // 정기 결제할때 private static final String PAYMENTS_CANCEL_URL = "/payments/cancel"; // 취소할때 private static final int DEFAULT_BILLING_AMOUNT = 9; private static final int BILLING_AMOUNT = 6; @Override protected JpaRepository getRepository() { return this.paymentRepository; } // 즉시 결제 @Override @Transactional public void immediateAddUser(PaymentForm paymentForm) { Workspace workspace = this.workspaceService.findOne(paymentForm.getWorkspaceId()); // 서버에서 결재 금액을 다시 계산한다. int definiteAmount = this.calculateDailyAmount(paymentForm.getBuyUser(), workspace.getExpireDate()); paymentForm.setPaymentAmount(definiteAmount); // 결재 실행 RestClientResultObject resultObject = this.executePayment(paymentForm, workspace); // 히스토리 생성 PaymentHistory paymentHistory = this.paymentHistoryService.addPaymentHistory(resultObject, paymentForm, workspace); // 결재 결과 처리 this.paymentResultProcess(paymentHistory, workspace, paymentForm, true); } private int calculateDailyAmount(int buyUser, Date expireDate) { int expireDateTerm = DateUtil.getDateDiff(new Date(), expireDate) + 1; // 사용자가 1명이 안될 때는 오류 발생 if (buyUser < 1) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.PAYMENT_BUY_USER_MUST_BE_GREATER_THAN_ZERO)); } if (expireDateTerm > 29) { // 기본 사용자(10명)을 초과한 결제를 진행했을 때 결제 금액을 계산한다. return this.calculateAmountDefaultUserOver(buyUser); } else { int totalAmount = (BILLING_AMOUNT * this.usdKrw * buyUser); int dayAmount = (totalAmount / 30); // 30일 기준으로 가격을 나눈다. double sale = buyUser * 0.01; double discount = 0; // 100명 이상 결제시 할인 적용 if (buyUser > 99) { discount = (totalAmount * (sale / 100)); } return (int) Math.round((dayAmount * expireDateTerm) - discount); } } // 기본 사용자(10명)을 초과한 결제를 진행했을 때 결제 금액을 계산한다. private int calculateAmountDefaultUserOver(int buyUser) { // 100프로 int totalAmount = (BILLING_AMOUNT * this.usdKrw * buyUser); double sale = buyUser * 0.01; double discount = 0; // 100명 이상 결제시 할인 적용 if (buyUser > 99) { discount = (totalAmount * (sale / 100)); } return (int) Math.floor((totalAmount - discount)); } // 정기 결제 @Override @Transactional public void paymentOneTime(PaymentForm paymentForm) { Workspace workspace = this.workspaceService.getWorkspace(paymentForm.getWorkspaceId()); // 결제하려는 사용자가 업무 공간의 관리자인지 확인한다. this.checkWorkspaceManager(workspace); // 구매 사용자 수 체크 this.verifyBuyUser(paymentForm); // 결제 유형 체크 this.verifyPaymentType(paymentForm.getType()); // 서버에서 결제 금액을 다시 계산한다. int definiteAmount = this.calculateAmount(paymentForm.getBuyUser()); paymentForm.setPaymentAmount(definiteAmount); // 결제 실행 RestClientResultObject resultObject = this.executePayment(paymentForm, workspace); // 히스토리 생성 PaymentHistory paymentHistory = this.paymentHistoryService.addPaymentHistory(resultObject, paymentForm, workspace); // 결제 결과 처리 this.paymentResultProcess(paymentHistory, workspace, paymentForm, false); // 결제 정보를 map 으로 만들어준다. - 이메일 발송에 사용 Map paymentMap = this.makePaymentMap(workspace, this.webAppUtil.getLoginUser().getName(), paymentForm.getBuyUser(), definiteAmount, null); // 결제 성공 메일 발송 this.systemEmailService.directEmail(new String[]{this.webAppUtil.getLoginUser().getAccount()}, EmailType.REGULAR_PAYMENT, paymentMap, this.webAppUtil.getLoginUser().getAccount()); } // 결제 정보를 map 으로 만들어준다. - 이메일 발송에 사용 private Map makePaymentMap(Workspace workspace, String userName, Integer buyUser, Integer price, String email) { Map paymentMap = new HashMap<>(); paymentMap.put("userName", userName); paymentMap.put("workspaceName", workspace.getName()); paymentMap.put("storageSize", workspace.getStorageSize() / 1024 / 1024 / 1024); paymentMap.put("buyUser", buyUser); paymentMap.put("price", CommonUtil.getDecimalFormat(price)); paymentMap.put("registerDate", DateUtil.convertDateToStr(new Date())); paymentMap.put("nextPaymentDay", DateUtil.convertDateToYYYYMMDD(DateUtil.addDays(workspace.getExpireDate(), 1))); paymentMap.put("startDate", DateUtil.convertDateToYYYYMMDD(workspace.getStartDate())); paymentMap.put("expireDate", DateUtil.convertDateToYYYYMMDD(workspace.getExpireDate())); paymentMap.put("email", email); return paymentMap; } // 결제하려는 사용자가 업무 공간의 관리자인지 확인한다. private void checkWorkspaceManager(Workspace workspace) { UserWorkspace userWorkspace = this.userWorkspaceService.findByUserIdAndWorkspaceId(this.webAppUtil.getLoginId(), workspace.getId()); if (!userWorkspace.getManagerYn()) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.PAYMENT_EXECUTE_ONLY_WORKSPACE_MANAGER)); } } // 스케쥴러로 한번 결제한 정보를 토대로 자동 결제를 실행한다. @Override @Transactional public void subscribeImmediate(Workspace workspace) { Payment payment = workspace.getPayment(); if (payment == null) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.PAYMENT_NOT_EXIST)); } User workspaceManager = this.userService.findByWorkspaceIdAndManagerYn(workspace); if (workspaceManager == null) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.USER_WORKSPACE_MANAGER_NOT_EXIST)); } PaymentForm paymentForm = ConvertUtil.copyProperties(payment, PaymentForm.class); paymentForm.setCustomerUid(workspace.makeCustomerUid(workspaceManager.getAccount())); paymentForm.setMerchantUid(workspaceManager.getAccount() + new Date().getTime()); paymentForm.setPaymentAmount(payment.getPrice()); // 히스토리 생성할 때 사용 HttpHeaders headers = new HttpHeaders(); headers.add("Authorization", this.getAccessToken()); RestTemplate restTemplate = new RestTemplate(); restTemplate.getMessageConverters().add(0, new StringHttpMessageConverter(Charset.forName("UTF-8"))); MultiValueMap parameters = new LinkedMultiValueMap<>(); parameters.add("customer_uid", paymentForm.getCustomerUid()); parameters.add("merchant_uid", paymentForm.getMerchantUid()); parameters.add("amount", paymentForm.getPaymentAmount().toString()); RestClientResultObject resultObject = this.postRequest(this.makeIamportRequestUrl(SUBSCRIBE_PAYMENTS_AGAIN), new HttpEntity<>(parameters, headers)); // 히스토리 생성 PaymentHistory paymentHistory = this.paymentHistoryService.addPaymentHistory(resultObject, paymentForm, workspace); // 결제 결과 처리 this.paymentResultProcess(paymentHistory, workspace, paymentForm, false); // 비활성화 사용자 처리 this.checkPaymentByUserCount(paymentHistory, payment, paymentForm, workspace); List userWorkspaces = this.userWorkspaceService.findByWorkspaceIdAndManagerYn(workspace.getId(), true); // 업무 공간 담당자 이름 String workspaceManagerName = ""; if (userWorkspaces.size() > 0) { workspaceManagerName = userWorkspaces.get(0).getUser().getName(); } // 결제 정보를 map 으로 만들어준다. - 이메일 발송에 사용 Map paymentMap = this.makePaymentMap(workspace, workspaceManagerName, payment.getBuyUser(), payment.getPrice(), null); // 결제 성공 메일 발송 this.systemEmailService.directEmail(new String[]{workspaceManager.getAccount()}, EmailType.REGULAR_PAYMENT, paymentMap, null); } // 결제된 사용자 수를 체크한다. private void checkPaymentByUserCount(PaymentHistory paymentHistory, Payment payment, PaymentForm paymentForm, Workspace workspace) { try { // 결제가 성공했을 때 참여 사용자와 결제된 사용자 수를 비교한다. if (paymentHistory.getPaymentResult().equals(PaymentHistory.PAYMENT_RESULT_SUCCESS)) { Integer activeUserCount = this.userWorkspaceService.countByWorkspaceIdAndUseYn(workspace.getId(), true); // 결제한 사용자보다 참여중인 사용자가 많을 경우 if (workspace.getMaxUser() < activeUserCount) { this.executeWorkspaceStandbyUser(workspace, payment, activeUserCount); } } } catch (Exception e) { this.cancelPayment(paymentForm.getMerchantUid(), ""); this.paymentHistoryService.cancelPaymentHistory(paymentForm, workspace, "결제 완료후 비활성화로 예정된 사용자 처리 과정에서 오류가 발생하였습니다."); } } // 업무 공간에 참여하는 사용자 참여 대기 상태로 변경하기 - 결제 인원 수를 제외한 인원들 private void executeWorkspaceStandbyUser(Workspace workspace, Payment payment, Integer activeUserCount) { int disableUserCount = activeUserCount - workspace.getMaxUser(); // 참여 대기로 예약된 사용자부터 참여 대기로 상태를 변경 ReservationDisableUser reservationDisableUser = payment.getReservationDisableUser(); if (reservationDisableUser != null) { String[] userIds = reservationDisableUser.getUserIds().split(","); for (String userId : userIds) { if (!StringUtils.isEmpty(userId)) { if (disableUserCount < 1) { break; } User user = this.userService.getUser(Long.valueOf(userId)); // 해당 업무 공간에서 사용자를 비활성화한다. this.userWorkspaceService.disabledUserWorkspace(user, workspace); // 사용자를 해당 업무 공간에서 참여 대기 상태로 변경했으면 카운터를 내린다. disableUserCount--; } } payment.setReservationDisableUser(null); this.paymentRepository.saveAndFlush(payment); } // 해당 업무 공간에 참여하는 사용자 중 번호가 높은 순서대로 참여 대기 상태로 변경한다. List userWorkspaces = this.userWorkspaceService.findByWorkspaceIdAndUseYn(workspace.getId(), true); for (UserWorkspace userWorkspace : userWorkspaces) { // 관리자는 항상 참여 상태여야 한다. if (userWorkspace.getManagerYn()) { continue; } if (disableUserCount < 1) { break; } // 해당 업무 공간에서 사용자를 비활성화한다. this.userWorkspaceService.disabledUserWorkspace(userWorkspace.getUser(), workspace); // 사용자를 해당 업무 공간에서 참여 대기 상태로 변경했으면 카운터를 내린다. disableUserCount--; } } // 서버에서 결제 금액을 다시 계산한다. private int calculateAmount(int buyUser) { // 사용자가 1명이 안될 때는 오류 발생 if (buyUser < 1) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.PAYMENT_BUY_USER_MUST_BE_GREATER_THAN_ZERO)); } if (buyUser < 11) { return (DEFAULT_BILLING_AMOUNT * this.usdKrw); } else { // 기본 사용자(10명)을 초과한 결제를 진행했을 때 결제 금액을 계산한다. return this.calculateAmountDefaultUserOver(buyUser); } } // 결제를 실행한다. private RestClientResultObject executePayment(PaymentForm paymentForm, Workspace workspace) { UserVo workspaceManager = this.webAppUtil.getLoginUser(); paymentForm.setCustomerUid(workspace.makeCustomerUid(workspaceManager.getAccount())); paymentForm.setMerchantUid(workspaceManager.getAccount() + new Date().getTime()); // HttpHeader 를 생성한다. HttpHeaders headers = this.makeHttpHeader(); MultiValueMap parameters = new LinkedMultiValueMap<>(); parameters.add("merchant_uid", paymentForm.getMerchantUid()); parameters.add("amount", paymentForm.getPaymentAmount().toString()); parameters.add("card_number", CommonUtil.decryptAES128(paymentForm.getCardNumber1()) + "-" + CommonUtil.decryptAES128(paymentForm.getCardNumber2()) + "-" + CommonUtil.decryptAES128(paymentForm.getCardNumber3()) + "-" + CommonUtil.decryptAES128(paymentForm.getCardNumber4())); parameters.add("expiry", CommonUtil.decryptAES128(paymentForm.getExpireYear()) + "-" + CommonUtil.decryptAES128(paymentForm.getExpireMonth())); parameters.add("birth", CommonUtil.decryptAES128(paymentForm.getBirth())); parameters.add("pwd_2digit", CommonUtil.decryptAES128(paymentForm.getCardPwd())); parameters.add("customer_uid", paymentForm.getCustomerUid()); parameters.add("buyer_name", workspaceManager.getName()); parameters.add("buyer_email", workspaceManager.getAccount()); return this.postRequest(this.makeIamportRequestUrl(SUBSCRIBE_PAYMENTS_ONETIME), new HttpEntity<>(parameters, headers)); } // 결제 결과를 처리한다. private PaymentHistoryVo paymentResultProcess(PaymentHistory paymentHistory, Workspace workspace, PaymentForm paymentForm, Boolean immediate) { if (paymentHistory.getPaymentResult().equals(PaymentHistory.PAYMENT_RESULT_SUCCESS)) { try { // 추가 결제 if (immediate) { Payment payment = workspace.getPayment(); // 인원 추가 payment.setBuyUser(payment.getBuyUser() + paymentForm.getBuyUser()); // 추가 결제로 인한 사용금액 변경 payment.setPrice(this.calculateAmountDefaultUserOver(payment.getBuyUser())); this.paymentRepository.saveAndFlush(payment); // 추가 결제 완료 후 최대 사용자 수, 저장 공간을 업데이트한다. this.workspaceService.updateWorkspaceByImmediatePayment(workspace, payment.getBuyUser()); } else { // 정기 결제로 결제를 진행한 경우 Payment payment = this.executePaymentOneTime(workspace, paymentForm); if (payment != null) { // 비활성화 사용자 처리 this.checkPaymentByUserCount(paymentHistory, payment, paymentForm, workspace); } } } catch (Exception e) { this.cancelPayment(paymentForm.getMerchantUid(), ""); PaymentHistory cancelPaymentHistory = this.paymentHistoryService.cancelPaymentHistory(paymentForm, workspace, "결제 과정에서 오류가 발생하였습니다. 다시 시도해 주세요."); return ConvertUtil.copyProperties(cancelPaymentHistory, PaymentHistoryVo.class); } } else { throw new OwlRuntimeException(this.messageAccessor.getMessage(paymentHistory.getPaymentResponse())); } return ConvertUtil.copyProperties(paymentHistory, PaymentHistoryVo.class); } // 정기 결제로 결제를 진행한 경우 private Payment executePaymentOneTime(Workspace workspace, PaymentForm paymentForm) { // 다음 결제일 지정 및 업무 공간에 결제한 신규 데이터 적용 this.workspaceService.updateWorkspace(workspace, paymentForm); if (workspace.getPayment() == null) { // 결제가 완료되고 결제 정보가 업무 공간에 업데이트가 되면 정기 결제를 위해 결제 정보를 생성한다. Payment payment = ConvertUtil.copyProperties(paymentForm, Payment.class); payment.setPrice(paymentForm.getPaymentAmount()); payment.setWorkspace(workspace); return this.paymentRepository.saveAndFlush(payment); } return null; } // 결제 진행중 발생한 오류로 인한 결제 취소 요청 private String cancelPayment(String merchantUid, String reason) { // HttpHeader 를 생성한다. HttpHeaders headers = this.makeHttpHeader(); // RestTemplate 를 생성한다. RestTemplate restTemplate = this.makeRestTemplate(); MultiValueMap parameters = new LinkedMultiValueMap<>(); parameters.add("merchant_uid", merchantUid); parameters.add("reason", reason); URI targetUrl = UriComponentsBuilder.fromUriString(IAMPORT_SERVER_URL) .path(PAYMENTS_CANCEL_URL) .build() .toUri(); String url = null; try { url = URLDecoder.decode(targetUrl.toString(), "UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } ResponseEntity result = restTemplate.postForEntity(url, new HttpEntity<>(parameters, headers), String.class); Map resultMap = ConvertUtil.convertJsonToMap(result.getBody()); return StringEscapeUtils.unescapeJava(MapUtil.getString(resultMap, "response")); } // HttpHeader 를 생성한다. private HttpHeaders makeHttpHeader() { HttpHeaders headers = new HttpHeaders(); headers.add("Authorization", this.getAccessToken()); return headers; } // RestTemplate 를 생성한다. private RestTemplate makeRestTemplate() { RestTemplate restTemplate = new RestTemplate(); restTemplate.getMessageConverters() .add(0, new StringHttpMessageConverter(Charset.forName("UTF-8"))); return restTemplate; } // 정기 결제 취소하기 @Override @Transactional public void cancelNextPayment(PaymentForm paymentForm) { Workspace workspace = this.workspaceService.getWorkspace(paymentForm.getWorkspaceId()); // 결제 취소하려는 사용자가 업무 공간의 관리자인지 확인한다. this.checkWorkspaceManager(workspace); Payment payment = workspace.getPayment(); if (payment == null) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.PAYMENT_NOT_EXIST)); } // 업무 공간 정기 결제 취소 - 정기 결제 정보 삭제 this.workspaceService.cancelWorkspacePayment(workspace); // 결제 정보를 map 으로 만들어준다. - 이메일 발송에 사용 Map paymentMap = this.makePaymentMap(workspace, this.webAppUtil.getLoginUser().getName(), 0, 0, CommonUtil.decryptAES128(this.webAppUtil.getLoginUser().getAccount())); paymentMap.put("maxUser", workspace.getMaxUser()); try { // 오늘 결제한 업무 공간을 사용자가 취소했는지 확인한다. PaymentHistory paymentHistory = this.paymentHistoryService.findByWorkspaceLastPaymentHistory(workspace); // 당일 결제 취소일 경우 if (paymentHistory.getRegisterDate().compareTo(new Date()) == 0) { paymentMap.put("refundPrice", CommonUtil.getDecimalFormat(paymentHistory.getPrice())); // 환불 금액 // 회계 담당자 이메일 주소 암호화 String encryptAccountingManager = CommonUtil.encryptAES128(this.paymentCancelManagerEmail); // 회계 담당자에게 결제 취소 알림 메일 this.systemEmailService.directEmail(new String[]{encryptAccountingManager}, EmailType.REGULAR_PAYMENT_CANCEL_BY_ACCOUNTING_MANAGER, paymentMap, encryptAccountingManager); } } catch (Exception e) { log.debug("환불 알림 메일 오류 발생" + e.getMessage()); } // 정기 결제 취소 알림 메일 this.systemEmailService.directEmail(new String[]{this.webAppUtil.getLoginUser().getAccount()}, EmailType.REGULAR_PAYMENT_CANCEL, paymentMap, this.webAppUtil.getLoginUser().getAccount()); } @Override public String getAccessToken() { RestTemplate restTemplate = new RestTemplate(); restTemplate.getMessageConverters() .add(0, new StringHttpMessageConverter(Charset.forName("UTF-8"))); MultiValueMap parameters = new LinkedMultiValueMap<>(); parameters.add("imp_key", IMP_KEY); parameters.add("imp_secret", IMP_SECRET); URI targetUrl = UriComponentsBuilder.fromUriString(IAMPORT_SERVER_URL) .path(ACCESS_TOKEN_REQUEST_URL) .build() .toUri(); String url = ""; try { url = URLDecoder.decode(targetUrl.toString(), "UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } String result = restTemplate.postForObject(url, parameters, String.class); Map resultMap = ConvertUtil.convertJsonToMap(result); Map tokenMap = (Map) resultMap.get("response"); return MapUtil.getString(tokenMap, "access_token"); } // 결제 상세 정보를 조회한다. @Override @Transactional(readOnly = true) public void detailPayment(Map resJsonData, PaymentForm paymentForm) { Workspace workspace = this.workspaceService.findOne(paymentForm.getWorkspaceId()); Payment payment = workspace.getPayment(); if (payment != null) { resJsonData.put(Constants.RES_KEY_CONTENTS, ConvertUtil.copyProperties(payment, PaymentVo.class)); } } // 결제 정보를 수정한다. @Override @Transactional public PaymentVo modifyPayment(PaymentForm paymentForm) { Workspace workspace = this.workspaceService.findOne(paymentForm.getWorkspaceId()); // 결제하려는 사용자가 업무 공간의 관리자인지 확인한다. this.checkWorkspaceManager(workspace); Payment payment = workspace.getPayment(); Map paymentMap = new HashMap<>(); paymentMap.put("beforeMaxUser", payment.getBuyUser()); // 이전 최대 사용자 수 paymentMap.put("beforePrice", CommonUtil.getDecimalFormat(payment.getPrice())); // 이전 가격 // 결제 사용자 수 체크 this.verifyBuyUser(paymentForm); // 결제 유형 체크 this.verifyPaymentType(paymentForm.getType()); payment.setBuyUser(paymentForm.getBuyUser()); payment.setType(paymentForm.getType()); // 서버에서 결제 금액을 다시 계산한다. int definiteAmount = this.calculateAmount(paymentForm.getBuyUser()); payment.setPrice(definiteAmount); if (payment.getReservationDisableUser() != null) { payment.setReservationDisableUser(null); } this.paymentRepository.saveAndFlush(payment); this.reservationDisableUserService.add(paymentForm, payment); paymentMap.put("userName", this.webAppUtil.getLoginUser().getName()); paymentMap.put("workspaceName", workspace.getName()); paymentMap.put("maxUser", paymentForm.getBuyUser()); paymentMap.put("price", CommonUtil.getDecimalFormat(definiteAmount)); paymentMap.put("nextPaymentDay", DateUtil.convertDateToYYYYMMDD(DateUtil.addDays(workspace.getExpireDate(), 1))); // 정기 결제 변경 알림 메일 this.systemEmailService.directEmail(new String[]{this.webAppUtil.getLoginUser().getAccount()}, EmailType.REGULAR_PAYMENT_MODIFY, paymentMap, this.webAppUtil.getLoginUser().getAccount()); return ConvertUtil.copyProperties(payment, PaymentVo.class); } // 결제 사용자 수 체크 private void verifyBuyUser(PaymentForm paymentForm) { if (paymentForm.getBuyUser() == null || paymentForm.getBuyUser() < 1) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.PAYMENT_BUY_USER_MUST_BE_GREATER_THAN_ZERO)); } // 1명으로 구입해도 10인까지 사용 가능하도록 변경 if (paymentForm.getBuyUser() < 10) { paymentForm.setBuyUser(10); } } // 결제 유형 체크 private void verifyPaymentType(String type) { if (StringUtils.isEmpty(type)) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.PAYMENT_NO_TYPE)); } } // 결제 아이디로 결제를 조회한다. @Override @Transactional(readOnly = true) public Payment getPayment(Long id) { if (id == null) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.PAYMENT_NOT_EXIST)); } Payment payment = this.findOne(id); if (payment == null) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.PAYMENT_NOT_EXIST)); } return payment; } // 결제 금액에 환율 정보를 업데이트한다. @Override @Transactional public void updateExchangeRatePayment() { List payments = this.paymentRepository.findAll(); for (Payment payment : payments) { // 서버에서 결제 금액을 다시 계산한다. int definiteAmount = this.calculateAmount(payment.getBuyUser()); payment.setPrice(definiteAmount); } this.paymentRepository.saveAll(payments); } private URI makeIamportRequestUrl(String requestPath) { return UriComponentsBuilder.fromUriString(IAMPORT_SERVER_URL) .path(requestPath) .build() .toUri(); } private RestClientResultObject postRequest(URI targetUrl, HttpEntity> request) { RestClientResultObject restResult = new RestClientResultObject(); RestTemplate restTemplate = new RestTemplate(); restTemplate.getMessageConverters().add(0, new StringHttpMessageConverter(Charset.forName("UTF-8"))); try { String url = URLDecoder.decode(targetUrl.toString(), "UTF-8"); ResponseEntity result = restTemplate.postForEntity(url, request, String.class); Map resultMap = ConvertUtil.convertJsonToMap(result.getBody()); restResult.setHttpStatus(result.getStatusCode()); restResult.setCode(MapUtil.getString(resultMap, "code")); if (resultMap.containsKey("message")) { restResult.setMessage(StringEscapeUtils.unescapeJava(MapUtil.getString(resultMap, "message"))); } if (resultMap.containsKey("response")) { Map response = (Map) resultMap.get("response"); restResult.setResponse(response); String responseStatus = MapUtil.getString(response, "status"); String failReason = MapUtil.getString(response, "fail_reason"); restResult.setIamportStatus(responseStatus); Boolean isNotPaidResult = !"paid".equals(responseStatus); if (isNotPaidResult) { restResult.setIamportFailReason(failReason); } } } catch (HttpClientErrorException e) { restResult.setMessage(e.getMessage()); restResult.setCode(e.getStatusCode().toString()); restResult.setHttpStatus(e.getStatusCode()); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } return restResult; } public static class RestClientResultObject { private static final String CODE_RESULT_SUCCESS = "0"; private static final String CODE_RESULT_FAILED = "-1"; private static final String IAMPORT_STATUS_PAID = "paid"; private HttpStatus httpStatus; private String code; private String message; private Map response; private String iamportStatus; private String iamportFailReason; public Boolean isValidResult() { if (isHttpRequestFailed() || isIamportResultFailed() || isIamportPaymentFailed()) { return Boolean.FALSE; } return Boolean.TRUE; } public Boolean isHttpRequestFailed() { return !HttpStatus.OK.equals(this.getHttpStatus()); } public Boolean isIamportResultFailed() { return CODE_RESULT_FAILED.equals(this.getCode()); } public Boolean isIamportPaymentFailed() { return !IAMPORT_STATUS_PAID.equals(this.getIamportStatus()); } public HttpStatus getHttpStatus() { return httpStatus; } public void setHttpStatus(HttpStatus httpStatus) { this.httpStatus = httpStatus; } public String getCode() { return code; } public void setCode(String code) { this.code = code; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } public Map getResponse() { return response; } public void setResponse(Map response) { this.response = response; } public String getIamportStatus() { return iamportStatus; } public void setIamportStatus(String iamportStatus) { this.iamportStatus = iamportStatus; } public String getIamportFailReason() { return iamportFailReason; } public void setIamportFailReason(String iamportFailReason) { this.iamportFailReason = iamportFailReason; } } }