From b92b9828efc802308937ecaeb833396ffcbb2f69 Mon Sep 17 00:00:00 2001 From: LimHyunwoo <81962309+imenuuu@users.noreply.github.com> Date: Thu, 5 Oct 2023 13:22:11 +0900 Subject: [PATCH 1/3] Update application.yml --- Match-Api/src/main/resources/application.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Match-Api/src/main/resources/application.yml b/Match-Api/src/main/resources/application.yml index 9fbdf413..b4d6db11 100644 --- a/Match-Api/src/main/resources/application.yml +++ b/Match-Api/src/main/resources/application.yml @@ -4,6 +4,9 @@ server: context-path: / spring: + thymeleaf: + prefix: classpath:/templates/ + suffix: .html profiles: include: - infrastructure From 13eb647122466c1968ff507bea72c182dc4e0746 Mon Sep 17 00:00:00 2001 From: LimHyunwoo <81962309+imenuuu@users.noreply.github.com> Date: Thu, 5 Oct 2023 13:28:48 +0900 Subject: [PATCH 2/3] Update NicepayController.java --- .../java/com/example/matchapi/common/NicepayController.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Match-Api/src/main/java/com/example/matchapi/common/NicepayController.java b/Match-Api/src/main/java/com/example/matchapi/common/NicepayController.java index dd5f8cf8..f2372672 100644 --- a/Match-Api/src/main/java/com/example/matchapi/common/NicepayController.java +++ b/Match-Api/src/main/java/com/example/matchapi/common/NicepayController.java @@ -48,7 +48,7 @@ public String indexDemo( model.addAttribute("amount", amount); model.addAttribute("clientId", nicePayProperties.getClient()); model.addAttribute("returnUrl",serverHost+"/serverAuth"); - return "/index"; + return "index"; } @RequestMapping("/serverAuth") @@ -62,4 +62,4 @@ public RedirectView requestPaymentAuth( redirectView.setUrl(redirectUrl+"/auth/payComplete/once"); return redirectView; } -} \ No newline at end of file +} From 1a493887d7f99352ae7580f215543f75b06f695c Mon Sep 17 00:00:00 2001 From: LimHyunwoo <81962309+imenuuu@users.noreply.github.com> Date: Tue, 10 Oct 2023 15:48:48 +0900 Subject: [PATCH 3/3] =?UTF-8?q?:recycle:=20:=20reids=20=ED=81=B4=EB=9F=AC?= =?UTF-8?q?=EC=8A=A4=ED=84=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../matchapi/common/NicepayController.java | 25 +++ .../common/aop/CheckIdExistAspect.java | 7 - .../donation/service/DonationService.java | 4 +- .../order/controller/OrderController.java | 30 +-- .../order/convertor/OrderConvertor.java | 56 ++++- .../matchapi/order/helper/DataEncrypt.java | 35 ++++ .../matchapi/order/service/OrderService.java | 194 ++++++++---------- .../portone/controller/PaymentController.java | 31 +++ .../matchapi/portone/dto/PaymentReq.java | 17 ++ .../portone/service/PaymentService.java | 99 +++++++++ .../example/matchapi/security/JwtService.java | 3 +- .../matchbatch/convertor/OrderConvertor.java | 12 +- .../matchbatch/service/OrderService.java | 86 ++++---- .../config/ConfigurationPropertiesConfig.java | 3 +- .../matchcommon/config/RedisConfig.java | 4 +- .../matchcommon/constants/MatchStatic.java | 2 + .../properties/NicePayProperties.java | 2 + .../properties/PortOneProperties.java | 21 ++ .../donation/entity/QUserCard.java | 4 +- .../matchdomain/donation/entity/UserCard.java | 5 +- .../order/exception/PortOneAuthErrorCode.java | 44 ++++ .../project/repository/ProjectRepository.java | 2 + .../redis/entity/OrderRequest.java | 1 + .../discord/config/DiscordInfoConfig.java | 1 - .../portone/client/PortOneFeignClient.java | 39 ++++ .../config/PortOneFeignConfiguration.java | 24 +++ .../pay/portone/config/PortOneInfoConfig.java | 23 +++ .../config/PortOneInfoErrorDecoder.java | 72 +++++++ .../portone/convertor/PortOneConvertor.java | 33 +++ .../pay/portone/dto/PortOneAuth.java | 14 ++ .../portone/dto/PortOneBillPayResponse.java | 70 +++++++ .../pay/portone/dto/PortOneBillResponse.java | 26 +++ .../pay/portone/dto/PortOneResponse.java | 32 +++ .../portone/dto/req/PayWithBillKeyReq.java | 14 ++ .../pay/portone/dto/req/PortOneAuthReq.java | 14 ++ .../pay/portone/dto/req/PortOneBillReq.java | 16 ++ .../portone/service/PortOneAuthService.java | 52 +++++ 37 files changed, 921 insertions(+), 196 deletions(-) create mode 100644 Match-Api/src/main/java/com/example/matchapi/order/helper/DataEncrypt.java create mode 100644 Match-Api/src/main/java/com/example/matchapi/portone/controller/PaymentController.java create mode 100644 Match-Api/src/main/java/com/example/matchapi/portone/dto/PaymentReq.java create mode 100644 Match-Api/src/main/java/com/example/matchapi/portone/service/PaymentService.java create mode 100644 Match-Common/src/main/java/com/example/matchcommon/properties/PortOneProperties.java create mode 100644 Match-Domain/src/main/java/com/example/matchdomain/order/exception/PortOneAuthErrorCode.java create mode 100644 Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/client/PortOneFeignClient.java create mode 100644 Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/config/PortOneFeignConfiguration.java create mode 100644 Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/config/PortOneInfoConfig.java create mode 100644 Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/config/PortOneInfoErrorDecoder.java create mode 100644 Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/convertor/PortOneConvertor.java create mode 100644 Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/dto/PortOneAuth.java create mode 100644 Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/dto/PortOneBillPayResponse.java create mode 100644 Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/dto/PortOneBillResponse.java create mode 100644 Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/dto/PortOneResponse.java create mode 100644 Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/dto/req/PayWithBillKeyReq.java create mode 100644 Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/dto/req/PortOneAuthReq.java create mode 100644 Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/dto/req/PortOneBillReq.java create mode 100644 Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/service/PortOneAuthService.java diff --git a/Match-Api/src/main/java/com/example/matchapi/common/NicepayController.java b/Match-Api/src/main/java/com/example/matchapi/common/NicepayController.java index b914b6a8..c8bdb63d 100644 --- a/Match-Api/src/main/java/com/example/matchapi/common/NicepayController.java +++ b/Match-Api/src/main/java/com/example/matchapi/common/NicepayController.java @@ -1,5 +1,6 @@ package com.example.matchapi.common; +import com.example.matchapi.order.helper.DataEncrypt; import com.example.matchapi.order.service.OrderService; import com.example.matchcommon.properties.NicePayProperties; import com.example.matchdomain.redis.entity.OrderRequest; @@ -34,7 +35,9 @@ public class NicepayController { private String redirectUrl; @Value("${server.host}") private String serverHost; + private final DataEncrypt dataEncrypt; + /* @RequestMapping("") public String indexDemo( @RequestParam String method, @@ -51,6 +54,26 @@ public String indexDemo( return "index"; } + @RequestMapping("/goPay") + public String goPay( + @RequestParam(required = false) String method, + @RequestParam String orderId, + @RequestParam int amount, + Model model + ){ + String ediDate = orderService.getyyyyMMddHHmmss(); + String hashString = dataEncrypt.encrypt(ediDate + orderId + amount + nicePayProperties.getKey()); + model.addAttribute("payMethod", "CreditCard"); + model.addAttribute("goodsName", "Sample Product"); + model.addAttribute("Amt", amount); + model.addAttribute("mid", nicePayProperties.getMid()); + model.addAttribute("moid", orderId); + model.addAttribute("EdiDate", ediDate); + model.addAttribute("signData", hashString); + // model.addAttribute("ReturnURL", "http://localhost:8080/nicepay3.0_utf-8/payResult_utf.jsp"); + return "gopay"; + } + @RequestMapping("/serverAuth") public RedirectView requestPaymentAuth( @RequestParam String tid, @@ -62,4 +85,6 @@ public RedirectView requestPaymentAuth( redirectView.setUrl(redirectUrl+"/auth/payComplete/once"); return redirectView; } + + */ } \ No newline at end of file diff --git a/Match-Api/src/main/java/com/example/matchapi/common/aop/CheckIdExistAspect.java b/Match-Api/src/main/java/com/example/matchapi/common/aop/CheckIdExistAspect.java index 80458d87..0a90ce3a 100644 --- a/Match-Api/src/main/java/com/example/matchapi/common/aop/CheckIdExistAspect.java +++ b/Match-Api/src/main/java/com/example/matchapi/common/aop/CheckIdExistAspect.java @@ -57,13 +57,6 @@ public void checkIdsExist(JoinPoint joinPoint) { } break; } - if ("cardId".equals(parameterNames[i])) { - Long cardId = (Long) args[i]; - UserCard userCard = userCardRepository.findByIdAndStatus(cardId, Status.ACTIVE).orElseThrow(() -> new NotFoundException(CARD_NOT_EXIST)); - if(!userCard.getUserId().equals(user.getId())) throw new BadRequestException(CARD_NOT_CORRECT_USER); - if(!userCard.getCardAbleStatus().equals(CardAbleStatus.ABLE)) throw new BadRequestException(CARD_NOT_ABLE); - break; - } } } diff --git a/Match-Api/src/main/java/com/example/matchapi/donation/service/DonationService.java b/Match-Api/src/main/java/com/example/matchapi/donation/service/DonationService.java index d3f1b2db..bff86a74 100644 --- a/Match-Api/src/main/java/com/example/matchapi/donation/service/DonationService.java +++ b/Match-Api/src/main/java/com/example/matchapi/donation/service/DonationService.java @@ -4,6 +4,7 @@ import com.example.matchapi.donation.dto.DonationRes; import com.example.matchapi.donation.helper.DonationHelper; import com.example.matchapi.order.service.OrderService; +import com.example.matchapi.portone.service.PaymentService; import com.example.matchapi.project.convertor.ProjectConvertor; import com.example.matchapi.project.dto.ProjectRes; import com.example.matchcommon.exception.BadRequestException; @@ -52,6 +53,7 @@ public class DonationService { private final DonationHelper donationHelper; private final ProjectConvertor projectConvertor; private final DonationHistoryRepository donationHistoryRepository; + private final PaymentService paymentService; public PageResponse> getDonationList(Long userId, int filter, int page, int size) { Pageable pageable = PageRequest.of(page, size); @@ -93,7 +95,7 @@ public void refundDonation(User user, Long donationId) { DonationUser donationUser = donationUserRepository.findById(donationId).orElseThrow(() -> new NotFoundException(DONATION_NOT_EXIST)); if(!donationUser.getUserId().equals(user.getId())) throw new BadRequestException(DONATION_NOT_CORRECT_USER); if(!donationUser.getDonationStatus().equals(EXECUTION_BEFORE)) throw new BadRequestException(CANNOT_DELETE_DONATION_STATUS); - orderService.cancelPayment(donationUser.getTid(), donationUser.getOrderId()); + paymentService.refundPayment(donationUser.getTid()); donationUser.setDonationStatus(EXECUTION_REFUND); } diff --git a/Match-Api/src/main/java/com/example/matchapi/order/controller/OrderController.java b/Match-Api/src/main/java/com/example/matchapi/order/controller/OrderController.java index 996aefae..80e8d060 100644 --- a/Match-Api/src/main/java/com/example/matchapi/order/controller/OrderController.java +++ b/Match-Api/src/main/java/com/example/matchapi/order/controller/OrderController.java @@ -20,6 +20,7 @@ import com.example.matchdomain.user.entity.User; import com.example.matchdomain.user.exception.UserAuthErrorCode; import com.example.matchinfrastructure.pay.nice.dto.NicePaymentAuth; +import com.example.matchinfrastructure.pay.portone.dto.PortOneBillResponse; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; @@ -61,26 +62,6 @@ public CommonResponse requestPay( return CommonResponse.onSuccess(orderId); } - @Deprecated - @PostMapping("/test/pay") - @ApiErrorCodeExample(OtherServerErrorCode.class) - @Operation(summary= "04-00 Order๐Ÿ’ธ ๊ฒฐ์ œ ์ธ์ฆ์šฉ API ์‚ฌ์šฉ X ํ…Œ์ŠคํŠธ์šฉ",description = "๊ฒฐ์ œ ์ธ์ฆ์šฉ API ์ž…๋‹ˆ๋‹ค ํ…Œ์ŠคํŠธ ์šฉ") - public CommonResponse requestPayment(@RequestParam String tid, - @RequestParam Long amount){ - log.info("04-00 Order ๊ฒฐ์ œ ์ธ์ฆ ํ…Œ์ŠคํŠธ์šฉ API ๊ฒฐ์ œ ID: " + tid + " ๊ฒฐ์ œ ๊ธˆ์•ก " +amount); - return CommonResponse.onSuccess(orderService.authPayment(tid, amount)); - } - - @Deprecated - @PostMapping("/test/cancel/pay") - @ApiErrorCodeExample(OtherServerErrorCode.class) - @Operation(summary= "04-00 Order๐Ÿ’ธ ๊ฒฐ์ œ ์ทจ์†Œ์šฉ API ์‚ฌ์šฉ X ํ…Œ์ŠคํŠธ์šฉ",description = "๊ฒฐ์ œ ์ธ์ฆ์šฉ API ์ž…๋‹ˆ๋‹ค ํ…Œ์ŠคํŠธ ์šฉ") - public CommonResponse cancelPayment(@RequestParam String tid, - @RequestParam String orderId){ - log.info("04-00 Order ๊ฒฐ์ œ ์ทจ์†Œ ํ…Œ์ŠคํŠธ์šฉ API ๊ฒฐ์ œ ID: " + tid + " ์ฃผ๋ฌธ ๋ฒˆํ˜ธ " +orderId); - return CommonResponse.onSuccess(orderService.cancelPayment(tid, orderId)); - } - @PostMapping("/pay/{projectId}") @ApiErrorCodeExample({OtherServerErrorCode.class, UserAuthErrorCode.class, RequestErrorCode.class, ProjectOneTimeErrorCode.class}) @@ -97,11 +78,10 @@ public CommonResponse requestPayment( @PostMapping("/pay/card") @ApiErrorCodeExample({UserAuthErrorCode.class, OtherServerErrorCode.class, RegistrationCardErrorCode.class, NicePayErrorCode.class}) @Operation(summary = "04-02 Order๐Ÿ’ธ ์ •๊ธฐ ๊ฒฐ์ œ์šฉ ์นด๋“œ ๋“ฑ๋ก api",description = "์ •๊ธฐ ๊ฒฐ์ œ๋ฅผ ์œ„ํ•œ ์นด๋“œ ๋“ฑ๋ก API ์ž…๋‹ˆ๋‹ค.") - public CommonResponse registrationCard( + public CommonResponse registrationCard( @Parameter(hidden = true) @AuthenticationPrincipal User user, @Valid @RequestBody OrderReq.RegistrationCard registrationCard){ - orderService.registrationCard(user, registrationCard); - return CommonResponse.onSuccess("์นด๋“œ ๋“ฑ๋ก ์„ฑ๊ณต"); + return CommonResponse.onSuccess(orderService.postCard(user, registrationCard)); } @GetMapping("/pay/card") @@ -124,8 +104,6 @@ public CommonResponse deleteBillCard(@Parameter(hidden = true) @Authenti @PostMapping("/pay/card/{cardId}/{projectId}") @ApiErrorCodeExample({UserAuthErrorCode.class, OtherServerErrorCode.class, ProjectRegualrErrorCode.class, DeleteCardErrorCode.class}) @Operation(summary = "04-05 Order๐Ÿ’ธ ์ •๊ธฐ ๊ฒฐ์ œ ๋“ฑ๋ก api #FRAME ๊ฒฐ์ œ ํ™”๋ฉด - ์ •๊ธฐ ๊ฒฐ์ œ",description = "์ •๊ธฐ ๊ฒฐ์ œ ์‹ ์ฒญํ•˜๊ธฐ API ์ž…๋‹ˆ๋‹ค.") - @CheckIdExist - @CheckRegularProject public CommonResponse regularDonation( @Parameter(hidden = true) @AuthenticationPrincipal User user, @Parameter(description = "์นด๋“œ id",example = "1") @PathVariable Long cardId, @@ -138,8 +116,6 @@ public CommonResponse regularDonation( @PostMapping("/pay/one/card/{cardId}/{projectId}") @ApiErrorCodeExample({UserAuthErrorCode.class, OtherServerErrorCode.class, ProjectOneTimeErrorCode.class, DeleteCardErrorCode.class}) @Operation(summary = "04-06 Order๐Ÿ’ธ ๋นŒํ‚ค๋กœ ๋‹จ๊ธฐ ๊ฒฐ์ œ api #FRAME ๊ฒฐ์ œ ํ™”๋ฉด - ๋‹จ๊ธฐ ๊ฒฐ์ œ",description = "๋‹จ ๊ฒฐ์ œ ์‹ ์ฒญํ•˜๊ธฐ API ์ž…๋‹ˆ๋‹ค.") - @CheckIdExist - @CheckOneTimeProject public CommonResponse oneTimeDonationCard( @Parameter(hidden = true) @AuthenticationPrincipal User user, @Parameter(description = "์นด๋“œ id",example = "1") @PathVariable Long cardId, diff --git a/Match-Api/src/main/java/com/example/matchapi/order/convertor/OrderConvertor.java b/Match-Api/src/main/java/com/example/matchapi/order/convertor/OrderConvertor.java index 0a6b65f9..84dd2092 100644 --- a/Match-Api/src/main/java/com/example/matchapi/order/convertor/OrderConvertor.java +++ b/Match-Api/src/main/java/com/example/matchapi/order/convertor/OrderConvertor.java @@ -2,12 +2,15 @@ import com.example.matchapi.order.dto.OrderReq; import com.example.matchapi.order.helper.OrderHelper; +import com.example.matchapi.portone.dto.PaymentReq; import com.example.matchcommon.annotation.Convertor; import com.example.matchdomain.donation.entity.*; -import com.example.matchdomain.donation.entity.enums.DonationStatus; -import com.example.matchdomain.donation.entity.enums.RegularStatus; +import com.example.matchdomain.donation.entity.enums.*; import com.example.matchdomain.redis.entity.OrderRequest; import com.example.matchinfrastructure.pay.nice.dto.*; +import com.example.matchinfrastructure.pay.portone.dto.PortOneBillPayResponse; +import com.example.matchinfrastructure.pay.portone.dto.PortOneBillResponse; +import com.siot.IamportRestClient.response.Payment; import lombok.RequiredArgsConstructor; import static java.lang.Integer.parseInt; @@ -82,7 +85,6 @@ public UserCard UserCard(Long id, OrderReq.RegistrationCard registrationCard, Ni .expMonth(registrationCard.getExpMonth()) .idNo(registrationCard.getIdNo()) .cardPw(registrationCard.getCardPw()) - .cardCode(nicePayBillkeyResponse.getCardCode()) .cardName(nicePayBillkeyResponse.getCardName()) .orderId(nicePayBillkeyResponse.getOrderId()) .build(); @@ -132,4 +134,52 @@ public DonationUser donationBillUser(NiceBillOkResponse niceBillOkResponse, Long .regularPaymentId(regularPaymentId) .build(); } + + public DonationUser donationUserPortone(Payment payment, Long userId, PaymentReq.ValidatePayment validatePayment, Long projectId, String flameName, String inherenceNumber) { + return DonationUser.builder() + .userId(userId) + .payMethod(orderHelper.getPayMethod(validatePayment.getPayMethod())) + .projectId(projectId) + .price((long) validatePayment.getAmount()) + .tid(payment.getImpUid()) + .orderId(payment.getMerchantUid()) + .donationStatus(DonationStatus.EXECUTION_BEFORE) + .payMethod(orderHelper.getPayMethod(payment.getPayMethod())) + .inherenceName(flameName) + .inherenceNumber(inherenceNumber) + .regularStatus(RegularStatus.ONE_TIME) + .build(); + } + + public UserCard UserBillCard(Long id, OrderReq.RegistrationCard registrationCard, PortOneBillResponse portOneBillResponse) { + return UserCard.builder() + .userId(id) + .bid(portOneBillResponse.getCustomer_uid()) + .cardNo(registrationCard.getCardNo()) + .expYear(registrationCard.getExpYear()) + .expMonth(registrationCard.getExpMonth()) + .idNo(registrationCard.getIdNo()) + .cardPw(registrationCard.getCardPw()) + .cardCode(CardCode.getNameByCode(portOneBillResponse.getCard_code())) + .cardName(portOneBillResponse.getCard_name()) + .customerId(portOneBillResponse.getCustomer_id()) + .cardAbleStatus(CardAbleStatus.ABLE) + .build(); + } + + public DonationUser donationBillPayUser(PortOneBillPayResponse response, Long id, Long amount, Long projectId, String flameName, String inherenceNumber, RegularStatus regularStatus, Long regularPaymentId) { + return DonationUser.builder() + .userId(id) + .projectId(projectId) + .price(amount) + .tid(response.getImp_uid()) + .orderId(response.getMerchant_uid()) + .donationStatus(DonationStatus.EXECUTION_BEFORE) + .payMethod(PayMethod.CARD) + .inherenceName(flameName) + .inherenceNumber(inherenceNumber) + .regularStatus(regularStatus) + .regularPaymentId(regularPaymentId) + .build(); + } } diff --git a/Match-Api/src/main/java/com/example/matchapi/order/helper/DataEncrypt.java b/Match-Api/src/main/java/com/example/matchapi/order/helper/DataEncrypt.java new file mode 100644 index 00000000..bba621ea --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/order/helper/DataEncrypt.java @@ -0,0 +1,35 @@ +package com.example.matchapi.order.helper; + +import org.apache.commons.codec.binary.Hex; +import org.springframework.stereotype.Service; + +import java.security.MessageDigest; + +@Service +public class DataEncrypt{ + MessageDigest md; + String strSRCData = ""; + String strENCData = ""; + String strOUTData = ""; + + public DataEncrypt(){ } + public String encrypt(String strData){ + String passACL = null; + MessageDigest md = null; + try{ + md = MessageDigest.getInstance("SHA-256"); + md.reset(); + md.update(strData.getBytes()); + byte[] raw = md.digest(); + passACL = encodeHex(raw); + }catch(Exception e){ + System.out.print("์•”ํ˜ธํ™” ์—๋Ÿฌ" + e.toString()); + } + return passACL; + } + + public String encodeHex(byte [] b){ + char [] c = Hex.encodeHex(b); + return new String(c); + } +} \ No newline at end of file diff --git a/Match-Api/src/main/java/com/example/matchapi/order/service/OrderService.java b/Match-Api/src/main/java/com/example/matchapi/order/service/OrderService.java index fc6e2096..588c5fe9 100644 --- a/Match-Api/src/main/java/com/example/matchapi/order/service/OrderService.java +++ b/Match-Api/src/main/java/com/example/matchapi/order/service/OrderService.java @@ -5,24 +5,37 @@ import com.example.matchapi.order.dto.OrderReq; import com.example.matchapi.order.dto.OrderRes; import com.example.matchapi.order.helper.OrderHelper; +import com.example.matchapi.portone.service.PaymentService; import com.example.matchcommon.exception.BadRequestException; +import com.example.matchcommon.exception.BaseException; import com.example.matchcommon.exception.InternalServerException; import com.example.matchcommon.exception.NotFoundException; import com.example.matchcommon.properties.NicePayProperties; import com.example.matchdomain.common.model.Status; import com.example.matchdomain.donation.entity.*; +import com.example.matchdomain.donation.entity.enums.CardAbleStatus; import com.example.matchdomain.donation.entity.enums.DonationStatus; import com.example.matchdomain.donation.entity.enums.RegularPayStatus; import com.example.matchdomain.donation.entity.enums.RegularStatus; import com.example.matchdomain.donation.repository.*; +import com.example.matchdomain.project.entity.Project; +import com.example.matchdomain.project.repository.ProjectRepository; import com.example.matchdomain.redis.entity.OrderRequest; import com.example.matchdomain.redis.repository.OrderRequestRepository; import com.example.matchdomain.user.entity.User; import com.example.matchdomain.user.repository.UserRepository; import com.example.matchinfrastructure.pay.nice.client.NiceAuthFeignClient; import com.example.matchinfrastructure.pay.nice.dto.*; +import com.example.matchinfrastructure.pay.portone.client.PortOneFeignClient; +import com.example.matchinfrastructure.pay.portone.convertor.PortOneConvertor; +import com.example.matchinfrastructure.pay.portone.dto.PortOneBillPayResponse; +import com.example.matchinfrastructure.pay.portone.dto.PortOneBillResponse; +import com.example.matchinfrastructure.pay.portone.dto.PortOneResponse; +import com.example.matchinfrastructure.pay.portone.service.PortOneAuthService; import lombok.RequiredArgsConstructor; import org.apache.commons.codec.binary.Hex; +import org.apache.commons.lang3.RandomStringUtils; +import org.springframework.http.HttpStatus; import org.springframework.stereotype.Service; import javax.crypto.Cipher; @@ -31,20 +44,19 @@ import javax.crypto.spec.SecretKeySpec; import javax.transaction.Transactional; import javax.validation.Valid; +import java.security.MessageDigest; +import java.text.SimpleDateFormat; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; -import java.util.UUID; +import java.util.*; import static com.example.matchcommon.constants.MatchStatic.*; import static com.example.matchdomain.donation.entity.enums.HistoryStatus.CREATE; import static com.example.matchdomain.donation.entity.enums.HistoryStatus.TURN_ON; -import static com.example.matchdomain.donation.exception.DeleteCardErrorCode.CARD_NOT_CORRECT_USER; -import static com.example.matchdomain.donation.exception.DeleteCardErrorCode.CARD_NOT_EXIST; +import static com.example.matchdomain.donation.exception.DeleteCardErrorCode.*; import static com.example.matchdomain.donation.exception.DonationGerErrorCode.DONATION_NOT_EXIST; import static com.example.matchdomain.order.exception.RegistrationCardErrorCode.FAILED_ERROR_ENCRYPT; +import static com.example.matchdomain.project.exception.ProjectOneTimeErrorCode.PROJECT_NOT_EXIST; @RequiredArgsConstructor @Service @@ -61,35 +73,11 @@ public class OrderService { private final UserRepository userRepository; private final DonationHistoryRepository donationHistoryRepository; private final DonationConvertor donationConvertor; - - @Transactional - public NicePaymentAuth authPayment(String tid, Long amount) { - String authorizationHeader = orderHelper.getNicePaymentAuthorizationHeader(); - NicePaymentAuth nicePaymentAuth = niceAuthFeignClient.paymentAuth(authorizationHeader, tid, new NicePayRequest(String.valueOf(amount))); - - orderHelper.checkNicePaymentsResult(nicePaymentAuth.getResultCode(), nicePaymentAuth.getResultMsg()); - return nicePaymentAuth; - } - - @Transactional - public String payForProject(OrderReq.OrderDetail orderDetail) { - String authorizationHeader = orderHelper.getNicePaymentAuthorizationHeader(); - NicePaymentAuth nicePaymentAuth = niceAuthFeignClient.paymentAuth(authorizationHeader, orderDetail.getTid(), new NicePayRequest(String.valueOf(orderDetail.getAmount()))); - - orderHelper.checkNicePaymentsResult(nicePaymentAuth.getResultCode(), nicePaymentAuth.getResultMsg()); - - return null; - } - - - @Transactional - public NicePaymentAuth cancelPayment(String tid, String orderId) { - NicePaymentAuth nicePaymentAuth = niceAuthFeignClient.cancelPayment(orderHelper.getNicePaymentAuthorizationHeader(), tid, new NicePayCancelRequest("๋‹จ์ˆœ์ทจ์†Œ", orderId)); - - orderHelper.checkNicePaymentsResult(nicePaymentAuth.getResultCode(), nicePaymentAuth.getResultMsg()); - - return nicePaymentAuth; - } + private final PortOneFeignClient portOneFeignClient; + private final PortOneAuthService portOneAuthService; + private final PortOneConvertor portOneConvertor; + private final ProjectRepository projectRepository; + private final PaymentService paymentService; @Transactional public String requestPayment(User user, OrderReq.OrderDetail orderDetail, Long projectId) { @@ -109,52 +97,18 @@ public String requestPayment(User user, OrderReq.OrderDetail orderDetail, Long p return flameName; } - @Transactional - public void registrationCard(User user, OrderReq.RegistrationCard registrationCard) { - String encrypt = encrypt(orderConvertor.createPlainText(registrationCard), nicePayProperties.getSecret().substring(0, 32), nicePayProperties.getSecret().substring(0, 16)); - String orderId = BILL + createRandomOrderId(); - Long userId = user.getId(); - //๋นŒํ‚ค ๋ฐœ๊ธ‰ - NicePayBillkeyResponse nicePayBillkeyResponse = niceAuthFeignClient.registrationCard( - orderHelper.getNicePaymentAuthorizationHeader(), - new NicePayRegistrationCardRequest(encrypt, orderId, "A2")); - - //๋‚˜์ด์Šค ์นด๋“œ ํ™•์ธ์šฉ OrderId, Bid ํ•„์š” - NiceBillOkResponse niceBillOkResponse = niceAuthFeignClient.billOkRequest(orderHelper.getNicePaymentAuthorizationHeader(), nicePayBillkeyResponse.getBid(), orderConvertor.niceBillOk(nicePayBillkeyResponse, orderId)); - - //์—๋Ÿฌ์ฝ”๋“œ ํ•ธ๋“ค๋ง - orderHelper.checkBillResult(niceBillOkResponse.getResultCode(), niceBillOkResponse.getResultMsg(), niceBillOkResponse.getTid(), niceBillOkResponse.getOrderId()); - - //๊ฒฐ์ œ ์™„๋ฃŒ ํ›„ - //Billing Key ์นด๋“œ ์ €์žฅ - UserCard userCard = userCardRepository.save(orderConvertor.UserCard(userId, registrationCard, nicePayBillkeyResponse)); - - } - - @Transactional - public String encrypt(String plainText, String secretKey, String iv) { - SecretKey secureKey = new SecretKeySpec(secretKey.getBytes(), "AES"); - try { - Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); - cipher.init(Cipher.ENCRYPT_MODE, secureKey, new IvParameterSpec(iv.getBytes())); - byte[] encrypted = cipher.doFinal(plainText.getBytes("UTF-8")); - return Hex.encodeHexString(encrypted); - } catch (Exception e) { - throw new InternalServerException(FAILED_ERROR_ENCRYPT); - } - } - @Transactional public String createRandomUUID() { return UUID.randomUUID().toString(); } - @Transactional - public String createRandomOrderId() { - return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yy.MM.dd.HH:mm")) + "-" + UUID.randomUUID().toString(); + public String createRandomId(){ + boolean useLetters = true; + boolean useNumbers = true; + String randomStr = RandomStringUtils.random(12, useLetters, useNumbers); + return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yy.MM.dd.HH:mm")) + "-" + randomStr; } - @Transactional public List getUserBillCard(Long userId) { List userCards = userCardRepository.findByUserIdAndStatus(userId,Status.ACTIVE); @@ -165,7 +119,7 @@ public List getUserBillCard(Long userId) { userBillCards.add( new OrderRes.UserBillCard( result.getId(), - result.getCardCode(), + result.getCardCode().getName(), result.getCardName(), orderHelper.maskMiddleNum(result.getCardNo()), result.getCardAbleStatus().getName() @@ -179,7 +133,7 @@ public List getUserBillCard(Long userId) { @Transactional public void deleteBillCard(Long cardId) { Optional userCard = userCardRepository.findByIdAndStatus(cardId,Status.ACTIVE); - NiceBillExpireResponse niceBillExpireResponse = niceAuthFeignClient.billKeyExpire(orderHelper.getNicePaymentAuthorizationHeader(), userCard.get().getBid(), new NiceBillExpireRequest(DELETE + createRandomOrderId())); + NiceBillExpireResponse niceBillExpireResponse = niceAuthFeignClient.billKeyExpire(orderHelper.getNicePaymentAuthorizationHeader(), userCard.get().getBid(), new NiceBillExpireRequest(DELETE + createRandomId())); System.out.println(niceBillExpireResponse.getResultCode() + niceBillExpireResponse.getResultMsg()); userCard.get().setStatus(Status.INACTIVE); } @@ -188,41 +142,47 @@ public void deleteBillCard(Long cardId) { @Transactional public void regularDonation(User user, OrderReq.RegularDonation regularDonation, Long cardId, Long projectId) { - Optional card = userCardRepository.findByIdAndStatus(cardId,Status.ACTIVE); + UserCard card = userCardRepository.findByIdAndStatus(cardId,Status.ACTIVE).orElseThrow(() -> new NotFoundException(CARD_NOT_EXIST)); + if(!card.getUserId().equals(user.getId())) throw new BadRequestException(CARD_NOT_CORRECT_USER); + if(!card.getCardAbleStatus().equals(CardAbleStatus.ABLE)) throw new BadRequestException(CARD_NOT_ABLE); + Project project = projectRepository.findByIdAndStatusAndRegularStatus(projectId, Status.ACTIVE, RegularStatus.REGULAR).orElseThrow(() ->new BadRequestException(PROJECT_NOT_EXIST)); - String orderId = REGULAR + createRandomOrderId(); + String orderId = REGULAR + createRandomId(); - NiceBillOkResponse niceBillOkResponse = niceAuthFeignClient.billOkRequest(orderHelper.getNicePaymentAuthorizationHeader(), card.get().getBid(), orderConvertor.billCardOneTime(regularDonation.getAmount(),orderId)); + String accessToken = portOneAuthService.getToken(); - orderHelper.checkNicePaymentsResult(niceBillOkResponse.getResultCode(), niceBillOkResponse.getResultMsg()); + PortOneResponse portOneResponse = portOneFeignClient.payWithBillKey(accessToken, portOneConvertor.PayWithBillKey(card.getBid(), orderId, regularDonation.getAmount(), project.getProjectName(), card.getCustomerId())); + + System.out.println(portOneResponse.getCode()); + System.out.println(portOneResponse.getMessage()); String flameName = orderHelper.createFlameName(user.getName()); String inherenceNumber = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yy.MM.dd.HH:mm")) + "." + createRandomUUID(); RegularPayment regularPayment = regularPaymentRepository.save(orderConvertor.RegularPayment(user.getId(), regularDonation, cardId, projectId)); - - DonationUser donationUser = donationUserRepository.save(orderConvertor.donationBillUser(niceBillOkResponse, user.getId(), regularDonation.getAmount(), projectId, flameName, inherenceNumber, RegularStatus.REGULAR, regularPayment.getId())); - + DonationUser donationUser = donationUserRepository.save(orderConvertor.donationBillPayUser(portOneResponse.getResponse(), user.getId(), regularDonation.getAmount(), projectId, flameName, inherenceNumber, RegularStatus.REGULAR, regularPayment.getId())); donationHistoryRepository.save(donationConvertor.DonationHistoryTurnOn(regularPayment.getId(), TURN_ON)); donationHistoryRepository.save(donationConvertor.DonationHistory(donationUser.getId(), CREATE)); } @Transactional - public void oneTimeDonationCard(User user, OrderReq.@Valid OneTimeDonation oneTimeDonation, Long cardId, Long projectId) { - Optional card = userCardRepository.findByIdAndStatus(cardId, Status.ACTIVE); + public void oneTimeDonationCard(User user, OrderReq. @Valid OneTimeDonation oneTimeDonation, Long cardId, Long projectId) { + UserCard card = userCardRepository.findByIdAndStatus(cardId,Status.ACTIVE).orElseThrow(() -> new NotFoundException(CARD_NOT_EXIST)); + + Project project = projectRepository.findByIdAndStatusAndRegularStatus(projectId, Status.ACTIVE, RegularStatus.ONE_TIME).orElseThrow(() ->new BadRequestException(PROJECT_NOT_EXIST)); - String orderId = ONE_TIME + createRandomOrderId(); + String orderId = ONE_TIME + createRandomId(); - NiceBillOkResponse niceBillOkResponse = niceAuthFeignClient.billOkRequest(orderHelper.getNicePaymentAuthorizationHeader(), card.get().getBid(), orderConvertor.billCardOneTime(oneTimeDonation.getAmount(),orderId)); + String accessToken = portOneAuthService.getToken(); - orderHelper.checkNicePaymentsResult(niceBillOkResponse.getResultCode(), niceBillOkResponse.getResultMsg()); + PortOneResponse portOneResponse = portOneFeignClient.payWithBillKey(accessToken, portOneConvertor.PayWithBillKey(card.getBid(), orderId, oneTimeDonation.getAmount(), project.getProjectName(), card.getCustomerId())); String flameName = orderHelper.createFlameName(user.getName()); String inherenceNumber = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yy.MM.dd.HH:mm")) + "." + createRandomUUID(); - DonationUser donationUser = donationUserRepository.save(orderConvertor.donationBillUser(niceBillOkResponse, user.getId(), oneTimeDonation.getAmount(), projectId, flameName, inherenceNumber, RegularStatus.ONE_TIME, null)); + DonationUser donationUser = donationUserRepository.save(orderConvertor.donationBillPayUser(portOneResponse.getResponse(), user.getId(), oneTimeDonation.getAmount(), projectId, flameName, inherenceNumber, RegularStatus.ONE_TIME, null)); donationHistoryRepository.save(donationConvertor.DonationHistory(donationUser.getId(), CREATE)); @@ -230,29 +190,13 @@ public void oneTimeDonationCard(User user, OrderReq.@Valid OneTimeDonation oneTi @Transactional public String saveRequest(User user, Long projectId) { - String orderId = ONE_TIME + createRandomOrderId(); + String orderId = ONE_TIME + createRandomId(); orderRequestRepository.save(orderConvertor.CreateRequest(user.getId(), projectId, orderId)); return orderId; } - @Transactional - public String saveRequest(Long projectId) { - String orderId = ONE_TIME + createRandomOrderId(); - - orderRequestRepository.save(orderConvertor.CreateRequest(1L, projectId, orderId)); - - return orderId; - } - - @Transactional - public OrderRequest getOrderRequest(String orderId) { - Optional orderRequest = orderRequestRepository.findById(orderId); - return orderRequest.get(); - - } - @Transactional public void requestPaymentAuth(String tid, Long amount) { NicePaymentAuth nicePaymentAuth = niceAuthFeignClient. @@ -280,7 +224,7 @@ public void requestPaymentAuth(String tid, Long amount) { public void adminRefundDonation(Long donationUserId) { DonationUser donationUser = donationUserRepository.findById(donationUserId).orElseThrow(()-> new BadRequestException(DONATION_NOT_EXIST)); donationUser.setDonationStatus(DonationStatus.EXECUTION_REFUND); - cancelPayment(donationUser.getTid(), donationUser.getOrderId()); + paymentService.refundPayment(donationUser.getTid()); donationUserRepository.save(donationUser); } @@ -303,4 +247,40 @@ public void revokePay(User user, Long cardId) { regularPaymentRepository.saveAll(regularPayments); } + + public final synchronized String getyyyyMMddHHmmss(){ + SimpleDateFormat yyyyMMddHHmmss = new SimpleDateFormat("yyyyMMddHHmmss"); + return yyyyMMddHHmmss.format(new Date()); + } + + public PortOneBillResponse postCard(User user, OrderReq.RegistrationCard registrationCard) { + String accessToken = portOneAuthService.getToken(); + String customerUid = BILL + createRandomId(); + String cardNo = formatString(registrationCard.getCardNo(), 4); + String expiry = "20" + registrationCard.getExpYear() + "-" + registrationCard.getExpMonth(); + PortOneResponse portOneResponse = portOneFeignClient.getBillKey( + accessToken, + customerUid, + portOneConvertor.PortOneBill(cardNo, expiry, registrationCard.getIdNo(), registrationCard.getCardPw()) + ); + if(portOneResponse.getCode()!=0){ + throw new BaseException(HttpStatus.BAD_REQUEST, false, "PORT_ONE_BILL_AUTH_001", portOneResponse.getMessage()); + } + UserCard userCard = userCardRepository.save(orderConvertor.UserBillCard(user.getId(), registrationCard, portOneResponse.getResponse())); + + return portOneResponse.getResponse(); + } + + public static String formatString(String input, int length) { + StringBuilder formatted = new StringBuilder(); + + for (int i = 0; i < input.length(); i++) { + if (i > 0 && i % length == 0) { + formatted.append('-'); + } + formatted.append(input.charAt(i)); + } + + return formatted.toString(); + } } \ No newline at end of file diff --git a/Match-Api/src/main/java/com/example/matchapi/portone/controller/PaymentController.java b/Match-Api/src/main/java/com/example/matchapi/portone/controller/PaymentController.java new file mode 100644 index 00000000..414bb564 --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/portone/controller/PaymentController.java @@ -0,0 +1,31 @@ +package com.example.matchapi.portone.controller; + +import com.example.matchapi.portone.dto.PaymentReq; +import com.example.matchapi.portone.service.PaymentService; +import com.example.matchapi.project.dto.ProjectReq; +import com.example.matchcommon.reponse.CommonResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/payments") +@Tag(name = "08-PortOne๐Ÿ’ธ",description = "PortOne ๊ฒฐ์ œ API") +@RequiredArgsConstructor +public class PaymentController { + private final PaymentService paymentService; + @PostMapping("/validate") + @Operation(summary = "08-01 Payment ๊ฐ€๊ฒฉ ๊ฒ€์ฆ๐Ÿ’ธ") + public CommonResponse validatePayment(@RequestBody PaymentReq.ValidatePayment validatePayment){ + paymentService.checkPayment(validatePayment); + return CommonResponse.onSuccess("๊ฒฐ์ œ ์„ฑ๊ณต"); + } + + @PostMapping("/refund") + @Deprecated + public CommonResponse refundPayment(@RequestParam String impUid){ + paymentService.refundPayment(impUid); + return CommonResponse.onSuccess("ํ™˜๋ถˆ ์„ฑ๊ณต"); + } +} diff --git a/Match-Api/src/main/java/com/example/matchapi/portone/dto/PaymentReq.java b/Match-Api/src/main/java/com/example/matchapi/portone/dto/PaymentReq.java new file mode 100644 index 00000000..b5101d63 --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/portone/dto/PaymentReq.java @@ -0,0 +1,17 @@ +package com.example.matchapi.portone.dto; + +import lombok.*; + +public class PaymentReq { + @Getter + @Setter + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class ValidatePayment{ + private String impUid; + private String orderId; + private int amount; + private String payMethod; + } +} diff --git a/Match-Api/src/main/java/com/example/matchapi/portone/service/PaymentService.java b/Match-Api/src/main/java/com/example/matchapi/portone/service/PaymentService.java new file mode 100644 index 00000000..905651e6 --- /dev/null +++ b/Match-Api/src/main/java/com/example/matchapi/portone/service/PaymentService.java @@ -0,0 +1,99 @@ +package com.example.matchapi.portone.service; + +import com.example.matchapi.order.convertor.OrderConvertor; +import com.example.matchapi.order.helper.OrderHelper; +import com.example.matchapi.portone.dto.PaymentReq; +import com.example.matchcommon.exception.BadRequestException; +import com.example.matchcommon.properties.PortOneProperties; +import com.example.matchdomain.common.model.Status; +import com.example.matchdomain.donation.repository.DonationUserRepository; +import com.example.matchdomain.redis.entity.OrderRequest; +import com.example.matchdomain.redis.repository.OrderRequestRepository; +import com.example.matchdomain.user.entity.User; +import com.example.matchdomain.user.repository.UserRepository; +import com.siot.IamportRestClient.IamportClient; +import com.siot.IamportRestClient.exception.IamportResponseException; +import com.siot.IamportRestClient.request.CancelData; +import com.siot.IamportRestClient.response.IamportResponse; +import com.siot.IamportRestClient.response.Payment; +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Optional; +import java.util.UUID; + +import static com.example.matchdomain.order.exception.PortOneAuthErrorCode.*; + +@Service +@RequiredArgsConstructor +public class PaymentService { + private final PortOneProperties portOneProperties; + private final OrderRequestRepository orderRequestRepository; + private final UserRepository userRepository; + private final OrderHelper orderHelper; + private final DonationUserRepository donationUserRepository; + private final OrderConvertor orderConvertor; + private final IamportClient iamportClient; + + @Autowired + public PaymentService(PortOneProperties portOneProperties, + OrderRequestRepository orderRequestRepository, + UserRepository userRepository, + OrderHelper orderHelper, + DonationUserRepository donationUserRepository, + OrderConvertor orderConvertor) { + this.portOneProperties = portOneProperties; + this.orderRequestRepository = orderRequestRepository; + this.userRepository = userRepository; + this.orderHelper = orderHelper; + this.donationUserRepository = donationUserRepository; + this.orderConvertor = orderConvertor; + this.iamportClient = new IamportClient(portOneProperties.getKey(), portOneProperties.getSecret()); + } + + public void checkPayment(PaymentReq.ValidatePayment validatePayment){ + try { + OrderRequest orderRequest = orderRequestRepository.findById(validatePayment.getOrderId()).orElseThrow(()->new BadRequestException(NOT_EXIST_ORDER_ID)); + IamportResponse payment = iamportClient.paymentByImpUid(validatePayment.getImpUid()); + Optional user = userRepository.findByIdAndStatus(Long.valueOf(orderRequest.getUserId()), Status.ACTIVE); + int paidAmount = payment.getResponse().getAmount().intValue(); //์‚ฌ์šฉ์ž๊ฐ€ ๊ฒฐ์ œํ•œ ๊ธˆ์•ก + + if(paidAmount!=validatePayment.getAmount()){ + CancelData cancelData = createCancelData(payment, 0); + iamportClient.cancelPaymentByImpUid(cancelData); + throw new BadRequestException(FAILED_ERROR_AUTH_AMOUNT); + }else{ + String flameName = orderHelper.createFlameName(user.get().getName()); + String inherenceNumber = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yy.MM.dd.HH:mm")) + "." + createRandomUUID(); + donationUserRepository.save(orderConvertor.donationUserPortone(payment.getResponse(), user.get().getId(), validatePayment, Long.valueOf(orderRequest.getProjectId()), flameName, inherenceNumber)); + } + } catch (IamportResponseException | IOException e) { + System.out.println(e.getMessage()); + throw new BadRequestException(FAILED_ERROR_AUTH); + } + } + + public String createRandomUUID() { + return UUID.randomUUID().toString(); + } + + private CancelData createCancelData(IamportResponse response, int refundAmount) { + if (refundAmount == 0) { //์ „์•ก ํ™˜๋ถˆ์ผ ๊ฒฝ์šฐ + return new CancelData(response.getResponse().getImpUid(), true); + } + return new CancelData(response.getResponse().getImpUid(), true, new BigDecimal(refundAmount)); + } + + public void refundPayment(String impUid) { + try { + iamportClient.cancelPaymentByImpUid(new CancelData(impUid, true)); + } catch (IamportResponseException | IOException e) { + throw new RuntimeException(e); + } + } +} diff --git a/Match-Api/src/main/java/com/example/matchapi/security/JwtService.java b/Match-Api/src/main/java/com/example/matchapi/security/JwtService.java index 435c87ba..6c7b96ef 100644 --- a/Match-Api/src/main/java/com/example/matchapi/security/JwtService.java +++ b/Match-Api/src/main/java/com/example/matchapi/security/JwtService.java @@ -4,6 +4,7 @@ import com.example.matchapi.user.dto.UserRes; import com.example.matchcommon.properties.JwtProperties; import com.example.matchdomain.redis.entity.AccessToken; +import com.example.matchdomain.redis.entity.RefreshToken; import com.example.matchdomain.redis.repository.AccessTokenRepository; import com.example.matchdomain.redis.repository.RefreshTokenRepository; import com.example.matchdomain.user.entity.User; @@ -101,7 +102,7 @@ public UserRes.Token createTokens(Long userId){ .signWith(encodedRefreshKey) .compact(); - //refreshTokenRepository.save(userConvertor.RefreshToken(userId,refreshToken,jwtProperties.getRefreshTokenSeconds())); + refreshTokenRepository.save(RefreshToken.builder().userId(userId.toString()).token(refreshToken).ttl(jwtProperties.getRefreshTokenSeconds()).build()); return new UserRes.Token(accessToken,refreshToken); } diff --git a/Match-Batch/src/main/java/com/example/matchbatch/convertor/OrderConvertor.java b/Match-Batch/src/main/java/com/example/matchbatch/convertor/OrderConvertor.java index 28c72081..d83d6cfa 100644 --- a/Match-Batch/src/main/java/com/example/matchbatch/convertor/OrderConvertor.java +++ b/Match-Batch/src/main/java/com/example/matchbatch/convertor/OrderConvertor.java @@ -4,10 +4,12 @@ import com.example.matchcommon.annotation.Convertor; import com.example.matchdomain.donation.entity.*; import com.example.matchdomain.donation.entity.enums.DonationStatus; +import com.example.matchdomain.donation.entity.enums.PayMethod; import com.example.matchdomain.donation.entity.enums.PaymentStatus; import com.example.matchdomain.donation.entity.enums.RegularStatus; import com.example.matchinfrastructure.pay.nice.dto.NiceBillOkRequest; import com.example.matchinfrastructure.pay.nice.dto.NiceBillOkResponse; +import com.example.matchinfrastructure.pay.portone.dto.PortOneBillPayResponse; import lombok.RequiredArgsConstructor; import java.time.LocalDateTime; @@ -28,14 +30,14 @@ public NiceBillOkRequest niceBillRequest(RegularPayment regularPayment, String o .build(); } - public DonationUser donationUser(NiceBillOkResponse niceBillOkResponse, Long userId, String flameName, String inherenceNumber, Long projectId, Long regularPaymentId) { + public DonationUser donationUser(PortOneBillPayResponse response, Long userId, String flameName, String inherenceNumber, Long projectId, Long regularPaymentId) { return DonationUser.builder() .userId(userId) - .price(niceBillOkResponse.getAmount()) - .tid(niceBillOkResponse.getTid()) - .orderId(niceBillOkResponse.getOrderId()) + .price(Long.valueOf(response.getAmount())) + .tid(response.getImp_uid()) + .orderId(response.getMerchant_uid()) .donationStatus(DonationStatus.EXECUTION_BEFORE) - .payMethod(orderHelper.getPayMethod(niceBillOkResponse.getPayMethod())) + .payMethod(PayMethod.CARD) .inherenceName(flameName) .inherenceNumber(inherenceNumber) .regularStatus(RegularStatus.REGULAR) diff --git a/Match-Batch/src/main/java/com/example/matchbatch/service/OrderService.java b/Match-Batch/src/main/java/com/example/matchbatch/service/OrderService.java index ae86e6ad..e7b54e47 100644 --- a/Match-Batch/src/main/java/com/example/matchbatch/service/OrderService.java +++ b/Match-Batch/src/main/java/com/example/matchbatch/service/OrderService.java @@ -14,8 +14,14 @@ import com.example.matchinfrastructure.discord.convertor.DiscordConvertor; import com.example.matchinfrastructure.pay.nice.client.NiceAuthFeignClient; import com.example.matchinfrastructure.pay.nice.dto.NiceBillOkResponse; +import com.example.matchinfrastructure.pay.portone.client.PortOneFeignClient; +import com.example.matchinfrastructure.pay.portone.convertor.PortOneConvertor; +import com.example.matchinfrastructure.pay.portone.dto.PortOneBillPayResponse; +import com.example.matchinfrastructure.pay.portone.dto.PortOneResponse; +import com.example.matchinfrastructure.pay.portone.service.PortOneAuthService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.RandomStringUtils; import org.springframework.stereotype.Service; import javax.transaction.Transactional; @@ -33,7 +39,6 @@ @RequiredArgsConstructor @Slf4j public class OrderService { - private final NiceAuthFeignClient niceAuthFeignClient; private final RegularPaymentRepository regularPaymentRepository; private final OrderHelper orderHelper; private final OrderConvertor orderConvertor; @@ -43,6 +48,9 @@ public class OrderService { private final DiscordConvertor discordConvertor; private final DonationHistoryRepository donationHistoryRepository; private final DonationConvertor donationConvertor; + private final PortOneFeignClient portOneFeignClient; + private final PortOneAuthService portOneAuthService; + private final PortOneConvertor portOneConvertor; @Transactional public void regularDonationPayment() { @@ -58,7 +66,7 @@ public void regularDonationPayment() { discordFeignClient.alertMessage(discordConvertor.AlertBatchMessage("์ •๊ธฐ ๊ฒฐ์ œ ์Šค์ผ€์ค„๋Ÿฌ ์‹œ์ž‘", regularPayments.size())); - + String accessToken = portOneAuthService.getTokens(); if (regularPayments.size() > 0) { LocalDate currentDate = LocalDate.now(); @@ -70,28 +78,26 @@ public void regularDonationPayment() { } else{ trueCnt +=1 ; - + UserCard userCard = regularPayment.getUserCard(); Long userId = regularPayment.getUserId(); - NiceBillOkResponse niceBillOkResponse = niceAuthFeignClient.billOkRequest(orderHelper.getNicePaymentAuthorizationHeader(), - regularPayment.getUserCard().getBid(), - orderConvertor.niceBillRequest(regularPayment, REGULAR + createRandomOrderId())); - if (niceBillOkResponse.getResultCode().equals("0000")) { - String flameName = orderHelper.createFlameName(regularPayment.getUser().getName()); - - String inherenceNumber = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yy.MM.dd.HH:mm")) + "." + createRandomUUID(); - - DonationUser donationUser = donationUserRepository.save(orderConvertor.donationUser(niceBillOkResponse, userId, flameName, inherenceNumber, regularPayment.getProjectId(),regularPayment.getId())); - donationHistories.add(donationConvertor.DonationHistory(donationUser.getId(), CREATE )); - - requestPaymentHistories.add(orderConvertor.RegularHistory(niceBillOkResponse, userId, COMPLETE, "SUCCESS",regularPayment.getId(), regularPayment.getPayDate(),regularPayment.getUserCardId())); - log.info("success Payment " + "userId :" + regularPayment.getUserId() + " orderId : " + niceBillOkResponse.getOrderId() + " bid :" + regularPayment.getUserCard().getBid() + " amount :" + regularPayment.getAmount() + "์› projectId :" + regularPayment.getProjectId()); - amount += regularPayment.getAmount(); - successCnt += 1; - } else { - requestPaymentHistories.add(orderConvertor.RegularHistory(niceBillOkResponse, userId, FAIL, niceBillOkResponse.getResultMsg(), regularPayment.getId(), regularPayment.getPayDate(), regularPayment.getUserCardId())); - - log.info("fail Payment " + "userId :" + regularPayment.getUserId() + " orderId : " + niceBillOkResponse.getOrderId() + " bid :" + regularPayment.getUserCard().getBid() + " amount :" + regularPayment.getAmount()); - } + + String orderId = REGULAR + createRandomOrderId(); + + PortOneResponse portOneResponse = portOneFeignClient.payWithBillKey(accessToken, portOneConvertor.PayWithBillKey(userCard.getBid(), orderId, regularPayment.getAmount(), LocalDateTime.now().getDayOfMonth() + "์›” ๋‹ฌ " +"MATCH ๊ธฐ๋ถ€๊ธˆ ์ •๊ธฐ ๊ฒฐ์ œ", userCard.getCustomerId())); + + + String flameName = orderHelper.createFlameName(regularPayment.getUser().getName()); + + String inherenceNumber = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yy.MM.dd.HH:mm")) + "." + createRandomUUID(); + + DonationUser donationUser = donationUserRepository.save(orderConvertor.donationUser(portOneResponse.getResponse(), userId, flameName, inherenceNumber, regularPayment.getProjectId(),regularPayment.getId())); + donationHistories.add(donationConvertor.DonationHistory(donationUser.getId(), CREATE )); + + //requestPaymentHistories.add(orderConvertor.RegularHistory(niceBillOkResponse, userId, COMPLETE, "SUCCESS",regularPayment.getId(), regularPayment.getPayDate(),regularPayment.getUserCardId())); + log.info("success Payment " + "userId :" + regularPayment.getUserId() + " orderId : " + orderId + " bid :" + regularPayment.getUserCard().getBid() + " amount :" + regularPayment.getAmount() + "์› projectId :" + regularPayment.getProjectId()); + amount += regularPayment.getAmount(); + successCnt += 1; + } } donationHistoryRepository.saveAll(donationHistories); @@ -131,8 +137,11 @@ public String createRandomUUID() { @Transactional public String createRandomOrderId() { - return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yy.MM.dd.HH:mm")) + "-" + UUID.randomUUID().toString(); + boolean useLetters = true; + boolean useNumbers = true; + String randomStr = RandomStringUtils.random(12, useLetters, useNumbers); + return LocalDateTime.now().format(DateTimeFormatter.ofPattern("yy.MM.dd.HH:mm")) + "-" + randomStr; } @Transactional @@ -146,33 +155,32 @@ public void regularPaymentRetry() { int amount = 0; int trueCnt = 0; int successCnt = 0 ; - + String accessToken = portOneAuthService.getTokens(); discordFeignClient.alertMessage(discordConvertor.AlertBatchMessage("์ •๊ธฐ ๊ฒฐ์ œ ์‹คํŒจ ํ•œ ๋ฆฌ์ŠคํŠธ ์Šค์ผ€์ค„๋Ÿฌ๊ฐ€ ์‹œ์ž‘", requestPaymentHistories.size())); for(RequestPaymentHistory requestPaymentHistory : requestPaymentHistories){ RegularPayment regularPayment = requestPaymentHistory.getRegularPayment(); UserCard userCard = requestPaymentHistory.getUserCard(); - NiceBillOkResponse niceBillOkResponse = niceAuthFeignClient.billOkRequest(orderHelper.getNicePaymentAuthorizationHeader(), - userCard.getBid(), - orderConvertor.niceBillRequest(requestPaymentHistory.getRegularPayment(), REGULAR + createRandomOrderId())); + String orderId = REGULAR + createRandomOrderId(); + + PortOneResponse portOneResponse = portOneFeignClient.payWithBillKey(accessToken, portOneConvertor.PayWithBillKey(userCard.getBid(), orderId, regularPayment.getAmount(), LocalDateTime.now().getDayOfMonth() + "์›” ๋‹ฌ " +"MATCH ๊ธฐ๋ถ€๊ธˆ ์ •๊ธฐ ๊ฒฐ์ œ", userCard.getCustomerId())); + trueCnt +=1 ; - if (niceBillOkResponse.getResultCode().equals("0000")) { - String flameName = orderHelper.createFlameName(requestPaymentHistory.getUser().getName()); + String flameName = orderHelper.createFlameName(requestPaymentHistory.getUser().getName()); - String inherenceNumber = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yy.MM.dd.HH:mm")) + "." + createRandomUUID(); + String inherenceNumber = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yy.MM.dd.HH:mm")) + "." + createRandomUUID(); - donationUsers.add(orderConvertor.donationUser(niceBillOkResponse, requestPaymentHistory.getUserId(), flameName, inherenceNumber, regularPayment.getProjectId(), regularPayment.getId())); + donationUsers.add(orderConvertor.donationUser(portOneResponse.getResponse(), requestPaymentHistory.getUserId(), flameName, inherenceNumber, regularPayment.getProjectId(), regularPayment.getId())); - requestPaymentHistory.setPaymentStatus(COMPLETE); - amount += regularPayment.getAmount(); - successCnt +=1; + requestPaymentHistory.setPaymentStatus(COMPLETE); + + amount += regularPayment.getAmount(); + successCnt +=1; + + log.info("success Payment Retry historyId: " + requestPaymentHistory.getId()+" userId :" + regularPayment.getUserId() + " orderId : " + orderId + " bid :" + regularPayment.getUserCard().getBid() + " amount :" + regularPayment.getAmount() + "์› projectId :" + regularPayment.getProjectId()); - log.info("success Payment Retry historyId: " + requestPaymentHistory.getId()+" userId :" + regularPayment.getUserId() + " orderId : " + niceBillOkResponse.getOrderId() + " bid :" + regularPayment.getUserCard().getBid() + " amount :" + regularPayment.getAmount() + "์› projectId :" + regularPayment.getProjectId()); - } else { - log.info("fail Payment Retry historyId: " + requestPaymentHistory.getId()+ " userId :" + regularPayment.getUserId() + " orderId : " + niceBillOkResponse.getOrderId() + " bid :" + regularPayment.getUserCard().getBid() + " amount :" + regularPayment.getAmount()); - } } donationUserRepository.saveAll(donationUsers); diff --git a/Match-Common/src/main/java/com/example/matchcommon/config/ConfigurationPropertiesConfig.java b/Match-Common/src/main/java/com/example/matchcommon/config/ConfigurationPropertiesConfig.java index 2d48b2cc..58ef4ff5 100644 --- a/Match-Common/src/main/java/com/example/matchcommon/config/ConfigurationPropertiesConfig.java +++ b/Match-Common/src/main/java/com/example/matchcommon/config/ConfigurationPropertiesConfig.java @@ -18,7 +18,8 @@ EmailPasswordProperties.class, MatchAligoUrl.class, ServerHostProperties.class, - WebReturnUrl.class + WebReturnUrl.class, + PortOneProperties.class }) @Configuration public class ConfigurationPropertiesConfig {} diff --git a/Match-Common/src/main/java/com/example/matchcommon/config/RedisConfig.java b/Match-Common/src/main/java/com/example/matchcommon/config/RedisConfig.java index 5e2746a8..18582312 100644 --- a/Match-Common/src/main/java/com/example/matchcommon/config/RedisConfig.java +++ b/Match-Common/src/main/java/com/example/matchcommon/config/RedisConfig.java @@ -37,8 +37,8 @@ public RedisConnectionFactory redisConnectionFactory() { .build()) .commandTimeout(Duration.ofSeconds(1000L)).build(); - //return new LettuceConnectionFactory(clusterConfiguration, clientConfiguration); - return new LettuceConnectionFactory(redisProperties.getHost(), redisProperties.getPort()); + return new LettuceConnectionFactory(clusterConfiguration, clientConfiguration); + //return new LettuceConnectionFactory(redisProperties.getHost(), redisProperties.getPort()); } @Bean diff --git a/Match-Common/src/main/java/com/example/matchcommon/constants/MatchStatic.java b/Match-Common/src/main/java/com/example/matchcommon/constants/MatchStatic.java index 4499859b..e78b5afa 100644 --- a/Match-Common/src/main/java/com/example/matchcommon/constants/MatchStatic.java +++ b/Match-Common/src/main/java/com/example/matchcommon/constants/MatchStatic.java @@ -25,5 +25,7 @@ public class MatchStatic { public static final String MATCH_NAME = "๋งค์น˜"; + public static final String PORT_ONE = "port_one"; + } diff --git a/Match-Common/src/main/java/com/example/matchcommon/properties/NicePayProperties.java b/Match-Common/src/main/java/com/example/matchcommon/properties/NicePayProperties.java index 0890eb8b..5122c509 100644 --- a/Match-Common/src/main/java/com/example/matchcommon/properties/NicePayProperties.java +++ b/Match-Common/src/main/java/com/example/matchcommon/properties/NicePayProperties.java @@ -18,5 +18,7 @@ public class NicePayProperties { private String secret; private String client; private String url; + private String mid; + private String key; } diff --git a/Match-Common/src/main/java/com/example/matchcommon/properties/PortOneProperties.java b/Match-Common/src/main/java/com/example/matchcommon/properties/PortOneProperties.java new file mode 100644 index 00000000..12cbb780 --- /dev/null +++ b/Match-Common/src/main/java/com/example/matchcommon/properties/PortOneProperties.java @@ -0,0 +1,21 @@ +package com.example.matchcommon.properties; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.ConstructorBinding; +import org.springframework.stereotype.Component; + +@Getter +@Setter +@RequiredArgsConstructor +@ConstructorBinding +@Component +@ConfigurationProperties("portone") +public class PortOneProperties { + private String code; + private String key; + private String secret; + private String billmid; +} diff --git a/Match-Domain/src/main/generated/com/example/matchdomain/donation/entity/QUserCard.java b/Match-Domain/src/main/generated/com/example/matchdomain/donation/entity/QUserCard.java index 278a0789..44e5fd69 100644 --- a/Match-Domain/src/main/generated/com/example/matchdomain/donation/entity/QUserCard.java +++ b/Match-Domain/src/main/generated/com/example/matchdomain/donation/entity/QUserCard.java @@ -28,7 +28,7 @@ public class QUserCard extends EntityPathBase { public final EnumPath cardAbleStatus = createEnum("cardAbleStatus", com.example.matchdomain.donation.entity.enums.CardAbleStatus.class); - public final StringPath cardCode = createString("cardCode"); + public final EnumPath cardCode = createEnum("cardCode", com.example.matchdomain.donation.entity.enums.CardCode.class); public final StringPath cardName = createString("cardName"); @@ -39,6 +39,8 @@ public class QUserCard extends EntityPathBase { //inherited public final DateTimePath createdAt = _super.createdAt; + public final StringPath customerId = createString("customerId"); + public final StringPath expMonth = createString("expMonth"); public final StringPath expYear = createString("expYear"); diff --git a/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/UserCard.java b/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/UserCard.java index 636f2a36..b717afe3 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/UserCard.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/donation/entity/UserCard.java @@ -2,6 +2,7 @@ import com.example.matchdomain.common.model.BaseEntity; import com.example.matchdomain.donation.entity.enums.CardAbleStatus; +import com.example.matchdomain.donation.entity.enums.CardCode; import com.example.matchdomain.user.entity.User; import lombok.*; import org.hibernate.annotations.DynamicInsert; @@ -42,6 +43,8 @@ public class UserCard extends BaseEntity { //๋นŒํ‚ค ๋ฒˆํ˜ธ private String bid; + private String customerId; + private String cardNo; private String expYear; @@ -52,7 +55,7 @@ public class UserCard extends BaseEntity { private String cardPw; - private String cardCode; + private CardCode cardCode; private String cardName; diff --git a/Match-Domain/src/main/java/com/example/matchdomain/order/exception/PortOneAuthErrorCode.java b/Match-Domain/src/main/java/com/example/matchdomain/order/exception/PortOneAuthErrorCode.java new file mode 100644 index 00000000..8dff2328 --- /dev/null +++ b/Match-Domain/src/main/java/com/example/matchdomain/order/exception/PortOneAuthErrorCode.java @@ -0,0 +1,44 @@ +package com.example.matchdomain.order.exception; + +import com.example.matchcommon.annotation.ExplainError; +import com.example.matchcommon.dto.ErrorReason; +import com.example.matchcommon.exception.errorcode.BaseErrorCode; +import lombok.AllArgsConstructor; +import lombok.Getter; +import org.springframework.http.HttpStatus; + +import java.lang.reflect.Field; +import java.util.Objects; + +import static org.springframework.http.HttpStatus.BAD_REQUEST; +import static org.springframework.http.HttpStatus.INTERNAL_SERVER_ERROR; + +@Getter +@AllArgsConstructor +public enum PortOneAuthErrorCode implements BaseErrorCode { + FAILED_ERROR_AUTH_AMOUNT(BAD_REQUEST,"ORDER_001", "์ฃผ๋ฌธ ๊ฐ€๊ฒฉ์ด ์ผ์น˜ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค."), + NOT_EXIST_ORDER_ID(BAD_REQUEST,"ORDER_002", "์ฃผ๋ฌธ ๋ฒˆํ˜ธ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค."), + FAILED_ERROR_AUTH(INTERNAL_SERVER_ERROR,"ORDER_003", "๊ฒฐ์ œ์š”์ฒญ์— ์‹คํŒจํ–ˆ์Šต๋‹ˆ๋‹ค."); + + + private final HttpStatus httpStatus; + private final String code; + private final String message; + + @Override + public ErrorReason getErrorReason() { + return ErrorReason.builder().message(message).code(code).isSuccess(false).build(); + } + + @Override + public String getExplainError() throws NoSuchFieldException { + Field field = this.getClass().getField(this.name()); + ExplainError annotation = field.getAnnotation(ExplainError.class); + return Objects.nonNull(annotation) ? annotation.value() : this.getMessage(); + } + + @Override + public ErrorReason getErrorReasonHttpStatus(){ + return ErrorReason.builder().message(message).code(code).isSuccess(false).httpStatus(httpStatus).build(); + } +} \ No newline at end of file diff --git a/Match-Domain/src/main/java/com/example/matchdomain/project/repository/ProjectRepository.java b/Match-Domain/src/main/java/com/example/matchdomain/project/repository/ProjectRepository.java index aadb7744..93dd93ec 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/project/repository/ProjectRepository.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/project/repository/ProjectRepository.java @@ -202,6 +202,8 @@ Page findTodayProject(@Param("userId") Long userId, @Param("project "GROUP BY P.id ", nativeQuery = true) ProjectDetail getProjectAppDetail(@Param("userId") Long userId, @Param("projectId") Long projectId); + Optional findByIdAndStatusAndRegularStatus(Long projectId, Status status, RegularStatus regular); + interface ProjectList { Long getId(); String getImgUrl(); diff --git a/Match-Domain/src/main/java/com/example/matchdomain/redis/entity/OrderRequest.java b/Match-Domain/src/main/java/com/example/matchdomain/redis/entity/OrderRequest.java index 09536e9c..bd2c2a4d 100644 --- a/Match-Domain/src/main/java/com/example/matchdomain/redis/entity/OrderRequest.java +++ b/Match-Domain/src/main/java/com/example/matchdomain/redis/entity/OrderRequest.java @@ -19,6 +19,7 @@ public class OrderRequest { private String projectId; + @TimeToLive private long ttl; diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/discord/config/DiscordInfoConfig.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/discord/config/DiscordInfoConfig.java index c2f4c8bf..f219da0d 100644 --- a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/discord/config/DiscordInfoConfig.java +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/discord/config/DiscordInfoConfig.java @@ -1,6 +1,5 @@ package com.example.matchinfrastructure.discord.config; -import com.example.matchinfrastructure.oauth.kakao.config.KakaoInfoErrorDecoder; import feign.codec.Encoder; import feign.codec.ErrorDecoder; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/client/PortOneFeignClient.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/client/PortOneFeignClient.java new file mode 100644 index 00000000..be91a0d0 --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/client/PortOneFeignClient.java @@ -0,0 +1,39 @@ +package com.example.matchinfrastructure.pay.portone.client; + +import com.example.matchinfrastructure.oauth.kakao.dto.KakaoUserAddressDto; +import com.example.matchinfrastructure.oauth.kakao.dto.KakaoUserInfoDto; +import com.example.matchinfrastructure.pay.portone.config.PortOneFeignConfiguration; +import com.example.matchinfrastructure.pay.portone.config.PortOneInfoConfig; +import com.example.matchinfrastructure.pay.portone.config.PortOneInfoErrorDecoder; +import com.example.matchinfrastructure.pay.portone.dto.PortOneAuth; +import com.example.matchinfrastructure.pay.portone.dto.PortOneBillPayResponse; +import com.example.matchinfrastructure.pay.portone.dto.PortOneBillResponse; +import com.example.matchinfrastructure.pay.portone.dto.PortOneResponse; +import com.example.matchinfrastructure.pay.portone.dto.req.PayWithBillKeyReq; +import com.example.matchinfrastructure.pay.portone.dto.req.PortOneAuthReq; +import com.example.matchinfrastructure.pay.portone.dto.req.PortOneBillReq; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.stereotype.Component; +import org.springframework.web.bind.annotation.*; + +@FeignClient( + name = "PortOneFeignClient", + url = "https://api.iamport.kr", + configuration = PortOneInfoErrorDecoder.class) +@Component +public interface PortOneFeignClient { + @PostMapping("/users/getToken") + public PortOneResponse getAccessToken(@RequestBody PortOneAuthReq portOneAuthReq); + + @PostMapping("/subscribe/customers/{customer_uid}") + public PortOneResponse getBillKey( + @RequestHeader("Authorization") String accessToken, + @PathVariable("customer_uid") String customer_uid, + @RequestBody PortOneBillReq portOneBillReq); + + @PostMapping("/subscribe/payments/again") + public PortOneResponse payWithBillKey( + @RequestHeader("Authorization") String accessToken, + @RequestBody PayWithBillKeyReq payWithBillKeyReq + ); +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/config/PortOneFeignConfiguration.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/config/PortOneFeignConfiguration.java new file mode 100644 index 00000000..2384b859 --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/config/PortOneFeignConfiguration.java @@ -0,0 +1,24 @@ +package com.example.matchinfrastructure.pay.portone.config; + +import feign.Logger; +import feign.RequestInterceptor; +import feign.codec.ErrorDecoder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Import; + +@Import(PortOneInfoErrorDecoder.class) +public class PortOneFeignConfiguration { + @Bean + public RequestInterceptor requestInterceptor() { + return template -> template.header("Content-Type", "application/json"); + } + @Bean + public ErrorDecoder errorDecoder() { + return new PortOneInfoErrorDecoder(); + } + + @Bean + Logger.Level feignLoggerLevel() { + return Logger.Level.FULL; + } +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/config/PortOneInfoConfig.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/config/PortOneInfoConfig.java new file mode 100644 index 00000000..789c8f75 --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/config/PortOneInfoConfig.java @@ -0,0 +1,23 @@ +package com.example.matchinfrastructure.pay.portone.config; + + +import feign.codec.Encoder; +import feign.codec.ErrorDecoder; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Import; + +@Import(PortOneInfoErrorDecoder.class) +public class PortOneInfoConfig { + + @Bean + @ConditionalOnMissingBean(value = ErrorDecoder.class) + public PortOneInfoErrorDecoder commonFeignErrorDecoder() { + return new PortOneInfoErrorDecoder(); + } + + @Bean + Encoder formEncoder() { + return new feign.form.FormEncoder(); + } +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/config/PortOneInfoErrorDecoder.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/config/PortOneInfoErrorDecoder.java new file mode 100644 index 00000000..d0e25bf4 --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/config/PortOneInfoErrorDecoder.java @@ -0,0 +1,72 @@ +package com.example.matchinfrastructure.pay.portone.config; + + +import com.example.matchcommon.exception.OtherServerException; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import feign.FeignException; +import feign.Response; +import feign.codec.ErrorDecoder; +import lombok.SneakyThrows; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import static com.example.matchcommon.exception.errorcode.OtherServerErrorCode.*; + +public class PortOneInfoErrorDecoder implements ErrorDecoder { + + @Override + public Exception decode(String methodKey, Response response) { + InputStream responseBodyStream = null; + try { + responseBodyStream = response.body().asInputStream(); + } catch (IOException e) { + throw new RuntimeException(e); + } + + String responseBody = null; + try { + responseBody = convertInputStreamToString(responseBodyStream); + } catch (IOException e) { + throw new RuntimeException(e); + } + + ObjectMapper objectMapper = new ObjectMapper(); + JsonNode jsonNode = null; + try { + jsonNode = objectMapper.readTree(responseBody); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + System.out.println("์—๋Ÿฌ ๋””์ฝ”๋”ฉ"); + System.out.println(jsonNode); + + if (response.status() >= 400) { + switch (response.status()) { + case 401: + throw new OtherServerException(OTHER_SERVER_UNAUTHORIZED); + case 403: + throw new OtherServerException(OTHER_SERVER_FORBIDDEN); + case 419: + throw new OtherServerException(OTHER_SERVER_EXPIRED_TOKEN); + default: + throw new OtherServerException(OTHER_SERVER_BAD_REQUEST); + } + } + + return FeignException.errorStatus(methodKey, response); + } + + private String convertInputStreamToString(InputStream inputStream) throws IOException { + ByteArrayOutputStream result = new ByteArrayOutputStream(); + byte[] buffer = new byte[1024]; + int length; + while ((length = inputStream.read(buffer)) != -1) { + result.write(buffer, 0, length); + } + return result.toString("UTF-8"); + } +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/convertor/PortOneConvertor.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/convertor/PortOneConvertor.java new file mode 100644 index 00000000..a4f3bef6 --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/convertor/PortOneConvertor.java @@ -0,0 +1,33 @@ +package com.example.matchinfrastructure.pay.portone.convertor; + +import com.example.matchcommon.annotation.Convertor; +import com.example.matchcommon.properties.PortOneProperties; +import com.example.matchinfrastructure.pay.portone.dto.req.PayWithBillKeyReq; +import com.example.matchinfrastructure.pay.portone.dto.req.PortOneBillReq; +import lombok.RequiredArgsConstructor; + +@Convertor +@RequiredArgsConstructor +public class PortOneConvertor { + private final PortOneProperties portOneProperties; + public PortOneBillReq PortOneBill(String cardNo, String expiry, String idNo, String cardPw) { + return PortOneBillReq + .builder() + .pg("nice."+portOneProperties.getBillmid()) + .card_number(cardNo) + .expiry(expiry) + .birth(idNo) + .pwd_2digit(cardPw) + .build(); + } + + public PayWithBillKeyReq PayWithBillKey(String bid, String orderId, Long amount, String projectName, String customerId) { + return PayWithBillKeyReq + .builder() + .customer_uid(bid) + .merchant_uid(orderId) + .amount(amount) + .name(projectName) + .build(); + } +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/dto/PortOneAuth.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/dto/PortOneAuth.java new file mode 100644 index 00000000..01808404 --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/dto/PortOneAuth.java @@ -0,0 +1,14 @@ +package com.example.matchinfrastructure.pay.portone.dto; + +import lombok.*; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class PortOneAuth { + private String access_token; + private Long expired_at; + private Long now; +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/dto/PortOneBillPayResponse.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/dto/PortOneBillPayResponse.java new file mode 100644 index 00000000..59325039 --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/dto/PortOneBillPayResponse.java @@ -0,0 +1,70 @@ +package com.example.matchinfrastructure.pay.portone.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +@AllArgsConstructor +@RequiredArgsConstructor +public class PortOneBillPayResponse { + private String imp_uid; + private String merchant_uid; + private String pay_method; + private String channel; + private String pg_provider; + private String emb_pg_provider; + private String pg_tid; + private String pg_id; + private boolean escrow; + private String apply_num; + private String bank_code; + private String bank_name; + private String card_code; + private String card_name; + private int card_quota; + private String card_number; + private int card_type; + private String vbank_code; + private String vbank_name; + private String vbank_num; + private String vbank_holder; + private int vbank_date; + private int vbank_issued_at; + private String name; + private int amount; + private int cancel_amount; + private String currency; + private String buyer_name; + private String buyer_email; + private String buyer_tel; + private String buyer_addr; + private String buyer_postcode; + private String custom_data; + private String user_agent; + private String status; + private int started_at; + private int paid_at; + private int failed_at; + private int cancelled_at; + private String fail_reason; + private String cancel_reason; + private String receipt_url; + private boolean cash_receipt_issued; + private String customer_uid; + private String customer_uid_usage; + private List cancel_history; + private List cancel_receipt_urls; + + public static class CancelHistory { + private String pg_tid; + private int amount; + private int cancelled_at; + private String reason; + private String receipt_url; + } +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/dto/PortOneBillResponse.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/dto/PortOneBillResponse.java new file mode 100644 index 00000000..84e711b5 --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/dto/PortOneBillResponse.java @@ -0,0 +1,26 @@ +package com.example.matchinfrastructure.pay.portone.dto; + +import lombok.*; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class PortOneBillResponse { + private String customer_uid; + private String pg_provider; + private String pg_id; + private String customer_id; + private String card_name; + private String card_code; + private String card_number; + private int card_type; + private String customer_name; + private String customer_tel; + private String customer_email; + private String customer_addr; + private String customer_postcode; + private int inserted; + private int updated; +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/dto/PortOneResponse.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/dto/PortOneResponse.java new file mode 100644 index 00000000..73a9a465 --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/dto/PortOneResponse.java @@ -0,0 +1,32 @@ +package com.example.matchinfrastructure.pay.portone.dto; + +import com.google.gson.annotations.SerializedName; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@AllArgsConstructor +@NoArgsConstructor +public class PortOneResponse { + @SerializedName("code") + int code; + + @SerializedName("message") + String message; + + @SerializedName("response") + T response; + + public int getCode() { + return code; + } + + public String getMessage() { + return message; + } + + public T getResponse() { + return response; + } +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/dto/req/PayWithBillKeyReq.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/dto/req/PayWithBillKeyReq.java new file mode 100644 index 00000000..25d78848 --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/dto/req/PayWithBillKeyReq.java @@ -0,0 +1,14 @@ +package com.example.matchinfrastructure.pay.portone.dto.req; + +import lombok.*; +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class PayWithBillKeyReq { + private String customer_uid; + private String merchant_uid; + private double amount; + private String name; +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/dto/req/PortOneAuthReq.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/dto/req/PortOneAuthReq.java new file mode 100644 index 00000000..a989fe98 --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/dto/req/PortOneAuthReq.java @@ -0,0 +1,14 @@ +package com.example.matchinfrastructure.pay.portone.dto.req; + +import lombok.*; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class PortOneAuthReq { + private String imp_key; + + private String imp_secret; +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/dto/req/PortOneBillReq.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/dto/req/PortOneBillReq.java new file mode 100644 index 00000000..197cd3a9 --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/dto/req/PortOneBillReq.java @@ -0,0 +1,16 @@ +package com.example.matchinfrastructure.pay.portone.dto.req; + +import lombok.*; + +@Getter +@Setter +@NoArgsConstructor +@AllArgsConstructor +@Builder +public class PortOneBillReq { + private String pg; + private String card_number; + private String expiry; + private String birth; + private String pwd_2digit; +} diff --git a/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/service/PortOneAuthService.java b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/service/PortOneAuthService.java new file mode 100644 index 00000000..28117605 --- /dev/null +++ b/Match-Infrastructure/src/main/java/com/example/matchinfrastructure/pay/portone/service/PortOneAuthService.java @@ -0,0 +1,52 @@ +package com.example.matchinfrastructure.pay.portone.service; + +import com.example.matchcommon.properties.PortOneProperties; +import com.example.matchinfrastructure.pay.portone.client.PortOneFeignClient; +import com.example.matchinfrastructure.pay.portone.dto.PortOneAuth; +import com.example.matchinfrastructure.pay.portone.dto.PortOneResponse; +import com.example.matchinfrastructure.pay.portone.dto.req.PortOneAuthReq; +import lombok.RequiredArgsConstructor; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.CachePut; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class PortOneAuthService { + private final PortOneFeignClient portOneFeignClient; + private final PortOneProperties portOneProperties; + + public String getToken() { + String token = fetchPortOneToken(); + System.out.println(token); + return token; + } + + @CachePut(value = "portOneTokenCache") + public String fetchPortOneToken() { + PortOneResponse portOneResponse = portOneFeignClient.getAccessToken(PortOneAuthReq.builder().imp_key(portOneProperties.getKey()).imp_secret(portOneProperties.getSecret()).build()); + return portOneResponse.getResponse().getAccess_token(); + } + + @CachePut(value = "portOneTokenCache") + public String refreshToken() { + String newToken = fetchPortOneToken(); + System.out.println("refresh Token"); + return newToken; + } + + //@Scheduled(fixedRate = 100) + @Scheduled(fixedRate = 1700000) // 30๋ถ„๋งˆ๋‹ค ์‹คํ–‰ (1800์ดˆ) + public void scheduleTokenRefresh() { + System.out.println("refresh token Schedule"); + String refreshToken = refreshToken(); + } + + public String getTokens() { + PortOneResponse portOneResponse = portOneFeignClient.getAccessToken(PortOneAuthReq.builder().imp_key(portOneProperties.getKey()).imp_secret(portOneProperties.getSecret()).build()); + return portOneResponse.getResponse().getAccess_token(); + } +} +