diff --git a/mens-api/src/main/java/kr/xit/ens/kakao/v1/service/AsyncKkopayEltrcDocService.java b/mens-api/src/main/java/kr/xit/ens/kakao/v1/service/AsyncKkopayEltrcDocService.java index 90b981b..a66a7b8 100644 --- a/mens-api/src/main/java/kr/xit/ens/kakao/v1/service/AsyncKkopayEltrcDocService.java +++ b/mens-api/src/main/java/kr/xit/ens/kakao/v1/service/AsyncKkopayEltrcDocService.java @@ -52,19 +52,19 @@ import lombok.extern.slf4j.Slf4j; public class AsyncKkopayEltrcDocService extends AbstractService implements IAsyncKkopayEltrcDocService { - @Value("${app.contract.kakao.host}") + @Value("${app.contract.kakao.api.pay.host}") private String HOST; - @Value("#{'${app.contract.kakao.api.send}'.split(';')}") + @Value("#{'${app.contract.kakao.api.pay.send}'.split(';')}") private String[] API_SEND; - @Value("#{'${app.contract.kakao.api.validToken}'.split(';')}") + @Value("#{'${app.contract.kakao.api.pay.validToken}'.split(';')}") private String[] API_VALID_TOKEN; - @Value("#{'${app.contract.kakao.api.modifyStatus}'.split(';')}") + @Value("#{'${app.contract.kakao.api.pay.modifyStatus}'.split(';')}") private String[] API_MODIFY_STATUS; - @Value("#{'${app.contract.kakao.api.findStatus}'.split(';')}") + @Value("#{'${app.contract.kakao.api.pay.findStatus}'.split(';')}") private String[] API_STATUS; - @Value("#{'${app.contract.kakao.api.bulksend}'.split(';')}") + @Value("#{'${app.contract.kakao.api.pay.bulksend}'.split(';')}") private String[] API_BULKSEND; - @Value("#{'${app.contract.kakao.api.bulkstatus}'.split(';')}") + @Value("#{'${app.contract.kakao.api.pay.bulkstatus}'.split(';')}") private String[] API_BULKSTATUS; private final ApiWebClientUtil webClient; @@ -104,7 +104,7 @@ public class AsyncKkopayEltrcDocService extends AbstractService implements if(Objects.requireNonNull(errors).size() > 0) throw BizRuntimeException.create(errors.toString()); return CompletableFuture.supplyAsync(() -> - webClient.exchangeKko(HOST + API_SEND[0], HttpMethod.valueOf(API_SEND[1]), JsonUtils.toJson(reqDTO), KkopayDocDTO.SendResponse.class, getEnsRlaybsnmDTO(reqDTO.getSignguCode(), reqDTO.getFfnlgCode()))) + webClient.exchangeKkopay(HOST + API_SEND[0], HttpMethod.valueOf(API_SEND[1]), JsonUtils.toJson(reqDTO), KkopayDocDTO.SendResponse.class, getEnsRlaybsnmDTO(reqDTO.getSignguCode(), reqDTO.getFfnlgCode()))) .handle((r, e) -> { if(e != null){ return webClient.sendError(e); @@ -131,7 +131,7 @@ public class AsyncKkopayEltrcDocService extends AbstractService implements .replace("{tokens}", reqDTO.getToken()); return CompletableFuture.supplyAsync(() -> - webClient.exchangeKko(url, HttpMethod.valueOf(API_VALID_TOKEN[1]), null, KkopayDocDTO.ValidTokenResponse.class, getEnsRlaybsnmDTO(reqDTO.getSignguCode(), reqDTO.getFfnlgCode()))) + webClient.exchangeKkopay(url, HttpMethod.valueOf(API_VALID_TOKEN[1]), null, KkopayDocDTO.ValidTokenResponse.class, getEnsRlaybsnmDTO(reqDTO.getSignguCode(), reqDTO.getFfnlgCode()))) .handle((r, e) -> { if(e != null){ return webClient.sendError(e); @@ -161,7 +161,7 @@ public class AsyncKkopayEltrcDocService extends AbstractService implements final String url = HOST + API_MODIFY_STATUS[0].replace(DOCUMENT_BINDER_UUID, reqDTO.getDocument_binder_uuid()); return CompletableFuture.supplyAsync(() -> - webClient.exchangeKko(url, HttpMethod.valueOf(API_MODIFY_STATUS[1]), body, Void.class, getEnsRlaybsnmDTO(reqDTO.getSignguCode(), reqDTO.getFfnlgCode()))) + webClient.exchangeKkopay(url, HttpMethod.valueOf(API_MODIFY_STATUS[1]), body, Void.class, getEnsRlaybsnmDTO(reqDTO.getSignguCode(), reqDTO.getFfnlgCode()))) .handle((r, e) -> { if(e != null){ return webClient.sendError(e); @@ -190,7 +190,7 @@ public class AsyncKkopayEltrcDocService extends AbstractService implements final String url = HOST + API_STATUS[0].replace(DOCUMENT_BINDER_UUID, reqDTO.getDocument_binder_uuid()); return CompletableFuture.supplyAsync(() -> - webClient.exchangeKko(url, HttpMethod.valueOf(API_STATUS[1]), null, KkopayDocDTO.DocStatusResponse.class, getEnsRlaybsnmDTO(reqDTO.getSignguCode(), reqDTO.getFfnlgCode()))) + webClient.exchangeKkopay(url, HttpMethod.valueOf(API_STATUS[1]), null, KkopayDocDTO.DocStatusResponse.class, getEnsRlaybsnmDTO(reqDTO.getSignguCode(), reqDTO.getFfnlgCode()))) .handle((r, e) -> { if(e != null){ return webClient.sendError(e); @@ -212,7 +212,7 @@ public class AsyncKkopayEltrcDocService extends AbstractService implements @Async("asyncExecutor") public CompletableFuture> requestSendBulk(final KkopayDocBulkDTO.BulkSendRequests reqDTO) { return CompletableFuture.supplyAsync(() -> - webClient.exchangeKko(HOST + API_BULKSEND[0], HttpMethod.valueOf(API_BULKSEND[1]), JsonUtils.toJson(reqDTO), KkopayDocBulkDTO.BulkSendResponses.class, getEnsRlaybsnmDTO(reqDTO.getSignguCode(), reqDTO.getFfnlgCode()))) + webClient.exchangeKkopay(HOST + API_BULKSEND[0], HttpMethod.valueOf(API_BULKSEND[1]), JsonUtils.toJson(reqDTO), KkopayDocBulkDTO.BulkSendResponses.class, getEnsRlaybsnmDTO(reqDTO.getSignguCode(), reqDTO.getFfnlgCode()))) .handle((r, e) -> { if(e != null){ return webClient.sendError(e); @@ -251,7 +251,7 @@ public class AsyncKkopayEltrcDocService extends AbstractService implements } return CompletableFuture.supplyAsync(() -> - webClient.exchangeKko(HOST + API_BULKSTATUS[0], HttpMethod.valueOf(API_BULKSTATUS[1]), JsonUtils.toJson(reqDTO), KkopayDocBulkDTO.BulkStatusResponses.class, getEnsRlaybsnmDTO(reqDTO.getSignguCode(), reqDTO.getFfnlgCode()))) + webClient.exchangeKkopay(HOST + API_BULKSTATUS[0], HttpMethod.valueOf(API_BULKSTATUS[1]), JsonUtils.toJson(reqDTO), KkopayDocBulkDTO.BulkStatusResponses.class, getEnsRlaybsnmDTO(reqDTO.getSignguCode(), reqDTO.getFfnlgCode()))) .handle((r, e) -> { if(e != null){ return webClient.sendError(e); diff --git a/mens-api/src/main/java/kr/xit/ens/kakao/v1/service/KkopayEltrcDocService.java b/mens-api/src/main/java/kr/xit/ens/kakao/v1/service/KkopayEltrcDocService.java index 1c0d7ab..783f0e5 100644 --- a/mens-api/src/main/java/kr/xit/ens/kakao/v1/service/KkopayEltrcDocService.java +++ b/mens-api/src/main/java/kr/xit/ens/kakao/v1/service/KkopayEltrcDocService.java @@ -52,21 +52,27 @@ import lombok.extern.slf4j.Slf4j; public class KkopayEltrcDocService extends AbstractService implements IKkopayEltrcDocService { - @Value("${app.contract.kakao.host}") + @Value("${app.contract.kakao.api.pay.host}") private String HOST; - @Value("#{'${app.contract.kakao.api.send}'.split(';')}") + + @Value("#{'${app.contract.kakao.api.pay.send}'.split(';')}") private String[] API_SEND; - @Value("#{'${app.contract.kakao.api.validToken}'.split(';')}") + + @Value("#{'${app.contract.kakao.api.pay.validToken}'.split(';')}") private String[] API_VALID_TOKEN; - @Value("#{'${app.contract.kakao.api.modifyStatus}'.split(';')}") + + @Value("#{'${app.contract.kakao.api.pay.modifyStatus}'.split(';')}") private String[] API_MODIFY_STATUS; - @Value("#{'${app.contract.kakao.api.findStatus}'.split(';')}") - private String[] API_STATUS; - @Value("#{'${app.contract.kakao.api.bulksend}'.split(';')}") + + @Value("#{'${app.contract.kakao.api.pay.bulksend}'.split(';')}") private String[] API_BULKSEND; - @Value("#{'${app.contract.kakao.api.bulkstatus}'.split(';')}") + + @Value("#{'${app.contract.kakao.api.pay.bulkstatus}'.split(';')}") private String[] API_BULKSTATUS; + @Value("#{'${app.contract.kakao.api.pay.findStatus}'.split(';')}") + private String[] API_STATUS; + private final ApiWebClientUtil webClient; private static final Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); private static final CharSequence DOCUMENT_BINDER_UUID = "{document_binder_uuid}"; @@ -101,7 +107,7 @@ public class KkopayEltrcDocService extends AbstractService implements if(Checks.isEmpty(receiver.getBirthday())) Objects.requireNonNull(errors).add("receiver.birthday=받는이 생년월일은 필수입니다."); } if(!Objects.requireNonNull(errors).isEmpty()) throw BizRuntimeException.create(errors.toString()); - return webClient.exchangeKko(HOST + API_SEND[0], HttpMethod.valueOf(API_SEND[1]), JsonUtils.toJson(reqDTO), KkopayDocDTO.SendResponse.class, getRlaybsnmInfo(reqDTO)); + return webClient.exchangeKkopay(HOST + API_SEND[0], HttpMethod.valueOf(API_SEND[1]), JsonUtils.toJson(reqDTO), KkopayDocDTO.SendResponse.class, getRlaybsnmInfo(reqDTO)); } /** @@ -119,7 +125,7 @@ public class KkopayEltrcDocService extends AbstractService implements final String url = HOST + API_VALID_TOKEN[0].replace(DOCUMENT_BINDER_UUID, reqDTO.getDocument_binder_uuid()) .replace("{tokens}", reqDTO.getToken()); - return webClient.exchangeKko(url, HttpMethod.valueOf(API_VALID_TOKEN[1]), null, KkopayDocDTO.ValidTokenResponse.class, getRlaybsnmInfo(reqDTO)); + return webClient.exchangeKkopay(url, HttpMethod.valueOf(API_VALID_TOKEN[1]), null, KkopayDocDTO.ValidTokenResponse.class, getRlaybsnmInfo(reqDTO)); } /** @@ -140,7 +146,7 @@ public class KkopayEltrcDocService extends AbstractService implements final String body = "{\"document\": {\"is_detail_read\": true} }"; final String url = HOST + API_MODIFY_STATUS[0].replace(DOCUMENT_BINDER_UUID, reqDTO.getDocument_binder_uuid()); - webClient.exchangeKko(url, HttpMethod.valueOf(API_MODIFY_STATUS[1]), body, Void.class, getRlaybsnmInfo(reqDTO)); + webClient.exchangeKkopay(url, HttpMethod.valueOf(API_MODIFY_STATUS[1]), body, Void.class, getRlaybsnmInfo(reqDTO)); } /** @@ -161,7 +167,7 @@ public class KkopayEltrcDocService extends AbstractService implements validate(reqDTO, null); final String url = HOST + API_STATUS[0].replace(DOCUMENT_BINDER_UUID, reqDTO.getDocument_binder_uuid()); - return webClient.exchangeKko(url, HttpMethod.valueOf(API_STATUS[1]), null, KkopayDocDTO.DocStatusResponse.class, getRlaybsnmInfo(reqDTO)); + return webClient.exchangeKkopay(url, HttpMethod.valueOf(API_STATUS[1]), null, KkopayDocDTO.DocStatusResponse.class, getRlaybsnmInfo(reqDTO)); } /** @@ -218,7 +224,7 @@ public class KkopayEltrcDocService extends AbstractService implements throw BizRuntimeException.create(errors.toString()); } - return webClient.exchangeKko(HOST + API_BULKSEND[0], HttpMethod.valueOf(API_BULKSEND[1]), JsonUtils.toJson(reqDTO), KkopayDocBulkDTO.BulkSendResponses.class, getRlaybsnmInfo(reqDTO)); + return webClient.exchangeKkopay(HOST + API_BULKSEND[0], HttpMethod.valueOf(API_BULKSEND[1]), JsonUtils.toJson(reqDTO), KkopayDocBulkDTO.BulkSendResponses.class, getRlaybsnmInfo(reqDTO)); } /** @@ -248,7 +254,7 @@ public class KkopayEltrcDocService extends AbstractService implements if(!errors.isEmpty()) { throw BizRuntimeException.create(errors.toString()); } - return webClient.exchangeKko(HOST + API_BULKSTATUS[0], HttpMethod.valueOf(API_BULKSTATUS[1]), JsonUtils.toJson(reqDTO), KkopayDocBulkDTO.BulkStatusResponses.class, getRlaybsnmInfo(reqDTO)); + return webClient.exchangeKkopay(HOST + API_BULKSTATUS[0], HttpMethod.valueOf(API_BULKSTATUS[1]), JsonUtils.toJson(reqDTO), KkopayDocBulkDTO.BulkStatusResponses.class, getRlaybsnmInfo(reqDTO)); } @Override @@ -257,7 +263,7 @@ public class KkopayEltrcDocService extends AbstractService implements .replace("{tokens}", reqDTO.getToken()); // 유효성 검증 - final KkopayDocDTO.ValidTokenResponse validTokenRes = webClient.exchangeKko(url, HttpMethod.valueOf(API_VALID_TOKEN[1]), null, + final KkopayDocDTO.ValidTokenResponse validTokenRes = webClient.exchangeKkopay(url, HttpMethod.valueOf(API_VALID_TOKEN[1]), null, KkopayDocDTO.ValidTokenResponse.class, getRlaybsnmInfo(reqDTO)); if(!"USED".equals(validTokenRes.getToken_status())){ @@ -270,7 +276,7 @@ public class KkopayEltrcDocService extends AbstractService implements // 정상 : HttpStatus.NO_CONTENT(204) return // error : body에 error_code, error_message return - final KkopayErrorDTO errorDTO = webClient.exchangeKko(url2, HttpMethod.valueOf(API_MODIFY_STATUS[1]), body, KkopayErrorDTO.class, getRlaybsnmInfo(reqDTO)); + final KkopayErrorDTO errorDTO = webClient.exchangeKkopay(url2, HttpMethod.valueOf(API_MODIFY_STATUS[1]), body, KkopayErrorDTO.class, getRlaybsnmInfo(reqDTO)); if(errorDTO != null){ return ApiResponseDTO.error(errorDTO.getErrorCode(), errorDTO.getErrorMessage()); } diff --git a/mens-api/src/main/java/kr/xit/ens/kakao/v2/service/IKkotalkEltrcDocService.java b/mens-api/src/main/java/kr/xit/ens/kakao/v2/service/IKkotalkEltrcDocService.java new file mode 100644 index 0000000..40becbf --- /dev/null +++ b/mens-api/src/main/java/kr/xit/ens/kakao/v2/service/IKkotalkEltrcDocService.java @@ -0,0 +1,95 @@ +package kr.xit.ens.kakao.v2.service; + +import kr.xit.biz.ens.model.kakao.v2.KkotalkDTO; +import kr.xit.biz.ens.model.kakao.v2.KkotalkDocDTO; +import kr.xit.core.model.ApiResponseDTO; + +/** + *
+ * description : 카카오 페이 전자 문서 발송 요청 인터 페이스
+ * packageName : kr.xit.ens.kakao.v1.service
+ * fileName    : IKkopayEltrcDocService
+ * author      : julim
+ * date        : 2023-04-28
+ * ======================================================================
+ * 변경일         변경자        변경 내용
+ * ----------------------------------------------------------------------
+ * 2023-04-28    julim       최초 생성
+ *
+ * 
+ */ +public interface IKkotalkEltrcDocService { + + /** + *
+     * 모바일웹 연계 문서발송 요청
+     * -.이용기관 서버에서 전자문서 서버로 문서발송 처리를 요청합니다.
+     * 
+ * @param reqDTO KkotalkDocDTO.SendRequest + * @return KkotalkDocDTO.SendResponse + */ + KkotalkDTO.SendResponse requestSend(final KkotalkDTO.SendRequest reqDTO); + + /** + *
+     * 토큰 유효성 검증(Redirect URL  접속 허용/불허)
+     * 
+ * @param reqDTO KkotalkDocDTO.ValidTokenRequest + * @return KkotalkDocDTO.ValidTokenResponse> + */ + KkotalkDocDTO.ValidTokenResponse validToken(final KkotalkDocDTO.ValidTokenRequest reqDTO); + + /** + *
+     * 문서 열람 처리 API
+     * -.문서에 대해서 열람 상태로 변경. 사용자가 문서열람 시(OTT 검증 완료 후 페이지 로딩 완료 시점) 반드시 문서 열람 상태 변경 API를 호출해야 함.
+     * -.미 호출 시 아래와 같은 문제 발생
+     * 1)유통증명시스템을 사용하는 경우 해당 API를 호출한 시점으로 열람정보가 등록되어 미 호출 시 열람정보가 등록 되지 않음.
+     * 2)문서상태조회 API(/v1/envelopes/${ENVELOPE_ID}/read) 호출 시 read_at최초 열람시간) 데이터가 내려가지 않음.
+     * 
+ * @param reqDTO KkotalkDTO.EnvelopeId + */ + void modifyStatus(final KkotalkDTO.EnvelopeId reqDTO); + + + /** + *
+     * 문서 상태 조회 API
+     * -.이용기관 서버에서 카카오페이 전자문서 서버로 문서 상태에 대한 조회를 요청 합니다.
+     * : 발송된 문서의 진행상태를 알고 싶은 경우, flow와 상관없이 요청 가능
+     * : polling 방식으로 호출할 경우, 호출 간격은 5초를 권장.
+     * -.doc_box_status 상태변경순서
+     * : RECEIVE(수신, 미처리) > READ(열람)/EXPIRED
+     * 
+ * @param reqDTO KkotalkDTO.EnvelopeId + * @return KkotalkDocDTO.EnvelopeStatusResponse + */ + KkotalkDocDTO.EnvelopeStatusResponse findStatus(final KkotalkDocDTO.EnvelopeId reqDTO); + + /** + *
+     * 대량(bulk) 문서발송 요청
+     * -.이용기관 서버에서 카카오페이 내문서함 서버로 대량(bulk) 문서발송 처리를 요청합니다.
+     * 
+ * @param reqDTO KkopayDocBulkDTO.BulkSendRequests + * @return KkopayDocBulkDTO.BulkSendResponses + */ + KkotalkDTO.BulkSendResponse requestSendBulk(final KkotalkDTO.BulkSendRequest reqDTO); + + /** + *
+     * 대량(bulk) 문서 상태 조회 API
+     * -.이용기관 서버에서 카카오페이 전자문서 서버로 문서 상태에 대한 조회를 요청 합니다.
+     * : 발송된 문서의 진행상태를 알고 싶은 경우, flow와 상관없이 요청 가능
+     * : polling 방식으로 호출할 경우, 호출 간격은 5초를 권장.
+     * : RECEIVED(수신,미수신) > READ(열람)/EXPIRED
+     * 
+ * @param reqDTO KkotalkDTO.BulkStatusRequest + * @return KkotalkDTO.BulkStatusResponse + */ + KkotalkDTO.BulkStatusResponse findBulkStatus(final KkotalkDTO.BulkStatusRequest reqDTO); + + + ApiResponseDTO findMyDocReadyAndMblPage(KkotalkDocDTO.ValidTokenRequest reqDTO); +} + diff --git a/mens-api/src/main/java/kr/xit/ens/kakao/v2/service/KkotalkEltrcDocService.java b/mens-api/src/main/java/kr/xit/ens/kakao/v2/service/KkotalkEltrcDocService.java new file mode 100644 index 0000000..f8426a4 --- /dev/null +++ b/mens-api/src/main/java/kr/xit/ens/kakao/v2/service/KkotalkEltrcDocService.java @@ -0,0 +1,332 @@ +package kr.xit.ens.kakao.v2.service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +import javax.validation.ConstraintViolation; +import javax.validation.Validation; +import javax.validation.Validator; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpMethod; +import org.springframework.stereotype.Component; + +import kr.xit.biz.common.ApiConstants.SndngSeCode; +import kr.xit.biz.ens.model.cmm.CmmEnsRequestDTO; +import kr.xit.biz.ens.model.cmm.CmmEnsRlaybsnmDTO; +import kr.xit.biz.ens.model.kakao.v2.KkotalkDTO; +import kr.xit.biz.ens.model.kakao.v2.KkotalkDocDTO; +import kr.xit.core.exception.BizRuntimeException; +import kr.xit.core.model.ApiResponseDTO; +import kr.xit.core.service.AbstractService; +import kr.xit.core.spring.annotation.TraceLogging; +import kr.xit.core.spring.util.ApiWebClientUtil; +import kr.xit.core.support.utils.Checks; +import kr.xit.core.support.utils.JsonUtils; +import kr.xit.ens.cmm.CmmEnsUtils; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +/** + *
+ * description : 카카오 페이 전자 문서 발송 요청 서비스
+ * packageName : kr.xit.ens.kakao.v1.service
+ * fileName    : KkopayEltrcDocService
+ * author      : julim
+ * date        : 2023-04-28
+ * ======================================================================
+ * 변경일         변경자        변경 내용
+ * ----------------------------------------------------------------------
+ * 2023-04-28    julim       최초 생성
+ *
+ * 
+ */ +@Slf4j +@RequiredArgsConstructor +@Component +public class KkotalkEltrcDocService extends AbstractService implements + IKkotalkEltrcDocService { + + @Value("${app.contract.kakao.api.talk.host}") + private String HOST; + + @Value("#{'${app.contract.kakao.api.talk.send}'.split(';')}") + private String[] API_SEND; + + @Value("#{'${app.contract.kakao.api.talk.bulksend}'.split(';')}") + private String[] API_BULKSEND; + + @Value("#{'${app.contract.kakao.api.talk.validToken}'.split(';')}") + private String[] API_VALID_TOKEN; + + @Value("#{'${app.contract.kakao.api.talk.modifyStatus}'.split(';')}") + private String[] API_MODIFY_STATUS; + + + @Value("#{'${app.contract.kakao.api.talk.bulkstatus}'.split(';')}") + private String[] API_BULKSTATUS; + + private final ApiWebClientUtil webClient; + private static final Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); + private static final CharSequence ENVELOPE_ID = "{ENVELOPE_ID}"; + + + /** + *
+     * 모바일웹 연계 문서발송 요청 : POST
+     * -.이용기관 서버에서 전자문서 서버로 문서발송 처리 요청
+     * 
+ * @param reqDTO KkoPayEltrDocDTO.RequestSendReq + * @return ApiResponseDTO + */ + @Override + @TraceLogging + public KkotalkDTO.SendResponse requestSend(final KkotalkDTO.SendRequest reqDTO) { + if(Checks.isEmpty(reqDTO.getProductCode())){ + throw BizRuntimeException.create("상품 코드는 필수 입니다."); + } + List errors = new ArrayList<>(); + errors = validate(reqDTO.getEnvelope(), errors); + + final KkotalkDocDTO.Envelope envelope = reqDTO.getEnvelope(); + if(envelope.getReviewExpiresAt() != null){ + if(envelope.getReviewExpiresAt().compareTo(envelope.getReadExpiresAt()) < 0){ + errors.add("reviewExpiresAt=재열람 만료일시를 최조 열람 만료일시 보다 큰 날짜로 입력해주세요."); + } + } + + if(Checks.isEmpty(envelope.getCi())){ + if(Checks.isEmpty(envelope.getName())) Objects.requireNonNull(errors).add("name=받는이 이름은 필수입니다."); + if(Checks.isEmpty(envelope.getPhoneNumber())) Objects.requireNonNull(errors).add("phoneNumber=받는이 전화번호는 필수입니다."); + if(Checks.isEmpty(envelope.getBirthday())) Objects.requireNonNull(errors).add("birthday=받는이 생년월일은 필수입니다."); + } + if(!Objects.requireNonNull(errors).isEmpty()) throw BizRuntimeException.create(errors.toString()); + return webClient.exchangeKkopay( + HOST + API_SEND[0].replace("{PRODUCT_CODE}", reqDTO.getProductCode()), + HttpMethod.valueOf(API_SEND[1]), + JsonUtils.toJson(envelope), + KkotalkDTO.SendResponse.class, + getRlaybsnmInfo(reqDTO)); + } + + /** + *
+     * 토큰 유효성 검증(Redirect URL  접속 허용/불허) : GET
+     * 
+ * @param reqDTO KkopayDocDTO.ValidTokenRequest + * @return ApiResponseDTO + */ + @Override + @TraceLogging + public KkotalkDocDTO.ValidTokenResponse validToken(final KkotalkDocDTO.ValidTokenRequest reqDTO) { + validate(reqDTO, null); + + return webClient.exchangeKkotalk( + HOST + + API_VALID_TOKEN[0].replace(ENVELOPE_ID, reqDTO.getEnvelopeId()) + .replace("{TOKEN}", reqDTO.getToken()), + HttpMethod.valueOf(API_VALID_TOKEN[1]), + null, + KkotalkDocDTO.ValidTokenResponse.class, + getRlaybsnmInfo(reqDTO)); + } + + /** + *
+     * 문서 열람 처리 API : POST
+     * -.문서에 대해서 열람 상태로 변경. 사용자가 문서열람 시(OTT 검증 완료 후 페이지 로딩 완료 시점) 반드시 문서 열람 상태 변경 API를 호출해야 함.
+     * -.미 호출 시 아래와 같은 문제 발생
+     * 1)유통증명시스템을 사용하는 경우 해당 API를 호출한 시점으로 열람정보가 등록되어 미 호출 시 열람정보가 등록 되지 않음.
+     * 2)문서상태조회 API(/v1/envelopes/${ENVELOPE_ID}/read) 호출 시 read_at최초 열람시간) 데이터가 내려가지 않음.
+     * 
+ * @param reqDTO KkopayDocAttrDTO.EnvelopeId + */ + @Override + @TraceLogging + public void modifyStatus(final KkotalkDTO.EnvelopeId reqDTO){ + validate(reqDTO.getEnvelopeId(), null); + + final String url = HOST + API_MODIFY_STATUS[0].replace(ENVELOPE_ID, reqDTO.getEnvelopeId()); + + webClient.exchangeKkopay(url, HttpMethod.valueOf(API_MODIFY_STATUS[1]), null, Void.class, getRlaybsnmInfo(reqDTO)); + } + + /** + *
+     * 문서 상태 조회 API : GET
+     * -.이용기관 서버에서 카카오페이 전자문서 서버로 문서 상태에 대한 조회를 요청 합니다.
+     * : 발송된 문서의 진행상태를 알고 싶은 경우, flow와 상관없이 요청 가능
+     * : polling 방식으로 호출할 경우, 호출 간격은 5초를 권장.
+     * : RECEIVE(수신,미수신) > READ(열람)/EXPIRED
+     * 
+ * @param reqDTO KkotalkDTO.EnvelopeId + * @return KkotalkDocDTO.EnvelopeStatusResponse + */ + @Override + @TraceLogging + public KkotalkDocDTO.EnvelopeStatusResponse findStatus(final KkotalkDocDTO.EnvelopeId reqDTO){ + validate(reqDTO, null); + + + KkotalkDTO.BulkStatusResponse res = webClient.exchangeKkotalk( + HOST + API_BULKSTATUS[0], + HttpMethod.valueOf(API_BULKSTATUS[1]), + JsonUtils.toJson(List.of(reqDTO.getEnvelopeId())), + KkotalkDTO.BulkStatusResponse.class, + getRlaybsnmInfo(reqDTO)); + return res.getEnvelopeStatus().get(0); + } + + /** + *
+     * 모바일웹 연계 문서발송 요청 : POST
+     * -.이용기관 서버에서 전자문서 서버로 문서발송 처리를 요청합니다.
+     * 
+ * @param reqDTO KkotalkDTO.BulkSendRequest + * @return KkotalkDTO.BulkSendResponse + */ + @Override + @TraceLogging + public KkotalkDTO.BulkSendResponse requestSendBulk(final KkotalkDTO.BulkSendRequest reqDTO) { + if(Checks.isEmpty(reqDTO.getProductCode())){ + throw BizRuntimeException.create("상품 코드는 필수 입니다."); + } + + List errors = new ArrayList<>(); + + List envelopes = reqDTO.getEnvelopes(); + for(int idx = 0; idx < envelopes.size(); idx++) { + final Set> list = validator.validate(envelopes.get(idx)); + if (!list.isEmpty()) { + + int finalIdx = idx; + errors.addAll(list.stream() + .map(row -> String.format("%s[%d]=%s", row.getPropertyPath(), finalIdx +1, row.getMessageTemplate())) + .toList() + ); + } + } + + for(int idx = 0; idx < envelopes.size(); idx++) { + final KkotalkDocDTO.Envelope envelope = envelopes.get(idx); + + if(envelope.getReviewExpiresAt() != null){ + if(envelope.getReviewExpiresAt().compareTo(envelope.getReadExpiresAt()) < 0){ + errors.add("reviewExpiresAt=재열람 만료일시를 최조 열람 만료일시 보다 큰 날짜로 입력해주세요."); + } + } + + if (Checks.isEmpty(envelope.getCi())) { + if (Checks.isEmpty(envelope.getName())) errors.add(String.format("받는이 이름은 필수입니다(name[%d] 번째 오류)", idx+1)); + if (Checks.isEmpty(envelope.getPhoneNumber())) errors.add(String.format("받는이 전화번호는 필수입니다(phoneNumber[%d] 번째 오류)", idx+1)); + if (Checks.isEmpty(envelope.getBirthday())) errors.add(String.format("받는이 생년월일은 필수입니다(birthday[%d] 번째 오류)", idx+1)); + } else { + final StringBuilder sb = new StringBuilder() + .append(StringUtils.defaultString(envelope.getName(), StringUtils.EMPTY)) + .append(StringUtils.defaultString(envelope.getPhoneNumber(), StringUtils.EMPTY)) + .append(StringUtils.defaultString(envelope.getBirthday(), StringUtils.EMPTY)); + + if(Checks.isNotEmpty(sb.toString())){ + errors.add(String.format("CI가 지정 되었습니다(받는이 정보 불필요:[%d] 번째 오류) .", idx+1)); + } + } + } + if(!errors.isEmpty()){ + throw BizRuntimeException.create(errors.toString()); + } + return webClient.exchangeKkotalk( + HOST + API_SEND[0].replace("{PRODUCT_CODE}", reqDTO.getProductCode()), + HttpMethod.valueOf(API_BULKSEND[1]), + JsonUtils.toJson(envelopes), + KkotalkDTO.BulkSendResponse.class, + getRlaybsnmInfo(reqDTO)); + } + + /** + *
+     * 대량(bulk) 문서 상태 조회 API : POST
+     * -.이용기관 서버에서 카카오페이 전자문서 서버로 문서 상태에 대한 조회를 요청 합니다.
+     * : 발송된 문서의 진행상태를 알고 싶은 경우, flow와 상관없이 요청 가능
+     * : polling 방식으로 호출할 경우, 호출 간격은 5초를 권장.
+     * : RECEIVE(수신,미수신) > READ(열람)/EXPIRED
+     * 
+ * @param reqDTO KkotalkDTO.BulkStatusRequest + * @return KkotalkDTO.BulkStatusResponse + */ + @Override + @TraceLogging + public KkotalkDTO.BulkStatusResponse findBulkStatus(final KkotalkDTO.BulkStatusRequest reqDTO) { + List errors = new ArrayList<>(); + + List envelopes = reqDTO.getEnvelopes(); + for(int idx = 0; idx < envelopes.size(); idx++) { + final String binderUuid = envelopes.get(idx); + if (Checks.isEmpty(binderUuid) || binderUuid.length() > 40) { + errors.add(String.format("문서 식별 번호는 40자를 넘을 수 없습니다[%d번째]", idx+1)); + } + } + if(!errors.isEmpty()) { + throw BizRuntimeException.create(errors.toString()); + } + return webClient.exchangeKkopay( + HOST + API_BULKSTATUS[0], + HttpMethod.valueOf(API_BULKSTATUS[1]), + JsonUtils.toJson(envelopes), + KkotalkDTO.BulkStatusResponse.class, + getRlaybsnmInfo(reqDTO)); + } + + @Override + public ApiResponseDTO findMyDocReadyAndMblPage(final KkotalkDocDTO.ValidTokenRequest reqDTO) { + final String url = HOST + API_VALID_TOKEN[0].replace(ENVELOPE_ID, reqDTO.getEnvelopeId()) + .replace("{TOKEN}", reqDTO.getToken()); + + // 유효성 검증 + final KkotalkDocDTO.ValidTokenResponse validTokenRes = webClient.exchangeKkotalk(url, HttpMethod.valueOf(API_VALID_TOKEN[1]), null, + KkotalkDocDTO.ValidTokenResponse.class, getRlaybsnmInfo(reqDTO)); + + // FIXME: USED ?? + if(!"USED".equals(validTokenRes.getStatus())){ + return ApiResponseDTO.error(validTokenRes.getErrorCode(), validTokenRes.getErrorMessage()); + } + + // 문서상태 변경 + final String url2 = HOST + API_MODIFY_STATUS[0].replace(ENVELOPE_ID, reqDTO.getEnvelopeId()); + + // 정상 : HttpStatus.NO_CONTENT(204) return + // error : body에 error_code, error_message return + final KkotalkDocDTO.KkotalkErrorDTO errorDTO = webClient.exchangeKkotalk(url2, HttpMethod.valueOf(API_MODIFY_STATUS[1]), null, KkotalkDocDTO.KkotalkErrorDTO.class, getRlaybsnmInfo(reqDTO)); + if(errorDTO != null){ + return ApiResponseDTO.error(errorDTO.getErrorCode(), errorDTO.getErrorMessage()); + } + + + return ApiResponseDTO.success(); + } + + //------------------------------------------------------------------------------------------------------------------- + private static List validate(T t, List errList) { + final Set> list = validator.validate(t); + + if(!list.isEmpty()) { + final List errors = list.stream() + .map(row -> String.format("%s=%s", row.getPropertyPath(), row.getMessageTemplate())) + .toList(); + + // 추가적인 유효성 검증이 필요 없는 경우 + if(errList == null){ + if(!errors.isEmpty()) throw BizRuntimeException.create(errors.toString()); + return null; + } + errList.addAll(errors); + } + return errList; + } + + private CmmEnsRlaybsnmDTO getRlaybsnmInfo(final CmmEnsRequestDTO request){ + return CmmEnsUtils.getRlaybsnmInfo(request.getSignguCode(), request.getFfnlgCode(), SndngSeCode.KAKAO); + } +} diff --git a/mens-api/src/main/java/kr/xit/ens/kakao/v2/web/KkotalkEltrcDocController.java b/mens-api/src/main/java/kr/xit/ens/kakao/v2/web/KkotalkEltrcDocController.java new file mode 100644 index 0000000..d26e017 --- /dev/null +++ b/mens-api/src/main/java/kr/xit/ens/kakao/v2/web/KkotalkEltrcDocController.java @@ -0,0 +1,201 @@ +package kr.xit.ens.kakao.v2.web; + +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.tags.Tag; +import kr.xit.biz.ens.model.kakao.v2.KkotalkDTO; +import kr.xit.biz.ens.model.kakao.v2.KkotalkDocDTO; +import kr.xit.core.model.ApiResponseDTO; +import kr.xit.core.model.IApiResponse; +import kr.xit.ens.kakao.v2.service.IKkotalkEltrcDocService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; + +/** + *
+ * description : 카카오톡 전자 문서 발송 controller
+ * packageName : kr.xit.ens.kakao.v2.web
+ * fileName    : KkotalkEltrcDocController
+ * author      : julim
+ * date        : 2024-08-12
+ * ======================================================================
+ * 변경일         변경자        변경 내용
+ * ----------------------------------------------------------------------
+ * 2024-08-12    julim       최초 생성
+ *
+ * 
+ */ +@Tag(name = "KkotalkEltrcDocController", description = "카카오톡 전자문서 API") +@Slf4j +@RequiredArgsConstructor +@RestController +@RequestMapping(value = "/api/ens/kakao/v2") +public class KkotalkEltrcDocController { + private final IKkotalkEltrcDocService service; + + /** + *
+     * 모바일웹 연계 문서발송 요청
+     * -.이용기관 서버에서 전자문서 서버로 문서발송 처리를 요청합니다.
+     * 
+ * @param reqDTO KkopayDocDTO.SendRequest + * @return ApiResponseDTO + */ + @Operation(summary = "문서발송 요청", description = "카카오톡 전자문서 서버로 문서발송 처리를 요청") + @io.swagger.v3.oas.annotations.parameters.RequestBody(required = true, content = { + @Content(mediaType = "application/json", examples = { + @ExampleObject( + name = "D10", + value = """ + { + "productCode": "D10_1", + "envelope": { + "title": "전자문서", + "content": { + "link": "https://nps.or.kr" + }, + "guide": "국민연금 공단에서 보내는 문서입니다.", + "payload": "이용기관 페이로드", + "readExpiresAt": "2023-12-31T10:00:00", + "reviewExpiresAt": "2023-12-31T13:00:00", + "useNonPersonalizedNotification": true, + "phoneNumber": "01099999999", + "name": "홍길동", + "birthday": "20000303", + "externalId": "external_id1" + }, + "signguCode": "51110", + "ffnlgCode": "11" + } + """), + @ExampleObject( + name = "D11", + value = """ + { + "productCode": "D11_1", + "envelope": { + "title": "전자문서", + "content": { + "html": "

MyFirstHeading

Myfirstparagraph.

" + }, + "guide": "국민연금 공단에서 보내는 문서입니다.", + "readExpiresAt": "2023-12-31T10:00:00", + "reviewExpiresAt": "2023-12-31T13:00:00", + "ci": "${CI}" + }, + "signguCode": "51110", + "ffnlgCode": "11" + } + """) + }) + }) + @PostMapping(value = "/envelopes", produces = MediaType.APPLICATION_JSON_VALUE) + public IApiResponse requestSend( + @RequestBody final KkotalkDTO.SendRequest reqDTO + ) { + return ApiResponseDTO.success(service.requestSend(reqDTO)); + } + + /** + *
+     * 토큰 유효성 검증(Redirect URL  접속 허용/불허)
+     * 
+ * @param reqDTO KkopayDocDTO.ValidTokenRequest + * @return ApiResponseDTO + */ + @Operation(summary = "토큰 유효성 검증", description = "Redirect URL 접속 허용/불허") + @PostMapping(value = "/validToken", produces = MediaType.APPLICATION_JSON_VALUE) + public IApiResponse validToken( + @RequestBody final KkotalkDTO.ValidTokenRequest reqDTO + ) { + return ApiResponseDTO.success(service.validToken(reqDTO)); + } + + /** + *
+     * 문서 열람처리 API
+     * -.문서에 대해서 열람 상태로 변경. 사용자가 문서열람 시(OTT 검증 완료 후 페이지 로딩 완료 시점) 반드시 문서 열람 상태 변경 API를 호출해야 함.
+     * -.미 호출 시 아래와 같은 문제 발생
+     * 1)유통증명시스템을 사용하는 경우 해당 API를 호출한 시점으로 열람정보가 등록되어 미 호출 시 열람정보가 등록 되지 않음.
+     * 2)문서상태조회 API(/v1/envelopes/${ENVELOPE_ID}/read) 호출 시 read_at최초 열람시간) 데이터가 내려가지 않음.
+     * 
+ * @param reqDTO KkotalkDocDTO.EnvelopeStatusResponse + * @return ApiResponseDTO + */ + @Operation(summary = "문서열람처리(문서 상태 변경)", description = "문서열람처리(문서 상태 변경)") + @PostMapping(value = "/modifyStatus", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) + public IApiResponse modifyStatus( + @RequestBody final KkotalkDocDTO.EnvelopeId reqDTO + ) { + service.modifyStatus(reqDTO); + return ApiResponseDTO.empty(); + } + + /** + *
+     * 문서 상태 조회 API
+     * -.이용기관 서버에서 카카오페이 전자문서 서버로 문서 상태에 대한 조회를 요청 합니다.
+     * : 발송된 문서의 진행상태를 알고 싶은 경우, flow와 상관없이 요청 가능
+     * : polling 방식으로 호출할 경우, 호출 간격은 5초를 권장.
+     * -.doc_box_status 상태변경순서
+     * : SENT(송신) > RECEIVED(수신) > READ(열람)/EXPIRED(미열람자료의 기한만료)
+     * 
+ * @param reqDTO KkotalkDTO.EnvelopeId + * @return ApiResponseDTO + */ + @Operation(summary = "문서 상태 조회", description = "문서 상태 조회") + @PostMapping(value = "/findStatus", produces = MediaType.APPLICATION_JSON_VALUE) + public IApiResponse findStatus( + @RequestBody final KkotalkDocDTO.EnvelopeId reqDTO + ) { + return ApiResponseDTO.success(service.findStatus(reqDTO)); + } + +// /** +// *
+//      * 모바일웹 연계 문서발송 요청
+//      * -.이용기관 서버에서 전자문서 서버로 문서발송 처리를 요청합니다.
+//      * 
+// * @param reqDTO KkopayDocBulkDTO.BulkSendRequests +// * @return BulkStatusResponses.BulkSendResponses +// */ +// /* +// @Operation(summary = "대량 문서발송 요청 -> batch sendBulks 에서 호출", description = "카카오페이 전자문서 서버로 대량 문서발송 처리를 요청 -> batch sendBulks 에서 호출") +// @PostMapping(value = "/documents/bulk", produces = MediaType.APPLICATION_JSON_VALUE) +// public BulkSendResponses requestSendBulk( +// @RequestBody final BulkSendRequests reqDTO +// ) { +// return service.requestSendBulk(reqDTO); +// } +// */ + @Operation(summary = "대량 문서발송 요청 -> batch sendBulks 에서 호출", description = "카카오페이 전자문서 서버로 대량 문서발송 처리를 요청 -> batch sendBulks 에서 호출") + @PostMapping(value = "/envelopes/bulk", produces = MediaType.APPLICATION_JSON_VALUE) + public IApiResponse requestSendBulk( + @RequestBody final KkotalkDTO.BulkSendRequest reqDTO + ) { + return ApiResponseDTO.success(service.requestSendBulk(reqDTO)); + } + + /** + *
+     * 모바일웹 연계 문서발송 요청
+     * -.이용기관 서버에서 전자문서 서버로 문서발송 처리를 요청합니다.
+     * 
+ * @param reqDTO KkotalkDocDTO.BulkStatusRequest + * @return KkotalkDocDTO.BulkStatusResponse + */ + @Operation(summary = "대량 문서 상태 조회 요청 -> batch statusBulks 에서 호출", description = "카카오페이 전자문서 서버로 대량 문서 상태 조회 요청 -> batch statusBulks 에서 호출") + @PostMapping(value = "/documents/bulk/status", produces = MediaType.APPLICATION_JSON_VALUE) + public IApiResponse findBulkStatus( + @RequestBody final KkotalkDTO.BulkStatusRequest reqDTO + ) { + return ApiResponseDTO.success(service.findBulkStatus(reqDTO)); + } +} diff --git a/mens-api/src/main/resources/config/application-ens.yml b/mens-api/src/main/resources/config/application-ens.yml index 3e76d03..6ccc6dc 100644 --- a/mens-api/src/main/resources/config/application-ens.yml +++ b/mens-api/src/main/resources/config/application-ens.yml @@ -14,16 +14,24 @@ app: maxPoolSize: 10 kakao: bulk-max-cnt: 10 - host: https://docs-gw.kakaopay.com # token: dd394da7f66211eb9cbe46e139ceffc2 # uuid: CON-41ef0535f67211ebbdedd2e6ed332381 api: - send: /v1/documents;POST - validToken: /v1/{document_binder_uuid}/tokens/{tokens};GET - modifyStatus: /v1/documents/{document_binder_uuid};POST - findStatus: /v1/documents/{document_binder_uuid}/status;GET - bulksend: /v1/documents/bulk;POST - bulkstatus: /v1/documents/bulk/status;POST + pay: + host: https://docs-gw.kakaopay.com + send: /v1/documents;POST + bulksend: /v1/documents/bulk;POST + validToken: /v1/{document_binder_uuid}/tokens/{tokens};GET + modifyStatus: /v1/documents/{document_binder_uuid};POST + bulkstatus: /v1/documents/bulk/status;POST + findStatus: /v1/documents/{document_binder_uuid}/status;GET + talk: + host: https://edoc-gw.kakao.com + send: /v1/envelopes/{PRODUCT_CODE};POST + bulksend: /v1/bulk/envelopes/{PRODUCT_CODE};POST + validToken: /v1/envelopes/{ENVELOPE_ID}/tokens/{TOKEN}/verify;GET + modifyStatus: /v1/envelopes/{ENVELOPE_ID}/read;POST + bulkstatus: /v1/envelopes/status;POST nice: host: https://svc.niceapi.co.kr:22001 # signgu-code: 11000 diff --git a/mens-batch/src/main/java/kr/xit/biz/ens/service/EnsBatchExtractService.java b/mens-batch/src/main/java/kr/xit/biz/ens/service/EnsBatchExtractService.java index a21f4b1..f95c927 100644 --- a/mens-batch/src/main/java/kr/xit/biz/ens/service/EnsBatchExtractService.java +++ b/mens-batch/src/main/java/kr/xit/biz/ens/service/EnsBatchExtractService.java @@ -88,7 +88,7 @@ public class EnsBatchExtractService extends AbstractService implements // 모바일 페이지 컨텐트 생성 if (Checks.isNotEmpty(t.getDocument_binder_uuid())) { mapper.insertMobilePageManage(t.getExternal_document_uuid()); - code = ApiConstants.DocBoxStatus.SENT.getCode(); + code = ApiConstants.KkopayDocStatus.SENT.getCode(); }else{ code = t.getError_code(); } diff --git a/mens-batch/src/main/java/kr/xit/biz/ens/service/EnsBatchSendService.java b/mens-batch/src/main/java/kr/xit/biz/ens/service/EnsBatchSendService.java index 6be6e12..381403b 100644 --- a/mens-batch/src/main/java/kr/xit/biz/ens/service/EnsBatchSendService.java +++ b/mens-batch/src/main/java/kr/xit/biz/ens/service/EnsBatchSendService.java @@ -66,7 +66,7 @@ import lombok.extern.slf4j.Slf4j; public class EnsBatchSendService extends AbstractService implements IEnsBatchSendService { @Value("${app.contract.host}") private String apiHost; - @Value("${app.contract.kakao.api.bulksend}") + @Value("${app.contract.kakao.api.pay.bulksend}") private String apiKkoBulkSend; @Value("${app.contract.kt.api.bulksend}") private String apiKtBcBulkSend; diff --git a/mens-batch/src/main/java/kr/xit/biz/ens/service/EnsBatchStatusService.java b/mens-batch/src/main/java/kr/xit/biz/ens/service/EnsBatchStatusService.java index f071f16..3c1bc10 100644 --- a/mens-batch/src/main/java/kr/xit/biz/ens/service/EnsBatchStatusService.java +++ b/mens-batch/src/main/java/kr/xit/biz/ens/service/EnsBatchStatusService.java @@ -51,7 +51,7 @@ import lombok.RequiredArgsConstructor; public class EnsBatchStatusService extends AbstractService implements IEnsBatchStatusService { @Value("${app.contract.host}") private String apiHost; - @Value("${app.contract.kakao.api.bulkstatus}") + @Value("${app.contract.kakao.api.pay.bulkstatus}") private String apiKkoBulkStatus; @Value("${app.contract.kakao.bulk-max-cnt}") diff --git a/mens-batch/src/main/java/kr/xit/biz/ens/web/ApiCallTestController.java b/mens-batch/src/main/java/kr/xit/biz/ens/web/ApiCallTestController.java index 8eb3ea3..b0041c7 100644 --- a/mens-batch/src/main/java/kr/xit/biz/ens/web/ApiCallTestController.java +++ b/mens-batch/src/main/java/kr/xit/biz/ens/web/ApiCallTestController.java @@ -43,9 +43,9 @@ import lombok.RequiredArgsConstructor; public class ApiCallTestController { @Value("${app.contract.host}") private String apiHost; - @Value("${app.contract.kakao.api.bulksend}") + @Value("${app.contract.kakao.api.pay.bulksend}") private String apiBulkSend; - @Value("${app.contract.kakao.api.bulkstatus}") + @Value("${app.contract.kakao.api.pay.bulkstatus}") private String apiBulkStatus; private final ApiWebClientUtil apiWebClient; @@ -97,7 +97,7 @@ public class ApiCallTestController { .append("http://localhost:8081") .append("/api/ens/kakao/v1/documents"); - return apiWebClient.exchangeKko( + return apiWebClient.exchangeKkopay( url.toString(), HttpMethod.POST, JsonUtils.toJson(reqDTO), @@ -154,7 +154,7 @@ public class ApiCallTestController { ) { final String url = apiHost + apiBulkSend; - return apiWebClient.exchangeKko( + return apiWebClient.exchangeKkopay( url, HttpMethod.POST, JsonUtils.toJson(reqDTO), @@ -190,7 +190,7 @@ public class ApiCallTestController { ) { final String url = apiHost + apiBulkStatus;; - return apiWebClient.exchangeKko( + return apiWebClient.exchangeKkopay( url, HttpMethod.POST, JsonUtils.toJson(reqDTO), diff --git a/mens-core/src/main/java/kr/xit/biz/common/ApiConstants.java b/mens-core/src/main/java/kr/xit/biz/common/ApiConstants.java index 0b9a362..42ffcab 100644 --- a/mens-core/src/main/java/kr/xit/biz/common/ApiConstants.java +++ b/mens-core/src/main/java/kr/xit/biz/common/ApiConstants.java @@ -107,11 +107,13 @@ public class ApiConstants { } /** + *
      * 카카오페이 문서 상태
      * SENT(송신) > RECEIVED(수신) > READ(열람)/EXPIRED(미열람자료의 기한만료)
+     * 
*/ @Getter - public enum DocBoxStatus { + public enum KkopayDocStatus { SENT("SENT") , RECEIVED("RECEIVED") , READ("READ") @@ -120,7 +122,28 @@ public class ApiConstants { private final String code; - DocBoxStatus(String code) { + KkopayDocStatus(String code) { + this.code = code; + } + + } + + /** + *
+     * 카카오톡 문서 상태
+     * RECEIVE(수신, 미열람) > READ(열람)/EXPIRED(최초열람만료일시 또는 재열람 만료일시 초과)
+     * 
+ */ + @Getter + public enum KkotalkDocStatus { + RECEIVED("RECEIVE") + , READ("READ") + , EXPIRED("EXPIRED") + ; + + private final String code; + + KkotalkDocStatus(String code) { this.code = code; } diff --git a/mens-core/src/main/java/kr/xit/biz/ens/model/kakao/v1/KkopayDocAttrDTO.java b/mens-core/src/main/java/kr/xit/biz/ens/model/kakao/v1/KkopayDocAttrDTO.java index 8704fc7..28fff79 100644 --- a/mens-core/src/main/java/kr/xit/biz/ens/model/kakao/v1/KkopayDocAttrDTO.java +++ b/mens-core/src/main/java/kr/xit/biz/ens/model/kakao/v1/KkopayDocAttrDTO.java @@ -220,11 +220,11 @@ public class KkopayDocAttrDTO { * READ - OTT 검증 완료후 문서 상태 변경 API 호출에 성공한 상태 * EXPIRED - 미열람 문서에 대한 열람 만료 시간이 지난 상태 * - * @see ApiConstants.DocBoxStatus + * @see ApiConstants.KkopayDocStatus */ @Schema(requiredMode = Schema.RequiredMode.REQUIRED, maxLength = 20, title = "진행상태(max:20)", example = " ") @Size(min = 1, max = 20, message = "진행상태는 필수입니다(max:20)") - private ApiConstants.DocBoxStatus doc_box_status; + private ApiConstants.KkopayDocStatus doc_box_status; /** * 송신 시간(long max:10) diff --git a/mens-core/src/main/java/kr/xit/biz/ens/model/kakao/v2/KkotalkDTO.java b/mens-core/src/main/java/kr/xit/biz/ens/model/kakao/v2/KkotalkDTO.java new file mode 100644 index 0000000..6d7f2da --- /dev/null +++ b/mens-core/src/main/java/kr/xit/biz/ens/model/kakao/v2/KkotalkDTO.java @@ -0,0 +1,116 @@ +package kr.xit.biz.ens.model.kakao.v2; + +import java.util.List; + +import javax.validation.Valid; +import javax.validation.constraints.Size; + +import io.swagger.v3.oas.annotations.media.Schema; +import kr.xit.biz.ens.model.cmm.CmmEnsRequestDTO; +import kr.xit.core.model.IApiResponse; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +/** + *
+ * description : 카카오톡 전자문서 요청 파라메터 및 응답 DTO
+ *
+ * packageName : kr.xit.ens.model.kakao.v2
+ * fileName    : KkotalkDTO
+ * author      : limju
+ * date        : 2024-08-12
+ * ======================================================================
+ * 변경일         변경자        변경 내용
+ * ----------------------------------------------------------------------
+ * 2024-08-12    limju       최초 생성
+ *
+ * 
+ */ +public class KkotalkDTO extends KkotalkDocDTO { + + //------------------ envelop ---------------------------------------------------------------------- + @Schema(name = "SendRequest DTO", description = "문서발송 request DTO") + @Data + @NoArgsConstructor + @AllArgsConstructor + @SuperBuilder + @EqualsAndHashCode(callSuper = false) + public static class SendRequest extends CmmEnsRequestDTO { + /** + *
+         * 상품 코드 - 필수
+         * D10_1|D10_2|D11_1|D11_2
+         * 
+ */ + @Schema(requiredMode = Schema.RequiredMode.REQUIRED, title = "상품코드", example = "D10_2", allowableValues = {"D10_1","D10_2","D11_1","D11_2"}) + @Size(min = 3, max = 5, message = "상품 코드는 필수 입니다(\"D10_1\",\"D10_2\",\"D11_1\",\"D11_2\"") + @Builder.Default + private String productCode = "D10_2"; + + @Schema(requiredMode = Schema.RequiredMode.REQUIRED) + @Valid + private Envelope envelope; + } + //------------------ envelop ---------------------------------------------------------------------- + + //------------------ bulk ---------------------------------------------------------------------- + @Schema(name = "BulkSendRequest DTO", description = "문서발송[bulk] request DTO") + @Data + @NoArgsConstructor + @AllArgsConstructor + @SuperBuilder + @EqualsAndHashCode(callSuper = false) + public static class BulkSendRequest extends CmmEnsRequestDTO { + /** + *
+         * 상품 코드 - 필수
+         * D10_1|D10_2|D11_1|D11_2
+         * 
+ */ + @Schema(requiredMode = Schema.RequiredMode.REQUIRED, title = "상품코드", example = "D10_2", allowableValues = {"D10_1","D10_2","D11_1","D11_2"}) + @Size(min = 3, max = 5, message = "상품 코드는 필수 입니다(\"D10_1\",\"D10_2\",\"D11_1\",\"D11_2\"") + @Builder.Default + private String productCode = "D10_2"; + + @Schema(requiredMode = Schema.RequiredMode.REQUIRED) + @Valid + private List envelopes; + } + + @Schema(name = "BulkSendResponse DTO", description = "문서발송(bulk) response DTO") + @Data + @NoArgsConstructor + @AllArgsConstructor + @SuperBuilder + @EqualsAndHashCode(callSuper = false) + public static class BulkSendResponse extends KkotalkErrorDTO { + private List envelopeIds; + } + + @Schema(name = "BulkStatusRequest DTO", description = "문서상태조회[bulk] request DTO") + @Data + @NoArgsConstructor + @AllArgsConstructor + @SuperBuilder + @EqualsAndHashCode(callSuper = false) + public static class BulkStatusRequest extends CmmEnsRequestDTO { + @Schema(requiredMode = Schema.RequiredMode.REQUIRED) + @Valid + private List envelopes; + } + + @Schema(name = "BulkStatusResponse DTO", description = "문서상태조회(bulk) response DTO") + @Data + @NoArgsConstructor + @AllArgsConstructor + @SuperBuilder + public static class BulkStatusResponse implements IApiResponse { + private List envelopeStatus; + } + //------------------ bulk ---------------------------------------------------------------------- + +} diff --git a/mens-core/src/main/java/kr/xit/biz/ens/model/kakao/v2/KkotalkDocDTO.java b/mens-core/src/main/java/kr/xit/biz/ens/model/kakao/v2/KkotalkDocDTO.java new file mode 100644 index 0000000..7904213 --- /dev/null +++ b/mens-core/src/main/java/kr/xit/biz/ens/model/kakao/v2/KkotalkDocDTO.java @@ -0,0 +1,427 @@ +package kr.xit.biz.ens.model.kakao.v2; + +import javax.validation.Valid; +import javax.validation.constraints.Pattern; +import javax.validation.constraints.Size; + +import org.hibernate.validator.constraints.NotEmpty; + +import com.fasterxml.jackson.annotation.JsonInclude; + +import io.swagger.v3.oas.annotations.media.Schema; +import kr.xit.biz.common.ApiConstants; +import kr.xit.biz.ens.model.cmm.CmmEnsRequestDTO; +import kr.xit.core.model.IApiResponse; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +/** + *
+ * description : 카카오톡 전자문서 DTO
+ *               API 호출시 속성값이 없는 경우 속성을 json에 포함시키지 않는다.
+ *               => @JsonInclude(JsonInclude.Include.NON_EMPTY) 설정
+ * packageName : kr.xit.biz.ens.model.kakao.v2
+ * fileName    : KkotalkDocDTO
+ * author      : limju
+ * date        : 2024-08-12
+ * ======================================================================
+ * 변경일         변경자        변경 내용
+ * ----------------------------------------------------------------------
+ * 2024-08-12    limju       최초 생성
+ *
+ * 
+ */ +public class KkotalkDocDTO { + + //------------------- Envelope ------------------------------------------------------------------------------------------------ + @Schema(name = "Envelope", description = "문서발송(단건) 요청 파라메터 DTO") + @Data + @NoArgsConstructor + @AllArgsConstructor + @SuperBuilder + @JsonInclude(JsonInclude.Include.NON_EMPTY) + public static class Envelope implements IApiResponse { + /** + * 발송할 문서의 제목 : 필수 - max 40자 + */ + @Schema(requiredMode = Schema.RequiredMode.REQUIRED, title = "발송할 문서의 제목", example = "문서 제목") + @Size(max = 40, message = "발송할 문서의 제목은 필수 입니다(max:40)") + private String title; + + @Schema(requiredMode = Schema.RequiredMode.REQUIRED) + @Valid + private Content content; + + /** + *
+         * 문서 원문(열람정보)에 대한 hash 값
+         * D10_2 상품 사용시 필수
+         * 
+ */ + @Schema(title = "문서 원문(열람정보)에 대한 hash 값", example = "6EFE827AC88914DE471C621AE") + @Pattern(regexp = "^$|^[a-fA-F0-9]{44}$|^[a-fA-F0-9]{64}$", message = "문서 해시값은 44자 또는 64자의 16진수여야 합니다") + private String hash; + + /** + * 문서 정보 안내 화면에 노출할 문구(최대: 500자) - 필수 + */ + @Schema(requiredMode = Schema.RequiredMode.REQUIRED, title = "문서 정보 안내 화면에 노출할 문구(최대: 500자)", example = "문서 정보 안내 화면에 노출할 문구") + @Size(min=1, max = 200, message = "문서 정보 안내 화면에 노출할 문구(max=500)") + private String guide; + + /** + * payload + */ + @Schema(title = "payload", example = "payload 파라미터 입니다.") + @Size(max = 200, message = "payload(max=200)") + private String payload; + + /** + *
+         * 최초 열람 만료 일시 지정(최대: 요청 일시로부터 6개월 이내) - 필수
+         * yyyy-MM-dd'T'HH:mm:ss 형식 > DateUtils.getTimeTOfNow()
+         * 
+ */ + @Schema(requiredMode = Schema.RequiredMode.REQUIRED, title = "최초열람마감시간(yyyy-MM-dd'T'HH:mm:ss)", example = "2023-12-31T10:00:00") + @Size(min = 19, max = 19, message = "최초열람마감시간(yyyy-MM-dd'T'HH:mm:ss)") + private String readExpiresAt; + + /** + *
+         * 재열람 만료 일시 지정
+         * 최소: readExpiredAt 이후의 시각
+         * 최대값: 9999-12-31'T'23:59:59, null 값이면 무제한
+         * 비고: DX0 상품인 경우 최대값은 요청일로부터 6개월 이내
+         * 
+ */ + @Schema(title = "재열람 만료 일시(yyyy-MM-dd'T'HH:mm:ss)", example = "2023-12-31T13:00:00") + @Size(max = 19, message = "재열람 만료 일시(yyyy-MM-dd'T'HH:mm:ss)") + private String reviewExpiresAt; + + /** + *
+         * 알림톡 내용에 개인정보 제거 여부
+         * default: false
+         * 
+ */ + @Schema(title = "알림톡 내용에 개인정보 제거 여부", example = " ", defaultValue = "false", allowableValues = {"true", "false"}) + @Builder.Default + private Boolean useNonPersonalizedNotification = false; + + /** + * 수신자 CI + */ + @Schema(title = "받는이 CI", example = " ") + @Size(max=88, message = "수신자 CI(max=88)") + private String ci; + + /** + *
+         * 수신자 전화번호
+         * ci 미전송시 필수
+         * 
+ */ + @Schema(title = "수신자 전화번호", example = "01012345678") + @Pattern(regexp = "^$|^\\d{11}$", message = "수신자 전화번호(max=11)") + private String phoneNumber; + + /** + *
+         * 수신자 이름
+         * ci 미전송시 필수
+         * 
+ */ + @Schema(title = "수신자 이름", example = "김페이") + @Size(max = 26, message = "수신자 이름(max=26)") + private String name; + + /** + *
+         * 수신자 생년월일 (YYYY-MM-DD 형식)
+         * ci 미전송시 필수
+         * 
+ */ + @Schema(requiredMode = Schema.RequiredMode.AUTO, title = "수신자 생년월일 (YYYYMMDD 형식)", example = "19801101") + @Pattern(regexp = "^$|^(19\\d{2}|20\\d{2})(0[1-9]|1[0-2])(0[1-9]|[1-2]\\d|3[0-1])$", message = "수신자 생년월일(YYYYMMDD)") + private String birthday; + + /** + *
+         * 문서매핑용 식별자 - 최대 40자
+         * 
+ */ + @Schema(requiredMode = Schema.RequiredMode.AUTO, title = "문서매핑용 식별자", example = " ") + @Size(max=40, message = "문서매핑용 식별자(max=40)") + private String externalId; + } + + @Schema(name = "Content", description = "문서 원문 웹링크 또는 HTML") + @Data + @NoArgsConstructor + @AllArgsConstructor + @SuperBuilder + @JsonInclude(JsonInclude.Include.NON_EMPTY) + public static class Content { + /** + *
+         * 본인인증 후 사용자에게 보여줄 웹페이지 주소
+         * D10 상품 사용시 필수
+         * 1000자 이하의 URL 형식
+         * 
+ */ + @Schema(title = "본인인증 후 사용자에게 보여줄 웹페이지 주소", example = "http://ipAddress/api/kakaopay/v1/ott") + @Size(max = 1000, message = "본인인증후 사용자에게 보여줄 페이지 주소는 필수입니다(max=1000)") + private String link; + + /** + *
+         * HTML 전문
+         * D11 상품 사용시 필수(최대 64KB)
+         */
+        @Schema(title = "HTML 전문", example = " ")
+        @Size(max = 65536, message = "HTML 전문(max=64KB)")
+        private String html;
+    }
+
+    //------------------- ValidToken ------------------------------------------------------------------------------------------------
+    @Schema(name = "ValidTokenRequest DTO", description = "카카오톡 전자문서 토큰 유효성 검증 파라메터 DTO")
+    @Data
+    @SuperBuilder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @EqualsAndHashCode(callSuper = false)
+    public static class ValidTokenRequest extends CmmEnsRequestDTO {
+        /**
+         * 문서 고유 ID, 34자로 고정
+         */
+        @Schema(requiredMode = Schema.RequiredMode.REQUIRED, title = "문서 고유 ID, 34자로 고정", example = " ")
+        // FIXME: 테스트후 주석 제거!!!
+        //@Size(min = 34, max = 34, message = "문서 고유 ID는 필수입니다(34자)")
+        private String envelopeId;
+
+        /**
+         * 카카오톡 전자문서 서버에서 생성한 일회용 토큰 : 필수
+         */
+        @Schema(requiredMode = Schema.RequiredMode.REQUIRED, title = "카카오톡 전자문서 서버에서 생성한 일회용 토큰", example = "CON-cc375944ae3d11ecb91e42193199ee3c")
+        @NotEmpty(message = "카카오톡 전자문서 서버 토큰은 필수입니다")
+        private String token;
+    }
+
+    @Schema(name = "ValidTokenResponse DTO", description = "카카오톡 토큰 검증 response DTO")
+    @Data
+    @SuperBuilder
+    @NoArgsConstructor
+    @AllArgsConstructor
+    @EqualsAndHashCode(callSuper = true)
+    public static class ValidTokenResponse extends KkotalkErrorDTO implements IApiResponse {
+        /**
+         * 문서 고유 ID, 34자로 고정
+         */
+        private String envelopeId;
+
+        /**
+         * 
+         * 문서매핑용 식별자 - 최대 40자
+         * 
+ */ + private String externalId; + + /** + *
+         * 진행상태 : 토큰검증시 필수
+         * 수신,미열람|열람|최초열람만료일시 또는 재열람 만료일시 초과
+         * RECEIVE|READ|EXPIRED
+         * 
+ * @see ApiConstants.KkotalkDocStatus + */ + private ApiConstants.KkotalkDocStatus status; + + /** + *
+         * 문서 송신 일시 - 토큰검증시 필수
+         * yyyy-MM-dd'T'HH:mm:ss 형식 > DateUtils.getTimeTOfNow()
+         * 
+         * 문서 수신 일시 - 토큰검증시 필수
+         * yyyy-MM-dd'T'HH:mm:ss 형식 > DateUtils.getTimeTOfNow()
+         * 
+ */ + private String receivedAt; + + /** + *
+         * 문서 열람 일시
+         * yyyy-MM-dd'T'HH:mm:ss 형식 > DateUtils.getTimeTOfNow()
+         * 
+ */ + private String readAt; + + /** + *
+         * 문서 열람 인증 일시
+         * yyyy-MM-dd'T'HH:mm:ss 형식 > DateUtils.getTimeTOfNow()
+         * 
+ */ + private String authenticatedAt; + + /** + *
+         * 토큰 검증 일시
+         * yyyy-MM-dd'T'HH:mm:ss 형식 > DateUtils.getTimeTOfNow()
+         * 
+ */ + private String ottVerifiedAt; + + /** + *
+         * 사용자의 알림톡 수신 가능 여부
+         * 
+ */ + private Boolean isNotificationUnavailable; + + /** + *
+         * 사용자의 알림톡 수신 일시
+         * yyyy-MM-dd'T'HH:mm:ss 형식 > DateUtils.getTimeTOfNow()
+         * 
+ */ + private String userNotifiedAt; + + /** + * 문서 발송하기 API 요청 시 전달한 페이로드 + */ + private String payload; + } + //------------------ ValidToken ---------------------------------------------------------------------- + + //------------------ DocStatus ---------------------------------------------------------------------- + @Schema(name = "EnvelopeStatusResponse DTO", description = "카카오톡 상태조회 response DTO") + @Data + @SuperBuilder + @NoArgsConstructor + @AllArgsConstructor + @JsonInclude(JsonInclude.Include.NON_EMPTY) + @EqualsAndHashCode(callSuper = true) + public static class EnvelopeStatusResponse extends ValidTokenResponse implements IApiResponse { + /** /** + *
+         * 문서 열람 만료 일시
+         * yyyy-MM-dd'T'HH:mm:ss 형식 > DateUtils.getTimeTOfNow()
+         * 
+ */ + private String readExpiredAt; + + /** /** + *
+         * 유통정보의 수신 시각
+         * 공인전자주소 활성화와 문서 수신이 모두 완료된 시각
+         * yyyy-MM-dd'T'HH:mm:ss 형식 > DateUtils.getTimeTOfNow()
+         * 
+ */ + private String distributionReceivedAt; + + /** + * 에러코드 + */ + private String errorCode; + + /** + * 에러 메세지 + */ + private String errorMessage; + } + //------------------ DocStatus ---------------------------------------------------------------------- + + + //------------------ SendResponse ---------------------------------------------------------------------- + @Schema(name = "EnvelopeRes DTO", description = "문서발송 응답 DTO") + @Data + @NoArgsConstructor + @AllArgsConstructor + @SuperBuilder + //@JsonInclude(JsonInclude.Include.NON_EMPTY) + public static class EnvelopeRes { + /** + * 문서 고유 ID, 34자로 고정 + */ + @Schema(requiredMode = Schema.RequiredMode.REQUIRED, title = "문서 고유 ID, 34자로 고정", example = " ") + @Size(min = 34, max = 34, message = "문서 고유 ID는 필수입니다(34자)") + private String envelopeId; + + /** + *
+         * 문서매핑용 식별자 - 최대 40자
+         * 
+ */ + private String externalId; + } + + @Schema(name = "KkotalkErrorResponse DTO", description = "카카오톡 에러 응답 DTO") + @Data + @NoArgsConstructor + @AllArgsConstructor + @SuperBuilder + public static class KkotalkErrorDTO { + /** + * 에러코드 + */ + private String errorCode; + + /** + * 에러 메세지 + */ + private String errorMessage; + + /** + * 트래킹(Tracking) ID + */ + private String edocGtid; + } + + + @Schema(name = "SendResponse DTO", description = "문서발송 응답 DTO") + @Data + @NoArgsConstructor + @AllArgsConstructor + @SuperBuilder + @EqualsAndHashCode(callSuper = false) + public static class SendResponse extends KkotalkErrorDTO { + /** + * 문서 고유 ID, 34자로 고정 - 필수 + */ + private String envelopeId; + + /** + *
+         * 문서매핑용 식별자 - 최대 40자
+         * 
+ */ + private String externalId; + } + + //------------------------------------------------------------------------------------- + + @Schema(name = "EnvelopeId DTO", description = "EnvelopeId DTO") + @Data + @NoArgsConstructor + @AllArgsConstructor + @SuperBuilder + public static class EnvelopeId extends CmmEnsRequestDTO{ + /** + * 문서 고유 ID, 34자로 고정 + */ + @Schema(requiredMode = Schema.RequiredMode.REQUIRED, title = "문서 고유 ID, 34자로 고정", example = " ") + // FIXME: 테스트후 주석 제거!!! + //@Size(min = 34, max = 34, message = "문서 고유 ID는 필수입니다(34자)") + private String envelopeId; + } + +} diff --git a/mens-core/src/main/java/kr/xit/core/consts/Constants.java b/mens-core/src/main/java/kr/xit/core/consts/Constants.java index b959db1..8f805fd 100644 --- a/mens-core/src/main/java/kr/xit/core/consts/Constants.java +++ b/mens-core/src/main/java/kr/xit/core/consts/Constants.java @@ -1,6 +1,8 @@ package kr.xit.core.consts; + import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; + import org.springframework.http.HttpHeaders; /** @@ -74,7 +76,12 @@ public class Constants { AUTHORITIES_KEY("role"), TOKEN_USER_NAME("userName"), TOKEN_USER_MAIL("userEmail"), - TOKEN_USER_ID("userId"); + TOKEN_USER_ID("userId"), + + PARTNER_REST_API_KEY("Target-Authorization"), + DEALER_REST_API_KEY("Authorization"), + SETTLE_ID("settle-Id"), + ; private final String code; diff --git a/mens-core/src/main/java/kr/xit/core/spring/util/ApiWebClientUtil.java b/mens-core/src/main/java/kr/xit/core/spring/util/ApiWebClientUtil.java index f09d5c6..2f68305 100644 --- a/mens-core/src/main/java/kr/xit/core/spring/util/ApiWebClientUtil.java +++ b/mens-core/src/main/java/kr/xit/core/spring/util/ApiWebClientUtil.java @@ -5,6 +5,17 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; + +import org.apache.commons.lang3.ObjectUtils; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.MediaType; +import org.springframework.http.client.MultipartBodyBuilder; +import org.springframework.stereotype.Component; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.reactive.function.BodyInserters; +import org.springframework.web.util.UriComponentsBuilder; + import kr.xit.biz.ens.model.cmm.CmmEnsRlaybsnmDTO; import kr.xit.core.consts.Constants; import kr.xit.core.exception.ClientErrorException; @@ -15,15 +26,6 @@ import kr.xit.core.spring.config.support.WebClientConfig; import kr.xit.core.support.utils.JsonUtils; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.ObjectUtils; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.MediaType; -import org.springframework.http.client.MultipartBodyBuilder; -import org.springframework.stereotype.Component; -import org.springframework.web.multipart.MultipartFile; -import org.springframework.web.reactive.function.BodyInserters; -import org.springframework.web.util.UriComponentsBuilder; /** *
@@ -104,7 +106,7 @@ public class ApiWebClientUtil {
      * @param ensDTO CmmEnsRlaybsnmDTO
      * @return rtnClzz
      */
-    public  T exchangeKko(final String url, final HttpMethod method, final Object body, final Class rtnClzz, final CmmEnsRlaybsnmDTO ensDTO) {
+    public  T exchangeKkopay(final String url, final HttpMethod method, final Object body, final Class rtnClzz, final CmmEnsRlaybsnmDTO ensDTO) {
 
         Map map = new HashMap<>();
         map.put(HttpHeaders.AUTHORIZATION,
@@ -114,6 +116,30 @@ public class ApiWebClientUtil {
         return exchange(url, method, body, rtnClzz, map);
     }
 
+    /**
+     * kakaotalk WebClient 호출 처리
+     * 에러(.onStatus status.is4xxClientError() || status.is5xxServerError())
+     * -> {@link WebClientConfig responseFilter} 에서 처리
+     * @param url String
+     * @param method HttpMethod
+     * @param body Object
+     * @param rtnClzz Class
+     * @param ensDTO CmmEnsRlaybsnmDTO
+     * @return rtnClzz
+     */
+    public  T exchangeKkotalk(final String url, final HttpMethod method, final Object body, final Class rtnClzz, final CmmEnsRlaybsnmDTO ensDTO) {
+
+        // FIXME: Kkotalk 값 설정
+        Map map = new HashMap<>();
+        map.put(Constants.JwtToken.PARTNER_REST_API_KEY.getCode(),
+            String.format("KakaoAK %s", ensDTO.getKakaoAccessToken()));
+        map.put(HttpHeaders.AUTHORIZATION,
+            String.format("KakaoAK %s", ensDTO.getKakaoContractUuid()));
+        map.put(Constants.JwtToken.SETTLE_ID.getCode(), "");
+
+        return exchange(url, method, body, rtnClzz, map);
+    }
+
     /**
      * KT-BC WebClient 호출 처리
      * 에러(.onStatus status.is4xxClientError() || status.is5xxServerError())
diff --git a/mens-core/src/main/java/kr/xit/core/support/utils/DateUtils.java b/mens-core/src/main/java/kr/xit/core/support/utils/DateUtils.java
index 82d3378..7648be0 100644
--- a/mens-core/src/main/java/kr/xit/core/support/utils/DateUtils.java
+++ b/mens-core/src/main/java/kr/xit/core/support/utils/DateUtils.java
@@ -1,9 +1,5 @@
 package kr.xit.core.support.utils;
 
-import lombok.AccessLevel;
-import lombok.NoArgsConstructor;
-import org.apache.commons.lang3.StringUtils;
-
 import java.text.SimpleDateFormat;
 import java.time.Instant;
 import java.time.LocalDate;
@@ -13,6 +9,10 @@ import java.time.format.DateTimeFormatter;
 import java.time.temporal.ChronoUnit;
 import java.util.Date;
 
+import org.apache.commons.lang3.StringUtils;
+
+import lombok.AccessLevel;
+import lombok.NoArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
 
 /**
@@ -89,6 +89,27 @@ public class DateUtils {
         return LocalDateTime.now();
     }
 
+    /**
+     * 현재시간 > yyyy-MM-dd'T'HH:mm:ss String 으로 반환
+     *
+     * @return yyyy-MM-dd'T'HH:mm:ss String
+     */
+    public static String getTimeTOfnow() {
+        return LocalDateTime.now()
+            .format(DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"));
+    }
+
+    /**
+     * yyyy-MM-dd'T'HH:mm:ss String > yyyy-MM-dd HH:mm:ss String 으로 반환
+     * @param timeT yyyy-MM-dd'T'HH:mm:ss String
+     *
+     * @return yyyy-MM-dd HH:mm:ss String
+     */
+    public static String getTimeOfTimeT(String timeT) {
+        return LocalDateTime.parse(timeT, DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss"))
+            .format(DateTimeFormatter.ofPattern(DEFAULT_YMD_DT_FMT));
+    }
+
     /**
      * LocalDateTime.parse 객체 반환
      *