From 8aa3ec14e118ae83f980612ebb99441f72659f5a Mon Sep 17 00:00:00 2001 From: Laptop-Limjihoon <lonelynight1026@gmail.com> Date: Mon, 5 Feb 2024 04:37:58 +0900 Subject: [PATCH 1/9] =?UTF-8?q?feat=20:=20TossPaymentEventListener=20?= =?UTF-8?q?=EB=A5=BC=20=ED=86=B5=ED=95=B4=20TossAPI=20=ED=95=B8=EB=93=A4?= =?UTF-8?q?=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../event/TossPaymentEventListener.java | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 src/main/java/bc1/gream/domain/payment/toss/service/event/TossPaymentEventListener.java diff --git a/src/main/java/bc1/gream/domain/payment/toss/service/event/TossPaymentEventListener.java b/src/main/java/bc1/gream/domain/payment/toss/service/event/TossPaymentEventListener.java new file mode 100644 index 00000000..fc9729ff --- /dev/null +++ b/src/main/java/bc1/gream/domain/payment/toss/service/event/TossPaymentEventListener.java @@ -0,0 +1,42 @@ +package bc1.gream.domain.payment.toss.service.event; + +import bc1.gream.domain.payment.toss.dto.response.TossPaymentSuccessResponseDto; +import java.nio.charset.StandardCharsets; +import java.util.Base64; +import java.util.Collections; +import net.minidev.json.JSONObject; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.stereotype.Component; +import org.springframework.transaction.event.TransactionalEventListener; +import org.springframework.web.client.RestTemplate; + +@Component +public class TossPaymentEventListener { + + @TransactionalEventListener + public void handleTossPaymentSuccess(TossPaymentSuccessEvent event) { + RestTemplate rest = new RestTemplate(); + HttpHeaders headers = new HttpHeaders(); + + String testSecretApiKey = event.getTestSecretApiKey() + ":"; + String encodedAuth = new String(Base64.getEncoder().encode(testSecretApiKey.getBytes(StandardCharsets.UTF_8))); + headers.setBasicAuth(encodedAuth); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); + + JSONObject param = new JSONObject(); + param.put("orderId", event.getOrderId()); + param.put("amount", event.getAmount()); + + TossPaymentSuccessResponseDto responseDto = rest.postForObject( + event.getSuccessUrl() + event.getPaymentKey(), + new HttpEntity<>(param, headers), + TossPaymentSuccessResponseDto.class + ); + + // Use the functional interface to pass the result back to TossPaymentController + event.getCallback().handle(responseDto); + } +} From 8806e02e68559cd61af5665ebd4d5e3a8c857e30 Mon Sep 17 00:00:00 2001 From: Laptop-Limjihoon <lonelynight1026@gmail.com> Date: Mon, 5 Feb 2024 04:38:13 +0900 Subject: [PATCH 2/9] =?UTF-8?q?feat=20:=20TossPaymentSuccessEvent=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../event/TossPaymentSuccessEvent.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 src/main/java/bc1/gream/domain/payment/toss/service/event/TossPaymentSuccessEvent.java diff --git a/src/main/java/bc1/gream/domain/payment/toss/service/event/TossPaymentSuccessEvent.java b/src/main/java/bc1/gream/domain/payment/toss/service/event/TossPaymentSuccessEvent.java new file mode 100644 index 00000000..179f4fe1 --- /dev/null +++ b/src/main/java/bc1/gream/domain/payment/toss/service/event/TossPaymentSuccessEvent.java @@ -0,0 +1,27 @@ +package bc1.gream.domain.payment.toss.service.event; + +import lombok.Getter; +import org.springframework.context.ApplicationEvent; + +@Getter +public class TossPaymentSuccessEvent extends ApplicationEvent { + + private final String paymentKey; + private final Long orderId; + private final Long amount; + private final String successUrl; + private final String testSecretApiKey; + + private final TossPaymentSuccessCallback callback; + + public TossPaymentSuccessEvent(Object source, String paymentKey, Long orderId, Long amount, String successUrl, String testSecretApiKey, + TossPaymentSuccessCallback callback) { + super(source); + this.paymentKey = paymentKey; + this.orderId = orderId; + this.amount = amount; + this.successUrl = successUrl; + this.testSecretApiKey = testSecretApiKey; + this.callback = callback; + } +} From f53e29496becfca0f1bbada1a4e1fbf060acb4dd Mon Sep 17 00:00:00 2001 From: Laptop-Limjihoon <lonelynight1026@gmail.com> Date: Mon, 5 Feb 2024 04:38:22 +0900 Subject: [PATCH 3/9] =?UTF-8?q?feat=20:=20TossPaymentSuccessCallback=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../toss/service/event/TossPaymentSuccessCallback.java | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/main/java/bc1/gream/domain/payment/toss/service/event/TossPaymentSuccessCallback.java diff --git a/src/main/java/bc1/gream/domain/payment/toss/service/event/TossPaymentSuccessCallback.java b/src/main/java/bc1/gream/domain/payment/toss/service/event/TossPaymentSuccessCallback.java new file mode 100644 index 00000000..8e4d63c3 --- /dev/null +++ b/src/main/java/bc1/gream/domain/payment/toss/service/event/TossPaymentSuccessCallback.java @@ -0,0 +1,9 @@ +package bc1.gream.domain.payment.toss.service.event; + +import bc1.gream.domain.payment.toss.dto.response.TossPaymentSuccessResponseDto; + +@FunctionalInterface +public interface TossPaymentSuccessCallback { + + void handle(TossPaymentSuccessResponseDto responseDto); +} From 774931eec575465d3eb67f573360bc01b619ae1b Mon Sep 17 00:00:00 2001 From: Laptop-Limjihoon <lonelynight1026@gmail.com> Date: Mon, 5 Feb 2024 04:39:26 +0900 Subject: [PATCH 4/9] =?UTF-8?q?feat=20:=20PaymentService=20=EC=97=90?= =?UTF-8?q?=EC=84=9C=20ApplicationEventPublisher=20=EC=9D=84=20=ED=86=B5?= =?UTF-8?q?=ED=95=B4=20TossPaymentSuccessEvent=20=ED=98=B8=EC=B6=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...aymentService.java => PaymentService.java} | 59 +++++-------------- 1 file changed, 15 insertions(+), 44 deletions(-) rename src/main/java/bc1/gream/domain/payment/toss/service/{TossPaymentService.java => PaymentService.java} (65%) diff --git a/src/main/java/bc1/gream/domain/payment/toss/service/TossPaymentService.java b/src/main/java/bc1/gream/domain/payment/toss/service/PaymentService.java similarity index 65% rename from src/main/java/bc1/gream/domain/payment/toss/service/TossPaymentService.java rename to src/main/java/bc1/gream/domain/payment/toss/service/PaymentService.java index 8e95a85a..91c07374 100644 --- a/src/main/java/bc1/gream/domain/payment/toss/service/TossPaymentService.java +++ b/src/main/java/bc1/gream/domain/payment/toss/service/PaymentService.java @@ -2,30 +2,25 @@ import bc1.gream.domain.payment.toss.dto.response.TossPaymentFailResponseDto; import bc1.gream.domain.payment.toss.dto.response.TossPaymentInitialResponseDto; -import bc1.gream.domain.payment.toss.dto.response.TossPaymentSuccessResponseDto; import bc1.gream.domain.payment.toss.entity.TossPayment; import bc1.gream.domain.payment.toss.mapper.TossPaymentMapper; import bc1.gream.domain.payment.toss.repository.TossPaymentRepository; +import bc1.gream.domain.payment.toss.service.event.TossPaymentSuccessCallback; +import bc1.gream.domain.payment.toss.service.event.TossPaymentSuccessEvent; import bc1.gream.global.common.ResultCase; import bc1.gream.global.exception.GlobalException; -import java.nio.charset.StandardCharsets; -import java.util.Base64; -import java.util.Collections; import lombok.RequiredArgsConstructor; -import net.minidev.json.JSONObject; import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.MediaType; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.client.RestTemplate; @Service @RequiredArgsConstructor -public class TossPaymentService { +public class PaymentService { private final TossPaymentRepository tossPaymentRepository; + private final ApplicationEventPublisher eventPublisher; @Value("${payment.toss.test_client_api_key}") private String testClientApiKey; @@ -57,9 +52,16 @@ public TossPaymentInitialResponseDto requestTossPayment(TossPayment tossPayment) * @return 토스페이 최종요청 결과 */ @Transactional - public TossPaymentSuccessResponseDto requestFinalTossPayment(String paymentKey, Long orderId, Long amount) { + public void requestFinalTossPayment(String paymentKey, Long orderId, Long amount, TossPaymentSuccessCallback callback) { this.verifyRequest(paymentKey, orderId, amount); - return this.sendFinalRequestToTossApi(paymentKey, orderId, amount); + eventPublisher.publishEvent(new TossPaymentSuccessEvent( + this, + paymentKey, + orderId, + amount, + successUrl, + testSecretApiKey, + callback)); // Provide method reference to the event listener } /** @@ -88,7 +90,7 @@ public TossPaymentFailResponseDto requestFail(String errorCode, String errorMsg, @Transactional void verifyRequest(String paymentKey, Long orderId, Long amount) { // 주문아이디 일치 검증 - TossPayment tossPayment = findBy(orderId); + TossPayment tossPayment = this.findBy(orderId); // 결제금액 일치 검증 if (tossPayment.getAmount().equals(amount)) { tossPayment.setPaymentKey(paymentKey); @@ -97,37 +99,6 @@ void verifyRequest(String paymentKey, Long orderId, Long amount) { throw new GlobalException(ResultCase.UNMATCHED_PAYMENT_AMOUNT); } - /** - * 토스페이API에 POST요청 - * - * @param paymentKey 토스 결제고유번호 - * @param orderId 서버 주문고유번호 - * @param amount 결제액 - * @return 토스페이API 요청결과 - */ - @Transactional - TossPaymentSuccessResponseDto sendFinalRequestToTossApi(String paymentKey, Long orderId, Long amount) { - RestTemplate rest = new RestTemplate(); - - HttpHeaders headers = new HttpHeaders(); - - testSecretApiKey = testSecretApiKey + ":"; - String encodedAuth = new String(Base64.getEncoder().encode(testSecretApiKey.getBytes(StandardCharsets.UTF_8))); - headers.setBasicAuth(encodedAuth); - headers.setContentType(MediaType.APPLICATION_JSON); - headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); - - JSONObject param = new JSONObject(); - param.put("orderId", orderId); - param.put("amount", amount); - - return rest.postForObject( - successUrl + paymentKey, - new HttpEntity<>(param, headers), - TossPaymentSuccessResponseDto.class - ); - } - @Transactional(readOnly = true) TossPayment findBy(Long orderId) { return tossPaymentRepository.findByOrderId(orderId) From cd1c593f42f8aeca05630f90e58b4deedd3e2be7 Mon Sep 17 00:00:00 2001 From: Laptop-Limjihoon <lonelynight1026@gmail.com> Date: Mon, 5 Feb 2024 04:40:31 +0900 Subject: [PATCH 5/9] =?UTF-8?q?feat=20:=20TossPaymentController=20?= =?UTF-8?q?=EC=97=90=EC=84=9C=20Semaphore=EB=A5=BC=20=ED=86=B5=ED=95=B4=20?= =?UTF-8?q?=EC=9D=B4=EB=B2=A4=ED=8A=B8=20=EC=B2=98=EB=A6=AC=EC=97=90=20?= =?UTF-8?q?=EB=94=B0=EB=A5=B8=20callback=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/TossPaymentController.java | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/src/main/java/bc1/gream/domain/payment/toss/controller/TossPaymentController.java b/src/main/java/bc1/gream/domain/payment/toss/controller/TossPaymentController.java index d03986e4..a8396c8f 100644 --- a/src/main/java/bc1/gream/domain/payment/toss/controller/TossPaymentController.java +++ b/src/main/java/bc1/gream/domain/payment/toss/controller/TossPaymentController.java @@ -6,13 +6,15 @@ import bc1.gream.domain.payment.toss.dto.response.TossPaymentSuccessResponseDto; import bc1.gream.domain.payment.toss.entity.TossPayment; import bc1.gream.domain.payment.toss.mapper.TossPaymentMapper; -import bc1.gream.domain.payment.toss.service.TossPaymentService; +import bc1.gream.domain.payment.toss.service.PaymentService; import bc1.gream.domain.payment.toss.validator.TossPaymentRequestValidator; import bc1.gream.global.common.RestResponse; import bc1.gream.global.security.UserDetailsImpl; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; +import java.util.concurrent.Semaphore; +import java.util.concurrent.atomic.AtomicReference; import lombok.RequiredArgsConstructor; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.GetMapping; @@ -27,7 +29,7 @@ @RequiredArgsConstructor public class TossPaymentController { - private final TossPaymentService tossPaymentService; + private final PaymentService paymentService; @PostMapping("/request") @Operation(summary = "토스페이 결제 검증/확인 요청", description = "결제정보에 대한 검증/확인 이후 필요한 값들을 반환합니다.") @@ -37,7 +39,7 @@ public RestResponse<TossPaymentInitialResponseDto> requestTossPayment( ) { TossPaymentRequestValidator.validate(requestDto); TossPayment payment = TossPaymentMapper.INSTANCE.fromTossPaymentInitialRequestDto(userDetails.getUser(), requestDto); - TossPaymentInitialResponseDto responseDto = tossPaymentService.requestTossPayment(payment); + TossPaymentInitialResponseDto responseDto = paymentService.requestTossPayment(payment); return RestResponse.success(responseDto); } @@ -47,9 +49,18 @@ public RestResponse<TossPaymentSuccessResponseDto> requestFinalTossPayment( @Schema(description = "토스 결제고유번호") @RequestParam String paymentKey, @Schema(description = "서버 주분고유번호") @RequestParam Long orderId, @Schema(description = "결제금액") @RequestParam Long amount - ) { - TossPaymentSuccessResponseDto responseDto = tossPaymentService.requestFinalTossPayment(paymentKey, orderId, amount); - return RestResponse.success(responseDto); + ) throws InterruptedException { + AtomicReference<TossPaymentSuccessResponseDto> responseDtoHolder = new AtomicReference<>(); + Semaphore semaphore = new Semaphore(0); + + paymentService.requestFinalTossPayment(paymentKey, orderId, amount, responseDto -> { + responseDtoHolder.set(responseDto); + semaphore.release(); + }); + + semaphore.acquire(); + + return RestResponse.success(responseDtoHolder.get()); } @GetMapping("/fail") @@ -59,7 +70,7 @@ public RestResponse<TossPaymentFailResponseDto> requestFail( @Schema(description = "에러 메세지") @RequestParam String errorMsg, @Schema(description = "서버 주문고유번호") @RequestParam Long orderId ) { - TossPaymentFailResponseDto responseDto = tossPaymentService.requestFail(errorCode, errorMsg, orderId); + TossPaymentFailResponseDto responseDto = paymentService.requestFail(errorCode, errorMsg, orderId); return RestResponse.success(responseDto); } } From dbf37882402073532c87e33a176a6ecab6daa2d0 Mon Sep 17 00:00:00 2001 From: Laptop-Limjihoon <lonelynight1026@gmail.com> Date: Mon, 5 Feb 2024 04:40:47 +0900 Subject: [PATCH 6/9] =?UTF-8?q?Revert=20"feat=20:=20TossPaymentController?= =?UTF-8?q?=20=EC=97=90=EC=84=9C=20Semaphore=EB=A5=BC=20=ED=86=B5=ED=95=B4?= =?UTF-8?q?=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20=EC=B2=98=EB=A6=AC=EC=97=90=20?= =?UTF-8?q?=EB=94=B0=EB=A5=B8=20callback=20=EC=B2=98=EB=A6=AC"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit cd1c593f42f8aeca05630f90e58b4deedd3e2be7. --- .../controller/TossPaymentController.java | 25 ++++++------------- 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/src/main/java/bc1/gream/domain/payment/toss/controller/TossPaymentController.java b/src/main/java/bc1/gream/domain/payment/toss/controller/TossPaymentController.java index a8396c8f..d03986e4 100644 --- a/src/main/java/bc1/gream/domain/payment/toss/controller/TossPaymentController.java +++ b/src/main/java/bc1/gream/domain/payment/toss/controller/TossPaymentController.java @@ -6,15 +6,13 @@ import bc1.gream.domain.payment.toss.dto.response.TossPaymentSuccessResponseDto; import bc1.gream.domain.payment.toss.entity.TossPayment; import bc1.gream.domain.payment.toss.mapper.TossPaymentMapper; -import bc1.gream.domain.payment.toss.service.PaymentService; +import bc1.gream.domain.payment.toss.service.TossPaymentService; import bc1.gream.domain.payment.toss.validator.TossPaymentRequestValidator; import bc1.gream.global.common.RestResponse; import bc1.gream.global.security.UserDetailsImpl; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; -import java.util.concurrent.Semaphore; -import java.util.concurrent.atomic.AtomicReference; import lombok.RequiredArgsConstructor; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.GetMapping; @@ -29,7 +27,7 @@ @RequiredArgsConstructor public class TossPaymentController { - private final PaymentService paymentService; + private final TossPaymentService tossPaymentService; @PostMapping("/request") @Operation(summary = "토스페이 결제 검증/확인 요청", description = "결제정보에 대한 검증/확인 이후 필요한 값들을 반환합니다.") @@ -39,7 +37,7 @@ public RestResponse<TossPaymentInitialResponseDto> requestTossPayment( ) { TossPaymentRequestValidator.validate(requestDto); TossPayment payment = TossPaymentMapper.INSTANCE.fromTossPaymentInitialRequestDto(userDetails.getUser(), requestDto); - TossPaymentInitialResponseDto responseDto = paymentService.requestTossPayment(payment); + TossPaymentInitialResponseDto responseDto = tossPaymentService.requestTossPayment(payment); return RestResponse.success(responseDto); } @@ -49,18 +47,9 @@ public RestResponse<TossPaymentSuccessResponseDto> requestFinalTossPayment( @Schema(description = "토스 결제고유번호") @RequestParam String paymentKey, @Schema(description = "서버 주분고유번호") @RequestParam Long orderId, @Schema(description = "결제금액") @RequestParam Long amount - ) throws InterruptedException { - AtomicReference<TossPaymentSuccessResponseDto> responseDtoHolder = new AtomicReference<>(); - Semaphore semaphore = new Semaphore(0); - - paymentService.requestFinalTossPayment(paymentKey, orderId, amount, responseDto -> { - responseDtoHolder.set(responseDto); - semaphore.release(); - }); - - semaphore.acquire(); - - return RestResponse.success(responseDtoHolder.get()); + ) { + TossPaymentSuccessResponseDto responseDto = tossPaymentService.requestFinalTossPayment(paymentKey, orderId, amount); + return RestResponse.success(responseDto); } @GetMapping("/fail") @@ -70,7 +59,7 @@ public RestResponse<TossPaymentFailResponseDto> requestFail( @Schema(description = "에러 메세지") @RequestParam String errorMsg, @Schema(description = "서버 주문고유번호") @RequestParam Long orderId ) { - TossPaymentFailResponseDto responseDto = paymentService.requestFail(errorCode, errorMsg, orderId); + TossPaymentFailResponseDto responseDto = tossPaymentService.requestFail(errorCode, errorMsg, orderId); return RestResponse.success(responseDto); } } From afe0ab7e1ed62168d4b4b652c5011b95a7756751 Mon Sep 17 00:00:00 2001 From: Laptop-Limjihoon <lonelynight1026@gmail.com> Date: Mon, 5 Feb 2024 04:41:49 +0900 Subject: [PATCH 7/9] =?UTF-8?q?feat=20:=20TossPaymentController=20?= =?UTF-8?q?=EC=97=90=EC=84=9C=20Semaphore=EB=A5=BC=20=ED=86=B5=ED=95=B4=20?= =?UTF-8?q?=EC=9D=B4=EB=B2=A4=ED=8A=B8=20=EC=B2=98=EB=A6=AC=EC=97=90=20?= =?UTF-8?q?=EB=94=B0=EB=A5=B8=20callback=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/TossPaymentController.java | 25 +++++++++++++----- .../controller/TossPaymentControllerTest.java | 26 ++++++++++++------- 2 files changed, 35 insertions(+), 16 deletions(-) diff --git a/src/main/java/bc1/gream/domain/payment/toss/controller/TossPaymentController.java b/src/main/java/bc1/gream/domain/payment/toss/controller/TossPaymentController.java index d03986e4..a8396c8f 100644 --- a/src/main/java/bc1/gream/domain/payment/toss/controller/TossPaymentController.java +++ b/src/main/java/bc1/gream/domain/payment/toss/controller/TossPaymentController.java @@ -6,13 +6,15 @@ import bc1.gream.domain.payment.toss.dto.response.TossPaymentSuccessResponseDto; import bc1.gream.domain.payment.toss.entity.TossPayment; import bc1.gream.domain.payment.toss.mapper.TossPaymentMapper; -import bc1.gream.domain.payment.toss.service.TossPaymentService; +import bc1.gream.domain.payment.toss.service.PaymentService; import bc1.gream.domain.payment.toss.validator.TossPaymentRequestValidator; import bc1.gream.global.common.RestResponse; import bc1.gream.global.security.UserDetailsImpl; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.Valid; +import java.util.concurrent.Semaphore; +import java.util.concurrent.atomic.AtomicReference; import lombok.RequiredArgsConstructor; import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.GetMapping; @@ -27,7 +29,7 @@ @RequiredArgsConstructor public class TossPaymentController { - private final TossPaymentService tossPaymentService; + private final PaymentService paymentService; @PostMapping("/request") @Operation(summary = "토스페이 결제 검증/확인 요청", description = "결제정보에 대한 검증/확인 이후 필요한 값들을 반환합니다.") @@ -37,7 +39,7 @@ public RestResponse<TossPaymentInitialResponseDto> requestTossPayment( ) { TossPaymentRequestValidator.validate(requestDto); TossPayment payment = TossPaymentMapper.INSTANCE.fromTossPaymentInitialRequestDto(userDetails.getUser(), requestDto); - TossPaymentInitialResponseDto responseDto = tossPaymentService.requestTossPayment(payment); + TossPaymentInitialResponseDto responseDto = paymentService.requestTossPayment(payment); return RestResponse.success(responseDto); } @@ -47,9 +49,18 @@ public RestResponse<TossPaymentSuccessResponseDto> requestFinalTossPayment( @Schema(description = "토스 결제고유번호") @RequestParam String paymentKey, @Schema(description = "서버 주분고유번호") @RequestParam Long orderId, @Schema(description = "결제금액") @RequestParam Long amount - ) { - TossPaymentSuccessResponseDto responseDto = tossPaymentService.requestFinalTossPayment(paymentKey, orderId, amount); - return RestResponse.success(responseDto); + ) throws InterruptedException { + AtomicReference<TossPaymentSuccessResponseDto> responseDtoHolder = new AtomicReference<>(); + Semaphore semaphore = new Semaphore(0); + + paymentService.requestFinalTossPayment(paymentKey, orderId, amount, responseDto -> { + responseDtoHolder.set(responseDto); + semaphore.release(); + }); + + semaphore.acquire(); + + return RestResponse.success(responseDtoHolder.get()); } @GetMapping("/fail") @@ -59,7 +70,7 @@ public RestResponse<TossPaymentFailResponseDto> requestFail( @Schema(description = "에러 메세지") @RequestParam String errorMsg, @Schema(description = "서버 주문고유번호") @RequestParam Long orderId ) { - TossPaymentFailResponseDto responseDto = tossPaymentService.requestFail(errorCode, errorMsg, orderId); + TossPaymentFailResponseDto responseDto = paymentService.requestFail(errorCode, errorMsg, orderId); return RestResponse.success(responseDto); } } diff --git a/src/test/java/bc1/gream/domain/payment/toss/controller/TossPaymentControllerTest.java b/src/test/java/bc1/gream/domain/payment/toss/controller/TossPaymentControllerTest.java index 0598302e..cc41f265 100644 --- a/src/test/java/bc1/gream/domain/payment/toss/controller/TossPaymentControllerTest.java +++ b/src/test/java/bc1/gream/domain/payment/toss/controller/TossPaymentControllerTest.java @@ -1,7 +1,9 @@ package bc1.gream.domain.payment.toss.controller; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.when; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; @@ -17,7 +19,8 @@ import bc1.gream.domain.payment.toss.entity.OrderName; import bc1.gream.domain.payment.toss.entity.PayType; import bc1.gream.domain.payment.toss.entity.TossPayment; -import bc1.gream.domain.payment.toss.service.TossPaymentService; +import bc1.gream.domain.payment.toss.service.PaymentService; +import bc1.gream.domain.payment.toss.service.event.TossPaymentSuccessCallback; import bc1.gream.global.security.WithMockCustomUser; import bc1.gream.test.UserTest; import com.fasterxml.jackson.core.type.TypeReference; @@ -46,7 +49,7 @@ class TossPaymentControllerTest { @Autowired private ObjectMapper objectMapper; @MockBean - private TossPaymentService tossPaymentService; + private PaymentService paymentService; @BeforeEach void setUp() { @@ -74,7 +77,7 @@ void setUp() { .userNickname(UserTest.TEST_USER_NICKNAME) .paymentHasSuccess(true) .build(); - given(tossPaymentService.requestTossPayment(any(TossPayment.class))) + given(paymentService.requestTossPayment(any(TossPayment.class))) .willReturn(responseDto); // WHEN @@ -132,12 +135,17 @@ void setUp() { .card(expectedPaymentCard) .type("NORMAL") .build(); + doAnswer(invocation -> { + TossPaymentSuccessCallback callback = invocation.getArgument(3); + callback.handle(responseDto); + return null; + }).when(paymentService).requestFinalTossPayment( + eq(paymentKey), + eq(orderId), + eq(amount), + any(TossPaymentSuccessCallback.class)); - // WHEN - when(tossPaymentService.requestFinalTossPayment(paymentKey, orderId, amount)) - .thenReturn(responseDto); - - // THEN + // WHEN, THEN mockMvc.perform( get(url) .param("paymentKey", paymentKey) @@ -183,7 +191,7 @@ void setUp() { .build(); // WHEN - when(tossPaymentService.requestFail(errorCode, errorMsg, orderId)) + when(paymentService.requestFail(errorCode, errorMsg, orderId)) .thenReturn(responseDto); // THEN From b059c564a1f4b461f992cd507ef4de63da817ec0 Mon Sep 17 00:00:00 2001 From: Laptop-Limjihoon <lonelynight1026@gmail.com> Date: Mon, 5 Feb 2024 04:43:02 +0900 Subject: [PATCH 8/9] =?UTF-8?q?feat=20:=20GlobalExceptionHandler=20Thread?= =?UTF-8?q?=20=EC=98=A4=EB=A5=98=20=EB=8C=80=ED=95=9C=20AOP=20=ED=95=B8?= =?UTF-8?q?=EB=93=A4=EB=9F=AC=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/bc1/gream/global/common/ResultCase.java | 3 ++- .../global/exception/GlobalExceptionHandler.java | 11 +++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/main/java/bc1/gream/global/common/ResultCase.java b/src/main/java/bc1/gream/global/common/ResultCase.java index 9982b14b..0eb80eb4 100644 --- a/src/main/java/bc1/gream/global/common/ResultCase.java +++ b/src/main/java/bc1/gream/global/common/ResultCase.java @@ -61,7 +61,8 @@ public enum ResultCase { // 시스템 에러 500 SYSTEM_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, 5002, "알 수 없는 에러가 발생했습니다."), // 잘못된 도메인 정렬값 입력 400 - INVALID_ORDER_CRITERIA(HttpStatus.BAD_REQUEST, 5001, "유효하지 않은 도메인 정렬값"), + INVALID_ORDER_CRITERIA(HttpStatus.BAD_REQUEST, 5003, "유효하지 않은 도메인 정렬값"), + THREAD_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, 5004, "유효하지 않은 도메인 정렬값"), // 쿠폰 6000번대 // 쿠폰이 존재하지 않을 때 404 diff --git a/src/main/java/bc1/gream/global/exception/GlobalExceptionHandler.java b/src/main/java/bc1/gream/global/exception/GlobalExceptionHandler.java index 61a8970e..9a2f13cf 100644 --- a/src/main/java/bc1/gream/global/exception/GlobalExceptionHandler.java +++ b/src/main/java/bc1/gream/global/exception/GlobalExceptionHandler.java @@ -49,6 +49,17 @@ public ResponseEntity<RestResponse<List<InvalidInputResponseDto>>> handlerValida return RestResponse.error(ResultCase.INVALID_INPUT, invalidInputList); } + /** + * Thread 오류 대한 핸들러 + * + * @param ex Thread 오류에 따른 InterruptedException + * @return Thread 에러케이스와 에러리스폰스 + */ + @ExceptionHandler(InterruptedException.class) + public ResponseEntity<RestResponse<ErrorResponseDto>> handlerInterruptedException(InterruptedException ex) { + return RestResponse.error(ResultCase.THREAD_ERROR, new ErrorResponseDto()); + } + /** * Business 오류 발생에 대한 핸들러 * From 9620b4ce24c5f8a5b4b9625fa1d87af95b901da9 Mon Sep 17 00:00:00 2001 From: Laptop-Limjihoon <lonelynight1026@gmail.com> Date: Mon, 5 Feb 2024 05:14:51 +0900 Subject: [PATCH 9/9] =?UTF-8?q?feat=20:=20AsyncConfig=20=EB=A5=BC=20?= =?UTF-8?q?=ED=86=B5=ED=95=B4=20TossPaymentEventListener=20=EC=97=90=20@As?= =?UTF-8?q?ync=20=EC=A7=80=EC=9B=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../toss/service/event/TossPaymentEventListener.java | 2 ++ src/main/java/bc1/gream/global/config/AsyncConfig.java | 10 ++++++++++ 2 files changed, 12 insertions(+) create mode 100644 src/main/java/bc1/gream/global/config/AsyncConfig.java diff --git a/src/main/java/bc1/gream/domain/payment/toss/service/event/TossPaymentEventListener.java b/src/main/java/bc1/gream/domain/payment/toss/service/event/TossPaymentEventListener.java index fc9729ff..54958871 100644 --- a/src/main/java/bc1/gream/domain/payment/toss/service/event/TossPaymentEventListener.java +++ b/src/main/java/bc1/gream/domain/payment/toss/service/event/TossPaymentEventListener.java @@ -8,6 +8,7 @@ import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; +import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Component; import org.springframework.transaction.event.TransactionalEventListener; import org.springframework.web.client.RestTemplate; @@ -15,6 +16,7 @@ @Component public class TossPaymentEventListener { + @Async @TransactionalEventListener public void handleTossPaymentSuccess(TossPaymentSuccessEvent event) { RestTemplate rest = new RestTemplate(); diff --git a/src/main/java/bc1/gream/global/config/AsyncConfig.java b/src/main/java/bc1/gream/global/config/AsyncConfig.java new file mode 100644 index 00000000..a236e02e --- /dev/null +++ b/src/main/java/bc1/gream/global/config/AsyncConfig.java @@ -0,0 +1,10 @@ +package bc1.gream.global.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableAsync; + +@EnableAsync +@Configuration +public class AsyncConfig { + +} \ No newline at end of file