From 62d3f859ac21275dc05e147bb13e99084c3276a8 Mon Sep 17 00:00:00 2001 From: pricelees Date: Tue, 15 Jul 2025 16:51:16 +0900 Subject: [PATCH 01/32] =?UTF-8?q?chore:=20payment=20=ED=8C=A8=ED=82=A4?= =?UTF-8?q?=EC=A7=80=20=EA=B5=AC=EC=A1=B0=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../{service => business}/PaymentService.java | 18 +++++++++--------- .../client}/PaymentConfig.java | 4 +--- .../client/PaymentProperties.java | 2 +- .../client/TossPaymentClient.java | 12 ++++++------ .../persistence}/CanceledPayment.java | 2 +- .../CanceledPaymentRepository.java | 4 +--- .../persistence}/Payment.java | 2 +- .../persistence}/PaymentRepository.java | 4 +--- .../dto/request/PaymentCancelRequest.java | 2 +- .../{ => web}/dto/request/PaymentRequest.java | 2 +- .../dto/response/PaymentCancelResponse.java | 4 +++- .../dto/response/PaymentResponse.java | 2 +- .../response/ReservationPaymentResponse.java | 4 ++-- .../response/TossPaymentErrorResponse.java | 2 +- .../PaymentCancelResponseDeserializer.java | 4 +++- .../controller/ReservationController.java | 10 +++++----- .../dto/request/ReservationRequest.java | 2 +- .../ReservationWithPaymentService.java | 10 +++++----- .../PaymentServiceTest.java | 12 ++++++------ .../client/SampleTossPaymentConst.java | 6 +++--- .../client/TossPaymentClientTest.java | 12 +++++++----- .../persistence}/CanceledPaymentTest.java | 2 +- .../persistence}/PaymentTest.java | 4 ++-- ...PaymentCancelResponseDeserializerTest.java | 4 +++- .../controller/ReservationControllerTest.java | 19 ++++++++++--------- .../ReservationWithPaymentServiceTest.java | 8 ++++---- 26 files changed, 80 insertions(+), 77 deletions(-) rename src/main/java/roomescape/payment/{service => business}/PaymentService.java (84%) rename src/main/java/roomescape/payment/{ => infrastructure/client}/PaymentConfig.java (94%) rename src/main/java/roomescape/payment/{ => infrastructure}/client/PaymentProperties.java (93%) rename src/main/java/roomescape/payment/{ => infrastructure}/client/TossPaymentClient.java (89%) rename src/main/java/roomescape/payment/{domain => infrastructure/persistence}/CanceledPayment.java (96%) rename src/main/java/roomescape/payment/{domain/repository => infrastructure/persistence}/CanceledPaymentRepository.java (71%) rename src/main/java/roomescape/payment/{domain => infrastructure/persistence}/Payment.java (98%) rename src/main/java/roomescape/payment/{domain/repository => infrastructure/persistence}/PaymentRepository.java (76%) rename src/main/java/roomescape/payment/{ => web}/dto/request/PaymentCancelRequest.java (67%) rename src/main/java/roomescape/payment/{ => web}/dto/request/PaymentRequest.java (69%) rename src/main/java/roomescape/payment/{ => web}/dto/response/PaymentCancelResponse.java (71%) rename src/main/java/roomescape/payment/{ => web}/dto/response/PaymentResponse.java (77%) rename src/main/java/roomescape/payment/{ => web}/dto/response/ReservationPaymentResponse.java (83%) rename src/main/java/roomescape/payment/{ => web}/dto/response/TossPaymentErrorResponse.java (61%) rename src/main/java/roomescape/payment/{dto/response => web/support}/PaymentCancelResponseDeserializer.java (91%) rename src/test/java/roomescape/payment/{service => business}/PaymentServiceTest.java (95%) rename src/test/java/roomescape/payment/{ => infrastructure}/client/SampleTossPaymentConst.java (96%) rename src/test/java/roomescape/payment/{ => infrastructure}/client/TossPaymentClientTest.java (91%) rename src/test/java/roomescape/payment/{domain => infrastructure/persistence}/CanceledPaymentTest.java (91%) rename src/test/java/roomescape/payment/{domain => infrastructure/persistence}/PaymentTest.java (98%) rename src/test/java/roomescape/payment/{dto/response => web/support}/PaymentCancelResponseDeserializerTest.java (93%) diff --git a/src/main/java/roomescape/payment/service/PaymentService.java b/src/main/java/roomescape/payment/business/PaymentService.java similarity index 84% rename from src/main/java/roomescape/payment/service/PaymentService.java rename to src/main/java/roomescape/payment/business/PaymentService.java index e4761559..add0f3ae 100644 --- a/src/main/java/roomescape/payment/service/PaymentService.java +++ b/src/main/java/roomescape/payment/business/PaymentService.java @@ -1,4 +1,4 @@ -package roomescape.payment.service; +package roomescape.payment.business; import java.time.OffsetDateTime; import java.util.Optional; @@ -9,14 +9,14 @@ import org.springframework.transaction.annotation.Transactional; import roomescape.common.exception.ErrorType; import roomescape.common.exception.RoomescapeException; -import roomescape.payment.domain.CanceledPayment; -import roomescape.payment.domain.Payment; -import roomescape.payment.domain.repository.CanceledPaymentRepository; -import roomescape.payment.domain.repository.PaymentRepository; -import roomescape.payment.dto.request.PaymentCancelRequest; -import roomescape.payment.dto.response.PaymentCancelResponse; -import roomescape.payment.dto.response.PaymentResponse; -import roomescape.payment.dto.response.ReservationPaymentResponse; +import roomescape.payment.infrastructure.persistence.CanceledPayment; +import roomescape.payment.infrastructure.persistence.CanceledPaymentRepository; +import roomescape.payment.infrastructure.persistence.Payment; +import roomescape.payment.infrastructure.persistence.PaymentRepository; +import roomescape.payment.web.dto.request.PaymentCancelRequest; +import roomescape.payment.web.dto.response.PaymentCancelResponse; +import roomescape.payment.web.dto.response.PaymentResponse; +import roomescape.payment.web.dto.response.ReservationPaymentResponse; import roomescape.reservation.domain.Reservation; @Service diff --git a/src/main/java/roomescape/payment/PaymentConfig.java b/src/main/java/roomescape/payment/infrastructure/client/PaymentConfig.java similarity index 94% rename from src/main/java/roomescape/payment/PaymentConfig.java rename to src/main/java/roomescape/payment/infrastructure/client/PaymentConfig.java index bed158a8..b566d0a2 100644 --- a/src/main/java/roomescape/payment/PaymentConfig.java +++ b/src/main/java/roomescape/payment/infrastructure/client/PaymentConfig.java @@ -1,4 +1,4 @@ -package roomescape.payment; +package roomescape.payment.infrastructure.client; import java.nio.charset.StandardCharsets; import java.time.Duration; @@ -12,8 +12,6 @@ import org.springframework.context.annotation.Configuration; import org.springframework.http.client.ClientHttpRequestFactory; import org.springframework.web.client.RestClient; -import roomescape.payment.client.PaymentProperties; - @Configuration @EnableConfigurationProperties(PaymentProperties.class) public class PaymentConfig { diff --git a/src/main/java/roomescape/payment/client/PaymentProperties.java b/src/main/java/roomescape/payment/infrastructure/client/PaymentProperties.java similarity index 93% rename from src/main/java/roomescape/payment/client/PaymentProperties.java rename to src/main/java/roomescape/payment/infrastructure/client/PaymentProperties.java index 24584579..cd5e047d 100644 --- a/src/main/java/roomescape/payment/client/PaymentProperties.java +++ b/src/main/java/roomescape/payment/infrastructure/client/PaymentProperties.java @@ -1,4 +1,4 @@ -package roomescape.payment.client; +package roomescape.payment.infrastructure.client; import org.springframework.boot.context.properties.ConfigurationProperties; diff --git a/src/main/java/roomescape/payment/client/TossPaymentClient.java b/src/main/java/roomescape/payment/infrastructure/client/TossPaymentClient.java similarity index 89% rename from src/main/java/roomescape/payment/client/TossPaymentClient.java rename to src/main/java/roomescape/payment/infrastructure/client/TossPaymentClient.java index 9ba7c8bc..f746bdb1 100644 --- a/src/main/java/roomescape/payment/client/TossPaymentClient.java +++ b/src/main/java/roomescape/payment/infrastructure/client/TossPaymentClient.java @@ -1,4 +1,4 @@ -package roomescape.payment.client; +package roomescape.payment.infrastructure.client; import java.io.IOException; import java.io.InputStream; @@ -16,11 +16,11 @@ import com.fasterxml.jackson.databind.ObjectMapper; import roomescape.common.exception.ErrorType; import roomescape.common.exception.RoomescapeException; -import roomescape.payment.dto.request.PaymentCancelRequest; -import roomescape.payment.dto.request.PaymentRequest; -import roomescape.payment.dto.response.PaymentCancelResponse; -import roomescape.payment.dto.response.PaymentResponse; -import roomescape.payment.dto.response.TossPaymentErrorResponse; +import roomescape.payment.web.dto.request.PaymentCancelRequest; +import roomescape.payment.web.dto.request.PaymentRequest; +import roomescape.payment.web.dto.response.PaymentCancelResponse; +import roomescape.payment.web.dto.response.PaymentResponse; +import roomescape.payment.web.dto.response.TossPaymentErrorResponse; @Component public class TossPaymentClient { diff --git a/src/main/java/roomescape/payment/domain/CanceledPayment.java b/src/main/java/roomescape/payment/infrastructure/persistence/CanceledPayment.java similarity index 96% rename from src/main/java/roomescape/payment/domain/CanceledPayment.java rename to src/main/java/roomescape/payment/infrastructure/persistence/CanceledPayment.java index 5d4750f9..5e252675 100644 --- a/src/main/java/roomescape/payment/domain/CanceledPayment.java +++ b/src/main/java/roomescape/payment/infrastructure/persistence/CanceledPayment.java @@ -1,4 +1,4 @@ -package roomescape.payment.domain; +package roomescape.payment.infrastructure.persistence; import java.time.OffsetDateTime; diff --git a/src/main/java/roomescape/payment/domain/repository/CanceledPaymentRepository.java b/src/main/java/roomescape/payment/infrastructure/persistence/CanceledPaymentRepository.java similarity index 71% rename from src/main/java/roomescape/payment/domain/repository/CanceledPaymentRepository.java rename to src/main/java/roomescape/payment/infrastructure/persistence/CanceledPaymentRepository.java index e3e05a47..903a51c8 100644 --- a/src/main/java/roomescape/payment/domain/repository/CanceledPaymentRepository.java +++ b/src/main/java/roomescape/payment/infrastructure/persistence/CanceledPaymentRepository.java @@ -1,11 +1,9 @@ -package roomescape.payment.domain.repository; +package roomescape.payment.infrastructure.persistence; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; -import roomescape.payment.domain.CanceledPayment; - public interface CanceledPaymentRepository extends JpaRepository { Optional findByPaymentKey(String paymentKey); diff --git a/src/main/java/roomescape/payment/domain/Payment.java b/src/main/java/roomescape/payment/infrastructure/persistence/Payment.java similarity index 98% rename from src/main/java/roomescape/payment/domain/Payment.java rename to src/main/java/roomescape/payment/infrastructure/persistence/Payment.java index 42094431..889f290f 100644 --- a/src/main/java/roomescape/payment/domain/Payment.java +++ b/src/main/java/roomescape/payment/infrastructure/persistence/Payment.java @@ -1,4 +1,4 @@ -package roomescape.payment.domain; +package roomescape.payment.infrastructure.persistence; import java.time.OffsetDateTime; diff --git a/src/main/java/roomescape/payment/domain/repository/PaymentRepository.java b/src/main/java/roomescape/payment/infrastructure/persistence/PaymentRepository.java similarity index 76% rename from src/main/java/roomescape/payment/domain/repository/PaymentRepository.java rename to src/main/java/roomescape/payment/infrastructure/persistence/PaymentRepository.java index 7d55352f..136593b7 100644 --- a/src/main/java/roomescape/payment/domain/repository/PaymentRepository.java +++ b/src/main/java/roomescape/payment/infrastructure/persistence/PaymentRepository.java @@ -1,11 +1,9 @@ -package roomescape.payment.domain.repository; +package roomescape.payment.infrastructure.persistence; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; -import roomescape.payment.domain.Payment; - public interface PaymentRepository extends JpaRepository { Optional findByReservationId(Long reservationId); diff --git a/src/main/java/roomescape/payment/dto/request/PaymentCancelRequest.java b/src/main/java/roomescape/payment/web/dto/request/PaymentCancelRequest.java similarity index 67% rename from src/main/java/roomescape/payment/dto/request/PaymentCancelRequest.java rename to src/main/java/roomescape/payment/web/dto/request/PaymentCancelRequest.java index 4641575b..bf925405 100644 --- a/src/main/java/roomescape/payment/dto/request/PaymentCancelRequest.java +++ b/src/main/java/roomescape/payment/web/dto/request/PaymentCancelRequest.java @@ -1,4 +1,4 @@ -package roomescape.payment.dto.request; +package roomescape.payment.web.dto.request; public record PaymentCancelRequest(String paymentKey, Long amount, String cancelReason) { } diff --git a/src/main/java/roomescape/payment/dto/request/PaymentRequest.java b/src/main/java/roomescape/payment/web/dto/request/PaymentRequest.java similarity index 69% rename from src/main/java/roomescape/payment/dto/request/PaymentRequest.java rename to src/main/java/roomescape/payment/web/dto/request/PaymentRequest.java index d7f2ea82..bcda7c41 100644 --- a/src/main/java/roomescape/payment/dto/request/PaymentRequest.java +++ b/src/main/java/roomescape/payment/web/dto/request/PaymentRequest.java @@ -1,4 +1,4 @@ -package roomescape.payment.dto.request; +package roomescape.payment.web.dto.request; public record PaymentRequest(String paymentKey, String orderId, Long amount, String paymentType) { } diff --git a/src/main/java/roomescape/payment/dto/response/PaymentCancelResponse.java b/src/main/java/roomescape/payment/web/dto/response/PaymentCancelResponse.java similarity index 71% rename from src/main/java/roomescape/payment/dto/response/PaymentCancelResponse.java rename to src/main/java/roomescape/payment/web/dto/response/PaymentCancelResponse.java index 1c6e2e0e..acf10776 100644 --- a/src/main/java/roomescape/payment/dto/response/PaymentCancelResponse.java +++ b/src/main/java/roomescape/payment/web/dto/response/PaymentCancelResponse.java @@ -1,9 +1,11 @@ -package roomescape.payment.dto.response; +package roomescape.payment.web.dto.response; import java.time.OffsetDateTime; import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import roomescape.payment.web.support.PaymentCancelResponseDeserializer; + @JsonDeserialize(using = PaymentCancelResponseDeserializer.class) public record PaymentCancelResponse( String cancelStatus, diff --git a/src/main/java/roomescape/payment/dto/response/PaymentResponse.java b/src/main/java/roomescape/payment/web/dto/response/PaymentResponse.java similarity index 77% rename from src/main/java/roomescape/payment/dto/response/PaymentResponse.java rename to src/main/java/roomescape/payment/web/dto/response/PaymentResponse.java index afee9601..45fac13d 100644 --- a/src/main/java/roomescape/payment/dto/response/PaymentResponse.java +++ b/src/main/java/roomescape/payment/web/dto/response/PaymentResponse.java @@ -1,4 +1,4 @@ -package roomescape.payment.dto.response; +package roomescape.payment.web.dto.response; import java.time.OffsetDateTime; diff --git a/src/main/java/roomescape/payment/dto/response/ReservationPaymentResponse.java b/src/main/java/roomescape/payment/web/dto/response/ReservationPaymentResponse.java similarity index 83% rename from src/main/java/roomescape/payment/dto/response/ReservationPaymentResponse.java rename to src/main/java/roomescape/payment/web/dto/response/ReservationPaymentResponse.java index 479d25e8..f62854b5 100644 --- a/src/main/java/roomescape/payment/dto/response/ReservationPaymentResponse.java +++ b/src/main/java/roomescape/payment/web/dto/response/ReservationPaymentResponse.java @@ -1,8 +1,8 @@ -package roomescape.payment.dto.response; +package roomescape.payment.web.dto.response; import java.time.OffsetDateTime; -import roomescape.payment.domain.Payment; +import roomescape.payment.infrastructure.persistence.Payment; import roomescape.reservation.dto.response.ReservationResponse; public record ReservationPaymentResponse(Long id, String orderId, String paymentKey, Long totalAmount, diff --git a/src/main/java/roomescape/payment/dto/response/TossPaymentErrorResponse.java b/src/main/java/roomescape/payment/web/dto/response/TossPaymentErrorResponse.java similarity index 61% rename from src/main/java/roomescape/payment/dto/response/TossPaymentErrorResponse.java rename to src/main/java/roomescape/payment/web/dto/response/TossPaymentErrorResponse.java index 2e066de9..7f5163ee 100644 --- a/src/main/java/roomescape/payment/dto/response/TossPaymentErrorResponse.java +++ b/src/main/java/roomescape/payment/web/dto/response/TossPaymentErrorResponse.java @@ -1,4 +1,4 @@ -package roomescape.payment.dto.response; +package roomescape.payment.web.dto.response; public record TossPaymentErrorResponse(String code, String message) { } diff --git a/src/main/java/roomescape/payment/dto/response/PaymentCancelResponseDeserializer.java b/src/main/java/roomescape/payment/web/support/PaymentCancelResponseDeserializer.java similarity index 91% rename from src/main/java/roomescape/payment/dto/response/PaymentCancelResponseDeserializer.java rename to src/main/java/roomescape/payment/web/support/PaymentCancelResponseDeserializer.java index 96bba21d..60993797 100644 --- a/src/main/java/roomescape/payment/dto/response/PaymentCancelResponseDeserializer.java +++ b/src/main/java/roomescape/payment/web/support/PaymentCancelResponseDeserializer.java @@ -1,4 +1,4 @@ -package roomescape.payment.dto.response; +package roomescape.payment.web.support; import java.io.IOException; import java.time.OffsetDateTime; @@ -9,6 +9,8 @@ import com.fasterxml.jackson.databind.DeserializationContext; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import roomescape.payment.web.dto.response.PaymentCancelResponse; + public class PaymentCancelResponseDeserializer extends StdDeserializer { private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern( diff --git a/src/main/java/roomescape/reservation/controller/ReservationController.java b/src/main/java/roomescape/reservation/controller/ReservationController.java index 89dcb95a..01709713 100644 --- a/src/main/java/roomescape/reservation/controller/ReservationController.java +++ b/src/main/java/roomescape/reservation/controller/ReservationController.java @@ -30,11 +30,11 @@ import roomescape.auth.web.support.MemberId; import roomescape.common.dto.response.RoomescapeApiResponse; import roomescape.common.dto.response.RoomescapeErrorResponse; import roomescape.common.exception.RoomescapeException; -import roomescape.payment.client.TossPaymentClient; -import roomescape.payment.dto.request.PaymentCancelRequest; -import roomescape.payment.dto.request.PaymentRequest; -import roomescape.payment.dto.response.PaymentCancelResponse; -import roomescape.payment.dto.response.PaymentResponse; +import roomescape.payment.infrastructure.client.TossPaymentClient; +import roomescape.payment.web.dto.request.PaymentCancelRequest; +import roomescape.payment.web.dto.request.PaymentRequest; +import roomescape.payment.web.dto.response.PaymentCancelResponse; +import roomescape.payment.web.dto.response.PaymentResponse; import roomescape.reservation.dto.request.AdminReservationRequest; import roomescape.reservation.dto.request.ReservationRequest; import roomescape.reservation.dto.request.WaitingRequest; diff --git a/src/main/java/roomescape/reservation/dto/request/ReservationRequest.java b/src/main/java/roomescape/reservation/dto/request/ReservationRequest.java index 28f1a363..06bbbecf 100644 --- a/src/main/java/roomescape/reservation/dto/request/ReservationRequest.java +++ b/src/main/java/roomescape/reservation/dto/request/ReservationRequest.java @@ -6,7 +6,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; -import roomescape.payment.dto.request.PaymentRequest; +import roomescape.payment.web.dto.request.PaymentRequest; @Schema(name = "회원의 예약 저장 요청", description = "회원의 예약 요청시 사용됩니다.") public record ReservationRequest( diff --git a/src/main/java/roomescape/reservation/service/ReservationWithPaymentService.java b/src/main/java/roomescape/reservation/service/ReservationWithPaymentService.java index 785182a8..5721d742 100644 --- a/src/main/java/roomescape/reservation/service/ReservationWithPaymentService.java +++ b/src/main/java/roomescape/reservation/service/ReservationWithPaymentService.java @@ -5,11 +5,11 @@ import java.time.OffsetDateTime; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import roomescape.payment.dto.request.PaymentCancelRequest; -import roomescape.payment.dto.response.PaymentCancelResponse; -import roomescape.payment.dto.response.PaymentResponse; -import roomescape.payment.dto.response.ReservationPaymentResponse; -import roomescape.payment.service.PaymentService; +import roomescape.payment.business.PaymentService; +import roomescape.payment.web.dto.request.PaymentCancelRequest; +import roomescape.payment.web.dto.response.PaymentCancelResponse; +import roomescape.payment.web.dto.response.PaymentResponse; +import roomescape.payment.web.dto.response.ReservationPaymentResponse; import roomescape.reservation.domain.Reservation; import roomescape.reservation.dto.request.ReservationRequest; import roomescape.reservation.dto.response.ReservationResponse; diff --git a/src/test/java/roomescape/payment/service/PaymentServiceTest.java b/src/test/java/roomescape/payment/business/PaymentServiceTest.java similarity index 95% rename from src/test/java/roomescape/payment/service/PaymentServiceTest.java rename to src/test/java/roomescape/payment/business/PaymentServiceTest.java index 19f28cde..d81b0d90 100644 --- a/src/test/java/roomescape/payment/service/PaymentServiceTest.java +++ b/src/test/java/roomescape/payment/business/PaymentServiceTest.java @@ -1,4 +1,4 @@ -package roomescape.payment.service; +package roomescape.payment.business; import static org.assertj.core.api.Assertions.*; @@ -13,19 +13,19 @@ import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.jdbc.Sql; import org.springframework.test.context.jdbc.Sql.ExecutionPhase; +import roomescape.common.exception.RoomescapeException; import roomescape.member.infrastructure.persistence.Member; import roomescape.member.infrastructure.persistence.MemberRepository; import roomescape.member.infrastructure.persistence.Role; -import roomescape.payment.domain.repository.CanceledPaymentRepository; -import roomescape.payment.dto.request.PaymentCancelRequest; -import roomescape.payment.dto.response.PaymentResponse; -import roomescape.payment.dto.response.ReservationPaymentResponse; +import roomescape.payment.infrastructure.persistence.CanceledPaymentRepository; +import roomescape.payment.web.dto.request.PaymentCancelRequest; +import roomescape.payment.web.dto.response.PaymentResponse; +import roomescape.payment.web.dto.response.ReservationPaymentResponse; import roomescape.reservation.domain.Reservation; import roomescape.reservation.domain.ReservationStatus; import roomescape.reservation.domain.ReservationTime; import roomescape.reservation.domain.repository.ReservationRepository; import roomescape.reservation.domain.repository.ReservationTimeRepository; -import roomescape.common.exception.RoomescapeException; import roomescape.theme.domain.Theme; import roomescape.theme.domain.repository.ThemeRepository; diff --git a/src/test/java/roomescape/payment/client/SampleTossPaymentConst.java b/src/test/java/roomescape/payment/infrastructure/client/SampleTossPaymentConst.java similarity index 96% rename from src/test/java/roomescape/payment/client/SampleTossPaymentConst.java rename to src/test/java/roomescape/payment/infrastructure/client/SampleTossPaymentConst.java index 688cc27f..99923a11 100644 --- a/src/test/java/roomescape/payment/client/SampleTossPaymentConst.java +++ b/src/test/java/roomescape/payment/infrastructure/client/SampleTossPaymentConst.java @@ -1,7 +1,7 @@ -package roomescape.payment.client; +package roomescape.payment.infrastructure.client; -import roomescape.payment.dto.request.PaymentCancelRequest; -import roomescape.payment.dto.request.PaymentRequest; +import roomescape.payment.web.dto.request.PaymentCancelRequest; +import roomescape.payment.web.dto.request.PaymentRequest; public class SampleTossPaymentConst { diff --git a/src/test/java/roomescape/payment/client/TossPaymentClientTest.java b/src/test/java/roomescape/payment/infrastructure/client/TossPaymentClientTest.java similarity index 91% rename from src/test/java/roomescape/payment/client/TossPaymentClientTest.java rename to src/test/java/roomescape/payment/infrastructure/client/TossPaymentClientTest.java index 98418893..6a844547 100644 --- a/src/test/java/roomescape/payment/client/TossPaymentClientTest.java +++ b/src/test/java/roomescape/payment/infrastructure/client/TossPaymentClientTest.java @@ -1,6 +1,8 @@ -package roomescape.payment.client; +package roomescape.payment.infrastructure.client; +import static org.assertj.core.api.Assertions.*; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.*; import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; import static org.springframework.test.web.client.match.MockRestRequestMatchers.*; import static org.springframework.test.web.client.response.MockRestResponseCreators.*; @@ -14,12 +16,12 @@ import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.test.web.client.MockRestServiceServer; -import roomescape.payment.dto.request.PaymentCancelRequest; -import roomescape.payment.dto.request.PaymentRequest; -import roomescape.payment.dto.response.PaymentCancelResponse; -import roomescape.payment.dto.response.PaymentResponse; import roomescape.common.exception.ErrorType; import roomescape.common.exception.RoomescapeException; +import roomescape.payment.web.dto.request.PaymentCancelRequest; +import roomescape.payment.web.dto.request.PaymentRequest; +import roomescape.payment.web.dto.response.PaymentCancelResponse; +import roomescape.payment.web.dto.response.PaymentResponse; @RestClientTest(TossPaymentClient.class) class TossPaymentClientTest { diff --git a/src/test/java/roomescape/payment/domain/CanceledPaymentTest.java b/src/test/java/roomescape/payment/infrastructure/persistence/CanceledPaymentTest.java similarity index 91% rename from src/test/java/roomescape/payment/domain/CanceledPaymentTest.java rename to src/test/java/roomescape/payment/infrastructure/persistence/CanceledPaymentTest.java index 019c4da3..6a9b0e8e 100644 --- a/src/test/java/roomescape/payment/domain/CanceledPaymentTest.java +++ b/src/test/java/roomescape/payment/infrastructure/persistence/CanceledPaymentTest.java @@ -1,4 +1,4 @@ -package roomescape.payment.domain; +package roomescape.payment.infrastructure.persistence; import static org.assertj.core.api.Assertions.*; diff --git a/src/test/java/roomescape/payment/domain/PaymentTest.java b/src/test/java/roomescape/payment/infrastructure/persistence/PaymentTest.java similarity index 98% rename from src/test/java/roomescape/payment/domain/PaymentTest.java rename to src/test/java/roomescape/payment/infrastructure/persistence/PaymentTest.java index 7b2030d7..e43aac43 100644 --- a/src/test/java/roomescape/payment/domain/PaymentTest.java +++ b/src/test/java/roomescape/payment/infrastructure/persistence/PaymentTest.java @@ -1,4 +1,4 @@ -package roomescape.payment.domain; +package roomescape.payment.infrastructure.persistence; import static org.assertj.core.api.Assertions.*; @@ -13,12 +13,12 @@ import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.NullAndEmptySource; import org.junit.jupiter.params.provider.NullSource; +import roomescape.common.exception.RoomescapeException; import roomescape.member.infrastructure.persistence.Member; import roomescape.member.infrastructure.persistence.Role; import roomescape.reservation.domain.Reservation; import roomescape.reservation.domain.ReservationStatus; import roomescape.reservation.domain.ReservationTime; -import roomescape.common.exception.RoomescapeException; import roomescape.theme.domain.Theme; class PaymentTest { diff --git a/src/test/java/roomescape/payment/dto/response/PaymentCancelResponseDeserializerTest.java b/src/test/java/roomescape/payment/web/support/PaymentCancelResponseDeserializerTest.java similarity index 93% rename from src/test/java/roomescape/payment/dto/response/PaymentCancelResponseDeserializerTest.java rename to src/test/java/roomescape/payment/web/support/PaymentCancelResponseDeserializerTest.java index 7ada8331..c88f9e96 100644 --- a/src/test/java/roomescape/payment/dto/response/PaymentCancelResponseDeserializerTest.java +++ b/src/test/java/roomescape/payment/web/support/PaymentCancelResponseDeserializerTest.java @@ -1,4 +1,4 @@ -package roomescape.payment.dto.response; +package roomescape.payment.web.support; import static org.junit.jupiter.api.Assertions.*; @@ -9,6 +9,8 @@ import org.junit.jupiter.api.Test; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.module.SimpleModule; +import roomescape.payment.web.dto.response.PaymentCancelResponse; + class PaymentCancelResponseDeserializerTest { private ObjectMapper objectMapper; diff --git a/src/test/java/roomescape/reservation/controller/ReservationControllerTest.java b/src/test/java/roomescape/reservation/controller/ReservationControllerTest.java index b5f625b1..44978828 100644 --- a/src/test/java/roomescape/reservation/controller/ReservationControllerTest.java +++ b/src/test/java/roomescape/reservation/controller/ReservationControllerTest.java @@ -2,6 +2,7 @@ package roomescape.reservation.controller; import static org.assertj.core.api.Assertions.*; import static org.hamcrest.Matchers.*; +import static org.mockito.ArgumentMatchers.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; @@ -35,15 +36,15 @@ import io.restassured.http.Header; import roomescape.member.infrastructure.persistence.Member; import roomescape.member.infrastructure.persistence.MemberRepository; import roomescape.member.infrastructure.persistence.Role; -import roomescape.payment.client.TossPaymentClient; -import roomescape.payment.domain.CanceledPayment; -import roomescape.payment.domain.Payment; -import roomescape.payment.domain.repository.CanceledPaymentRepository; -import roomescape.payment.domain.repository.PaymentRepository; -import roomescape.payment.dto.request.PaymentCancelRequest; -import roomescape.payment.dto.request.PaymentRequest; -import roomescape.payment.dto.response.PaymentCancelResponse; -import roomescape.payment.dto.response.PaymentResponse; +import roomescape.payment.infrastructure.client.TossPaymentClient; +import roomescape.payment.infrastructure.persistence.CanceledPayment; +import roomescape.payment.infrastructure.persistence.CanceledPaymentRepository; +import roomescape.payment.infrastructure.persistence.Payment; +import roomescape.payment.infrastructure.persistence.PaymentRepository; +import roomescape.payment.web.dto.request.PaymentCancelRequest; +import roomescape.payment.web.dto.request.PaymentRequest; +import roomescape.payment.web.dto.response.PaymentCancelResponse; +import roomescape.payment.web.dto.response.PaymentResponse; import roomescape.reservation.domain.Reservation; import roomescape.reservation.domain.ReservationStatus; import roomescape.reservation.domain.ReservationTime; diff --git a/src/test/java/roomescape/reservation/service/ReservationWithPaymentServiceTest.java b/src/test/java/roomescape/reservation/service/ReservationWithPaymentServiceTest.java index fe0bec73..478b1d38 100644 --- a/src/test/java/roomescape/reservation/service/ReservationWithPaymentServiceTest.java +++ b/src/test/java/roomescape/reservation/service/ReservationWithPaymentServiceTest.java @@ -16,10 +16,10 @@ import org.springframework.test.context.jdbc.Sql.ExecutionPhase; import roomescape.member.infrastructure.persistence.Member; import roomescape.member.infrastructure.persistence.MemberRepository; import roomescape.member.infrastructure.persistence.Role; -import roomescape.payment.domain.repository.CanceledPaymentRepository; -import roomescape.payment.domain.repository.PaymentRepository; -import roomescape.payment.dto.request.PaymentCancelRequest; -import roomescape.payment.dto.response.PaymentResponse; +import roomescape.payment.infrastructure.persistence.CanceledPaymentRepository; +import roomescape.payment.infrastructure.persistence.PaymentRepository; +import roomescape.payment.web.dto.request.PaymentCancelRequest; +import roomescape.payment.web.dto.response.PaymentResponse; import roomescape.reservation.domain.Reservation; import roomescape.reservation.domain.ReservationStatus; import roomescape.reservation.domain.ReservationTime; -- 2.47.2 From 3193a905db122a58e90cc25f8ca86cdc8a765e44 Mon Sep 17 00:00:00 2001 From: pricelees Date: Tue, 15 Jul 2025 17:34:11 +0900 Subject: [PATCH 02/32] =?UTF-8?q?refactor:=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EC=93=B0=EC=9D=B4=EB=8A=94=20=EC=83=98?= =?UTF-8?q?=ED=94=8C=20=EC=9D=91=EB=8B=B5=20=EC=83=81=EC=88=98=20=ED=81=B4?= =?UTF-8?q?=EB=9E=98=EC=8A=A4=EC=9D=B8=20SampleTossPaymentConst=EC=9D=98?= =?UTF-8?q?=20=EC=BD=94=ED=8B=80=EB=A6=B0=20=EC=A0=84=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...ntConst.java => SampleTossPaymentConst.kt} | 110 ++++++++++++------ .../client/TossPaymentClientTest.java | 1 + 2 files changed, 74 insertions(+), 37 deletions(-) rename src/test/java/roomescape/payment/{infrastructure/client/SampleTossPaymentConst.java => SampleTossPaymentConst.kt} (65%) diff --git a/src/test/java/roomescape/payment/infrastructure/client/SampleTossPaymentConst.java b/src/test/java/roomescape/payment/SampleTossPaymentConst.kt similarity index 65% rename from src/test/java/roomescape/payment/infrastructure/client/SampleTossPaymentConst.java rename to src/test/java/roomescape/payment/SampleTossPaymentConst.kt index 99923a11..9ed6b179 100644 --- a/src/test/java/roomescape/payment/infrastructure/client/SampleTossPaymentConst.java +++ b/src/test/java/roomescape/payment/SampleTossPaymentConst.kt @@ -1,44 +1,73 @@ -package roomescape.payment.infrastructure.client; +package roomescape.payment -import roomescape.payment.web.dto.request.PaymentCancelRequest; -import roomescape.payment.web.dto.request.PaymentRequest; +import roomescape.payment.SampleTossPaymentConst.amount +import roomescape.payment.web.dto.request.PaymentCancelRequest +import roomescape.payment.web.dto.request.PaymentRequest +import kotlin.math.roundToLong -public class SampleTossPaymentConst { +object SampleTossPaymentConst { + @JvmField + val paymentKey: String = "5EnNZRJGvaBX7zk2yd8ydw26XvwXkLrx9POLqKQjmAw4b0e1" - public static final PaymentRequest paymentRequest = new PaymentRequest( - "5EnNZRJGvaBX7zk2yd8ydw26XvwXkLrx9POLqKQjmAw4b0e1", "MC4wODU4ODQwMzg4NDk0", 1000L, "카드"); + @JvmField + val orderId: String = "MC4wODU4ODQwMzg4NDk0" - public static final String paymentRequestJson = """ + @JvmField + val amount: Long = 1000L + + @JvmField + val paymentType: String = "카드" + + @JvmField + val cancelReason: String = "테스트 결제 취소" + + @JvmField + val paymentRequest: PaymentRequest = PaymentRequest( + paymentKey, + orderId, + amount, + paymentType + ) + + @JvmField + val paymentRequestJson: String = """ { - "paymentKey": "5EnNZRJGvaBX7zk2yd8ydw26XvwXkLrx9POLqKQjmAw4b0e1", - "orderId": "MC4wODU4ODQwMzg4NDk0", - "amount": 1000, - "paymentType": "카드" + "paymentKey": "$paymentKey", + "orderId": "$orderId", + "amount": $amount, + "paymentType": "$paymentType" } - """; + """.trimIndent() - public static final PaymentCancelRequest cancelRequest = new PaymentCancelRequest( - "5EnNZRJGvaBX7zk2yd8ydw26XvwXkLrx9POLqKQjmAw4b0e1", 1000L, "테스트 결제 취소"); + @JvmField + val cancelRequest: PaymentCancelRequest = PaymentCancelRequest( + paymentKey, + amount, + cancelReason + ) - public static final String cancelRequestJson = """ + @JvmField + val cancelRequestJson: String = """ { - "cancelReason": "테스트 결제 취소" + "cancelReason": "$cancelReason" } - """; + """.trimIndent() - public static final String tossPaymentErrorJson = """ + @JvmField + val tossPaymentErrorJson: String = """ { "code": "ERROR_CODE", "message": "Error message" - } - """; + } + """.trimIndent() - public static final String confirmJson = """ + @JvmField + val confirmJson: String = """ { "mId": "tosspayments", "lastTransactionKey": "9C62B18EEF0DE3EB7F4422EB6D14BC6E", - "paymentKey": "5EnNZRJGvaBX7zk2yd8ydw26XvwXkLrx9POLqKQjmAw4b0e1", - "orderId": "MC4wODU4ODQwMzg4NDk0", + "paymentKey": "$paymentKey", + "orderId": "$orderId", "orderName": "토스 티셔츠 외 2건", "taxExemptionAmount": 0, "status": "DONE", @@ -88,22 +117,24 @@ public class SampleTossPaymentConst { "url": "https://api.tosspayments.com/v1/payments/5EnNZRJGvaBX7zk2yd8ydw26XvwXkLrx9POLqKQjmAw4b0e1/checkout" }, "currency": "KRW", - "totalAmount": 1000, - "balanceAmount": 1000, - "suppliedAmount": 909, - "vat": 91, + "totalAmount": $amount, + "balanceAmount": $amount, + "suppliedAmount": ${(amount / 1.1).roundToLong()}, + "vat": ${amount - (amount / 1.1).roundToLong()}, "taxFreeAmount": 0, - "method": "카드", + "method": "$paymentType", "version": "2022-11-16" } - """; + + """.trimIndent() - public static final String cancelJson = """ + @JvmField + val cancelJson: String = """ { "mId": "tosspayments", "lastTransactionKey": "090A796806E726BBB929F4A2CA7DB9A7", - "paymentKey": "5EnNZRJGvaBX7zk2yd8ydw26XvwXkLrx9POLqKQjmAw4b0e1", - "orderId": "MC4wODU4ODQwMzg4NDk0", + "paymentKey": "$paymentKey", + "orderId": "$orderId", "orderName": "토스 티셔츠 외 2건", "taxExemptionAmount": 0, "status": "CANCELED", @@ -135,12 +166,12 @@ public class SampleTossPaymentConst { "cancels": [ { "transactionKey": "090A796806E726BBB929F4A2CA7DB9A7", - "cancelReason": "테스트 결제 취소", + "cancelReason": "$cancelReason", "taxExemptionAmount": 0, "canceledAt": "2024-02-13T12:20:23+09:00", "easyPayDiscountAmount": 0, "receiptKey": null, - "cancelAmount": 1000, + "cancelAmount": $amount, "taxFreeAmount": 0, "refundableAmount": 0, "cancelStatus": "DONE", @@ -166,13 +197,18 @@ public class SampleTossPaymentConst { "url": "https://api.tosspayments.com/v1/payments/5EnNZRJGvaBX7zk2yd8ydw26XvwXkLrx9POLqKQjmAw4b0e1/checkout" }, "currency": "KRW", - "totalAmount": 1000, + "totalAmount": $amount, "balanceAmount": 0, "suppliedAmount": 0, "vat": 0, "taxFreeAmount": 0, - "method": "카드", + "method": "$paymentType", "version": "2022-11-16" } - """; + + """.trimIndent() } + +fun main() { + println((amount / 1.1).roundToLong()) +} \ No newline at end of file diff --git a/src/test/java/roomescape/payment/infrastructure/client/TossPaymentClientTest.java b/src/test/java/roomescape/payment/infrastructure/client/TossPaymentClientTest.java index 6a844547..ff252897 100644 --- a/src/test/java/roomescape/payment/infrastructure/client/TossPaymentClientTest.java +++ b/src/test/java/roomescape/payment/infrastructure/client/TossPaymentClientTest.java @@ -18,6 +18,7 @@ import org.springframework.test.web.client.MockRestServiceServer; import roomescape.common.exception.ErrorType; import roomescape.common.exception.RoomescapeException; +import roomescape.payment.SampleTossPaymentConst; import roomescape.payment.web.dto.request.PaymentCancelRequest; import roomescape.payment.web.dto.request.PaymentRequest; import roomescape.payment.web.dto.response.PaymentCancelResponse; -- 2.47.2 From f59853ead11ad798e69255722b3c13fc831843c8 Mon Sep 17 00:00:00 2001 From: pricelees Date: Tue, 15 Jul 2025 17:34:35 +0900 Subject: [PATCH 03/32] Rename .java to .kt --- ...onseDeserializer.java => PaymentCancelResponseDeserializer.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/main/java/roomescape/payment/web/support/{PaymentCancelResponseDeserializer.java => PaymentCancelResponseDeserializer.kt} (100%) diff --git a/src/main/java/roomescape/payment/web/support/PaymentCancelResponseDeserializer.java b/src/main/java/roomescape/payment/web/support/PaymentCancelResponseDeserializer.kt similarity index 100% rename from src/main/java/roomescape/payment/web/support/PaymentCancelResponseDeserializer.java rename to src/main/java/roomescape/payment/web/support/PaymentCancelResponseDeserializer.kt -- 2.47.2 From 3bf65422d33713f25b5fb625ddde0ef1b040510f Mon Sep 17 00:00:00 2001 From: pricelees Date: Tue, 15 Jul 2025 17:34:35 +0900 Subject: [PATCH 04/32] =?UTF-8?q?refactor:=20=EC=B7=A8=EC=86=8C=20?= =?UTF-8?q?=EC=9D=91=EB=8B=B5=EC=9D=84=20=EC=97=AD=EC=A7=81=EB=A0=AC?= =?UTF-8?q?=ED=99=94=ED=95=98=EB=8A=94=20PaymentCancelResponseDeserializer?= =?UTF-8?q?=20=EC=BD=94=ED=8B=80=EB=A6=B0=20=EB=A7=88=EC=9D=B4=EA=B7=B8?= =?UTF-8?q?=EB=A0=88=EC=9D=B4=EC=85=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PaymentCancelResponseDeserializer.kt | 60 +++++++++---------- 1 file changed, 27 insertions(+), 33 deletions(-) diff --git a/src/main/java/roomescape/payment/web/support/PaymentCancelResponseDeserializer.kt b/src/main/java/roomescape/payment/web/support/PaymentCancelResponseDeserializer.kt index 60993797..c1e7b904 100644 --- a/src/main/java/roomescape/payment/web/support/PaymentCancelResponseDeserializer.kt +++ b/src/main/java/roomescape/payment/web/support/PaymentCancelResponseDeserializer.kt @@ -1,38 +1,32 @@ -package roomescape.payment.web.support; +package roomescape.payment.web.support -import java.io.IOException; -import java.time.OffsetDateTime; -import java.time.format.DateTimeFormatter; +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.core.TreeNode +import com.fasterxml.jackson.databind.DeserializationContext +import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.deser.std.StdDeserializer +import roomescape.payment.web.dto.response.PaymentCancelResponse +import java.io.IOException +import java.time.OffsetDateTime -import com.fasterxml.jackson.core.JsonParser; -import com.fasterxml.jackson.databind.DeserializationContext; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +class PaymentCancelResponseDeserializer( + vc: Class? = null +) : StdDeserializer(vc) { -import roomescape.payment.web.dto.response.PaymentCancelResponse; + @Throws(IOException::class) + override fun deserialize( + jsonParser: JsonParser, + deserializationContext: DeserializationContext? + ): PaymentCancelResponse { + val cancels: JsonNode = jsonParser.codec.readTree(jsonParser) + .get("cancels") + .get(0) as JsonNode -public class PaymentCancelResponseDeserializer extends StdDeserializer { - - private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern( - "yyyy-MM-dd'T'HH:mm:ssXXX"); - - public PaymentCancelResponseDeserializer() { - this(null); - } - - public PaymentCancelResponseDeserializer(Class vc) { - super(vc); - } - - @Override - public PaymentCancelResponse deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) - throws IOException { - JsonNode cancels = (JsonNode)jsonParser.getCodec().readTree(jsonParser).get("cancels").get(0); - return new PaymentCancelResponse( - cancels.get("cancelStatus").asText(), - cancels.get("cancelReason").asText(), - cancels.get("cancelAmount").asLong(), - OffsetDateTime.parse(cancels.get("canceledAt").asText()) - ); - } + return PaymentCancelResponse( + cancels.get("cancelStatus").asText(), + cancels.get("cancelReason").asText(), + cancels.get("cancelAmount").asLong(), + OffsetDateTime.parse(cancels.get("canceledAt").asText()) + ) + } } -- 2.47.2 From 4af6c86cfbd82b4beffa9513608342a78d3e9b8e Mon Sep 17 00:00:00 2001 From: pricelees Date: Tue, 15 Jul 2025 17:34:56 +0900 Subject: [PATCH 05/32] Rename .java to .kt --- ...rializerTest.java => PaymentCancelResponseDeserializerTest.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/test/java/roomescape/payment/web/support/{PaymentCancelResponseDeserializerTest.java => PaymentCancelResponseDeserializerTest.kt} (100%) diff --git a/src/test/java/roomescape/payment/web/support/PaymentCancelResponseDeserializerTest.java b/src/test/java/roomescape/payment/web/support/PaymentCancelResponseDeserializerTest.kt similarity index 100% rename from src/test/java/roomescape/payment/web/support/PaymentCancelResponseDeserializerTest.java rename to src/test/java/roomescape/payment/web/support/PaymentCancelResponseDeserializerTest.kt -- 2.47.2 From 2fca7b2cd1dc1722d829ff6b83d99fdee99618d2 Mon Sep 17 00:00:00 2001 From: pricelees Date: Tue, 15 Jul 2025 17:34:56 +0900 Subject: [PATCH 06/32] =?UTF-8?q?refactor:=20PaymentCancelResponseDeserial?= =?UTF-8?q?izer=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=ED=8B=80?= =?UTF-8?q?=EB=A6=B0=20=EB=A7=88=EC=9D=B4=EA=B7=B8=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EC=85=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../PaymentCancelResponseDeserializerTest.kt | 79 +++++++------------ 1 file changed, 30 insertions(+), 49 deletions(-) diff --git a/src/test/java/roomescape/payment/web/support/PaymentCancelResponseDeserializerTest.kt b/src/test/java/roomescape/payment/web/support/PaymentCancelResponseDeserializerTest.kt index c88f9e96..d440f41b 100644 --- a/src/test/java/roomescape/payment/web/support/PaymentCancelResponseDeserializerTest.kt +++ b/src/test/java/roomescape/payment/web/support/PaymentCancelResponseDeserializerTest.kt @@ -1,54 +1,35 @@ -package roomescape.payment.web.support; +package roomescape.payment.web.support -import static org.junit.jupiter.api.Assertions.*; +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.databind.module.SimpleModule +import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper +import io.kotest.assertions.assertSoftly +import io.kotest.core.spec.style.StringSpec +import io.kotest.matchers.shouldBe +import roomescape.payment.SampleTossPaymentConst +import roomescape.payment.web.dto.response.PaymentCancelResponse -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; +class PaymentCancelResponseDeserializerTest : StringSpec({ -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.module.SimpleModule; + val objectMapper: ObjectMapper = jacksonObjectMapper().registerModule( + SimpleModule().addDeserializer( + PaymentCancelResponse::class.java, + PaymentCancelResponseDeserializer() + ) + ) -import roomescape.payment.web.dto.response.PaymentCancelResponse; + "결제 취소 응답을 역직렬화하여 PaymentCancelResponse 객체를 생성한다" { + val cancelResponseJson: String = SampleTossPaymentConst.cancelJson + val cancelResponse: PaymentCancelResponse = objectMapper.readValue( + cancelResponseJson, + PaymentCancelResponse::class.java + ) -class PaymentCancelResponseDeserializerTest { - - private ObjectMapper objectMapper; - - @BeforeEach - void setUp() { - objectMapper = new ObjectMapper(); - SimpleModule simpleModule = new SimpleModule(); - simpleModule.addDeserializer(PaymentCancelResponse.class, new PaymentCancelResponseDeserializer()); - objectMapper.registerModule(simpleModule); - } - - @Test - @DisplayName("결제 취소 정보를 역직렬화하여 PaymentCancelResponse 객체를 생성한다.") - void deserialize() { - // given - String json = """ - { - "notUsedField": "notUsedValue", - "cancels": [ - { - "cancelStatus": "CANCELLED", - "cancelReason": "테스트 결제 취소", - "cancelAmount": 10000, - "canceledAt": "2021-07-01T10:10:10+09:00", - "notUsedField": "notUsedValue" - } - ] - }"""; - - // when - PaymentCancelResponse response = assertDoesNotThrow( - () -> objectMapper.readValue(json, PaymentCancelResponse.class)); - - // then - assertEquals("CANCELLED", response.cancelStatus()); - assertEquals("테스트 결제 취소", response.cancelReason()); - assertEquals(10000, response.cancelAmount()); - assertEquals("2021-07-01T10:10:10+09:00", response.canceledAt().toString()); - } -} + assertSoftly(cancelResponse) { + cancelResponse.cancelStatus shouldBe "DONE" + cancelResponse.cancelReason shouldBe SampleTossPaymentConst.cancelReason + cancelResponse.cancelAmount shouldBe SampleTossPaymentConst.amount + cancelResponse.canceledAt.toString() shouldBe "2024-02-13T12:20:23+09:00" + } + } +}) -- 2.47.2 From be79d7948c8529964a70f30df3b2a7749f2e351d Mon Sep 17 00:00:00 2001 From: pricelees Date: Tue, 15 Jul 2025 18:31:54 +0900 Subject: [PATCH 07/32] Rename .java to .kt --- .../client/{PaymentProperties.java => PaymentProperties.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/main/java/roomescape/payment/infrastructure/client/{PaymentProperties.java => PaymentProperties.kt} (100%) diff --git a/src/main/java/roomescape/payment/infrastructure/client/PaymentProperties.java b/src/main/java/roomescape/payment/infrastructure/client/PaymentProperties.kt similarity index 100% rename from src/main/java/roomescape/payment/infrastructure/client/PaymentProperties.java rename to src/main/java/roomescape/payment/infrastructure/client/PaymentProperties.kt -- 2.47.2 From 028185204ed5daaf047582c3f994f91c3fcee48f Mon Sep 17 00:00:00 2001 From: pricelees Date: Tue, 15 Jul 2025 18:31:54 +0900 Subject: [PATCH 08/32] =?UTF-8?q?refactor:=20PaymentProperties=EC=97=90=20?= =?UTF-8?q?API=20=EA=B8=B0=EB=B3=B8=20=EA=B2=BD=EB=A1=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EB=B0=8F=20=EC=BD=94=ED=8B=80=EB=A6=B0=20=EB=A7=88?= =?UTF-8?q?=EC=9D=B4=EA=B7=B8=EB=A0=88=EC=9D=B4=EC=85=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../client/PaymentProperties.kt | 34 +++++-------------- src/main/resources/application.yaml | 1 + 2 files changed, 9 insertions(+), 26 deletions(-) diff --git a/src/main/java/roomescape/payment/infrastructure/client/PaymentProperties.kt b/src/main/java/roomescape/payment/infrastructure/client/PaymentProperties.kt index cd5e047d..06758eb8 100644 --- a/src/main/java/roomescape/payment/infrastructure/client/PaymentProperties.kt +++ b/src/main/java/roomescape/payment/infrastructure/client/PaymentProperties.kt @@ -1,29 +1,11 @@ -package roomescape.payment.infrastructure.client; +package roomescape.payment.infrastructure.client -import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.context.properties.ConfigurationProperties @ConfigurationProperties(prefix = "payment") -public class PaymentProperties { - - private final String confirmSecretKey; - private final int readTimeout; - private final int connectTimeout; - - public PaymentProperties(String confirmSecretKey, int readTimeout, int connectTimeout) { - this.confirmSecretKey = confirmSecretKey; - this.readTimeout = readTimeout; - this.connectTimeout = connectTimeout; - } - - public String getConfirmSecretKey() { - return confirmSecretKey; - } - - public int getReadTimeout() { - return readTimeout; - } - - public int getConnectTimeout() { - return connectTimeout; - } -} +data class PaymentProperties( + @JvmField val apiBaseUrl: String, + @JvmField val confirmSecretKey: String, + @JvmField val readTimeout: Int, + @JvmField val connectTimeout: Int +) diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index b704da5f..82422963 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -25,6 +25,7 @@ security: expire-length: 1800000 # 30 분 payment: + api-base-url: https://api.tosspayments.com confirm-secret-key: test_gsk_docs_OaPz8L5KdmQXkzRz3y47BMw6 read-timeout: 3 connect-timeout: 30 -- 2.47.2 From 6aba38a9abbe9e409aabdb4cf05eb2287c69498e Mon Sep 17 00:00:00 2001 From: pricelees Date: Tue, 15 Jul 2025 18:32:32 +0900 Subject: [PATCH 09/32] Rename .java to .kt --- .../client/{PaymentConfig.java => PaymentConfig.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/main/java/roomescape/payment/infrastructure/client/{PaymentConfig.java => PaymentConfig.kt} (100%) diff --git a/src/main/java/roomescape/payment/infrastructure/client/PaymentConfig.java b/src/main/java/roomescape/payment/infrastructure/client/PaymentConfig.kt similarity index 100% rename from src/main/java/roomescape/payment/infrastructure/client/PaymentConfig.java rename to src/main/java/roomescape/payment/infrastructure/client/PaymentConfig.kt -- 2.47.2 From 199cbe573fe2e0ae82f694e9fe153d81898b0c50 Mon Sep 17 00:00:00 2001 From: pricelees Date: Tue, 15 Jul 2025 18:32:32 +0900 Subject: [PATCH 10/32] =?UTF-8?q?refactor:=20PaymentConfig=20=EC=BD=94?= =?UTF-8?q?=ED=8B=80=EB=A6=B0=20=EB=A7=88=EC=9D=B4=EA=B7=B8=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EC=85=98=20=EB=B0=8F=20RestClient=EB=A5=BC=20?= =?UTF-8?q?=EB=B0=98=ED=99=98=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../infrastructure/client/PaymentConfig.kt | 63 ++++++++++--------- .../client/TossPaymentClient.java | 10 +-- 2 files changed, 39 insertions(+), 34 deletions(-) diff --git a/src/main/java/roomescape/payment/infrastructure/client/PaymentConfig.kt b/src/main/java/roomescape/payment/infrastructure/client/PaymentConfig.kt index b566d0a2..512d4e6a 100644 --- a/src/main/java/roomescape/payment/infrastructure/client/PaymentConfig.kt +++ b/src/main/java/roomescape/payment/infrastructure/client/PaymentConfig.kt @@ -1,36 +1,41 @@ -package roomescape.payment.infrastructure.client; +package roomescape.payment.infrastructure.client -import java.nio.charset.StandardCharsets; -import java.time.Duration; -import java.util.Base64; - -import org.springframework.boot.context.properties.EnableConfigurationProperties; -import org.springframework.boot.web.client.ClientHttpRequestFactories; -import org.springframework.boot.web.client.ClientHttpRequestFactorySettings; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.http.client.ClientHttpRequestFactory; -import org.springframework.web.client.RestClient; +import org.springframework.boot.context.properties.EnableConfigurationProperties +import org.springframework.boot.http.client.ClientHttpRequestFactoryBuilder +import org.springframework.boot.http.client.ClientHttpRequestFactorySettings +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration +import org.springframework.web.client.RestClient +import java.nio.charset.StandardCharsets +import java.time.Duration +import java.util.* @Configuration -@EnableConfigurationProperties(PaymentProperties.class) -public class PaymentConfig { +@EnableConfigurationProperties(PaymentProperties::class) +class PaymentConfig { - @Bean - public RestClient.Builder restClientBuilder(PaymentProperties paymentProperties) { - ClientHttpRequestFactorySettings settings = ClientHttpRequestFactorySettings.DEFAULTS - .withReadTimeout(Duration.ofSeconds(paymentProperties.getReadTimeout())) - .withConnectTimeout(Duration.ofSeconds(paymentProperties.getConnectTimeout())); - ClientHttpRequestFactory requestFactory = ClientHttpRequestFactories.get(settings); + @Bean + fun paymentClient( + paymentProperties: PaymentProperties, + restClientBuilder: RestClient.Builder + ): RestClient { + val settings: ClientHttpRequestFactorySettings = ClientHttpRequestFactorySettings.defaults().also { + it.withReadTimeout(Duration.ofSeconds(paymentProperties.readTimeout.toLong())) + it.withConnectTimeout(Duration.ofSeconds(paymentProperties.connectTimeout.toLong())) + } + val requestFactory = ClientHttpRequestFactoryBuilder.jdk().build(settings) - return RestClient.builder().baseUrl("https://api.tosspayments.com") - .defaultHeader("Authorization", getAuthorizations(paymentProperties.getConfirmSecretKey())) - .requestFactory(requestFactory); - } + return restClientBuilder + .baseUrl(paymentProperties.apiBaseUrl) + .defaultHeader("Authorization", getAuthorizations(paymentProperties.confirmSecretKey)) + .requestFactory(requestFactory) + .build() + } - private String getAuthorizations(String secretKey) { - Base64.Encoder encoder = Base64.getEncoder(); - byte[] encodedBytes = encoder.encode((secretKey + ":").getBytes(StandardCharsets.UTF_8)); - return "Basic " + new String(encodedBytes); - } + private fun getAuthorizations(secretKey: String): String { + val encodedSecretKey = Base64.getEncoder() + .encodeToString("$secretKey:".toByteArray(StandardCharsets.UTF_8)) + + return "Basic $encodedSecretKey" + } } diff --git a/src/main/java/roomescape/payment/infrastructure/client/TossPaymentClient.java b/src/main/java/roomescape/payment/infrastructure/client/TossPaymentClient.java index f746bdb1..373504dd 100644 --- a/src/main/java/roomescape/payment/infrastructure/client/TossPaymentClient.java +++ b/src/main/java/roomescape/payment/infrastructure/client/TossPaymentClient.java @@ -27,15 +27,15 @@ public class TossPaymentClient { private static final Logger log = LoggerFactory.getLogger(TossPaymentClient.class); - private final RestClient restClient; + private final RestClient paymentClient; - public TossPaymentClient(RestClient.Builder restClientBuilder) { - this.restClient = restClientBuilder.build(); + public TossPaymentClient(RestClient paymentClient) { + this.paymentClient = paymentClient; } public PaymentResponse confirmPayment(PaymentRequest paymentRequest) { logPaymentInfo(paymentRequest); - return restClient.post() + return paymentClient.post() .uri("/v1/payments/confirm") .contentType(MediaType.APPLICATION_JSON) .body(paymentRequest) @@ -49,7 +49,7 @@ public class TossPaymentClient { logPaymentCancelInfo(cancelRequest); Map param = Map.of("cancelReason", cancelRequest.cancelReason()); - return restClient.post() + return paymentClient.post() .uri("/v1/payments/{paymentKey}/cancel", cancelRequest.paymentKey()) .contentType(MediaType.APPLICATION_JSON) .body(param) -- 2.47.2 From 2734290661450eed6563da8675748737dec0f718 Mon Sep 17 00:00:00 2001 From: pricelees Date: Tue, 15 Jul 2025 18:32:56 +0900 Subject: [PATCH 11/32] =?UTF-8?q?fix:=20=ED=95=84=EC=9A=94=ED=95=9C=20?= =?UTF-8?q?=EC=9D=91=EB=8B=B5=20=ED=95=84=EB=93=9C=EB=A7=8C=20=EA=B0=80?= =?UTF-8?q?=EC=A0=B8=EC=98=A4=EA=B8=B0=20=EC=9C=84=ED=95=9C=20\@JsonIgnore?= =?UTF-8?q?Properties=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../roomescape/payment/web/dto/response/PaymentResponse.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/roomescape/payment/web/dto/response/PaymentResponse.java b/src/main/java/roomescape/payment/web/dto/response/PaymentResponse.java index 45fac13d..ea351332 100644 --- a/src/main/java/roomescape/payment/web/dto/response/PaymentResponse.java +++ b/src/main/java/roomescape/payment/web/dto/response/PaymentResponse.java @@ -2,6 +2,9 @@ package roomescape.payment.web.dto.response; import java.time.OffsetDateTime; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; + +@JsonIgnoreProperties(ignoreUnknown = true) public record PaymentResponse( String paymentKey, String orderId, -- 2.47.2 From 571f6b2e1135218c5dd93bd015630d12934f3163 Mon Sep 17 00:00:00 2001 From: pricelees Date: Wed, 16 Jul 2025 11:14:11 +0900 Subject: [PATCH 12/32] =?UTF-8?q?refactor:=20payment=20=ED=8C=A8=ED=82=A4?= =?UTF-8?q?=EC=A7=80=20=EB=82=B4=20DTO=20=EC=BD=94=ED=8B=80=EB=A6=B0=20?= =?UTF-8?q?=EB=A7=88=EC=9D=B4=EA=B7=B8=EB=A0=88=EC=9D=B4=EC=85=98=20?= =?UTF-8?q?=EB=B0=8F=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=ED=86=B5=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../payment/business/PaymentService.java | 21 +++--- .../java/roomescape/payment/web/PaymentDTO.kt | 69 +++++++++++++++++++ .../web/dto/request/PaymentCancelRequest.java | 4 -- .../web/dto/request/PaymentRequest.java | 4 -- .../dto/response/PaymentCancelResponse.java | 16 ----- .../web/dto/response/PaymentResponse.java | 14 ---- .../response/ReservationPaymentResponse.java | 15 ---- .../response/TossPaymentErrorResponse.java | 4 -- .../PaymentCancelResponseDeserializer.kt | 11 ++- .../controller/ReservationController.java | 28 ++++---- .../dto/request/ReservationRequest.java | 6 +- .../ReservationWithPaymentService.java | 16 ++--- .../payment/SampleTossPaymentConst.kt | 10 ++- .../payment/business/PaymentServiceTest.java | 29 ++++---- .../PaymentCancelResponseDeserializerTest.kt | 8 +-- .../controller/ReservationControllerTest.java | 22 +++--- .../ReservationWithPaymentServiceTest.java | 20 +++--- 17 files changed, 154 insertions(+), 143 deletions(-) create mode 100644 src/main/java/roomescape/payment/web/PaymentDTO.kt delete mode 100644 src/main/java/roomescape/payment/web/dto/request/PaymentCancelRequest.java delete mode 100644 src/main/java/roomescape/payment/web/dto/request/PaymentRequest.java delete mode 100644 src/main/java/roomescape/payment/web/dto/response/PaymentCancelResponse.java delete mode 100644 src/main/java/roomescape/payment/web/dto/response/PaymentResponse.java delete mode 100644 src/main/java/roomescape/payment/web/dto/response/ReservationPaymentResponse.java delete mode 100644 src/main/java/roomescape/payment/web/dto/response/TossPaymentErrorResponse.java diff --git a/src/main/java/roomescape/payment/business/PaymentService.java b/src/main/java/roomescape/payment/business/PaymentService.java index add0f3ae..2d22dd43 100644 --- a/src/main/java/roomescape/payment/business/PaymentService.java +++ b/src/main/java/roomescape/payment/business/PaymentService.java @@ -13,10 +13,8 @@ import roomescape.payment.infrastructure.persistence.CanceledPayment; import roomescape.payment.infrastructure.persistence.CanceledPaymentRepository; import roomescape.payment.infrastructure.persistence.Payment; import roomescape.payment.infrastructure.persistence.PaymentRepository; -import roomescape.payment.web.dto.request.PaymentCancelRequest; -import roomescape.payment.web.dto.response.PaymentCancelResponse; -import roomescape.payment.web.dto.response.PaymentResponse; -import roomescape.payment.web.dto.response.ReservationPaymentResponse; +import roomescape.payment.web.PaymentCancel; +import roomescape.payment.web.ReservationPaymentResponse; import roomescape.reservation.domain.Reservation; @Service @@ -31,9 +29,10 @@ public class PaymentService { this.canceledPaymentRepository = canceledPaymentRepository; } - public ReservationPaymentResponse savePayment(PaymentResponse paymentResponse, Reservation reservation) { - Payment payment = new Payment(paymentResponse.orderId(), paymentResponse.paymentKey(), - paymentResponse.totalAmount(), reservation, paymentResponse.approvedAt()); + public ReservationPaymentResponse savePayment(roomescape.payment.web.PaymentApprove.Response paymentResponse, + Reservation reservation) { + Payment payment = new Payment(paymentResponse.orderId, paymentResponse.paymentKey, + paymentResponse.totalAmount, reservation, paymentResponse.approvedAt); Payment saved = paymentRepository.save(payment); return ReservationPaymentResponse.from(saved); } @@ -43,12 +42,12 @@ public class PaymentService { return paymentRepository.findByReservationId(reservationId); } - public void saveCanceledPayment(PaymentCancelResponse cancelInfo, OffsetDateTime approvedAt, String paymentKey) { + public void saveCanceledPayment(PaymentCancel.Response cancelInfo, OffsetDateTime approvedAt, String paymentKey) { canceledPaymentRepository.save(new CanceledPayment( - paymentKey, cancelInfo.cancelReason(), cancelInfo.cancelAmount(), approvedAt, cancelInfo.canceledAt())); + paymentKey, cancelInfo.cancelReason, cancelInfo.cancelAmount, approvedAt, cancelInfo.canceledAt)); } - public PaymentCancelRequest cancelPaymentByAdmin(Long reservationId) { + public PaymentCancel.Request cancelPaymentByAdmin(Long reservationId) { String paymentKey = findPaymentByReservationId(reservationId) .orElseThrow(() -> new RoomescapeException(ErrorType.PAYMENT_NOT_POUND, String.format("[reservationId: %d]", reservationId), HttpStatus.NOT_FOUND)) @@ -56,7 +55,7 @@ public class PaymentService { // 취소 시간은 현재 시간으로 일단 생성한 뒤, 결제 취소 완료 후 해당 시간으로 변경합니다. CanceledPayment canceled = cancelPayment(paymentKey, "고객 요청", OffsetDateTime.now()); - return new PaymentCancelRequest(paymentKey, canceled.getCancelAmount(), canceled.getCancelReason()); + return new PaymentCancel.Request(paymentKey, canceled.getCancelAmount(), canceled.getCancelReason()); } private CanceledPayment cancelPayment(String paymentKey, String cancelReason, OffsetDateTime canceledAt) { diff --git a/src/main/java/roomescape/payment/web/PaymentDTO.kt b/src/main/java/roomescape/payment/web/PaymentDTO.kt new file mode 100644 index 00000000..ae13950e --- /dev/null +++ b/src/main/java/roomescape/payment/web/PaymentDTO.kt @@ -0,0 +1,69 @@ +package roomescape.payment.web + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties +import com.fasterxml.jackson.databind.annotation.JsonDeserialize +import roomescape.payment.web.support.PaymentCancelResponseDeserializer +import roomescape.reservation.dto.response.ReservationResponse +import java.time.OffsetDateTime + +class PaymentApprove { + @JvmRecord + data class Request( + @JvmField val paymentKey: String, + @JvmField val orderId: String, + @JvmField val amount: Long, + @JvmField val paymentType: String + ) + + @JvmRecord + @JsonIgnoreProperties(ignoreUnknown = true) + data class Response( + @JvmField val paymentKey: String, + @JvmField val orderId: String, + @JvmField val approvedAt: OffsetDateTime, + @JvmField val totalAmount: Long + ) +} + +class PaymentCancel { + @JvmRecord + data class Request( + @JvmField val paymentKey: String, + @JvmField val amount: Long, + @JvmField val cancelReason: String + ) + + @JvmRecord + @JsonDeserialize(using = PaymentCancelResponseDeserializer::class) + data class Response( + @JvmField val cancelStatus: String, + @JvmField val cancelReason: String, + @JvmField val cancelAmount: Long, + @JvmField val canceledAt: OffsetDateTime + ) +} + + +@JvmRecord +data class ReservationPaymentResponse( + val id: Long, + val orderId: String, + val paymentKey: String, + val totalAmount: Long, + val reservation: ReservationResponse, + val approvedAt: OffsetDateTime +) { + companion object { + @JvmStatic + fun from(saved: roomescape.payment.infrastructure.persistence.Payment): ReservationPaymentResponse { + return ReservationPaymentResponse( + saved.id, + saved.orderId, + saved.paymentKey, + saved.totalAmount, + ReservationResponse.from(saved.reservation), + saved.approvedAt + ) + } + } +} diff --git a/src/main/java/roomescape/payment/web/dto/request/PaymentCancelRequest.java b/src/main/java/roomescape/payment/web/dto/request/PaymentCancelRequest.java deleted file mode 100644 index bf925405..00000000 --- a/src/main/java/roomescape/payment/web/dto/request/PaymentCancelRequest.java +++ /dev/null @@ -1,4 +0,0 @@ -package roomescape.payment.web.dto.request; - -public record PaymentCancelRequest(String paymentKey, Long amount, String cancelReason) { -} diff --git a/src/main/java/roomescape/payment/web/dto/request/PaymentRequest.java b/src/main/java/roomescape/payment/web/dto/request/PaymentRequest.java deleted file mode 100644 index bcda7c41..00000000 --- a/src/main/java/roomescape/payment/web/dto/request/PaymentRequest.java +++ /dev/null @@ -1,4 +0,0 @@ -package roomescape.payment.web.dto.request; - -public record PaymentRequest(String paymentKey, String orderId, Long amount, String paymentType) { -} diff --git a/src/main/java/roomescape/payment/web/dto/response/PaymentCancelResponse.java b/src/main/java/roomescape/payment/web/dto/response/PaymentCancelResponse.java deleted file mode 100644 index acf10776..00000000 --- a/src/main/java/roomescape/payment/web/dto/response/PaymentCancelResponse.java +++ /dev/null @@ -1,16 +0,0 @@ -package roomescape.payment.web.dto.response; - -import java.time.OffsetDateTime; - -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; - -import roomescape.payment.web.support.PaymentCancelResponseDeserializer; - -@JsonDeserialize(using = PaymentCancelResponseDeserializer.class) -public record PaymentCancelResponse( - String cancelStatus, - String cancelReason, - Long cancelAmount, - OffsetDateTime canceledAt -) { -} diff --git a/src/main/java/roomescape/payment/web/dto/response/PaymentResponse.java b/src/main/java/roomescape/payment/web/dto/response/PaymentResponse.java deleted file mode 100644 index ea351332..00000000 --- a/src/main/java/roomescape/payment/web/dto/response/PaymentResponse.java +++ /dev/null @@ -1,14 +0,0 @@ -package roomescape.payment.web.dto.response; - -import java.time.OffsetDateTime; - -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; - -@JsonIgnoreProperties(ignoreUnknown = true) -public record PaymentResponse( - String paymentKey, - String orderId, - OffsetDateTime approvedAt, - Long totalAmount -) { -} diff --git a/src/main/java/roomescape/payment/web/dto/response/ReservationPaymentResponse.java b/src/main/java/roomescape/payment/web/dto/response/ReservationPaymentResponse.java deleted file mode 100644 index f62854b5..00000000 --- a/src/main/java/roomescape/payment/web/dto/response/ReservationPaymentResponse.java +++ /dev/null @@ -1,15 +0,0 @@ -package roomescape.payment.web.dto.response; - -import java.time.OffsetDateTime; - -import roomescape.payment.infrastructure.persistence.Payment; -import roomescape.reservation.dto.response.ReservationResponse; - -public record ReservationPaymentResponse(Long id, String orderId, String paymentKey, Long totalAmount, - ReservationResponse reservation, OffsetDateTime approvedAt) { - - public static ReservationPaymentResponse from(Payment saved) { - return new ReservationPaymentResponse(saved.getId(), saved.getOrderId(), saved.getPaymentKey(), - saved.getTotalAmount(), ReservationResponse.from(saved.getReservation()), saved.getApprovedAt()); - } -} diff --git a/src/main/java/roomescape/payment/web/dto/response/TossPaymentErrorResponse.java b/src/main/java/roomescape/payment/web/dto/response/TossPaymentErrorResponse.java deleted file mode 100644 index 7f5163ee..00000000 --- a/src/main/java/roomescape/payment/web/dto/response/TossPaymentErrorResponse.java +++ /dev/null @@ -1,4 +0,0 @@ -package roomescape.payment.web.dto.response; - -public record TossPaymentErrorResponse(String code, String message) { -} diff --git a/src/main/java/roomescape/payment/web/support/PaymentCancelResponseDeserializer.kt b/src/main/java/roomescape/payment/web/support/PaymentCancelResponseDeserializer.kt index c1e7b904..14d59c12 100644 --- a/src/main/java/roomescape/payment/web/support/PaymentCancelResponseDeserializer.kt +++ b/src/main/java/roomescape/payment/web/support/PaymentCancelResponseDeserializer.kt @@ -5,24 +5,23 @@ import com.fasterxml.jackson.core.TreeNode import com.fasterxml.jackson.databind.DeserializationContext import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.deser.std.StdDeserializer -import roomescape.payment.web.dto.response.PaymentCancelResponse +import roomescape.payment.web.PaymentCancel import java.io.IOException import java.time.OffsetDateTime class PaymentCancelResponseDeserializer( - vc: Class? = null -) : StdDeserializer(vc) { - + vc: Class? = null +) : StdDeserializer(vc) { @Throws(IOException::class) override fun deserialize( jsonParser: JsonParser, deserializationContext: DeserializationContext? - ): PaymentCancelResponse { + ): PaymentCancel.Response { val cancels: JsonNode = jsonParser.codec.readTree(jsonParser) .get("cancels") .get(0) as JsonNode - return PaymentCancelResponse( + return PaymentCancel.Response( cancels.get("cancelStatus").asText(), cancels.get("cancelReason").asText(), cancels.get("cancelAmount").asLong(), diff --git a/src/main/java/roomescape/reservation/controller/ReservationController.java b/src/main/java/roomescape/reservation/controller/ReservationController.java index 01709713..f5df5f57 100644 --- a/src/main/java/roomescape/reservation/controller/ReservationController.java +++ b/src/main/java/roomescape/reservation/controller/ReservationController.java @@ -31,10 +31,8 @@ import roomescape.common.dto.response.RoomescapeApiResponse; import roomescape.common.dto.response.RoomescapeErrorResponse; import roomescape.common.exception.RoomescapeException; import roomescape.payment.infrastructure.client.TossPaymentClient; -import roomescape.payment.web.dto.request.PaymentCancelRequest; -import roomescape.payment.web.dto.request.PaymentRequest; -import roomescape.payment.web.dto.response.PaymentCancelResponse; -import roomescape.payment.web.dto.response.PaymentResponse; +import roomescape.payment.web.PaymentApprove; +import roomescape.payment.web.PaymentCancel; import roomescape.reservation.dto.request.AdminReservationRequest; import roomescape.reservation.dto.request.ReservationRequest; import roomescape.reservation.dto.request.WaitingRequest; @@ -120,13 +118,13 @@ public class ReservationController { return RoomescapeApiResponse.success(); } - PaymentCancelRequest paymentCancelRequest = reservationWithPaymentService.removeReservationWithPayment( + PaymentCancel.Request paymentCancelRequest = reservationWithPaymentService.removeReservationWithPayment( reservationId, memberId); - PaymentCancelResponse paymentCancelResponse = paymentClient.cancelPayment(paymentCancelRequest); + PaymentCancel.Response paymentCancelResponse = paymentClient.cancelPayment(paymentCancelRequest); - reservationWithPaymentService.updateCanceledTime(paymentCancelRequest.paymentKey(), - paymentCancelResponse.canceledAt()); + reservationWithPaymentService.updateCanceledTime(paymentCancelRequest.paymentKey, + paymentCancelResponse.canceledAt); return RoomescapeApiResponse.success(); } @@ -144,21 +142,21 @@ public class ReservationController { @MemberId @Parameter(hidden = true) Long memberId, HttpServletResponse response ) { - PaymentRequest paymentRequest = reservationRequest.getPaymentRequest(); - PaymentResponse paymentResponse = paymentClient.confirmPayment(paymentRequest); + PaymentApprove.Request paymentRequest = reservationRequest.getPaymentRequest(); + PaymentApprove.Response paymentResponse = paymentClient.confirmPayment(paymentRequest); try { ReservationResponse reservationResponse = reservationWithPaymentService.addReservationWithPayment( reservationRequest, paymentResponse, memberId); return getCreatedReservationResponse(reservationResponse, response); } catch (RoomescapeException e) { - PaymentCancelRequest cancelRequest = new PaymentCancelRequest(paymentRequest.paymentKey(), - paymentRequest.amount(), e.getMessage()); + PaymentCancel.Request cancelRequest = new PaymentCancel.Request(paymentRequest.paymentKey, + paymentRequest.amount, e.getMessage()); - PaymentCancelResponse paymentCancelResponse = paymentClient.cancelPayment(cancelRequest); + PaymentCancel.Response paymentCancelResponse = paymentClient.cancelPayment(cancelRequest); - reservationWithPaymentService.saveCanceledPayment(paymentCancelResponse, paymentResponse.approvedAt(), - paymentRequest.paymentKey()); + reservationWithPaymentService.saveCanceledPayment(paymentCancelResponse, paymentResponse.approvedAt, + paymentRequest.paymentKey); throw e; } } diff --git a/src/main/java/roomescape/reservation/dto/request/ReservationRequest.java b/src/main/java/roomescape/reservation/dto/request/ReservationRequest.java index 06bbbecf..e15b00fb 100644 --- a/src/main/java/roomescape/reservation/dto/request/ReservationRequest.java +++ b/src/main/java/roomescape/reservation/dto/request/ReservationRequest.java @@ -6,7 +6,7 @@ import com.fasterxml.jackson.annotation.JsonIgnore; import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.NotNull; -import roomescape.payment.web.dto.request.PaymentRequest; +import roomescape.payment.web.PaymentApprove; @Schema(name = "회원의 예약 저장 요청", description = "회원의 예약 요청시 사용됩니다.") public record ReservationRequest( @@ -30,7 +30,7 @@ public record ReservationRequest( ) { @JsonIgnore - public PaymentRequest getPaymentRequest() { - return new PaymentRequest(paymentKey, orderId, amount, paymentType); + public PaymentApprove.Request getPaymentRequest() { + return new PaymentApprove.Request(paymentKey, orderId, amount, paymentType); } } diff --git a/src/main/java/roomescape/reservation/service/ReservationWithPaymentService.java b/src/main/java/roomescape/reservation/service/ReservationWithPaymentService.java index 5721d742..edec7bae 100644 --- a/src/main/java/roomescape/reservation/service/ReservationWithPaymentService.java +++ b/src/main/java/roomescape/reservation/service/ReservationWithPaymentService.java @@ -6,10 +6,9 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import roomescape.payment.business.PaymentService; -import roomescape.payment.web.dto.request.PaymentCancelRequest; -import roomescape.payment.web.dto.response.PaymentCancelResponse; -import roomescape.payment.web.dto.response.PaymentResponse; -import roomescape.payment.web.dto.response.ReservationPaymentResponse; +import roomescape.payment.web.PaymentApprove; +import roomescape.payment.web.PaymentCancel; +import roomescape.payment.web.ReservationPaymentResponse; import roomescape.reservation.domain.Reservation; import roomescape.reservation.dto.request.ReservationRequest; import roomescape.reservation.dto.response.ReservationResponse; @@ -27,7 +26,8 @@ public class ReservationWithPaymentService { this.paymentService = paymentService; } - public ReservationResponse addReservationWithPayment(ReservationRequest request, PaymentResponse paymentInfo, + public ReservationResponse addReservationWithPayment(ReservationRequest request, + PaymentApprove.Response paymentInfo, Long memberId) { Reservation reservation = reservationService.addReservation(request, memberId); ReservationPaymentResponse reservationPaymentResponse = paymentService.savePayment(paymentInfo, reservation); @@ -35,12 +35,12 @@ public class ReservationWithPaymentService { return reservationPaymentResponse.reservation(); } - public void saveCanceledPayment(PaymentCancelResponse cancelInfo, OffsetDateTime approvedAt, String paymentKey) { + public void saveCanceledPayment(PaymentCancel.Response cancelInfo, OffsetDateTime approvedAt, String paymentKey) { paymentService.saveCanceledPayment(cancelInfo, approvedAt, paymentKey); } - public PaymentCancelRequest removeReservationWithPayment(Long reservationId, Long memberId) { - PaymentCancelRequest paymentCancelRequest = paymentService.cancelPaymentByAdmin(reservationId); + public PaymentCancel.Request removeReservationWithPayment(Long reservationId, Long memberId) { + PaymentCancel.Request paymentCancelRequest = paymentService.cancelPaymentByAdmin(reservationId); reservationService.removeReservationById(reservationId, memberId); return paymentCancelRequest; } diff --git a/src/test/java/roomescape/payment/SampleTossPaymentConst.kt b/src/test/java/roomescape/payment/SampleTossPaymentConst.kt index 9ed6b179..ba1db53e 100644 --- a/src/test/java/roomescape/payment/SampleTossPaymentConst.kt +++ b/src/test/java/roomescape/payment/SampleTossPaymentConst.kt @@ -1,8 +1,8 @@ package roomescape.payment import roomescape.payment.SampleTossPaymentConst.amount -import roomescape.payment.web.dto.request.PaymentCancelRequest -import roomescape.payment.web.dto.request.PaymentRequest +import roomescape.payment.web.PaymentApprove +import roomescape.payment.web.PaymentCancel import kotlin.math.roundToLong object SampleTossPaymentConst { @@ -22,7 +22,7 @@ object SampleTossPaymentConst { val cancelReason: String = "테스트 결제 취소" @JvmField - val paymentRequest: PaymentRequest = PaymentRequest( + val paymentRequest: PaymentApprove.Request = PaymentApprove.Request( paymentKey, orderId, amount, @@ -40,7 +40,7 @@ object SampleTossPaymentConst { """.trimIndent() @JvmField - val cancelRequest: PaymentCancelRequest = PaymentCancelRequest( + val cancelRequest: PaymentCancel.Request = PaymentCancel.Request( paymentKey, amount, cancelReason @@ -125,7 +125,6 @@ object SampleTossPaymentConst { "method": "$paymentType", "version": "2022-11-16" } - """.trimIndent() @JvmField @@ -205,7 +204,6 @@ object SampleTossPaymentConst { "method": "$paymentType", "version": "2022-11-16" } - """.trimIndent() } diff --git a/src/test/java/roomescape/payment/business/PaymentServiceTest.java b/src/test/java/roomescape/payment/business/PaymentServiceTest.java index d81b0d90..ee975e38 100644 --- a/src/test/java/roomescape/payment/business/PaymentServiceTest.java +++ b/src/test/java/roomescape/payment/business/PaymentServiceTest.java @@ -18,9 +18,9 @@ import roomescape.member.infrastructure.persistence.Member; import roomescape.member.infrastructure.persistence.MemberRepository; import roomescape.member.infrastructure.persistence.Role; import roomescape.payment.infrastructure.persistence.CanceledPaymentRepository; -import roomescape.payment.web.dto.request.PaymentCancelRequest; -import roomescape.payment.web.dto.response.PaymentResponse; -import roomescape.payment.web.dto.response.ReservationPaymentResponse; +import roomescape.payment.web.PaymentApprove; +import roomescape.payment.web.PaymentCancel; +import roomescape.payment.web.ReservationPaymentResponse; import roomescape.reservation.domain.Reservation; import roomescape.reservation.domain.ReservationStatus; import roomescape.reservation.domain.ReservationTime; @@ -50,7 +50,8 @@ class PaymentServiceTest { @DisplayName("결제 정보를 저장한다.") void savePayment() { // given - PaymentResponse paymentInfo = new PaymentResponse("payment-key", "order-id", OffsetDateTime.now(), 10000L); + PaymentApprove.Response paymentInfo = new PaymentApprove.Response("payment-key", "order-id", + OffsetDateTime.now(), 10000L); LocalDateTime localDateTime = LocalDateTime.now().plusHours(1L); LocalDate date = localDateTime.toLocalDate(); ReservationTime time = reservationTimeRepository.save(new ReservationTime(localDateTime.toLocalTime())); @@ -64,14 +65,15 @@ class PaymentServiceTest { // then assertThat(reservationPaymentResponse.reservation().id()).isEqualTo(reservation.getId()); - assertThat(reservationPaymentResponse.paymentKey()).isEqualTo(paymentInfo.paymentKey()); + assertThat(reservationPaymentResponse.paymentKey()).isEqualTo(paymentInfo.paymentKey); } @Test @DisplayName("예약 ID로 결제 정보를 제거하고, 결제 취소 테이블에 취소 정보를 저장한다.") void cancelPaymentByAdmin() { // given - PaymentResponse paymentInfo = new PaymentResponse("payment-key", "order-id", OffsetDateTime.now(), 10000L); + PaymentApprove.Response paymentInfo = new PaymentApprove.Response("payment-key", "order-id", + OffsetDateTime.now(), 10000L); LocalDateTime localDateTime = LocalDateTime.now().plusHours(1L); LocalDate date = localDateTime.toLocalDate(); ReservationTime time = reservationTimeRepository.save(new ReservationTime(localDateTime.toLocalTime())); @@ -83,13 +85,13 @@ class PaymentServiceTest { paymentService.savePayment(paymentInfo, reservation); // when - PaymentCancelRequest paymentCancelRequest = paymentService.cancelPaymentByAdmin(reservation.getId()); + PaymentCancel.Request paymentCancelRequest = paymentService.cancelPaymentByAdmin(reservation.getId()); // then assertThat(canceledPaymentRepository.findByPaymentKey("payment-key")).isNotEmpty(); - assertThat(paymentCancelRequest.paymentKey()).isEqualTo(paymentInfo.paymentKey()); - assertThat(paymentCancelRequest.cancelReason()).isEqualTo("고객 요청"); - assertThat(paymentCancelRequest.amount()).isEqualTo(10000L); + assertThat(paymentCancelRequest.paymentKey).isEqualTo(paymentInfo.paymentKey); + assertThat(paymentCancelRequest.cancelReason).isEqualTo("고객 요청"); + assertThat(paymentCancelRequest.amount).isEqualTo(10000L); } @Test @@ -107,7 +109,8 @@ class PaymentServiceTest { @DisplayName("결제 취소 정보에 있는 취소 시간을 업데이트한다.") void updateCanceledTime() { // given - PaymentResponse paymentInfo = new PaymentResponse("payment-key", "order-id", OffsetDateTime.now(), 10000L); + PaymentApprove.Response paymentInfo = new PaymentApprove.Response("payment-key", "order-id", + OffsetDateTime.now(), 10000L); LocalDateTime localDateTime = LocalDateTime.now().plusHours(1L); LocalDate date = localDateTime.toLocalDate(); ReservationTime time = reservationTimeRepository.save(new ReservationTime(localDateTime.toLocalTime())); @@ -121,10 +124,10 @@ class PaymentServiceTest { // when OffsetDateTime canceledAt = OffsetDateTime.now().plusHours(2L); - paymentService.updateCanceledTime(paymentInfo.paymentKey(), canceledAt); + paymentService.updateCanceledTime(paymentInfo.paymentKey, canceledAt); // then - canceledPaymentRepository.findByPaymentKey(paymentInfo.paymentKey()) + canceledPaymentRepository.findByPaymentKey(paymentInfo.paymentKey) .ifPresent(canceledPayment -> assertThat(canceledPayment.getCanceledAt()).isEqualTo(canceledAt)); } diff --git a/src/test/java/roomescape/payment/web/support/PaymentCancelResponseDeserializerTest.kt b/src/test/java/roomescape/payment/web/support/PaymentCancelResponseDeserializerTest.kt index d440f41b..32c33f90 100644 --- a/src/test/java/roomescape/payment/web/support/PaymentCancelResponseDeserializerTest.kt +++ b/src/test/java/roomescape/payment/web/support/PaymentCancelResponseDeserializerTest.kt @@ -7,22 +7,22 @@ import io.kotest.assertions.assertSoftly import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe import roomescape.payment.SampleTossPaymentConst -import roomescape.payment.web.dto.response.PaymentCancelResponse +import roomescape.payment.web.PaymentCancel class PaymentCancelResponseDeserializerTest : StringSpec({ val objectMapper: ObjectMapper = jacksonObjectMapper().registerModule( SimpleModule().addDeserializer( - PaymentCancelResponse::class.java, + PaymentCancel.Response::class.java, PaymentCancelResponseDeserializer() ) ) "결제 취소 응답을 역직렬화하여 PaymentCancelResponse 객체를 생성한다" { val cancelResponseJson: String = SampleTossPaymentConst.cancelJson - val cancelResponse: PaymentCancelResponse = objectMapper.readValue( + val cancelResponse: PaymentCancel.Response = objectMapper.readValue( cancelResponseJson, - PaymentCancelResponse::class.java + PaymentCancel.Response::class.java ) assertSoftly(cancelResponse) { diff --git a/src/test/java/roomescape/reservation/controller/ReservationControllerTest.java b/src/test/java/roomescape/reservation/controller/ReservationControllerTest.java index 44978828..9095b18f 100644 --- a/src/test/java/roomescape/reservation/controller/ReservationControllerTest.java +++ b/src/test/java/roomescape/reservation/controller/ReservationControllerTest.java @@ -41,10 +41,8 @@ import roomescape.payment.infrastructure.persistence.CanceledPayment; import roomescape.payment.infrastructure.persistence.CanceledPaymentRepository; import roomescape.payment.infrastructure.persistence.Payment; import roomescape.payment.infrastructure.persistence.PaymentRepository; -import roomescape.payment.web.dto.request.PaymentCancelRequest; -import roomescape.payment.web.dto.request.PaymentRequest; -import roomescape.payment.web.dto.response.PaymentCancelResponse; -import roomescape.payment.web.dto.response.PaymentResponse; +import roomescape.payment.web.PaymentApprove; +import roomescape.payment.web.PaymentCancel; import roomescape.reservation.domain.Reservation; import roomescape.reservation.domain.ReservationStatus; import roomescape.reservation.domain.ReservationTime; @@ -105,8 +103,8 @@ public class ReservationControllerTest { "paymentType", "DEFAULT" ); - when(paymentClient.confirmPayment(any(PaymentRequest.class))).thenReturn( - new PaymentResponse("pk", "oi", OffsetDateTime.of(date, time, ZoneOffset.ofHours(9)), 1000L)); + when(paymentClient.confirmPayment(any(PaymentApprove.Request.class))).thenReturn( + new PaymentApprove.Response("pk", "oi", OffsetDateTime.of(date, time, ZoneOffset.ofHours(9)), 1000L)); RestAssured.given().log().all() .contentType(ContentType.JSON) @@ -404,8 +402,8 @@ public class ReservationControllerTest { new Payment("pk", "oi", 1000L, saved, OffsetDateTime.now().minusHours(1L))); // when - when(paymentClient.cancelPayment(any(PaymentCancelRequest.class))) - .thenReturn(new PaymentCancelResponse("pk", "고객 요청", savedPayment.getTotalAmount(), + when(paymentClient.cancelPayment(any(PaymentCancel.Request.class))) + .thenReturn(new PaymentCancel.Response("pk", "고객 요청", savedPayment.getTotalAmount(), OffsetDateTime.now())); // then @@ -433,11 +431,11 @@ public class ReservationControllerTest { String paymentKey = "pk"; OffsetDateTime canceledAt = OffsetDateTime.now().plusHours(1L).withNano(0); OffsetDateTime approvedAt = OffsetDateTime.of(localDateTime, ZoneOffset.ofHours(9)); - when(paymentClient.confirmPayment(any(PaymentRequest.class))) - .thenReturn(new PaymentResponse(paymentKey, "oi", approvedAt, 1000L)); + when(paymentClient.confirmPayment(any(PaymentApprove.Request.class))) + .thenReturn(new PaymentApprove.Response(paymentKey, "oi", approvedAt, 1000L)); - when(paymentClient.cancelPayment(any(PaymentCancelRequest.class))) - .thenReturn(new PaymentCancelResponse(paymentKey, "고객 요청", 1000L, canceledAt)); + when(paymentClient.cancelPayment(any(PaymentCancel.Request.class))) + .thenReturn(new PaymentCancel.Response(paymentKey, "고객 요청", 1000L, canceledAt)); RestAssured.given().log().all() .contentType(ContentType.JSON) diff --git a/src/test/java/roomescape/reservation/service/ReservationWithPaymentServiceTest.java b/src/test/java/roomescape/reservation/service/ReservationWithPaymentServiceTest.java index 478b1d38..4f74c7e6 100644 --- a/src/test/java/roomescape/reservation/service/ReservationWithPaymentServiceTest.java +++ b/src/test/java/roomescape/reservation/service/ReservationWithPaymentServiceTest.java @@ -18,8 +18,8 @@ import roomescape.member.infrastructure.persistence.MemberRepository; import roomescape.member.infrastructure.persistence.Role; import roomescape.payment.infrastructure.persistence.CanceledPaymentRepository; import roomescape.payment.infrastructure.persistence.PaymentRepository; -import roomescape.payment.web.dto.request.PaymentCancelRequest; -import roomescape.payment.web.dto.response.PaymentResponse; +import roomescape.payment.web.PaymentApprove; +import roomescape.payment.web.PaymentCancel; import roomescape.reservation.domain.Reservation; import roomescape.reservation.domain.ReservationStatus; import roomescape.reservation.domain.ReservationTime; @@ -53,7 +53,8 @@ class ReservationWithPaymentServiceTest { @DisplayName("예약과 결제 정보를 추가한다.") void addReservationWithPayment() { // given - PaymentResponse paymentInfo = new PaymentResponse("payment-key", "order-id", OffsetDateTime.now(), 10000L); + PaymentApprove.Response paymentInfo = new PaymentApprove.Response("payment-key", "order-id", + OffsetDateTime.now(), 10000L); LocalDateTime localDateTime = LocalDateTime.now().plusDays(1L).withNano(0); LocalDate date = localDateTime.toLocalDate(); ReservationTime time = reservationTimeRepository.save(new ReservationTime(localDateTime.toLocalTime())); @@ -88,7 +89,8 @@ class ReservationWithPaymentServiceTest { @DisplayName("예약 ID를 이용하여 예약과 결제 정보를 제거하고, 결제 취소 정보를 저장한다.") void removeReservationWithPayment() { // given - PaymentResponse paymentInfo = new PaymentResponse("payment-key", "order-id", OffsetDateTime.now(), 10000L); + PaymentApprove.Response paymentInfo = new PaymentApprove.Response("payment-key", "order-id", + OffsetDateTime.now(), 10000L); LocalDateTime localDateTime = LocalDateTime.now().plusDays(1L).withNano(0); LocalDate date = localDateTime.toLocalDate(); ReservationTime time = reservationTimeRepository.save(new ReservationTime(localDateTime.toLocalTime())); @@ -101,11 +103,11 @@ class ReservationWithPaymentServiceTest { reservationRequest, paymentInfo, member.getId()); // when - PaymentCancelRequest paymentCancelRequest = reservationWithPaymentService.removeReservationWithPayment( + PaymentCancel.Request paymentCancelRequest = reservationWithPaymentService.removeReservationWithPayment( reservationResponse.id(), member.getId()); // then - assertThat(paymentCancelRequest.cancelReason()).isEqualTo("고객 요청"); + assertThat(paymentCancelRequest.cancelReason).isEqualTo("고객 요청"); assertThat(reservationRepository.findById(reservationResponse.id())).isEmpty(); assertThat(paymentRepository.findByPaymentKey("payment-key")).isEmpty(); assertThat(canceledPaymentRepository.findByPaymentKey("payment-key")).isNotEmpty(); @@ -115,7 +117,8 @@ class ReservationWithPaymentServiceTest { @DisplayName("결제 정보가 없으면 True를 반환한다.") void isNotPaidReservation() { // given - PaymentResponse paymentInfo = new PaymentResponse("payment-key", "order-id", OffsetDateTime.now(), 10000L); + PaymentApprove.Response paymentInfo = new PaymentApprove.Response("payment-key", "order-id", + OffsetDateTime.now(), 10000L); LocalDateTime localDateTime = LocalDateTime.now().plusHours(1L); LocalDate date = localDateTime.toLocalDate(); ReservationTime time = reservationTimeRepository.save(new ReservationTime(localDateTime.toLocalTime())); @@ -136,7 +139,8 @@ class ReservationWithPaymentServiceTest { @DisplayName("결제 정보가 있으면 False를 반환한다.") void isPaidReservation() { // given - PaymentResponse paymentInfo = new PaymentResponse("payment-key", "order-id", OffsetDateTime.now(), 10000L); + PaymentApprove.Response paymentInfo = new PaymentApprove.Response("payment-key", "order-id", + OffsetDateTime.now(), 10000L); LocalDateTime localDateTime = LocalDateTime.now().plusDays(1L).withNano(0); LocalDate date = localDateTime.toLocalDate(); ReservationTime time = reservationTimeRepository.save(new ReservationTime(localDateTime.toLocalTime())); -- 2.47.2 From 635f015b3c3ad58ddeb0e3f453c5e4f08409005f Mon Sep 17 00:00:00 2001 From: pricelees Date: Wed, 16 Jul 2025 11:15:30 +0900 Subject: [PATCH 13/32] =?UTF-8?q?refactor:=20payment=20=EB=82=B4=20RestCli?= =?UTF-8?q?ent=20=EA=B4=80=EB=A0=A8=20=ED=81=B4=EB=9E=98=EC=8A=A4(error,?= =?UTF-8?q?=20deserializer)=20=ED=8C=A8=ED=82=A4=EC=A7=80=20=EC=9D=B4?= =?UTF-8?q?=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../client}/PaymentCancelResponseDeserializer.kt | 4 ++-- .../infrastructure/client/TossPaymentErrorResponse.kt | 7 +++++++ src/main/java/roomescape/payment/web/PaymentDTO.kt | 2 +- .../web/support/PaymentCancelResponseDeserializerTest.kt | 1 + 4 files changed, 11 insertions(+), 3 deletions(-) rename src/main/java/roomescape/payment/{web/support => infrastructure/client}/PaymentCancelResponseDeserializer.kt (95%) create mode 100644 src/main/java/roomescape/payment/infrastructure/client/TossPaymentErrorResponse.kt diff --git a/src/main/java/roomescape/payment/web/support/PaymentCancelResponseDeserializer.kt b/src/main/java/roomescape/payment/infrastructure/client/PaymentCancelResponseDeserializer.kt similarity index 95% rename from src/main/java/roomescape/payment/web/support/PaymentCancelResponseDeserializer.kt rename to src/main/java/roomescape/payment/infrastructure/client/PaymentCancelResponseDeserializer.kt index 14d59c12..075e7e5e 100644 --- a/src/main/java/roomescape/payment/web/support/PaymentCancelResponseDeserializer.kt +++ b/src/main/java/roomescape/payment/infrastructure/client/PaymentCancelResponseDeserializer.kt @@ -1,4 +1,4 @@ -package roomescape.payment.web.support +package roomescape.payment.infrastructure.client import com.fasterxml.jackson.core.JsonParser import com.fasterxml.jackson.core.TreeNode @@ -28,4 +28,4 @@ class PaymentCancelResponseDeserializer( OffsetDateTime.parse(cancels.get("canceledAt").asText()) ) } -} +} \ No newline at end of file diff --git a/src/main/java/roomescape/payment/infrastructure/client/TossPaymentErrorResponse.kt b/src/main/java/roomescape/payment/infrastructure/client/TossPaymentErrorResponse.kt new file mode 100644 index 00000000..aebddc49 --- /dev/null +++ b/src/main/java/roomescape/payment/infrastructure/client/TossPaymentErrorResponse.kt @@ -0,0 +1,7 @@ +package roomescape.payment.infrastructure.client + +@JvmRecord +data class TossPaymentErrorResponse( + val code: String, + val message: String +) diff --git a/src/main/java/roomescape/payment/web/PaymentDTO.kt b/src/main/java/roomescape/payment/web/PaymentDTO.kt index ae13950e..eec2c82f 100644 --- a/src/main/java/roomescape/payment/web/PaymentDTO.kt +++ b/src/main/java/roomescape/payment/web/PaymentDTO.kt @@ -2,7 +2,7 @@ package roomescape.payment.web import com.fasterxml.jackson.annotation.JsonIgnoreProperties import com.fasterxml.jackson.databind.annotation.JsonDeserialize -import roomescape.payment.web.support.PaymentCancelResponseDeserializer +import roomescape.payment.infrastructure.client.PaymentCancelResponseDeserializer import roomescape.reservation.dto.response.ReservationResponse import java.time.OffsetDateTime diff --git a/src/test/java/roomescape/payment/web/support/PaymentCancelResponseDeserializerTest.kt b/src/test/java/roomescape/payment/web/support/PaymentCancelResponseDeserializerTest.kt index 32c33f90..97908a0a 100644 --- a/src/test/java/roomescape/payment/web/support/PaymentCancelResponseDeserializerTest.kt +++ b/src/test/java/roomescape/payment/web/support/PaymentCancelResponseDeserializerTest.kt @@ -7,6 +7,7 @@ import io.kotest.assertions.assertSoftly import io.kotest.core.spec.style.StringSpec import io.kotest.matchers.shouldBe import roomescape.payment.SampleTossPaymentConst +import roomescape.payment.infrastructure.client.PaymentCancelResponseDeserializer import roomescape.payment.web.PaymentCancel class PaymentCancelResponseDeserializerTest : StringSpec({ -- 2.47.2 From 51b0877508979b6f2e3d211a2661dcb0de4c67f9 Mon Sep 17 00:00:00 2001 From: pricelees Date: Wed, 16 Jul 2025 11:16:17 +0900 Subject: [PATCH 14/32] =?UTF-8?q?refactor:=20PaymentConfig=EC=9D=98=20?= =?UTF-8?q?=EB=B9=88=20=ED=83=80=EC=9E=85=EC=9D=84=20RestClient=20->=20Res?= =?UTF-8?q?tClient.Builder=EC=9C=BC=EB=A1=9C=20=EB=A1=A4=EB=B0=B1=20?= =?UTF-8?q?=EB=B0=8F=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=A0=84=EC=9A=A9=20?= =?UTF-8?q?property=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../infrastructure/client/PaymentConfig.kt | 8 ++-- .../client/TossPaymentClient.java | 41 +++++++++---------- .../client/TossPaymentClientTest.java | 22 +++++----- src/test/resources/application.yaml | 6 +++ 4 files changed, 38 insertions(+), 39 deletions(-) diff --git a/src/main/java/roomescape/payment/infrastructure/client/PaymentConfig.kt b/src/main/java/roomescape/payment/infrastructure/client/PaymentConfig.kt index 512d4e6a..a7d7cb94 100644 --- a/src/main/java/roomescape/payment/infrastructure/client/PaymentConfig.kt +++ b/src/main/java/roomescape/payment/infrastructure/client/PaymentConfig.kt @@ -15,21 +15,19 @@ import java.util.* class PaymentConfig { @Bean - fun paymentClient( + fun tossPaymentClientBuilder( paymentProperties: PaymentProperties, - restClientBuilder: RestClient.Builder - ): RestClient { + ): RestClient.Builder { val settings: ClientHttpRequestFactorySettings = ClientHttpRequestFactorySettings.defaults().also { it.withReadTimeout(Duration.ofSeconds(paymentProperties.readTimeout.toLong())) it.withConnectTimeout(Duration.ofSeconds(paymentProperties.connectTimeout.toLong())) } val requestFactory = ClientHttpRequestFactoryBuilder.jdk().build(settings) - return restClientBuilder + return RestClient.builder() .baseUrl(paymentProperties.apiBaseUrl) .defaultHeader("Authorization", getAuthorizations(paymentProperties.confirmSecretKey)) .requestFactory(requestFactory) - .build() } private fun getAuthorizations(secretKey: String): String { diff --git a/src/main/java/roomescape/payment/infrastructure/client/TossPaymentClient.java b/src/main/java/roomescape/payment/infrastructure/client/TossPaymentClient.java index 373504dd..9b2cba2c 100644 --- a/src/main/java/roomescape/payment/infrastructure/client/TossPaymentClient.java +++ b/src/main/java/roomescape/payment/infrastructure/client/TossPaymentClient.java @@ -16,58 +16,55 @@ import com.fasterxml.jackson.databind.ObjectMapper; import roomescape.common.exception.ErrorType; import roomescape.common.exception.RoomescapeException; -import roomescape.payment.web.dto.request.PaymentCancelRequest; -import roomescape.payment.web.dto.request.PaymentRequest; -import roomescape.payment.web.dto.response.PaymentCancelResponse; -import roomescape.payment.web.dto.response.PaymentResponse; -import roomescape.payment.web.dto.response.TossPaymentErrorResponse; +import roomescape.payment.web.PaymentApprove; +import roomescape.payment.web.PaymentCancel; @Component public class TossPaymentClient { private static final Logger log = LoggerFactory.getLogger(TossPaymentClient.class); - private final RestClient paymentClient; + private final RestClient tossPaymentClient; - public TossPaymentClient(RestClient paymentClient) { - this.paymentClient = paymentClient; + public TossPaymentClient(RestClient.Builder tossPaymentClientBuilder) { + this.tossPaymentClient = tossPaymentClientBuilder.build(); } - public PaymentResponse confirmPayment(PaymentRequest paymentRequest) { + public PaymentApprove.Response confirmPayment(PaymentApprove.Request paymentRequest) { logPaymentInfo(paymentRequest); - return paymentClient.post() + return tossPaymentClient.post() .uri("/v1/payments/confirm") .contentType(MediaType.APPLICATION_JSON) .body(paymentRequest) .retrieve() .onStatus(status -> status.is4xxClientError() || status.is5xxServerError(), (req, res) -> handlePaymentError(res)) - .body(PaymentResponse.class); + .body(PaymentApprove.Response.class); } - public PaymentCancelResponse cancelPayment(PaymentCancelRequest cancelRequest) { + public PaymentCancel.Response cancelPayment(PaymentCancel.Request cancelRequest) { logPaymentCancelInfo(cancelRequest); - Map param = Map.of("cancelReason", cancelRequest.cancelReason()); + Map param = Map.of("cancelReason", cancelRequest.cancelReason); - return paymentClient.post() - .uri("/v1/payments/{paymentKey}/cancel", cancelRequest.paymentKey()) + return tossPaymentClient.post() + .uri("/v1/payments/{paymentKey}/cancel", cancelRequest.paymentKey) .contentType(MediaType.APPLICATION_JSON) .body(param) .retrieve() .onStatus(status -> status.is4xxClientError() || status.is5xxServerError(), (req, res) -> handlePaymentError(res)) - .body(PaymentCancelResponse.class); + .body(PaymentCancel.Response.class); } - private void logPaymentInfo(PaymentRequest paymentRequest) { + private void logPaymentInfo(PaymentApprove.Request paymentRequest) { log.info("결제 승인 요청: paymentKey={}, orderId={}, amount={}, paymentType={}", - paymentRequest.paymentKey(), paymentRequest.orderId(), paymentRequest.amount(), - paymentRequest.paymentType()); + paymentRequest.paymentKey, paymentRequest.orderId, paymentRequest.amount, + paymentRequest.paymentType); } - private void logPaymentCancelInfo(PaymentCancelRequest cancelRequest) { + private void logPaymentCancelInfo(PaymentCancel.Request cancelRequest) { log.info("결제 취소 요청: paymentKey={}, amount={}, cancelReason={}", - cancelRequest.paymentKey(), cancelRequest.amount(), cancelRequest.cancelReason()); + cancelRequest.paymentKey, cancelRequest.amount, cancelRequest.cancelReason); } private void handlePaymentError(ClientHttpResponse res) @@ -77,7 +74,7 @@ public class TossPaymentClient { TossPaymentErrorResponse errorResponse = getErrorResponse(res); throw new RoomescapeException(errorType, - String.format("[ErrorCode = %s, ErrorMessage = %s]", errorResponse.code(), errorResponse.message()), + String.format("[ErrorCode = %s, ErrorMessage = %s]", errorResponse.code, errorResponse.message), statusCode); } diff --git a/src/test/java/roomescape/payment/infrastructure/client/TossPaymentClientTest.java b/src/test/java/roomescape/payment/infrastructure/client/TossPaymentClientTest.java index ff252897..29069c64 100644 --- a/src/test/java/roomescape/payment/infrastructure/client/TossPaymentClientTest.java +++ b/src/test/java/roomescape/payment/infrastructure/client/TossPaymentClientTest.java @@ -19,10 +19,8 @@ import org.springframework.test.web.client.MockRestServiceServer; import roomescape.common.exception.ErrorType; import roomescape.common.exception.RoomescapeException; import roomescape.payment.SampleTossPaymentConst; -import roomescape.payment.web.dto.request.PaymentCancelRequest; -import roomescape.payment.web.dto.request.PaymentRequest; -import roomescape.payment.web.dto.response.PaymentCancelResponse; -import roomescape.payment.web.dto.response.PaymentResponse; +import roomescape.payment.web.PaymentApprove; +import roomescape.payment.web.PaymentCancel; @RestClientTest(TossPaymentClient.class) class TossPaymentClientTest { @@ -46,12 +44,12 @@ class TossPaymentClientTest { .body(SampleTossPaymentConst.confirmJson)); // when - PaymentRequest paymentRequest = SampleTossPaymentConst.paymentRequest; - PaymentResponse paymentResponse = tossPaymentClient.confirmPayment(paymentRequest); + PaymentApprove.Request paymentRequest = SampleTossPaymentConst.paymentRequest; + PaymentApprove.Response paymentResponse = tossPaymentClient.confirmPayment(paymentRequest); // then - assertThat(paymentResponse.paymentKey()).isEqualTo(paymentRequest.paymentKey()); - assertThat(paymentResponse.orderId()).isEqualTo(paymentRequest.orderId()); + assertThat(paymentResponse.paymentKey).isEqualTo(paymentRequest.paymentKey); + assertThat(paymentResponse.orderId).isEqualTo(paymentRequest.orderId); } @Test @@ -67,12 +65,12 @@ class TossPaymentClientTest { .body(SampleTossPaymentConst.cancelJson)); // when - PaymentCancelRequest cancelRequest = SampleTossPaymentConst.cancelRequest; - PaymentCancelResponse paymentCancelResponse = tossPaymentClient.cancelPayment(cancelRequest); + PaymentCancel.Request cancelRequest = SampleTossPaymentConst.cancelRequest; + PaymentCancel.Response paymentCancelResponse = tossPaymentClient.cancelPayment(cancelRequest); // then - assertThat(paymentCancelResponse.cancelStatus()).isEqualTo("DONE"); - assertThat(paymentCancelResponse.cancelReason()).isEqualTo(cancelRequest.cancelReason()); + assertThat(paymentCancelResponse.cancelStatus).isEqualTo("DONE"); + assertThat(paymentCancelResponse.cancelReason).isEqualTo(cancelRequest.cancelReason); } @Test diff --git a/src/test/resources/application.yaml b/src/test/resources/application.yaml index c9b0197a..350ecfc3 100644 --- a/src/test/resources/application.yaml +++ b/src/test/resources/application.yaml @@ -25,3 +25,9 @@ security: secret-key: daijawligagaf@LIJ$@U)9nagnalkkgalijaddljfi access: expire-length: 1800000 # 30 분 + +payment: + api-base-url: https://api.tosspayments.com + confirm-secret-key: test_gsk_docs_OaPz8L5KdmQXkzRz3y47BMw6 + read-timeout: 3 + connect-timeout: 30 \ No newline at end of file -- 2.47.2 From 6fdc9767c1eef4e5a12892a5589cdd48bfe2072d Mon Sep 17 00:00:00 2001 From: pricelees Date: Wed, 16 Jul 2025 12:09:17 +0900 Subject: [PATCH 15/32] Rename .java to .kt --- .../client/{TossPaymentClient.java => TossPaymentClient.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/main/java/roomescape/payment/infrastructure/client/{TossPaymentClient.java => TossPaymentClient.kt} (100%) diff --git a/src/main/java/roomescape/payment/infrastructure/client/TossPaymentClient.java b/src/main/java/roomescape/payment/infrastructure/client/TossPaymentClient.kt similarity index 100% rename from src/main/java/roomescape/payment/infrastructure/client/TossPaymentClient.java rename to src/main/java/roomescape/payment/infrastructure/client/TossPaymentClient.kt -- 2.47.2 From 313d9850d1154a184a0de8a6bf51dbf7e68a0f36 Mon Sep 17 00:00:00 2001 From: pricelees Date: Wed, 16 Jul 2025 12:09:17 +0900 Subject: [PATCH 16/32] =?UTF-8?q?refactor:=20TossPaymentClient=20=EC=BD=94?= =?UTF-8?q?=ED=8B=80=EB=A6=B0=20=EB=A7=88=EC=9D=B4=EA=B7=B8=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EC=85=98=20=EB=B0=8F=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../client/TossPaymentClient.kt | 178 ++++++++++-------- .../client/TossPaymentClientTest.java | 117 ------------ .../client/TossPaymentClientTest.kt | 139 ++++++++++++++ 3 files changed, 237 insertions(+), 197 deletions(-) delete mode 100644 src/test/java/roomescape/payment/infrastructure/client/TossPaymentClientTest.java create mode 100644 src/test/java/roomescape/payment/infrastructure/client/TossPaymentClientTest.kt diff --git a/src/main/java/roomescape/payment/infrastructure/client/TossPaymentClient.kt b/src/main/java/roomescape/payment/infrastructure/client/TossPaymentClient.kt index 9b2cba2c..e46b95da 100644 --- a/src/main/java/roomescape/payment/infrastructure/client/TossPaymentClient.kt +++ b/src/main/java/roomescape/payment/infrastructure/client/TossPaymentClient.kt @@ -1,95 +1,113 @@ -package roomescape.payment.infrastructure.client; +package roomescape.payment.infrastructure.client -import java.io.IOException; -import java.io.InputStream; -import java.util.Map; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.http.HttpStatusCode; -import org.springframework.http.MediaType; -import org.springframework.http.client.ClientHttpResponse; -import org.springframework.stereotype.Component; -import org.springframework.web.client.RestClient; - -import com.fasterxml.jackson.databind.ObjectMapper; - -import roomescape.common.exception.ErrorType; -import roomescape.common.exception.RoomescapeException; -import roomescape.payment.web.PaymentApprove; -import roomescape.payment.web.PaymentCancel; +import com.fasterxml.jackson.databind.ObjectMapper +import io.github.oshai.kotlinlogging.KLogger +import io.github.oshai.kotlinlogging.KotlinLogging +import org.springframework.http.HttpRequest +import org.springframework.http.HttpStatus +import org.springframework.http.HttpStatusCode +import org.springframework.http.MediaType +import org.springframework.http.client.ClientHttpResponse +import org.springframework.stereotype.Component +import org.springframework.web.client.RestClient +import roomescape.common.exception.ErrorType +import roomescape.common.exception.RoomescapeException +import roomescape.payment.web.PaymentApprove +import roomescape.payment.web.PaymentCancel +import java.io.IOException +import java.util.Map @Component -public class TossPaymentClient { +class TossPaymentClient( + private val log: KLogger = KotlinLogging.logger {}, + tossPaymentClientBuilder: RestClient.Builder +) { + companion object { + private const val CONFIRM_URL: String = "/v1/payments/confirm" + private const val CANCEL_URL: String = "/v1/payments/{paymentKey}/cancel" + } - private static final Logger log = LoggerFactory.getLogger(TossPaymentClient.class); + private val tossPaymentClient: RestClient = tossPaymentClientBuilder.build() - private final RestClient tossPaymentClient; + fun confirmPayment(paymentRequest: PaymentApprove.Request): PaymentApprove.Response { + logPaymentInfo(paymentRequest) - public TossPaymentClient(RestClient.Builder tossPaymentClientBuilder) { - this.tossPaymentClient = tossPaymentClientBuilder.build(); - } + return tossPaymentClient.post() + .uri(CONFIRM_URL) + .contentType(MediaType.APPLICATION_JSON) + .body(paymentRequest) + .retrieve() + .onStatus( + { status: HttpStatusCode -> status.is4xxClientError || status.is5xxServerError }, + { req: HttpRequest, res: ClientHttpResponse -> handlePaymentError(res) } + ) + .body(PaymentApprove.Response::class.java) + ?: throw RoomescapeException(ErrorType.PAYMENT_SERVER_ERROR, HttpStatus.INTERNAL_SERVER_ERROR) + } - public PaymentApprove.Response confirmPayment(PaymentApprove.Request paymentRequest) { - logPaymentInfo(paymentRequest); - return tossPaymentClient.post() - .uri("/v1/payments/confirm") - .contentType(MediaType.APPLICATION_JSON) - .body(paymentRequest) - .retrieve() - .onStatus(status -> status.is4xxClientError() || status.is5xxServerError(), - (req, res) -> handlePaymentError(res)) - .body(PaymentApprove.Response.class); - } + fun cancelPayment(cancelRequest: PaymentCancel.Request): PaymentCancel.Response { + logPaymentCancelInfo(cancelRequest) + val param = Map.of("cancelReason", cancelRequest.cancelReason) - public PaymentCancel.Response cancelPayment(PaymentCancel.Request cancelRequest) { - logPaymentCancelInfo(cancelRequest); - Map param = Map.of("cancelReason", cancelRequest.cancelReason); + return tossPaymentClient.post() + .uri(CANCEL_URL, cancelRequest.paymentKey) + .contentType(MediaType.APPLICATION_JSON) + .body(param) + .retrieve() + .onStatus( + { status: HttpStatusCode -> status.is4xxClientError || status.is5xxServerError }, + { req: HttpRequest, res: ClientHttpResponse -> handlePaymentError(res) } + ) + .body(PaymentCancel.Response::class.java) + ?: throw RoomescapeException(ErrorType.PAYMENT_SERVER_ERROR, HttpStatus.INTERNAL_SERVER_ERROR) + } - return tossPaymentClient.post() - .uri("/v1/payments/{paymentKey}/cancel", cancelRequest.paymentKey) - .contentType(MediaType.APPLICATION_JSON) - .body(param) - .retrieve() - .onStatus(status -> status.is4xxClientError() || status.is5xxServerError(), - (req, res) -> handlePaymentError(res)) - .body(PaymentCancel.Response.class); - } + private fun logPaymentInfo(paymentRequest: PaymentApprove.Request) { + log.info { + "결제 승인 요청: paymentKey=${paymentRequest.paymentKey}, orderId=${paymentRequest.orderId}, " + + "amount=${paymentRequest.amount}, paymentType=${paymentRequest.paymentType}" + } + } - private void logPaymentInfo(PaymentApprove.Request paymentRequest) { - log.info("결제 승인 요청: paymentKey={}, orderId={}, amount={}, paymentType={}", - paymentRequest.paymentKey, paymentRequest.orderId, paymentRequest.amount, - paymentRequest.paymentType); - } + private fun logPaymentCancelInfo(cancelRequest: PaymentCancel.Request) { + log.info { + "결제 취소 요청: paymentKey=${cancelRequest.paymentKey}, amount=${cancelRequest.amount}, " + + "cancelReason=${cancelRequest.cancelReason}" + } + } - private void logPaymentCancelInfo(PaymentCancel.Request cancelRequest) { - log.info("결제 취소 요청: paymentKey={}, amount={}, cancelReason={}", - cancelRequest.paymentKey, cancelRequest.amount, cancelRequest.cancelReason); - } + @Throws(IOException::class) + private fun handlePaymentError( + res: ClientHttpResponse + ): Nothing { + val statusCode = res.statusCode + val errorType = getErrorTypeByStatusCode(statusCode) + val errorResponse = getErrorResponse(res) - private void handlePaymentError(ClientHttpResponse res) - throws IOException { - HttpStatusCode statusCode = res.getStatusCode(); - ErrorType errorType = getErrorTypeByStatusCode(statusCode); - TossPaymentErrorResponse errorResponse = getErrorResponse(res); + throw RoomescapeException( + errorType, + "[ErrorCode = ${errorResponse.code}, ErrorMessage = ${errorResponse.message}]", + statusCode + ) + } - throw new RoomescapeException(errorType, - String.format("[ErrorCode = %s, ErrorMessage = %s]", errorResponse.code, errorResponse.message), - statusCode); - } + @Throws(IOException::class) + private fun getErrorResponse( + res: ClientHttpResponse + ): TossPaymentErrorResponse { + val body = res.body + val objectMapper = ObjectMapper() + val errorResponse = objectMapper.readValue(body, TossPaymentErrorResponse::class.java) + body.close() + return errorResponse + } - private TossPaymentErrorResponse getErrorResponse(ClientHttpResponse res) throws IOException { - InputStream body = res.getBody(); - ObjectMapper objectMapper = new ObjectMapper(); - TossPaymentErrorResponse errorResponse = objectMapper.readValue(body, TossPaymentErrorResponse.class); - body.close(); - return errorResponse; - } - - private ErrorType getErrorTypeByStatusCode(HttpStatusCode statusCode) { - if (statusCode.is4xxClientError()) { - return ErrorType.PAYMENT_ERROR; - } - return ErrorType.PAYMENT_SERVER_ERROR; - } + private fun getErrorTypeByStatusCode( + statusCode: HttpStatusCode + ): ErrorType { + if (statusCode.is4xxClientError) { + return ErrorType.PAYMENT_ERROR + } + return ErrorType.PAYMENT_SERVER_ERROR + } } diff --git a/src/test/java/roomescape/payment/infrastructure/client/TossPaymentClientTest.java b/src/test/java/roomescape/payment/infrastructure/client/TossPaymentClientTest.java deleted file mode 100644 index 29069c64..00000000 --- a/src/test/java/roomescape/payment/infrastructure/client/TossPaymentClientTest.java +++ /dev/null @@ -1,117 +0,0 @@ -package roomescape.payment.infrastructure.client; - -import static org.assertj.core.api.Assertions.*; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.AssertionsForClassTypes.*; -import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; -import static org.springframework.test.web.client.match.MockRestRequestMatchers.*; -import static org.springframework.test.web.client.response.MockRestResponseCreators.*; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.client.RestClientTest; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.test.web.client.MockRestServiceServer; - -import roomescape.common.exception.ErrorType; -import roomescape.common.exception.RoomescapeException; -import roomescape.payment.SampleTossPaymentConst; -import roomescape.payment.web.PaymentApprove; -import roomescape.payment.web.PaymentCancel; - -@RestClientTest(TossPaymentClient.class) -class TossPaymentClientTest { - - @Autowired - private TossPaymentClient tossPaymentClient; - - @Autowired - private MockRestServiceServer mockServer; - - @Test - @DisplayName("결제를 승인한다.") - void confirmPayment() { - // given - mockServer.expect(requestTo("/v1/payments/confirm")) - .andExpect(method(HttpMethod.POST)) - .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(content().json(SampleTossPaymentConst.paymentRequestJson)) - .andRespond(withStatus(HttpStatus.OK) - .contentType(MediaType.APPLICATION_JSON) - .body(SampleTossPaymentConst.confirmJson)); - - // when - PaymentApprove.Request paymentRequest = SampleTossPaymentConst.paymentRequest; - PaymentApprove.Response paymentResponse = tossPaymentClient.confirmPayment(paymentRequest); - - // then - assertThat(paymentResponse.paymentKey).isEqualTo(paymentRequest.paymentKey); - assertThat(paymentResponse.orderId).isEqualTo(paymentRequest.orderId); - } - - @Test - @DisplayName("결제를 취소한다.") - void cancelPayment() { - // given - mockServer.expect(requestTo("/v1/payments/5EnNZRJGvaBX7zk2yd8ydw26XvwXkLrx9POLqKQjmAw4b0e1/cancel")) - .andExpect(method(HttpMethod.POST)) - .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(content().json(SampleTossPaymentConst.cancelRequestJson)) - .andRespond(withStatus(HttpStatus.OK) - .contentType(MediaType.APPLICATION_JSON) - .body(SampleTossPaymentConst.cancelJson)); - - // when - PaymentCancel.Request cancelRequest = SampleTossPaymentConst.cancelRequest; - PaymentCancel.Response paymentCancelResponse = tossPaymentClient.cancelPayment(cancelRequest); - - // then - assertThat(paymentCancelResponse.cancelStatus).isEqualTo("DONE"); - assertThat(paymentCancelResponse.cancelReason).isEqualTo(cancelRequest.cancelReason); - } - - @Test - @DisplayName("결제 승인 중 400 에러가 발생한다.") - void confirmPaymentWithError() { - // given - mockServer.expect(requestTo("/v1/payments/confirm")) - .andExpect(method(HttpMethod.POST)) - .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(content().json(SampleTossPaymentConst.paymentRequestJson)) - .andRespond(withStatus(HttpStatus.BAD_REQUEST) - .contentType(MediaType.APPLICATION_JSON) - .body(SampleTossPaymentConst.tossPaymentErrorJson)); - - // when & then - assertThatThrownBy(() -> tossPaymentClient.confirmPayment(SampleTossPaymentConst.paymentRequest)) - .isInstanceOf(RoomescapeException.class) - .hasFieldOrPropertyWithValue("errorType", ErrorType.PAYMENT_ERROR) - .hasFieldOrPropertyWithValue("invalidValue", - "[ErrorCode = ERROR_CODE, ErrorMessage = Error message]") - .hasFieldOrPropertyWithValue("httpStatus", HttpStatus.BAD_REQUEST); - } - - @Test - @DisplayName("결제 취소 중 500 에러가 발생한다.") - void cancelPaymentWithError() { - // given - mockServer.expect(requestTo("/v1/payments/5EnNZRJGvaBX7zk2yd8ydw26XvwXkLrx9POLqKQjmAw4b0e1/cancel")) - .andExpect(method(HttpMethod.POST)) - .andExpect(content().contentType(MediaType.APPLICATION_JSON)) - .andExpect(content().json(SampleTossPaymentConst.cancelRequestJson)) - .andRespond(withStatus(HttpStatus.INTERNAL_SERVER_ERROR) - .contentType(MediaType.APPLICATION_JSON) - .body(SampleTossPaymentConst.tossPaymentErrorJson)); - - // when & then - assertThatThrownBy(() -> tossPaymentClient.cancelPayment(SampleTossPaymentConst.cancelRequest)) - .isInstanceOf(RoomescapeException.class) - .hasFieldOrPropertyWithValue("errorType", ErrorType.PAYMENT_SERVER_ERROR) - .hasFieldOrPropertyWithValue("invalidValue", - "[ErrorCode = ERROR_CODE, ErrorMessage = Error message]") - .hasFieldOrPropertyWithValue("httpStatus", HttpStatus.INTERNAL_SERVER_ERROR); - } -} diff --git a/src/test/java/roomescape/payment/infrastructure/client/TossPaymentClientTest.kt b/src/test/java/roomescape/payment/infrastructure/client/TossPaymentClientTest.kt new file mode 100644 index 00000000..a36e409e --- /dev/null +++ b/src/test/java/roomescape/payment/infrastructure/client/TossPaymentClientTest.kt @@ -0,0 +1,139 @@ +package roomescape.payment.infrastructure.client + +import io.kotest.assertions.assertSoftly +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.shouldBe +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.autoconfigure.web.client.RestClientTest +import org.springframework.http.HttpMethod +import org.springframework.http.HttpStatus +import org.springframework.http.MediaType +import org.springframework.test.web.client.MockRestServiceServer +import org.springframework.test.web.client.ResponseActions +import org.springframework.test.web.client.match.MockRestRequestMatchers.* +import org.springframework.test.web.client.response.MockRestResponseCreators.withStatus +import org.springframework.test.web.client.response.MockRestResponseCreators.withSuccess +import roomescape.common.exception.ErrorType +import roomescape.common.exception.RoomescapeException +import roomescape.payment.SampleTossPaymentConst +import roomescape.payment.web.PaymentApprove +import roomescape.payment.web.PaymentCancel + +@RestClientTest(TossPaymentClient::class) +class TossPaymentClientTest( + @Autowired val client: TossPaymentClient, + @Autowired val mockServer: MockRestServiceServer +) : FunSpec() { + + init { + context("결제 승인 요청") { + fun commonAction(): ResponseActions = mockServer.expect { + requestTo("/v1/payments/confirm") + }.andExpect { + method(HttpMethod.POST) + }.andExpect { + content().contentType(MediaType.APPLICATION_JSON) + }.andExpect { + content().json(SampleTossPaymentConst.paymentRequestJson) + } + + test("성공 응답") { + commonAction().andRespond { + withSuccess() + .contentType(MediaType.APPLICATION_JSON) + .body(SampleTossPaymentConst.confirmJson) + .createResponse(it) + } + + // when + val paymentRequest = SampleTossPaymentConst.paymentRequest + val paymentResponse: PaymentApprove.Response = client.confirmPayment(paymentRequest) + + assertSoftly(paymentResponse) { + this.paymentKey shouldBe paymentRequest.paymentKey + this.orderId shouldBe paymentRequest.orderId + this.totalAmount shouldBe paymentRequest.amount + } + } + + test("400 에러 발생") { + commonAction().andRespond { + withStatus(HttpStatus.BAD_REQUEST) + .contentType(MediaType.APPLICATION_JSON) + .body(SampleTossPaymentConst.tossPaymentErrorJson) + .createResponse(it) + } + + // when + val paymentRequest = SampleTossPaymentConst.paymentRequest + + // then + val exception = shouldThrow { + client.confirmPayment(paymentRequest) + } + + assertSoftly(exception) { + this.errorType shouldBe ErrorType.PAYMENT_ERROR + this.invalidValue shouldBe "[ErrorCode = ERROR_CODE, ErrorMessage = Error message]" + this.httpStatus shouldBe HttpStatus.BAD_REQUEST + } + + } + } + + context("결제 취소 요청") { + fun commonAction(): ResponseActions = mockServer.expect { + requestTo("/v1/payments/${SampleTossPaymentConst.paymentKey}/cancel") + }.andExpect { + method(HttpMethod.POST) + }.andExpect { + content().contentType(MediaType.APPLICATION_JSON) + }.andExpect { + content().json(SampleTossPaymentConst.cancelRequestJson) + } + + test("성공 응답") { + commonAction().andRespond { + withSuccess() + .contentType(MediaType.APPLICATION_JSON) + .body(SampleTossPaymentConst.cancelJson) + .createResponse(it) + } + + // when + val cancelRequest: PaymentCancel.Request = SampleTossPaymentConst.cancelRequest + val cancelResponse: PaymentCancel.Response = client.cancelPayment(cancelRequest) + + assertSoftly(cancelResponse) { + this.cancelStatus shouldBe "DONE" + this.cancelReason shouldBe cancelRequest.cancelReason + this.cancelAmount shouldBe cancelRequest.amount + } + } + + test("500 에러 발생") { + commonAction().andRespond { + withStatus(HttpStatus.INTERNAL_SERVER_ERROR) + .contentType(MediaType.APPLICATION_JSON) + .body(SampleTossPaymentConst.tossPaymentErrorJson) + .createResponse(it) + } + + // when + val cancelRequest: PaymentCancel.Request = SampleTossPaymentConst.cancelRequest + + // then + val exception = shouldThrow { + client.cancelPayment(cancelRequest) + } + + assertSoftly(exception) { + this.errorType shouldBe ErrorType.PAYMENT_SERVER_ERROR + this.invalidValue shouldBe "[ErrorCode = ERROR_CODE, ErrorMessage = Error message]" + this.httpStatus shouldBe HttpStatus.INTERNAL_SERVER_ERROR + } + } + } + } +} -- 2.47.2 From 1f0ac0b550e7582eb4f6d3f7f0513a544ce3f732 Mon Sep 17 00:00:00 2001 From: pricelees Date: Wed, 16 Jul 2025 13:05:35 +0900 Subject: [PATCH 17/32] =?UTF-8?q?refactor:=20Payment=20->=20PaymentEntity?= =?UTF-8?q?=20=ED=81=B4=EB=9E=98=EC=8A=A4=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../payment/business/PaymentService.java | 19 ++++++++++--------- .../{Payment.java => PaymentEntity.java} | 6 +++--- .../persistence/PaymentRepository.java | 6 +++--- .../java/roomescape/payment/web/PaymentDTO.kt | 3 ++- ...aymentTest.java => PaymentEntityTest.java} | 12 ++++++------ .../controller/ReservationControllerTest.java | 8 ++++---- 6 files changed, 28 insertions(+), 26 deletions(-) rename src/main/java/roomescape/payment/infrastructure/persistence/{Payment.java => PaymentEntity.java} (94%) rename src/test/java/roomescape/payment/infrastructure/persistence/{PaymentTest.java => PaymentEntityTest.java} (80%) diff --git a/src/main/java/roomescape/payment/business/PaymentService.java b/src/main/java/roomescape/payment/business/PaymentService.java index 2d22dd43..61ac1baf 100644 --- a/src/main/java/roomescape/payment/business/PaymentService.java +++ b/src/main/java/roomescape/payment/business/PaymentService.java @@ -11,8 +11,9 @@ import roomescape.common.exception.ErrorType; import roomescape.common.exception.RoomescapeException; import roomescape.payment.infrastructure.persistence.CanceledPayment; import roomescape.payment.infrastructure.persistence.CanceledPaymentRepository; -import roomescape.payment.infrastructure.persistence.Payment; +import roomescape.payment.infrastructure.persistence.PaymentEntity; import roomescape.payment.infrastructure.persistence.PaymentRepository; +import roomescape.payment.web.PaymentApprove; import roomescape.payment.web.PaymentCancel; import roomescape.payment.web.ReservationPaymentResponse; import roomescape.reservation.domain.Reservation; @@ -29,16 +30,16 @@ public class PaymentService { this.canceledPaymentRepository = canceledPaymentRepository; } - public ReservationPaymentResponse savePayment(roomescape.payment.web.PaymentApprove.Response paymentResponse, + public ReservationPaymentResponse savePayment(PaymentApprove.Response paymentResponse, Reservation reservation) { - Payment payment = new Payment(paymentResponse.orderId, paymentResponse.paymentKey, + PaymentEntity paymentEntity = new PaymentEntity(paymentResponse.orderId, paymentResponse.paymentKey, paymentResponse.totalAmount, reservation, paymentResponse.approvedAt); - Payment saved = paymentRepository.save(payment); + PaymentEntity saved = paymentRepository.save(paymentEntity); return ReservationPaymentResponse.from(saved); } @Transactional(readOnly = true) - public Optional findPaymentByReservationId(Long reservationId) { + public Optional findPaymentByReservationId(Long reservationId) { return paymentRepository.findByReservationId(reservationId); } @@ -59,12 +60,12 @@ public class PaymentService { } private CanceledPayment cancelPayment(String paymentKey, String cancelReason, OffsetDateTime canceledAt) { - Payment payment = paymentRepository.findByPaymentKey(paymentKey) + PaymentEntity paymentEntity = paymentRepository.findByPaymentKey(paymentKey) .orElseThrow(() -> throwPaymentNotFoundByPaymentKey(paymentKey)); - paymentRepository.delete(payment); + paymentRepository.delete(paymentEntity); - return canceledPaymentRepository.save(new CanceledPayment(paymentKey, cancelReason, payment.getTotalAmount(), - payment.getApprovedAt(), canceledAt)); + return canceledPaymentRepository.save(new CanceledPayment(paymentKey, cancelReason, paymentEntity.getTotalAmount(), + paymentEntity.getApprovedAt(), canceledAt)); } public void updateCanceledTime(String paymentKey, OffsetDateTime canceledAt) { diff --git a/src/main/java/roomescape/payment/infrastructure/persistence/Payment.java b/src/main/java/roomescape/payment/infrastructure/persistence/PaymentEntity.java similarity index 94% rename from src/main/java/roomescape/payment/infrastructure/persistence/Payment.java rename to src/main/java/roomescape/payment/infrastructure/persistence/PaymentEntity.java index 889f290f..a8e9477d 100644 --- a/src/main/java/roomescape/payment/infrastructure/persistence/Payment.java +++ b/src/main/java/roomescape/payment/infrastructure/persistence/PaymentEntity.java @@ -17,7 +17,7 @@ import roomescape.common.exception.RoomescapeException; import roomescape.reservation.domain.Reservation; @Entity -public class Payment { +public class PaymentEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -39,10 +39,10 @@ public class Payment { @Column(nullable = false) private OffsetDateTime approvedAt; - protected Payment() { + protected PaymentEntity() { } - public Payment(String orderId, String paymentKey, Long totalAmount, Reservation reservation, + public PaymentEntity(String orderId, String paymentKey, Long totalAmount, Reservation reservation, OffsetDateTime approvedAt) { validate(orderId, paymentKey, totalAmount, reservation, approvedAt); this.orderId = orderId; diff --git a/src/main/java/roomescape/payment/infrastructure/persistence/PaymentRepository.java b/src/main/java/roomescape/payment/infrastructure/persistence/PaymentRepository.java index 136593b7..6cd4ea1f 100644 --- a/src/main/java/roomescape/payment/infrastructure/persistence/PaymentRepository.java +++ b/src/main/java/roomescape/payment/infrastructure/persistence/PaymentRepository.java @@ -4,9 +4,9 @@ import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; -public interface PaymentRepository extends JpaRepository { +public interface PaymentRepository extends JpaRepository { - Optional findByReservationId(Long reservationId); + Optional findByReservationId(Long reservationId); - Optional findByPaymentKey(String paymentKey); + Optional findByPaymentKey(String paymentKey); } diff --git a/src/main/java/roomescape/payment/web/PaymentDTO.kt b/src/main/java/roomescape/payment/web/PaymentDTO.kt index eec2c82f..276ac68f 100644 --- a/src/main/java/roomescape/payment/web/PaymentDTO.kt +++ b/src/main/java/roomescape/payment/web/PaymentDTO.kt @@ -3,6 +3,7 @@ package roomescape.payment.web import com.fasterxml.jackson.annotation.JsonIgnoreProperties import com.fasterxml.jackson.databind.annotation.JsonDeserialize import roomescape.payment.infrastructure.client.PaymentCancelResponseDeserializer +import roomescape.payment.infrastructure.persistence.PaymentEntity import roomescape.reservation.dto.response.ReservationResponse import java.time.OffsetDateTime @@ -55,7 +56,7 @@ data class ReservationPaymentResponse( ) { companion object { @JvmStatic - fun from(saved: roomescape.payment.infrastructure.persistence.Payment): ReservationPaymentResponse { + fun from(saved: PaymentEntity): ReservationPaymentResponse { return ReservationPaymentResponse( saved.id, saved.orderId, diff --git a/src/test/java/roomescape/payment/infrastructure/persistence/PaymentTest.java b/src/test/java/roomescape/payment/infrastructure/persistence/PaymentEntityTest.java similarity index 80% rename from src/test/java/roomescape/payment/infrastructure/persistence/PaymentTest.java rename to src/test/java/roomescape/payment/infrastructure/persistence/PaymentEntityTest.java index e43aac43..592784c1 100644 --- a/src/test/java/roomescape/payment/infrastructure/persistence/PaymentTest.java +++ b/src/test/java/roomescape/payment/infrastructure/persistence/PaymentEntityTest.java @@ -21,7 +21,7 @@ import roomescape.reservation.domain.ReservationStatus; import roomescape.reservation.domain.ReservationTime; import roomescape.theme.domain.Theme; -class PaymentTest { +class PaymentEntityTest { private Reservation reservation; @@ -39,7 +39,7 @@ class PaymentTest { @DisplayName("paymentKey가 null 또는 빈값이면 예외가 발생한다.") @NullAndEmptySource void invalidPaymentKey(String paymentKey) { - assertThatThrownBy(() -> new Payment("order-id", paymentKey, 10000L, reservation, OffsetDateTime.now())) + assertThatThrownBy(() -> new PaymentEntity("order-id", paymentKey, 10000L, reservation, OffsetDateTime.now())) .isInstanceOf(RoomescapeException.class); } @@ -47,7 +47,7 @@ class PaymentTest { @DisplayName("orderId가 null 또는 빈값이면 예외가 발생한다.") @NullAndEmptySource void invalidOrderId(String orderId) { - assertThatThrownBy(() -> new Payment(orderId, "payment-key", 10000L, reservation, OffsetDateTime.now())) + assertThatThrownBy(() -> new PaymentEntity(orderId, "payment-key", 10000L, reservation, OffsetDateTime.now())) .isInstanceOf(RoomescapeException.class); } @@ -56,7 +56,7 @@ class PaymentTest { @CsvSource(value = {"null", "-1"}, nullValues = {"null"}) void invalidOrderId(Long totalAmount) { assertThatThrownBy( - () -> new Payment("orderId", "payment-key", totalAmount, reservation, OffsetDateTime.now())) + () -> new PaymentEntity("orderId", "payment-key", totalAmount, reservation, OffsetDateTime.now())) .isInstanceOf(RoomescapeException.class); } @@ -64,7 +64,7 @@ class PaymentTest { @DisplayName("Reservation이 null이면 예외가 발생한다.") @NullSource void invalidReservation(Reservation reservation) { - assertThatThrownBy(() -> new Payment("orderId", "payment-key", 10000L, reservation, OffsetDateTime.now())) + assertThatThrownBy(() -> new PaymentEntity("orderId", "payment-key", 10000L, reservation, OffsetDateTime.now())) .isInstanceOf(RoomescapeException.class); } @@ -72,7 +72,7 @@ class PaymentTest { @DisplayName("승인 날짜가 null이면 예외가 발생한다.") @NullSource void invalidApprovedAt(OffsetDateTime approvedAt) { - assertThatThrownBy(() -> new Payment("orderId", "payment-key", 10000L, reservation, approvedAt)) + assertThatThrownBy(() -> new PaymentEntity("orderId", "payment-key", 10000L, reservation, approvedAt)) .isInstanceOf(RoomescapeException.class); } } diff --git a/src/test/java/roomescape/reservation/controller/ReservationControllerTest.java b/src/test/java/roomescape/reservation/controller/ReservationControllerTest.java index 9095b18f..e985b12b 100644 --- a/src/test/java/roomescape/reservation/controller/ReservationControllerTest.java +++ b/src/test/java/roomescape/reservation/controller/ReservationControllerTest.java @@ -39,7 +39,7 @@ import roomescape.member.infrastructure.persistence.Role; import roomescape.payment.infrastructure.client.TossPaymentClient; import roomescape.payment.infrastructure.persistence.CanceledPayment; import roomescape.payment.infrastructure.persistence.CanceledPaymentRepository; -import roomescape.payment.infrastructure.persistence.Payment; +import roomescape.payment.infrastructure.persistence.PaymentEntity; import roomescape.payment.infrastructure.persistence.PaymentRepository; import roomescape.payment.web.PaymentApprove; import roomescape.payment.web.PaymentCancel; @@ -398,12 +398,12 @@ public class ReservationControllerTest { Reservation saved = reservationRepository.save( new Reservation(date, time, theme, member, ReservationStatus.CONFIRMED)); - Payment savedPayment = paymentRepository.save( - new Payment("pk", "oi", 1000L, saved, OffsetDateTime.now().minusHours(1L))); + PaymentEntity savedPaymentEntity = paymentRepository.save( + new PaymentEntity("pk", "oi", 1000L, saved, OffsetDateTime.now().minusHours(1L))); // when when(paymentClient.cancelPayment(any(PaymentCancel.Request.class))) - .thenReturn(new PaymentCancel.Response("pk", "고객 요청", savedPayment.getTotalAmount(), + .thenReturn(new PaymentCancel.Response("pk", "고객 요청", savedPaymentEntity.getTotalAmount(), OffsetDateTime.now())); // then -- 2.47.2 From a693406f32bf506668b87238be6646c1b8aafb92 Mon Sep 17 00:00:00 2001 From: pricelees Date: Wed, 16 Jul 2025 13:06:27 +0900 Subject: [PATCH 18/32] =?UTF-8?q?refactor:=20CanceledPayment=20->=20Cancel?= =?UTF-8?q?edPaymentEntity=20=ED=81=B4=EB=9E=98=EC=8A=A4=EB=AA=85=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../roomescape/payment/business/PaymentService.java | 12 ++++++------ ...nceledPayment.java => CanceledPaymentEntity.java} | 6 +++--- .../persistence/CanceledPaymentRepository.java | 4 ++-- ...ymentTest.java => CanceledPaymentEntityTest.java} | 4 ++-- .../controller/ReservationControllerTest.java | 5 +++-- 5 files changed, 16 insertions(+), 15 deletions(-) rename src/main/java/roomescape/payment/infrastructure/persistence/{CanceledPayment.java => CanceledPaymentEntity.java} (88%) rename src/test/java/roomescape/payment/infrastructure/persistence/{CanceledPaymentTest.java => CanceledPaymentEntityTest.java} (79%) diff --git a/src/main/java/roomescape/payment/business/PaymentService.java b/src/main/java/roomescape/payment/business/PaymentService.java index 61ac1baf..ca7ca2b5 100644 --- a/src/main/java/roomescape/payment/business/PaymentService.java +++ b/src/main/java/roomescape/payment/business/PaymentService.java @@ -9,7 +9,7 @@ import org.springframework.transaction.annotation.Transactional; import roomescape.common.exception.ErrorType; import roomescape.common.exception.RoomescapeException; -import roomescape.payment.infrastructure.persistence.CanceledPayment; +import roomescape.payment.infrastructure.persistence.CanceledPaymentEntity; import roomescape.payment.infrastructure.persistence.CanceledPaymentRepository; import roomescape.payment.infrastructure.persistence.PaymentEntity; import roomescape.payment.infrastructure.persistence.PaymentRepository; @@ -44,7 +44,7 @@ public class PaymentService { } public void saveCanceledPayment(PaymentCancel.Response cancelInfo, OffsetDateTime approvedAt, String paymentKey) { - canceledPaymentRepository.save(new CanceledPayment( + canceledPaymentRepository.save(new CanceledPaymentEntity( paymentKey, cancelInfo.cancelReason, cancelInfo.cancelAmount, approvedAt, cancelInfo.canceledAt)); } @@ -54,22 +54,22 @@ public class PaymentService { String.format("[reservationId: %d]", reservationId), HttpStatus.NOT_FOUND)) .getPaymentKey(); // 취소 시간은 현재 시간으로 일단 생성한 뒤, 결제 취소 완료 후 해당 시간으로 변경합니다. - CanceledPayment canceled = cancelPayment(paymentKey, "고객 요청", OffsetDateTime.now()); + CanceledPaymentEntity canceled = cancelPayment(paymentKey, "고객 요청", OffsetDateTime.now()); return new PaymentCancel.Request(paymentKey, canceled.getCancelAmount(), canceled.getCancelReason()); } - private CanceledPayment cancelPayment(String paymentKey, String cancelReason, OffsetDateTime canceledAt) { + private CanceledPaymentEntity cancelPayment(String paymentKey, String cancelReason, OffsetDateTime canceledAt) { PaymentEntity paymentEntity = paymentRepository.findByPaymentKey(paymentKey) .orElseThrow(() -> throwPaymentNotFoundByPaymentKey(paymentKey)); paymentRepository.delete(paymentEntity); - return canceledPaymentRepository.save(new CanceledPayment(paymentKey, cancelReason, paymentEntity.getTotalAmount(), + return canceledPaymentRepository.save(new CanceledPaymentEntity(paymentKey, cancelReason, paymentEntity.getTotalAmount(), paymentEntity.getApprovedAt(), canceledAt)); } public void updateCanceledTime(String paymentKey, OffsetDateTime canceledAt) { - CanceledPayment canceledPayment = canceledPaymentRepository.findByPaymentKey(paymentKey) + CanceledPaymentEntity canceledPayment = canceledPaymentRepository.findByPaymentKey(paymentKey) .orElseThrow(() -> throwPaymentNotFoundByPaymentKey(paymentKey)); canceledPayment.setCanceledAt(canceledAt); } diff --git a/src/main/java/roomescape/payment/infrastructure/persistence/CanceledPayment.java b/src/main/java/roomescape/payment/infrastructure/persistence/CanceledPaymentEntity.java similarity index 88% rename from src/main/java/roomescape/payment/infrastructure/persistence/CanceledPayment.java rename to src/main/java/roomescape/payment/infrastructure/persistence/CanceledPaymentEntity.java index 5e252675..c014be72 100644 --- a/src/main/java/roomescape/payment/infrastructure/persistence/CanceledPayment.java +++ b/src/main/java/roomescape/payment/infrastructure/persistence/CanceledPaymentEntity.java @@ -12,7 +12,7 @@ import roomescape.common.exception.ErrorType; import roomescape.common.exception.RoomescapeException; @Entity -public class CanceledPayment { +public class CanceledPaymentEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @@ -24,10 +24,10 @@ public class CanceledPayment { private OffsetDateTime approvedAt; private OffsetDateTime canceledAt; - protected CanceledPayment() { + protected CanceledPaymentEntity() { } - public CanceledPayment(String paymentKey, String cancelReason, Long cancelAmount, OffsetDateTime approvedAt, + public CanceledPaymentEntity(String paymentKey, String cancelReason, Long cancelAmount, OffsetDateTime approvedAt, OffsetDateTime canceledAt) { validateDate(approvedAt, canceledAt); this.paymentKey = paymentKey; diff --git a/src/main/java/roomescape/payment/infrastructure/persistence/CanceledPaymentRepository.java b/src/main/java/roomescape/payment/infrastructure/persistence/CanceledPaymentRepository.java index 903a51c8..9a004706 100644 --- a/src/main/java/roomescape/payment/infrastructure/persistence/CanceledPaymentRepository.java +++ b/src/main/java/roomescape/payment/infrastructure/persistence/CanceledPaymentRepository.java @@ -4,7 +4,7 @@ import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; -public interface CanceledPaymentRepository extends JpaRepository { +public interface CanceledPaymentRepository extends JpaRepository { - Optional findByPaymentKey(String paymentKey); + Optional findByPaymentKey(String paymentKey); } diff --git a/src/test/java/roomescape/payment/infrastructure/persistence/CanceledPaymentTest.java b/src/test/java/roomescape/payment/infrastructure/persistence/CanceledPaymentEntityTest.java similarity index 79% rename from src/test/java/roomescape/payment/infrastructure/persistence/CanceledPaymentTest.java rename to src/test/java/roomescape/payment/infrastructure/persistence/CanceledPaymentEntityTest.java index 6a9b0e8e..b2404991 100644 --- a/src/test/java/roomescape/payment/infrastructure/persistence/CanceledPaymentTest.java +++ b/src/test/java/roomescape/payment/infrastructure/persistence/CanceledPaymentEntityTest.java @@ -9,14 +9,14 @@ import org.junit.jupiter.api.Test; import roomescape.common.exception.RoomescapeException; -class CanceledPaymentTest { +class CanceledPaymentEntityTest { @Test @DisplayName("취소 날짜가 승인 날짜 이전이면 예외가 발생한다") void invalidDate() { OffsetDateTime approvedAt = OffsetDateTime.now(); OffsetDateTime canceledAt = approvedAt.minusMinutes(1L); - assertThatThrownBy(() -> new CanceledPayment("payment-key", "reason", 10000L, approvedAt, canceledAt)) + assertThatThrownBy(() -> new CanceledPaymentEntity("payment-key", "reason", 10000L, approvedAt, canceledAt)) .isInstanceOf(RoomescapeException.class); } } \ No newline at end of file diff --git a/src/test/java/roomescape/reservation/controller/ReservationControllerTest.java b/src/test/java/roomescape/reservation/controller/ReservationControllerTest.java index e985b12b..1b138f2a 100644 --- a/src/test/java/roomescape/reservation/controller/ReservationControllerTest.java +++ b/src/test/java/roomescape/reservation/controller/ReservationControllerTest.java @@ -37,7 +37,7 @@ import roomescape.member.infrastructure.persistence.Member; import roomescape.member.infrastructure.persistence.MemberRepository; import roomescape.member.infrastructure.persistence.Role; import roomescape.payment.infrastructure.client.TossPaymentClient; -import roomescape.payment.infrastructure.persistence.CanceledPayment; +import roomescape.payment.infrastructure.persistence.CanceledPaymentEntity; import roomescape.payment.infrastructure.persistence.CanceledPaymentRepository; import roomescape.payment.infrastructure.persistence.PaymentEntity; import roomescape.payment.infrastructure.persistence.PaymentRepository; @@ -447,7 +447,8 @@ public class ReservationControllerTest { .statusCode(400); // then - Optional canceledPaymentOptional = canceledPaymentRepository.findByPaymentKey(paymentKey); + Optional canceledPaymentOptional = canceledPaymentRepository.findByPaymentKey( + paymentKey); assertThat(canceledPaymentOptional).isNotNull(); assertThat(canceledPaymentOptional.get().getCanceledAt()).isEqualTo(canceledAt); assertThat(canceledPaymentOptional.get().getCancelReason()).isEqualTo("고객 요청"); -- 2.47.2 From 0791e953c776eebc80ae8acfa19de47eebe4480c Mon Sep 17 00:00:00 2001 From: pricelees Date: Wed, 16 Jul 2025 13:33:40 +0900 Subject: [PATCH 19/32] Rename .java to .kt --- .../persistence/{PaymentEntity.java => PaymentEntity.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/main/java/roomescape/payment/infrastructure/persistence/{PaymentEntity.java => PaymentEntity.kt} (100%) diff --git a/src/main/java/roomescape/payment/infrastructure/persistence/PaymentEntity.java b/src/main/java/roomescape/payment/infrastructure/persistence/PaymentEntity.kt similarity index 100% rename from src/main/java/roomescape/payment/infrastructure/persistence/PaymentEntity.java rename to src/main/java/roomescape/payment/infrastructure/persistence/PaymentEntity.kt -- 2.47.2 From 2ee192009970feb31579191f0cbbc30228834c2b Mon Sep 17 00:00:00 2001 From: pricelees Date: Wed, 16 Jul 2025 13:33:41 +0900 Subject: [PATCH 20/32] =?UTF-8?q?refactor:=20PaymentEntity=20=EC=BD=94?= =?UTF-8?q?=ED=8B=80=EB=A6=B0=20=EC=A0=84=ED=99=98=20=EB=B0=8F=20=EB=B6=88?= =?UTF-8?q?=ED=95=84=EC=9A=94=ED=95=9C=20null-check=20validation=20?= =?UTF-8?q?=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../payment/business/PaymentService.java | 2 +- .../persistence/PaymentEntity.kt | 121 +++--------------- .../java/roomescape/payment/web/PaymentDTO.kt | 2 +- .../repository/ReservationRepository.java | 2 +- .../persistence/PaymentEntityTest.java | 78 ----------- .../controller/ReservationControllerTest.java | 2 +- 6 files changed, 25 insertions(+), 182 deletions(-) delete mode 100644 src/test/java/roomescape/payment/infrastructure/persistence/PaymentEntityTest.java diff --git a/src/main/java/roomescape/payment/business/PaymentService.java b/src/main/java/roomescape/payment/business/PaymentService.java index ca7ca2b5..40ac6086 100644 --- a/src/main/java/roomescape/payment/business/PaymentService.java +++ b/src/main/java/roomescape/payment/business/PaymentService.java @@ -32,7 +32,7 @@ public class PaymentService { public ReservationPaymentResponse savePayment(PaymentApprove.Response paymentResponse, Reservation reservation) { - PaymentEntity paymentEntity = new PaymentEntity(paymentResponse.orderId, paymentResponse.paymentKey, + PaymentEntity paymentEntity = new PaymentEntity(null, paymentResponse.orderId, paymentResponse.paymentKey, paymentResponse.totalAmount, reservation, paymentResponse.approvedAt); PaymentEntity saved = paymentRepository.save(paymentEntity); return ReservationPaymentResponse.from(saved); diff --git a/src/main/java/roomescape/payment/infrastructure/persistence/PaymentEntity.kt b/src/main/java/roomescape/payment/infrastructure/persistence/PaymentEntity.kt index a8e9477d..8de5d4da 100644 --- a/src/main/java/roomescape/payment/infrastructure/persistence/PaymentEntity.kt +++ b/src/main/java/roomescape/payment/infrastructure/persistence/PaymentEntity.kt @@ -1,108 +1,29 @@ -package roomescape.payment.infrastructure.persistence; +package roomescape.payment.infrastructure.persistence -import java.time.OffsetDateTime; - -import org.springframework.http.HttpStatus; - -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.FetchType; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.OneToOne; -import roomescape.common.exception.ErrorType; -import roomescape.common.exception.RoomescapeException; -import roomescape.reservation.domain.Reservation; +import jakarta.persistence.* +import roomescape.reservation.domain.Reservation +import java.time.OffsetDateTime @Entity -public class PaymentEntity { +@Table(name = "payment") +class PaymentEntity( + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + var id: Long? = null, - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; + @Column(nullable = false) + var orderId: String, - @Column(nullable = false) - private String orderId; + @Column(nullable = false) + var paymentKey: String, - @Column(nullable = false) - private String paymentKey; + @Column(nullable = false) + var totalAmount: Long, - @Column(nullable = false) - private Long totalAmount; + @OneToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "reservation_id", nullable = false) + var reservation: Reservation, - @OneToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "reservation_id", nullable = false) - private Reservation reservation; - - @Column(nullable = false) - private OffsetDateTime approvedAt; - - protected PaymentEntity() { - } - - public PaymentEntity(String orderId, String paymentKey, Long totalAmount, Reservation reservation, - OffsetDateTime approvedAt) { - validate(orderId, paymentKey, totalAmount, reservation, approvedAt); - this.orderId = orderId; - this.paymentKey = paymentKey; - this.totalAmount = totalAmount; - this.reservation = reservation; - this.approvedAt = approvedAt; - } - - private void validate(String orderId, String paymentKey, Long totalAmount, Reservation reservation, - OffsetDateTime approvedAt) { - validateIsNullOrBlank(orderId, "orderId"); - validateIsNullOrBlank(paymentKey, "paymentKey"); - validateIsInvalidAmount(totalAmount); - validateIsNull(reservation, "reservation"); - validateIsNull(approvedAt, "approvedAt"); - } - - private void validateIsNullOrBlank(String input, String fieldName) { - if (input == null || input.isBlank()) { - throw new RoomescapeException(ErrorType.REQUEST_DATA_BLANK, String.format("[value : %s]", fieldName), - HttpStatus.BAD_REQUEST); - } - } - - private void validateIsInvalidAmount(Long totalAmount) { - if (totalAmount == null || totalAmount < 0) { - throw new RoomescapeException(ErrorType.INVALID_REQUEST_DATA, - String.format("[totalAmount : %d]", totalAmount), HttpStatus.BAD_REQUEST); - } - } - - private void validateIsNull(T value, String fieldName) { - if (value == null) { - throw new RoomescapeException(ErrorType.REQUEST_DATA_BLANK, String.format("[value : %s]", fieldName), - HttpStatus.BAD_REQUEST); - } - } - - public Long getId() { - return id; - } - - public String getOrderId() { - return orderId; - } - - public String getPaymentKey() { - return paymentKey; - } - - public Long getTotalAmount() { - return totalAmount; - } - - public Reservation getReservation() { - return reservation; - } - - public OffsetDateTime getApprovedAt() { - return approvedAt; - } -} + @Column(nullable = false) + var approvedAt: OffsetDateTime +) \ No newline at end of file diff --git a/src/main/java/roomescape/payment/web/PaymentDTO.kt b/src/main/java/roomescape/payment/web/PaymentDTO.kt index 276ac68f..2d97f96a 100644 --- a/src/main/java/roomescape/payment/web/PaymentDTO.kt +++ b/src/main/java/roomescape/payment/web/PaymentDTO.kt @@ -58,7 +58,7 @@ data class ReservationPaymentResponse( @JvmStatic fun from(saved: PaymentEntity): ReservationPaymentResponse { return ReservationPaymentResponse( - saved.id, + saved.id!!, saved.orderId, saved.paymentKey, saved.totalAmount, diff --git a/src/main/java/roomescape/reservation/domain/repository/ReservationRepository.java b/src/main/java/roomescape/reservation/domain/repository/ReservationRepository.java index e89d7279..af83b3dc 100644 --- a/src/main/java/roomescape/reservation/domain/repository/ReservationRepository.java +++ b/src/main/java/roomescape/reservation/domain/repository/ReservationRepository.java @@ -54,7 +54,7 @@ public interface ReservationRepository extends JpaRepository, ) FROM Reservation r JOIN r.theme t - LEFT JOIN Payment p + LEFT JOIN PaymentEntity p ON p.reservation = r WHERE r.member.id = :memberId """) diff --git a/src/test/java/roomescape/payment/infrastructure/persistence/PaymentEntityTest.java b/src/test/java/roomescape/payment/infrastructure/persistence/PaymentEntityTest.java deleted file mode 100644 index 592784c1..00000000 --- a/src/test/java/roomescape/payment/infrastructure/persistence/PaymentEntityTest.java +++ /dev/null @@ -1,78 +0,0 @@ -package roomescape.payment.infrastructure.persistence; - -import static org.assertj.core.api.Assertions.*; - -import java.time.LocalDate; -import java.time.LocalTime; -import java.time.OffsetDateTime; - -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.CsvSource; -import org.junit.jupiter.params.provider.NullAndEmptySource; -import org.junit.jupiter.params.provider.NullSource; - -import roomescape.common.exception.RoomescapeException; -import roomescape.member.infrastructure.persistence.Member; -import roomescape.member.infrastructure.persistence.Role; -import roomescape.reservation.domain.Reservation; -import roomescape.reservation.domain.ReservationStatus; -import roomescape.reservation.domain.ReservationTime; -import roomescape.theme.domain.Theme; - -class PaymentEntityTest { - - private Reservation reservation; - - @BeforeEach - void setUp() { - LocalDate now = LocalDate.now(); - ReservationTime reservationTime = new ReservationTime(LocalTime.now()); - Theme theme = new Theme("name", "desc", "thumb"); - Member member = new Member(null, "name", "email", "password", Role.MEMBER); - - reservation = new Reservation(now, reservationTime, theme, member, ReservationStatus.CONFIRMED); - } - - @ParameterizedTest - @DisplayName("paymentKey가 null 또는 빈값이면 예외가 발생한다.") - @NullAndEmptySource - void invalidPaymentKey(String paymentKey) { - assertThatThrownBy(() -> new PaymentEntity("order-id", paymentKey, 10000L, reservation, OffsetDateTime.now())) - .isInstanceOf(RoomescapeException.class); - } - - @ParameterizedTest - @DisplayName("orderId가 null 또는 빈값이면 예외가 발생한다.") - @NullAndEmptySource - void invalidOrderId(String orderId) { - assertThatThrownBy(() -> new PaymentEntity(orderId, "payment-key", 10000L, reservation, OffsetDateTime.now())) - .isInstanceOf(RoomescapeException.class); - } - - @ParameterizedTest - @DisplayName("amount가 null 또는 0 이하면 예외가 발생한다.") - @CsvSource(value = {"null", "-1"}, nullValues = {"null"}) - void invalidOrderId(Long totalAmount) { - assertThatThrownBy( - () -> new PaymentEntity("orderId", "payment-key", totalAmount, reservation, OffsetDateTime.now())) - .isInstanceOf(RoomescapeException.class); - } - - @ParameterizedTest - @DisplayName("Reservation이 null이면 예외가 발생한다.") - @NullSource - void invalidReservation(Reservation reservation) { - assertThatThrownBy(() -> new PaymentEntity("orderId", "payment-key", 10000L, reservation, OffsetDateTime.now())) - .isInstanceOf(RoomescapeException.class); - } - - @ParameterizedTest - @DisplayName("승인 날짜가 null이면 예외가 발생한다.") - @NullSource - void invalidApprovedAt(OffsetDateTime approvedAt) { - assertThatThrownBy(() -> new PaymentEntity("orderId", "payment-key", 10000L, reservation, approvedAt)) - .isInstanceOf(RoomescapeException.class); - } -} diff --git a/src/test/java/roomescape/reservation/controller/ReservationControllerTest.java b/src/test/java/roomescape/reservation/controller/ReservationControllerTest.java index 1b138f2a..62577810 100644 --- a/src/test/java/roomescape/reservation/controller/ReservationControllerTest.java +++ b/src/test/java/roomescape/reservation/controller/ReservationControllerTest.java @@ -399,7 +399,7 @@ public class ReservationControllerTest { Reservation saved = reservationRepository.save( new Reservation(date, time, theme, member, ReservationStatus.CONFIRMED)); PaymentEntity savedPaymentEntity = paymentRepository.save( - new PaymentEntity("pk", "oi", 1000L, saved, OffsetDateTime.now().minusHours(1L))); + new PaymentEntity(null, "pk", "oi", 1000L, saved, OffsetDateTime.now().minusHours(1L))); // when when(paymentClient.cancelPayment(any(PaymentCancel.Request.class))) -- 2.47.2 From a92339f7d467caaae9533199de404e02d95880b6 Mon Sep 17 00:00:00 2001 From: pricelees Date: Wed, 16 Jul 2025 13:37:24 +0900 Subject: [PATCH 21/32] Rename .java to .kt --- .../{CanceledPaymentEntity.java => CanceledPaymentEntity.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/main/java/roomescape/payment/infrastructure/persistence/{CanceledPaymentEntity.java => CanceledPaymentEntity.kt} (100%) diff --git a/src/main/java/roomescape/payment/infrastructure/persistence/CanceledPaymentEntity.java b/src/main/java/roomescape/payment/infrastructure/persistence/CanceledPaymentEntity.kt similarity index 100% rename from src/main/java/roomescape/payment/infrastructure/persistence/CanceledPaymentEntity.java rename to src/main/java/roomescape/payment/infrastructure/persistence/CanceledPaymentEntity.kt -- 2.47.2 From 2c61d9ff2ba80f70ff8757959e320373108c3de3 Mon Sep 17 00:00:00 2001 From: pricelees Date: Wed, 16 Jul 2025 13:37:25 +0900 Subject: [PATCH 22/32] =?UTF-8?q?refactor:=20CanceledPaymentEntity=20?= =?UTF-8?q?=EC=BD=94=ED=8B=80=EB=A6=B0=20=EC=A0=84=ED=99=98=20=EB=B0=8F=20?= =?UTF-8?q?validation=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../payment/business/PaymentService.java | 4 +- .../persistence/CanceledPaymentEntity.kt | 77 ++++--------------- .../CanceledPaymentEntityTest.java | 22 ------ 3 files changed, 16 insertions(+), 87 deletions(-) delete mode 100644 src/test/java/roomescape/payment/infrastructure/persistence/CanceledPaymentEntityTest.java diff --git a/src/main/java/roomescape/payment/business/PaymentService.java b/src/main/java/roomescape/payment/business/PaymentService.java index 40ac6086..27dde99f 100644 --- a/src/main/java/roomescape/payment/business/PaymentService.java +++ b/src/main/java/roomescape/payment/business/PaymentService.java @@ -44,7 +44,7 @@ public class PaymentService { } public void saveCanceledPayment(PaymentCancel.Response cancelInfo, OffsetDateTime approvedAt, String paymentKey) { - canceledPaymentRepository.save(new CanceledPaymentEntity( + canceledPaymentRepository.save(new CanceledPaymentEntity(null, paymentKey, cancelInfo.cancelReason, cancelInfo.cancelAmount, approvedAt, cancelInfo.canceledAt)); } @@ -64,7 +64,7 @@ public class PaymentService { .orElseThrow(() -> throwPaymentNotFoundByPaymentKey(paymentKey)); paymentRepository.delete(paymentEntity); - return canceledPaymentRepository.save(new CanceledPaymentEntity(paymentKey, cancelReason, paymentEntity.getTotalAmount(), + return canceledPaymentRepository.save(new CanceledPaymentEntity(null, paymentKey, cancelReason, paymentEntity.getTotalAmount(), paymentEntity.getApprovedAt(), canceledAt)); } diff --git a/src/main/java/roomescape/payment/infrastructure/persistence/CanceledPaymentEntity.kt b/src/main/java/roomescape/payment/infrastructure/persistence/CanceledPaymentEntity.kt index c014be72..fb19ca0a 100644 --- a/src/main/java/roomescape/payment/infrastructure/persistence/CanceledPaymentEntity.kt +++ b/src/main/java/roomescape/payment/infrastructure/persistence/CanceledPaymentEntity.kt @@ -1,67 +1,18 @@ -package roomescape.payment.infrastructure.persistence; +package roomescape.payment.infrastructure.persistence -import java.time.OffsetDateTime; - -import org.springframework.http.HttpStatus; - -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import roomescape.common.exception.ErrorType; -import roomescape.common.exception.RoomescapeException; +import jakarta.persistence.* +import java.time.OffsetDateTime @Entity -public class CanceledPaymentEntity { +@Table(name = "canceled_payment") +class CanceledPaymentEntity( + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + var id: Long? = null, - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - private String paymentKey; - private String cancelReason; - private Long cancelAmount; - private OffsetDateTime approvedAt; - private OffsetDateTime canceledAt; - - protected CanceledPaymentEntity() { - } - - public CanceledPaymentEntity(String paymentKey, String cancelReason, Long cancelAmount, OffsetDateTime approvedAt, - OffsetDateTime canceledAt) { - validateDate(approvedAt, canceledAt); - this.paymentKey = paymentKey; - this.cancelReason = cancelReason; - this.cancelAmount = cancelAmount; - this.approvedAt = approvedAt; - this.canceledAt = canceledAt; - } - - private void validateDate(OffsetDateTime approvedAt, OffsetDateTime canceledAt) { - if (canceledAt.isBefore(approvedAt)) { - throw new RoomescapeException(ErrorType.CANCELED_BEFORE_PAYMENT, - String.format("[approvedAt: %s, canceledAt: %s]", approvedAt, canceledAt), - HttpStatus.CONFLICT); - } - } - - public String getCancelReason() { - return cancelReason; - } - - public Long getCancelAmount() { - return cancelAmount; - } - - public OffsetDateTime getApprovedAt() { - return approvedAt; - } - - public OffsetDateTime getCanceledAt() { - return canceledAt; - } - - public void setCanceledAt(OffsetDateTime canceledAt) { - this.canceledAt = canceledAt; - } -} + var paymentKey: String, + var cancelReason: String, + var cancelAmount: Long, + var approvedAt: OffsetDateTime, + var canceledAt: OffsetDateTime, +) diff --git a/src/test/java/roomescape/payment/infrastructure/persistence/CanceledPaymentEntityTest.java b/src/test/java/roomescape/payment/infrastructure/persistence/CanceledPaymentEntityTest.java deleted file mode 100644 index b2404991..00000000 --- a/src/test/java/roomescape/payment/infrastructure/persistence/CanceledPaymentEntityTest.java +++ /dev/null @@ -1,22 +0,0 @@ -package roomescape.payment.infrastructure.persistence; - -import static org.assertj.core.api.Assertions.*; - -import java.time.OffsetDateTime; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import roomescape.common.exception.RoomescapeException; - -class CanceledPaymentEntityTest { - - @Test - @DisplayName("취소 날짜가 승인 날짜 이전이면 예외가 발생한다") - void invalidDate() { - OffsetDateTime approvedAt = OffsetDateTime.now(); - OffsetDateTime canceledAt = approvedAt.minusMinutes(1L); - assertThatThrownBy(() -> new CanceledPaymentEntity("payment-key", "reason", 10000L, approvedAt, canceledAt)) - .isInstanceOf(RoomescapeException.class); - } -} \ No newline at end of file -- 2.47.2 From 54c0eab4b1c7b0a9498c7f8c454037cfe157a584 Mon Sep 17 00:00:00 2001 From: pricelees Date: Wed, 16 Jul 2025 13:38:17 +0900 Subject: [PATCH 23/32] Rename .java to .kt --- ...anceledPaymentRepository.java => CanceledPaymentRepository.kt} | 0 .../persistence/{PaymentRepository.java => PaymentRepository.kt} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename src/main/java/roomescape/payment/infrastructure/persistence/{CanceledPaymentRepository.java => CanceledPaymentRepository.kt} (100%) rename src/main/java/roomescape/payment/infrastructure/persistence/{PaymentRepository.java => PaymentRepository.kt} (100%) diff --git a/src/main/java/roomescape/payment/infrastructure/persistence/CanceledPaymentRepository.java b/src/main/java/roomescape/payment/infrastructure/persistence/CanceledPaymentRepository.kt similarity index 100% rename from src/main/java/roomescape/payment/infrastructure/persistence/CanceledPaymentRepository.java rename to src/main/java/roomescape/payment/infrastructure/persistence/CanceledPaymentRepository.kt diff --git a/src/main/java/roomescape/payment/infrastructure/persistence/PaymentRepository.java b/src/main/java/roomescape/payment/infrastructure/persistence/PaymentRepository.kt similarity index 100% rename from src/main/java/roomescape/payment/infrastructure/persistence/PaymentRepository.java rename to src/main/java/roomescape/payment/infrastructure/persistence/PaymentRepository.kt -- 2.47.2 From 698b78919fd6bddfc8403f6b15dffbf79fd912ad Mon Sep 17 00:00:00 2001 From: pricelees Date: Wed, 16 Jul 2025 13:38:18 +0900 Subject: [PATCH 24/32] =?UTF-8?q?refactor:=20payment=20=ED=8C=A8=ED=82=A4?= =?UTF-8?q?=EC=A7=80=20=EB=82=B4=20Repository=20=ED=81=B4=EB=9E=98?= =?UTF-8?q?=EC=8A=A4=20=EC=BD=94=ED=8B=80=EB=A6=B0=20=EC=A0=84=ED=99=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../persistence/CanceledPaymentRepository.kt | 12 +++++------- .../persistence/PaymentRepository.kt | 14 ++++++-------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/src/main/java/roomescape/payment/infrastructure/persistence/CanceledPaymentRepository.kt b/src/main/java/roomescape/payment/infrastructure/persistence/CanceledPaymentRepository.kt index 9a004706..cb5d2e53 100644 --- a/src/main/java/roomescape/payment/infrastructure/persistence/CanceledPaymentRepository.kt +++ b/src/main/java/roomescape/payment/infrastructure/persistence/CanceledPaymentRepository.kt @@ -1,10 +1,8 @@ -package roomescape.payment.infrastructure.persistence; +package roomescape.payment.infrastructure.persistence -import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository +import java.util.* -import org.springframework.data.jpa.repository.JpaRepository; - -public interface CanceledPaymentRepository extends JpaRepository { - - Optional findByPaymentKey(String paymentKey); +interface CanceledPaymentRepository : JpaRepository { + fun findByPaymentKey(paymentKey: String): Optional } diff --git a/src/main/java/roomescape/payment/infrastructure/persistence/PaymentRepository.kt b/src/main/java/roomescape/payment/infrastructure/persistence/PaymentRepository.kt index 6cd4ea1f..c0ccd469 100644 --- a/src/main/java/roomescape/payment/infrastructure/persistence/PaymentRepository.kt +++ b/src/main/java/roomescape/payment/infrastructure/persistence/PaymentRepository.kt @@ -1,12 +1,10 @@ -package roomescape.payment.infrastructure.persistence; +package roomescape.payment.infrastructure.persistence -import java.util.Optional; +import org.springframework.data.jpa.repository.JpaRepository +import java.util.* -import org.springframework.data.jpa.repository.JpaRepository; +interface PaymentRepository : JpaRepository { + fun findByReservationId(reservationId: Long): Optional -public interface PaymentRepository extends JpaRepository { - - Optional findByReservationId(Long reservationId); - - Optional findByPaymentKey(String paymentKey); + fun findByPaymentKey(paymentKey: String): Optional } -- 2.47.2 From 55d4460f315a4d48b9c5478471f2b96d19ad463b Mon Sep 17 00:00:00 2001 From: pricelees Date: Wed, 16 Jul 2025 14:14:05 +0900 Subject: [PATCH 25/32] Rename .java to .kt --- .../payment/business/{PaymentService.java => PaymentService.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/main/java/roomescape/payment/business/{PaymentService.java => PaymentService.kt} (100%) diff --git a/src/main/java/roomescape/payment/business/PaymentService.java b/src/main/java/roomescape/payment/business/PaymentService.kt similarity index 100% rename from src/main/java/roomescape/payment/business/PaymentService.java rename to src/main/java/roomescape/payment/business/PaymentService.kt -- 2.47.2 From 3ca58b2daef205316fc758899f5201de1083f324 Mon Sep 17 00:00:00 2001 From: pricelees Date: Wed, 16 Jul 2025 14:14:05 +0900 Subject: [PATCH 26/32] =?UTF-8?q?refactor:=20PaymentService=20=EC=BD=94?= =?UTF-8?q?=ED=8B=80=EB=A6=B0=20=EC=A0=84=ED=99=98=20=EB=B0=8F=20=EC=A0=80?= =?UTF-8?q?=EC=9E=A5=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../payment/business/PaymentService.kt | 140 +++++++++--------- .../java/roomescape/payment/web/PaymentDTO.kt | 25 ++-- 2 files changed, 81 insertions(+), 84 deletions(-) diff --git a/src/main/java/roomescape/payment/business/PaymentService.kt b/src/main/java/roomescape/payment/business/PaymentService.kt index 27dde99f..fdf283ae 100644 --- a/src/main/java/roomescape/payment/business/PaymentService.kt +++ b/src/main/java/roomescape/payment/business/PaymentService.kt @@ -1,82 +1,84 @@ -package roomescape.payment.business; +package roomescape.payment.business -import java.time.OffsetDateTime; -import java.util.Optional; - -import org.springframework.http.HttpStatus; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import roomescape.common.exception.ErrorType; -import roomescape.common.exception.RoomescapeException; -import roomescape.payment.infrastructure.persistence.CanceledPaymentEntity; -import roomescape.payment.infrastructure.persistence.CanceledPaymentRepository; -import roomescape.payment.infrastructure.persistence.PaymentEntity; -import roomescape.payment.infrastructure.persistence.PaymentRepository; -import roomescape.payment.web.PaymentApprove; -import roomescape.payment.web.PaymentCancel; -import roomescape.payment.web.ReservationPaymentResponse; -import roomescape.reservation.domain.Reservation; +import org.springframework.http.HttpStatus +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional +import roomescape.common.exception.ErrorType +import roomescape.common.exception.RoomescapeException +import roomescape.payment.infrastructure.persistence.CanceledPaymentEntity +import roomescape.payment.infrastructure.persistence.CanceledPaymentRepository +import roomescape.payment.infrastructure.persistence.PaymentEntity +import roomescape.payment.infrastructure.persistence.PaymentRepository +import roomescape.payment.web.PaymentApprove +import roomescape.payment.web.PaymentCancel +import roomescape.payment.web.ReservationPaymentResponse +import roomescape.payment.web.toReservationPaymentResponse +import roomescape.reservation.domain.Reservation +import java.time.OffsetDateTime +import java.util.* +import java.util.function.Supplier @Service -@Transactional -public class PaymentService { +class PaymentService( + private val paymentRepository: PaymentRepository, + private val canceledPaymentRepository: CanceledPaymentRepository +) { - private final PaymentRepository paymentRepository; - private final CanceledPaymentRepository canceledPaymentRepository; + @Transactional + fun savePayment( + paymentResponse: PaymentApprove.Response, + reservation: Reservation + ): ReservationPaymentResponse = PaymentEntity( + orderId = paymentResponse.orderId, + paymentKey = paymentResponse.paymentKey, + totalAmount = paymentResponse.totalAmount, + reservation = reservation, + approvedAt = paymentResponse.approvedAt + ).also { + paymentRepository.save(it) + }.toReservationPaymentResponse() - public PaymentService(PaymentRepository paymentRepository, CanceledPaymentRepository canceledPaymentRepository) { - this.paymentRepository = paymentRepository; - this.canceledPaymentRepository = canceledPaymentRepository; - } + @Transactional(readOnly = true) + fun findPaymentByReservationId(reservationId: Long): Optional { + return paymentRepository.findByReservationId(reservationId) + } - public ReservationPaymentResponse savePayment(PaymentApprove.Response paymentResponse, - Reservation reservation) { - PaymentEntity paymentEntity = new PaymentEntity(null, paymentResponse.orderId, paymentResponse.paymentKey, - paymentResponse.totalAmount, reservation, paymentResponse.approvedAt); - PaymentEntity saved = paymentRepository.save(paymentEntity); - return ReservationPaymentResponse.from(saved); - } + fun saveCanceledPayment(cancelInfo: PaymentCancel.Response, approvedAt: OffsetDateTime, paymentKey: String) { + canceledPaymentRepository.save(CanceledPaymentEntity(null, + paymentKey, cancelInfo.cancelReason, cancelInfo.cancelAmount, approvedAt, cancelInfo.canceledAt)) + } - @Transactional(readOnly = true) - public Optional findPaymentByReservationId(Long reservationId) { - return paymentRepository.findByReservationId(reservationId); - } + fun cancelPaymentByAdmin(reservationId: Long): PaymentCancel.Request { + val paymentKey = findPaymentByReservationId(reservationId) + .orElseThrow(java.util.function.Supplier { + RoomescapeException(roomescape.common.exception.ErrorType.PAYMENT_NOT_POUND, + kotlin.String.format("[reservationId: %d]", reservationId), org.springframework.http.HttpStatus.NOT_FOUND) + })!! + .paymentKey + // 취소 시간은 현재 시간으로 일단 생성한 뒤, 결제 취소 완료 후 해당 시간으로 변경합니다. + val canceled = cancelPayment(paymentKey, "고객 요청", OffsetDateTime.now()) - public void saveCanceledPayment(PaymentCancel.Response cancelInfo, OffsetDateTime approvedAt, String paymentKey) { - canceledPaymentRepository.save(new CanceledPaymentEntity(null, - paymentKey, cancelInfo.cancelReason, cancelInfo.cancelAmount, approvedAt, cancelInfo.canceledAt)); - } + return PaymentCancel.Request(paymentKey, canceled.cancelAmount, canceled.cancelReason) + } - public PaymentCancel.Request cancelPaymentByAdmin(Long reservationId) { - String paymentKey = findPaymentByReservationId(reservationId) - .orElseThrow(() -> new RoomescapeException(ErrorType.PAYMENT_NOT_POUND, - String.format("[reservationId: %d]", reservationId), HttpStatus.NOT_FOUND)) - .getPaymentKey(); - // 취소 시간은 현재 시간으로 일단 생성한 뒤, 결제 취소 완료 후 해당 시간으로 변경합니다. - CanceledPaymentEntity canceled = cancelPayment(paymentKey, "고객 요청", OffsetDateTime.now()); + private fun cancelPayment(paymentKey: String, cancelReason: String, canceledAt: OffsetDateTime): CanceledPaymentEntity { + val paymentEntity = paymentRepository.findByPaymentKey(paymentKey) + .orElseThrow(Supplier { throwPaymentNotFoundByPaymentKey(paymentKey) }) + paymentRepository.delete(paymentEntity) - return new PaymentCancel.Request(paymentKey, canceled.getCancelAmount(), canceled.getCancelReason()); - } + return canceledPaymentRepository.save(CanceledPaymentEntity(null, paymentKey, cancelReason, paymentEntity.totalAmount, + paymentEntity.approvedAt, canceledAt)) + } - private CanceledPaymentEntity cancelPayment(String paymentKey, String cancelReason, OffsetDateTime canceledAt) { - PaymentEntity paymentEntity = paymentRepository.findByPaymentKey(paymentKey) - .orElseThrow(() -> throwPaymentNotFoundByPaymentKey(paymentKey)); - paymentRepository.delete(paymentEntity); + fun updateCanceledTime(paymentKey: String, canceledAt: OffsetDateTime) { + val canceledPayment = canceledPaymentRepository.findByPaymentKey(paymentKey) + .orElseThrow(Supplier { throwPaymentNotFoundByPaymentKey(paymentKey) }) + canceledPayment.canceledAt = canceledAt + } - return canceledPaymentRepository.save(new CanceledPaymentEntity(null, paymentKey, cancelReason, paymentEntity.getTotalAmount(), - paymentEntity.getApprovedAt(), canceledAt)); - } - - public void updateCanceledTime(String paymentKey, OffsetDateTime canceledAt) { - CanceledPaymentEntity canceledPayment = canceledPaymentRepository.findByPaymentKey(paymentKey) - .orElseThrow(() -> throwPaymentNotFoundByPaymentKey(paymentKey)); - canceledPayment.setCanceledAt(canceledAt); - } - - private RoomescapeException throwPaymentNotFoundByPaymentKey(String paymentKey) { - return new RoomescapeException( - ErrorType.PAYMENT_NOT_POUND, String.format("[paymentKey: %s]", paymentKey), - HttpStatus.NOT_FOUND); - } + private fun throwPaymentNotFoundByPaymentKey(paymentKey: String?): RoomescapeException { + return RoomescapeException( + ErrorType.PAYMENT_NOT_POUND, String.format("[paymentKey: %s]", paymentKey), + HttpStatus.NOT_FOUND) + } } diff --git a/src/main/java/roomescape/payment/web/PaymentDTO.kt b/src/main/java/roomescape/payment/web/PaymentDTO.kt index 2d97f96a..8b4b12e2 100644 --- a/src/main/java/roomescape/payment/web/PaymentDTO.kt +++ b/src/main/java/roomescape/payment/web/PaymentDTO.kt @@ -53,18 +53,13 @@ data class ReservationPaymentResponse( val totalAmount: Long, val reservation: ReservationResponse, val approvedAt: OffsetDateTime -) { - companion object { - @JvmStatic - fun from(saved: PaymentEntity): ReservationPaymentResponse { - return ReservationPaymentResponse( - saved.id!!, - saved.orderId, - saved.paymentKey, - saved.totalAmount, - ReservationResponse.from(saved.reservation), - saved.approvedAt - ) - } - } -} +) + +fun PaymentEntity.toReservationPaymentResponse(): ReservationPaymentResponse = ReservationPaymentResponse( + id = this.id!!, + orderId = this.orderId, + paymentKey = this.paymentKey, + totalAmount = this.totalAmount, + reservation = ReservationResponse.from(this.reservation), + approvedAt = this.approvedAt +) \ No newline at end of file -- 2.47.2 From 4e4956e9dceffd845cc8c58b6a52dd225ce37457 Mon Sep 17 00:00:00 2001 From: pricelees Date: Wed, 16 Jul 2025 14:46:55 +0900 Subject: [PATCH 27/32] =?UTF-8?q?refactor:=20payment=20=EA=B4=80=EB=A0=A8?= =?UTF-8?q?=20Repository=EC=97=90=EC=84=9C=20=EB=AA=A8=EB=93=A0=20Optional?= =?UTF-8?q?=20=EB=B0=98=ED=99=98=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../persistence/CanceledPaymentRepository.kt | 3 +-- .../persistence/PaymentRepository.kt | 10 +++++++--- .../payment/business/PaymentServiceTest.java | 9 ++++++--- .../controller/ReservationControllerTest.java | 13 ++++++------- .../ReservationWithPaymentServiceTest.java | 19 ++++++++++--------- 5 files changed, 30 insertions(+), 24 deletions(-) diff --git a/src/main/java/roomescape/payment/infrastructure/persistence/CanceledPaymentRepository.kt b/src/main/java/roomescape/payment/infrastructure/persistence/CanceledPaymentRepository.kt index cb5d2e53..70466653 100644 --- a/src/main/java/roomescape/payment/infrastructure/persistence/CanceledPaymentRepository.kt +++ b/src/main/java/roomescape/payment/infrastructure/persistence/CanceledPaymentRepository.kt @@ -1,8 +1,7 @@ package roomescape.payment.infrastructure.persistence import org.springframework.data.jpa.repository.JpaRepository -import java.util.* interface CanceledPaymentRepository : JpaRepository { - fun findByPaymentKey(paymentKey: String): Optional + fun findByPaymentKey(paymentKey: String): CanceledPaymentEntity? } diff --git a/src/main/java/roomescape/payment/infrastructure/persistence/PaymentRepository.kt b/src/main/java/roomescape/payment/infrastructure/persistence/PaymentRepository.kt index c0ccd469..bf83f375 100644 --- a/src/main/java/roomescape/payment/infrastructure/persistence/PaymentRepository.kt +++ b/src/main/java/roomescape/payment/infrastructure/persistence/PaymentRepository.kt @@ -1,10 +1,14 @@ package roomescape.payment.infrastructure.persistence import org.springframework.data.jpa.repository.JpaRepository -import java.util.* +import org.springframework.data.jpa.repository.Query interface PaymentRepository : JpaRepository { - fun findByReservationId(reservationId: Long): Optional - fun findByPaymentKey(paymentKey: String): Optional + fun existsByReservationId(reservationId: Long): Boolean + + @Query("SELECT p.paymentKey FROM PaymentEntity p WHERE p.reservation.id = :reservationId") + fun findPaymentKeyByReservationId(reservationId: Long): String? + + fun findByPaymentKey(paymentKey: String): PaymentEntity? } diff --git a/src/test/java/roomescape/payment/business/PaymentServiceTest.java b/src/test/java/roomescape/payment/business/PaymentServiceTest.java index ee975e38..10a5dbb8 100644 --- a/src/test/java/roomescape/payment/business/PaymentServiceTest.java +++ b/src/test/java/roomescape/payment/business/PaymentServiceTest.java @@ -17,6 +17,7 @@ import roomescape.common.exception.RoomescapeException; import roomescape.member.infrastructure.persistence.Member; import roomescape.member.infrastructure.persistence.MemberRepository; import roomescape.member.infrastructure.persistence.Role; +import roomescape.payment.infrastructure.persistence.CanceledPaymentEntity; import roomescape.payment.infrastructure.persistence.CanceledPaymentRepository; import roomescape.payment.web.PaymentApprove; import roomescape.payment.web.PaymentCancel; @@ -88,7 +89,7 @@ class PaymentServiceTest { PaymentCancel.Request paymentCancelRequest = paymentService.cancelPaymentByAdmin(reservation.getId()); // then - assertThat(canceledPaymentRepository.findByPaymentKey("payment-key")).isNotEmpty(); + assertThat(canceledPaymentRepository.findByPaymentKey("payment-key")).isNotNull(); assertThat(paymentCancelRequest.paymentKey).isEqualTo(paymentInfo.paymentKey); assertThat(paymentCancelRequest.cancelReason).isEqualTo("고객 요청"); assertThat(paymentCancelRequest.amount).isEqualTo(10000L); @@ -127,8 +128,10 @@ class PaymentServiceTest { paymentService.updateCanceledTime(paymentInfo.paymentKey, canceledAt); // then - canceledPaymentRepository.findByPaymentKey(paymentInfo.paymentKey) - .ifPresent(canceledPayment -> assertThat(canceledPayment.getCanceledAt()).isEqualTo(canceledAt)); + CanceledPaymentEntity canceledPayment = canceledPaymentRepository.findByPaymentKey(paymentInfo.paymentKey); + + assertThat(canceledPayment).isNotNull(); + assertThat(canceledPayment.getCanceledAt()).isEqualTo(canceledAt); } @Test diff --git a/src/test/java/roomescape/reservation/controller/ReservationControllerTest.java b/src/test/java/roomescape/reservation/controller/ReservationControllerTest.java index 62577810..a026ee41 100644 --- a/src/test/java/roomescape/reservation/controller/ReservationControllerTest.java +++ b/src/test/java/roomescape/reservation/controller/ReservationControllerTest.java @@ -447,13 +447,12 @@ public class ReservationControllerTest { .statusCode(400); // then - Optional canceledPaymentOptional = canceledPaymentRepository.findByPaymentKey( - paymentKey); - assertThat(canceledPaymentOptional).isNotNull(); - assertThat(canceledPaymentOptional.get().getCanceledAt()).isEqualTo(canceledAt); - assertThat(canceledPaymentOptional.get().getCancelReason()).isEqualTo("고객 요청"); - assertThat(canceledPaymentOptional.get().getCancelAmount()).isEqualTo(1000L); - assertThat(canceledPaymentOptional.get().getApprovedAt()).isEqualTo(approvedAt); + CanceledPaymentEntity canceledPayment = canceledPaymentRepository.findByPaymentKey(paymentKey); + assertThat(canceledPayment).isNotNull(); + assertThat(canceledPayment.getCanceledAt()).isEqualTo(canceledAt); + assertThat(canceledPayment.getCancelReason()).isEqualTo("고객 요청"); + assertThat(canceledPayment.getCancelAmount()).isEqualTo(1000L); + assertThat(canceledPayment.getApprovedAt()).isEqualTo(approvedAt); } @DisplayName("테마만을 이용하여 예약을 조회한다.") diff --git a/src/test/java/roomescape/reservation/service/ReservationWithPaymentServiceTest.java b/src/test/java/roomescape/reservation/service/ReservationWithPaymentServiceTest.java index 4f74c7e6..b0f589c3 100644 --- a/src/test/java/roomescape/reservation/service/ReservationWithPaymentServiceTest.java +++ b/src/test/java/roomescape/reservation/service/ReservationWithPaymentServiceTest.java @@ -17,6 +17,7 @@ import roomescape.member.infrastructure.persistence.Member; import roomescape.member.infrastructure.persistence.MemberRepository; import roomescape.member.infrastructure.persistence.Role; import roomescape.payment.infrastructure.persistence.CanceledPaymentRepository; +import roomescape.payment.infrastructure.persistence.PaymentEntity; import roomescape.payment.infrastructure.persistence.PaymentRepository; import roomescape.payment.web.PaymentApprove; import roomescape.payment.web.PaymentCancel; @@ -76,13 +77,13 @@ class ReservationWithPaymentServiceTest { assertThat(reservation.getReservationTime().getId()).isEqualTo(time.getId()); assertThat(reservation.getReservationStatus()).isEqualTo(ReservationStatus.CONFIRMED); }); - paymentRepository.findByPaymentKey("payment-key") - .ifPresent(payment -> { - assertThat(payment.getReservation().getId()).isEqualTo(reservationResponse.id()); - assertThat(payment.getPaymentKey()).isEqualTo("payment-key"); - assertThat(payment.getOrderId()).isEqualTo("order-id"); - assertThat(payment.getTotalAmount()).isEqualTo(10000L); - }); + + PaymentEntity payment = paymentRepository.findByPaymentKey("payment-key"); + assertThat(payment).isNotNull(); + assertThat(payment.getReservation().getId()).isEqualTo(reservationResponse.id()); + assertThat(payment.getPaymentKey()).isEqualTo("payment-key"); + assertThat(payment.getOrderId()).isEqualTo("order-id"); + assertThat(payment.getTotalAmount()).isEqualTo(10000L); } @Test @@ -109,8 +110,8 @@ class ReservationWithPaymentServiceTest { // then assertThat(paymentCancelRequest.cancelReason).isEqualTo("고객 요청"); assertThat(reservationRepository.findById(reservationResponse.id())).isEmpty(); - assertThat(paymentRepository.findByPaymentKey("payment-key")).isEmpty(); - assertThat(canceledPaymentRepository.findByPaymentKey("payment-key")).isNotEmpty(); + assertThat(paymentRepository.findByPaymentKey("payment-key")).isNull(); + assertThat(canceledPaymentRepository.findByPaymentKey("payment-key")).isNotNull(); } @Test -- 2.47.2 From d52e30df4f8cd7a9832a6fd3e55e0103b0fcd610 Mon Sep 17 00:00:00 2001 From: pricelees Date: Wed, 16 Jul 2025 14:52:23 +0900 Subject: [PATCH 28/32] =?UTF-8?q?refactor:=20PaymentService=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EC=88=98=EC=A0=95=20=EB=B0=8F=20\@Transactional=20?= =?UTF-8?q?=EB=AA=85=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 읽고 null이 아니면 필드 조회 -> 필드를 쿼리로 바로 조회 - 단순한 존재 여부 판단 -> find => exists로 수정 --- .../payment/business/PaymentService.kt | 90 ++++++++++++------- .../ReservationWithPaymentService.java | 2 +- 2 files changed, 58 insertions(+), 34 deletions(-) diff --git a/src/main/java/roomescape/payment/business/PaymentService.kt b/src/main/java/roomescape/payment/business/PaymentService.kt index fdf283ae..02f2d06b 100644 --- a/src/main/java/roomescape/payment/business/PaymentService.kt +++ b/src/main/java/roomescape/payment/business/PaymentService.kt @@ -15,15 +15,12 @@ import roomescape.payment.web.ReservationPaymentResponse import roomescape.payment.web.toReservationPaymentResponse import roomescape.reservation.domain.Reservation import java.time.OffsetDateTime -import java.util.* -import java.util.function.Supplier @Service class PaymentService( private val paymentRepository: PaymentRepository, private val canceledPaymentRepository: CanceledPaymentRepository ) { - @Transactional fun savePayment( paymentResponse: PaymentApprove.Response, @@ -39,46 +36,73 @@ class PaymentService( }.toReservationPaymentResponse() @Transactional(readOnly = true) - fun findPaymentByReservationId(reservationId: Long): Optional { - return paymentRepository.findByReservationId(reservationId) - } + fun isReservationPaid( + reservationId: Long + ): Boolean = paymentRepository.existsByReservationId(reservationId) - fun saveCanceledPayment(cancelInfo: PaymentCancel.Response, approvedAt: OffsetDateTime, paymentKey: String) { - canceledPaymentRepository.save(CanceledPaymentEntity(null, - paymentKey, cancelInfo.cancelReason, cancelInfo.cancelAmount, approvedAt, cancelInfo.canceledAt)) - } + @Transactional + fun saveCanceledPayment( + cancelInfo: PaymentCancel.Response, + approvedAt: OffsetDateTime, + paymentKey: String + ): CanceledPaymentEntity = CanceledPaymentEntity( + paymentKey = paymentKey, + cancelReason = cancelInfo.cancelReason, + cancelAmount = cancelInfo.cancelAmount, + approvedAt = approvedAt, + canceledAt = cancelInfo.canceledAt + ).also { canceledPaymentRepository.save(it) } + + @Transactional fun cancelPaymentByAdmin(reservationId: Long): PaymentCancel.Request { - val paymentKey = findPaymentByReservationId(reservationId) - .orElseThrow(java.util.function.Supplier { - RoomescapeException(roomescape.common.exception.ErrorType.PAYMENT_NOT_POUND, - kotlin.String.format("[reservationId: %d]", reservationId), org.springframework.http.HttpStatus.NOT_FOUND) - })!! - .paymentKey + val paymentKey: String = paymentRepository.findPaymentKeyByReservationId(reservationId) + ?: throw RoomescapeException( + ErrorType.PAYMENT_NOT_POUND, + "[reservationId: $reservationId]", + HttpStatus.NOT_FOUND + ) // 취소 시간은 현재 시간으로 일단 생성한 뒤, 결제 취소 완료 후 해당 시간으로 변경합니다. - val canceled = cancelPayment(paymentKey, "고객 요청", OffsetDateTime.now()) + val canceled: CanceledPaymentEntity = cancelPayment(paymentKey) return PaymentCancel.Request(paymentKey, canceled.cancelAmount, canceled.cancelReason) } - private fun cancelPayment(paymentKey: String, cancelReason: String, canceledAt: OffsetDateTime): CanceledPaymentEntity { - val paymentEntity = paymentRepository.findByPaymentKey(paymentKey) - .orElseThrow(Supplier { throwPaymentNotFoundByPaymentKey(paymentKey) }) - paymentRepository.delete(paymentEntity) + private fun cancelPayment( + paymentKey: String, + cancelReason: String = "고객 요청", + canceledAt: OffsetDateTime = OffsetDateTime.now() + ): CanceledPaymentEntity { + val paymentEntity: PaymentEntity = paymentRepository.findByPaymentKey(paymentKey) + ?.also { paymentRepository.delete(it) } + ?: throw RoomescapeException( + ErrorType.PAYMENT_NOT_POUND, + "[paymentKey: $paymentKey]", + HttpStatus.NOT_FOUND + ) - return canceledPaymentRepository.save(CanceledPaymentEntity(null, paymentKey, cancelReason, paymentEntity.totalAmount, - paymentEntity.approvedAt, canceledAt)) + return CanceledPaymentEntity( + paymentKey = paymentKey, + cancelReason = cancelReason, + cancelAmount = paymentEntity.totalAmount, + approvedAt = paymentEntity.approvedAt, + canceledAt = canceledAt + ).also { + canceledPaymentRepository.save(it) + } } - fun updateCanceledTime(paymentKey: String, canceledAt: OffsetDateTime) { - val canceledPayment = canceledPaymentRepository.findByPaymentKey(paymentKey) - .orElseThrow(Supplier { throwPaymentNotFoundByPaymentKey(paymentKey) }) - canceledPayment.canceledAt = canceledAt - } - - private fun throwPaymentNotFoundByPaymentKey(paymentKey: String?): RoomescapeException { - return RoomescapeException( - ErrorType.PAYMENT_NOT_POUND, String.format("[paymentKey: %s]", paymentKey), - HttpStatus.NOT_FOUND) + @Transactional + fun updateCanceledTime( + paymentKey: String, + canceledAt: OffsetDateTime + ) { + canceledPaymentRepository.findByPaymentKey(paymentKey)?.let { + it.canceledAt = canceledAt + } ?: throw RoomescapeException( + ErrorType.PAYMENT_NOT_POUND, + "[paymentKey: $paymentKey]", + HttpStatus.NOT_FOUND + ) } } diff --git a/src/main/java/roomescape/reservation/service/ReservationWithPaymentService.java b/src/main/java/roomescape/reservation/service/ReservationWithPaymentService.java index edec7bae..853e6e30 100644 --- a/src/main/java/roomescape/reservation/service/ReservationWithPaymentService.java +++ b/src/main/java/roomescape/reservation/service/ReservationWithPaymentService.java @@ -47,7 +47,7 @@ public class ReservationWithPaymentService { @Transactional(readOnly = true) public boolean isNotPaidReservation(Long reservationId) { - return paymentService.findPaymentByReservationId(reservationId).isEmpty(); + return !paymentService.isReservationPaid(reservationId); } public void updateCanceledTime(String paymentKey, OffsetDateTime canceledAt) { -- 2.47.2 From 54207c282654c5c8d815b8c07addffe12ec42e98 Mon Sep 17 00:00:00 2001 From: pricelees Date: Wed, 16 Jul 2025 17:36:29 +0900 Subject: [PATCH 29/32] =?UTF-8?q?fix:=20ErrorType=EB=82=B4=20=EC=98=A4?= =?UTF-8?q?=ED=83=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/roomescape/common/exception/ErrorType.kt | 5 +++-- src/main/java/roomescape/payment/business/PaymentService.kt | 6 +++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/roomescape/common/exception/ErrorType.kt b/src/main/java/roomescape/common/exception/ErrorType.kt index abc8e03d..8d19efe4 100644 --- a/src/main/java/roomescape/common/exception/ErrorType.kt +++ b/src/main/java/roomescape/common/exception/ErrorType.kt @@ -32,7 +32,7 @@ enum class ErrorType( RESERVATION_NOT_FOUND("예약(Reservation) 정보가 존재하지 않습니다."), RESERVATION_TIME_NOT_FOUND("예약 시간(ReservationTime) 정보가 존재하지 않습니다."), THEME_NOT_FOUND("테마(Theme) 정보가 존재하지 않습니다."), - PAYMENT_NOT_POUND("결제(Payment) 정보가 존재하지 않습니다."), + PAYMENT_NOT_FOUND("결제(Payment) 정보가 존재하지 않습니다."), // 405 Method Not Allowed METHOD_NOT_ALLOWED("지원하지 않는 HTTP Method 입니다."), @@ -52,7 +52,8 @@ enum class ErrorType( // Payment Error PAYMENT_ERROR("결제(취소)에 실패했습니다. 결제(취소) 정보를 확인해주세요."), - PAYMENT_SERVER_ERROR("결제 서버에서 에러가 발생하였습니다. 잠시 후 다시 시도해주세요."); + PAYMENT_SERVER_ERROR("결제 서버에서 에러가 발생하였습니다. 잠시 후 다시 시도해주세요.") + ; companion object { @JvmStatic diff --git a/src/main/java/roomescape/payment/business/PaymentService.kt b/src/main/java/roomescape/payment/business/PaymentService.kt index 02f2d06b..1b4e6deb 100644 --- a/src/main/java/roomescape/payment/business/PaymentService.kt +++ b/src/main/java/roomescape/payment/business/PaymentService.kt @@ -58,7 +58,7 @@ class PaymentService( fun cancelPaymentByAdmin(reservationId: Long): PaymentCancel.Request { val paymentKey: String = paymentRepository.findPaymentKeyByReservationId(reservationId) ?: throw RoomescapeException( - ErrorType.PAYMENT_NOT_POUND, + ErrorType.PAYMENT_NOT_FOUND, "[reservationId: $reservationId]", HttpStatus.NOT_FOUND ) @@ -76,7 +76,7 @@ class PaymentService( val paymentEntity: PaymentEntity = paymentRepository.findByPaymentKey(paymentKey) ?.also { paymentRepository.delete(it) } ?: throw RoomescapeException( - ErrorType.PAYMENT_NOT_POUND, + ErrorType.PAYMENT_NOT_FOUND, "[paymentKey: $paymentKey]", HttpStatus.NOT_FOUND ) @@ -100,7 +100,7 @@ class PaymentService( canceledPaymentRepository.findByPaymentKey(paymentKey)?.let { it.canceledAt = canceledAt } ?: throw RoomescapeException( - ErrorType.PAYMENT_NOT_POUND, + ErrorType.PAYMENT_NOT_FOUND, "[paymentKey: $paymentKey]", HttpStatus.NOT_FOUND ) -- 2.47.2 From cd6f44ca60d986e2ba4ce175f25488e3a7ab6497 Mon Sep 17 00:00:00 2001 From: pricelees Date: Wed, 16 Jul 2025 18:07:07 +0900 Subject: [PATCH 30/32] =?UTF-8?q?test:=20Payment=20=EB=B0=8F=20=EA=B4=80?= =?UTF-8?q?=EB=A0=A8=20Fixture=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../member/controller/MemberControllerTest.kt | 7 +- src/test/java/roomescape/util/Fixtures.kt | 127 ++++++++++++++++-- 2 files changed, 123 insertions(+), 11 deletions(-) diff --git a/src/test/java/roomescape/member/controller/MemberControllerTest.kt b/src/test/java/roomescape/member/controller/MemberControllerTest.kt index 7f24f6d8..7eddc94e 100644 --- a/src/test/java/roomescape/member/controller/MemberControllerTest.kt +++ b/src/test/java/roomescape/member/controller/MemberControllerTest.kt @@ -11,6 +11,7 @@ import roomescape.member.web.MemberController import roomescape.member.web.MembersResponse import roomescape.util.MemberFixture import roomescape.util.RoomescapeApiTest +import kotlin.random.Random @WebMvcTest(controllers = [MemberController::class]) class MemberControllerTest( @@ -22,9 +23,9 @@ class MemberControllerTest( val endpoint = "/members" every { memberRepository.findAll() } returns listOf( - MemberFixture.create(name = "name1"), - MemberFixture.create(name = "name2"), - MemberFixture.create(name = "name3"), + MemberFixture.create(id = Random.nextLong(), name = "name1"), + MemberFixture.create(id = Random.nextLong(), name = "name2"), + MemberFixture.create(id = Random.nextLong(), name = "name3"), ) `when`("관리자가 보내면") { diff --git a/src/test/java/roomescape/util/Fixtures.kt b/src/test/java/roomescape/util/Fixtures.kt index 4ae7a6b6..abe061ac 100644 --- a/src/test/java/roomescape/util/Fixtures.kt +++ b/src/test/java/roomescape/util/Fixtures.kt @@ -1,37 +1,81 @@ package roomescape.util -import roomescape.member.infrastructure.persistence.Member -import roomescape.member.infrastructure.persistence.Role import roomescape.auth.infrastructure.jwt.JwtHandler import roomescape.auth.web.LoginRequest -import java.util.concurrent.atomic.AtomicLong +import roomescape.member.infrastructure.persistence.Member +import roomescape.member.infrastructure.persistence.Role +import roomescape.payment.infrastructure.persistence.CanceledPaymentEntity +import roomescape.payment.infrastructure.persistence.PaymentEntity +import roomescape.payment.web.PaymentApprove +import roomescape.payment.web.PaymentCancel +import roomescape.reservation.domain.Reservation +import roomescape.reservation.domain.ReservationStatus +import roomescape.reservation.domain.ReservationTime +import roomescape.theme.domain.Theme +import java.time.LocalDate +import java.time.LocalTime +import java.time.OffsetDateTime +import kotlin.random.Random object MemberFixture { const val NOT_LOGGED_IN_USERID: Long = 0 - val idCounter: AtomicLong = AtomicLong(1L) - fun create( - id: Long? = idCounter.incrementAndGet(), + id: Long? = null, name: String = "sangdol", account: String = "default", password: String = "password", role: Role = Role.ADMIN ): Member = Member(id, name, "$account@email.com", password, role) - fun admin(): Member = create(account = "admin", role = Role.ADMIN) + fun admin(): Member = create( + id = 2L, + account = "admin", + role = Role.ADMIN + ) fun adminLoginRequest(): LoginRequest = LoginRequest( email = admin().email, password = admin().password ) - fun user(): Member = create(account = "user", role = Role.MEMBER) + fun user(): Member = create( + id = 1L, + account = "user", + role = Role.MEMBER + ) fun userLoginRequest(): LoginRequest = LoginRequest( email = user().email, password = user().password ) } +object ReservationTimeFixture { + fun create( + id: Long? = null, + startAt: LocalTime = LocalTime.now().plusHours(1), + ): ReservationTime = ReservationTime(id, startAt) +} + +object ThemeFixture { + fun create( + id: Long? = null, + name: String = "Default Theme", + description: String = "Default Description", + thumbnail: String = "https://example.com/default-thumbnail.jpg" + ): Theme = Theme(id, name, description, thumbnail) +} + +object ReservationFixture { + fun create( + id: Long? = null, + date: LocalDate = LocalDate.now().plusWeeks(1), + theme: Theme = ThemeFixture.create(), + reservationTime: ReservationTime = ReservationTimeFixture.create(), + member: Member = MemberFixture.create(), + status: ReservationStatus = ReservationStatus.CONFIRMED_PAYMENT_REQUIRED + ): Reservation = Reservation(id, date, reservationTime, theme, member, status) +} + object JwtFixture { const val SECRET_KEY: String = "daijawligagaf@LIJ$@U)9nagnalkkgalijaddljfi" const val EXPIRATION_TIME: Long = 1000 * 60 * 60 @@ -41,3 +85,70 @@ object JwtFixture { expirationTime: Long = EXPIRATION_TIME ): JwtHandler = JwtHandler(secretKey, expirationTime) } + +object PaymentFixture { + const val PAYMENT_KEY: String = "paymentKey" + const val ORDER_ID: String = "orderId" + const val AMOUNT: Long = 10000L + + fun create( + id: Long? = null, + orderId: String = ORDER_ID, + paymentKey: String = PAYMENT_KEY, + totalAmount: Long = AMOUNT, + reservationId: Long = Random.nextLong(), + approvedAt: OffsetDateTime = OffsetDateTime.now() + ): PaymentEntity = PaymentEntity( + id = id, + orderId = orderId, + paymentKey = paymentKey, + totalAmount = totalAmount, + reservation = ReservationFixture.create(id = reservationId), + approvedAt = approvedAt + ) + + fun createCanceled( + id: Long? = null, + paymentKey: String = PAYMENT_KEY, + cancelReason: String = "Test Cancel", + cancelAmount: Long = AMOUNT, + approvedAt: OffsetDateTime = OffsetDateTime.now(), + canceledAt: OffsetDateTime = approvedAt.plusHours(1) + ): CanceledPaymentEntity = CanceledPaymentEntity( + id = id, + paymentKey = paymentKey, + cancelReason = cancelReason, + cancelAmount = cancelAmount, + approvedAt = approvedAt, + canceledAt = canceledAt + + ) + + fun createApproveRequest(): PaymentApprove.Request = PaymentApprove.Request( + paymentKey = PAYMENT_KEY, + orderId = ORDER_ID, + amount = AMOUNT, + paymentType = "CARD" + ) + + fun createApproveResponse(): PaymentApprove.Response = PaymentApprove.Response( + paymentKey = PAYMENT_KEY, + orderId = ORDER_ID, + approvedAt = OffsetDateTime.now(), + totalAmount = AMOUNT + ) + + fun createCancelRequest(): PaymentCancel.Request = PaymentCancel.Request( + paymentKey = PAYMENT_KEY, + amount = AMOUNT, + cancelReason = "Test Cancel" + ) + + fun createCancelResponse(): PaymentCancel.Response = PaymentCancel.Response( + cancelStatus = "SUCCESS", + cancelReason = "Test Cancel", + cancelAmount = AMOUNT, + canceledAt = OffsetDateTime.now().plusMinutes(1) + ) +} + -- 2.47.2 From c308401f3fe3adb633ae8aa61b59c1b4cb15d059 Mon Sep 17 00:00:00 2001 From: pricelees Date: Wed, 16 Jul 2025 18:07:30 +0900 Subject: [PATCH 31/32] =?UTF-8?q?test:=20Payment=20/=20CanceledPaymentRepo?= =?UTF-8?q?sitory=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CanceledPaymentRepositoryTest.kt | 37 +++++++ .../persistence/PaymentRepositoryTest.kt | 103 ++++++++++++++++++ src/test/resources/application.yaml | 16 +-- 3 files changed, 146 insertions(+), 10 deletions(-) create mode 100644 src/test/java/roomescape/payment/infrastructure/persistence/CanceledPaymentRepositoryTest.kt create mode 100644 src/test/java/roomescape/payment/infrastructure/persistence/PaymentRepositoryTest.kt diff --git a/src/test/java/roomescape/payment/infrastructure/persistence/CanceledPaymentRepositoryTest.kt b/src/test/java/roomescape/payment/infrastructure/persistence/CanceledPaymentRepositoryTest.kt new file mode 100644 index 00000000..1043a737 --- /dev/null +++ b/src/test/java/roomescape/payment/infrastructure/persistence/CanceledPaymentRepositoryTest.kt @@ -0,0 +1,37 @@ +package roomescape.payment.infrastructure.persistence + +import io.kotest.assertions.assertSoftly +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.shouldBe +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest +import roomescape.util.PaymentFixture +import java.util.* + +@DataJpaTest +class CanceledPaymentRepositoryTest( + @Autowired val canceledPaymentRepository: CanceledPaymentRepository, +): FunSpec() { + init { + context("paymentKey로 CanceledPaymentEntity 조회") { + val paymentKey = "test-payment-key" + beforeTest { + PaymentFixture.createCanceled(paymentKey = paymentKey) + .also { canceledPaymentRepository.save(it) } + } + + test("정상 반환") { + canceledPaymentRepository.findByPaymentKey(paymentKey)?.let { + assertSoftly(it) { + this.paymentKey shouldBe paymentKey + } + } ?: throw AssertionError("Unexpected null value") + } + + test("null 반환") { + canceledPaymentRepository.findByPaymentKey(UUID.randomUUID().toString()) + .also { it shouldBe null } + } + } + } +} diff --git a/src/test/java/roomescape/payment/infrastructure/persistence/PaymentRepositoryTest.kt b/src/test/java/roomescape/payment/infrastructure/persistence/PaymentRepositoryTest.kt new file mode 100644 index 00000000..ab3217d0 --- /dev/null +++ b/src/test/java/roomescape/payment/infrastructure/persistence/PaymentRepositoryTest.kt @@ -0,0 +1,103 @@ +package roomescape.payment.infrastructure.persistence + +import io.kotest.assertions.assertSoftly +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.shouldBe +import jakarta.persistence.EntityManager +import org.springframework.beans.factory.annotation.Autowired +import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest +import roomescape.util.PaymentFixture +import roomescape.util.ReservationFixture + +@DataJpaTest +class PaymentRepositoryTest( + @Autowired val paymentRepository: PaymentRepository, + @Autowired val entityManager: EntityManager +) : FunSpec() { + + var reservationId: Long = 0L + + init { + context("existsByReservationId") { + beforeTest { + reservationId = setupReservation() + PaymentFixture.create(reservationId = reservationId) + .also { paymentRepository.save(it) } + } + + test("true") { + paymentRepository.existsByReservationId(reservationId) + .also { it shouldBe true } + } + + test("false") { + paymentRepository.existsByReservationId(reservationId + 1) + .also { it shouldBe false } + } + } + + context("findPaymentKeyByReservationId") { + lateinit var paymentKey: String + + beforeTest { + reservationId = setupReservation() + paymentKey = PaymentFixture.create(reservationId = reservationId) + .also { paymentRepository.save(it) } + .paymentKey + } + + test("정상 반환") { + paymentRepository.findPaymentKeyByReservationId(reservationId) + ?.let { it shouldBe paymentKey } + ?: throw AssertionError("Unexpected null value") + } + + test("null 반환") { + paymentRepository.findPaymentKeyByReservationId(reservationId + 1) + .also { it shouldBe null } + } + } + + context("findByPaymentKey") { + lateinit var payment: PaymentEntity + + beforeTest { + reservationId = setupReservation() + payment = PaymentFixture.create(reservationId = reservationId) + .also { paymentRepository.save(it) } + } + + test("정상 반환") { + paymentRepository.findByPaymentKey(payment.paymentKey) + ?.also { + assertSoftly(it) { + this.id shouldBe payment.id + this.orderId shouldBe payment.orderId + this.paymentKey shouldBe payment.paymentKey + this.totalAmount shouldBe payment.totalAmount + this.reservation.id shouldBe payment.reservation.id + this.approvedAt shouldBe payment.approvedAt + } + } + ?: throw AssertionError("Unexpected null value") + } + + test("null 반환") { + paymentRepository.findByPaymentKey("non-existent-key") + .also { it shouldBe null } + } + } + } + + private fun setupReservation(): Long { + return ReservationFixture.create().also { + entityManager.persist(it.member) + entityManager.persist(it.theme) + entityManager.persist(it.reservationTime) + entityManager.persist(it) + + entityManager.flush() + entityManager.clear() + }.id + } +} diff --git a/src/test/resources/application.yaml b/src/test/resources/application.yaml index 350ecfc3..9bc65da2 100644 --- a/src/test/resources/application.yaml +++ b/src/test/resources/application.yaml @@ -9,15 +9,6 @@ spring: sql: init: data-locations: - h2: - console: - enabled: true - path: /h2-console - datasource: - driver-class-name: org.h2.Driver - url: jdbc:h2:mem:database-test - username: sa - password: security: jwt: @@ -30,4 +21,9 @@ payment: api-base-url: https://api.tosspayments.com confirm-secret-key: test_gsk_docs_OaPz8L5KdmQXkzRz3y47BMw6 read-timeout: 3 - connect-timeout: 30 \ No newline at end of file + connect-timeout: 30 + +logging: + level: + org.springframework.orm.jpa: DEBUG + org.springframework.transaction: DEBUG -- 2.47.2 From 8577b68496f4e371da1e7e5a5504a4795c54942a Mon Sep 17 00:00:00 2001 From: pricelees Date: Wed, 16 Jul 2025 18:08:37 +0900 Subject: [PATCH 32/32] =?UTF-8?q?test:=20PaymentService=20=EC=BD=94?= =?UTF-8?q?=ED=8B=80=EB=A6=B0=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 검증이 불필요하다고 느껴지는 로직은 생략 --- .../payment/business/PaymentServiceKTest.kt | 129 ++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 src/test/java/roomescape/payment/business/PaymentServiceKTest.kt diff --git a/src/test/java/roomescape/payment/business/PaymentServiceKTest.kt b/src/test/java/roomescape/payment/business/PaymentServiceKTest.kt new file mode 100644 index 00000000..e72b228c --- /dev/null +++ b/src/test/java/roomescape/payment/business/PaymentServiceKTest.kt @@ -0,0 +1,129 @@ +package roomescape.payment.business + +import io.kotest.assertions.assertSoftly +import io.kotest.assertions.throwables.shouldThrow +import io.kotest.core.spec.style.FunSpec +import io.kotest.matchers.shouldBe +import io.mockk.every +import io.mockk.just +import io.mockk.mockk +import io.mockk.runs +import org.springframework.http.HttpStatus +import roomescape.common.exception.ErrorType +import roomescape.common.exception.RoomescapeException +import roomescape.payment.infrastructure.persistence.CanceledPaymentRepository +import roomescape.payment.infrastructure.persistence.PaymentRepository +import roomescape.payment.web.PaymentCancel +import roomescape.util.PaymentFixture +import java.time.OffsetDateTime + +class PaymentServiceKTest : FunSpec({ + val paymentRepository: PaymentRepository = mockk() + val canceledPaymentRepository: CanceledPaymentRepository = mockk() + + val paymentService = PaymentService(paymentRepository, canceledPaymentRepository) + + context("cancelPaymentByAdmin") { + val reservationId = 1L + test("reservationId로 paymentKey를 찾을 수 없으면 예외를 던진다.") { + every { paymentRepository.findPaymentKeyByReservationId(reservationId) } returns null + + val exception = shouldThrow { + paymentService.cancelPaymentByAdmin(reservationId) + } + + assertSoftly(exception) { + this.errorType shouldBe ErrorType.PAYMENT_NOT_FOUND + this.httpStatus shouldBe HttpStatus.NOT_FOUND + } + } + + context("reservationId로 paymentKey를 찾고난 후") { + val paymentKey = "test-payment-key" + + every { + paymentRepository.findPaymentKeyByReservationId(reservationId) + } returns paymentKey + + test("해당 paymentKey로 paymentEntity를 찾을 수 없으면 예외를 던진다.") { + every { + paymentRepository.findByPaymentKey(paymentKey) + } returns null + + val exception = shouldThrow { + paymentService.cancelPaymentByAdmin(reservationId) + } + + assertSoftly(exception) { + this.errorType shouldBe ErrorType.PAYMENT_NOT_FOUND + this.httpStatus shouldBe HttpStatus.NOT_FOUND + } + } + + test("해당 paymentKey로 paymentEntity를 찾고, cancelPaymentEntity를 저장한다.") { + val paymentEntity = PaymentFixture.create(paymentKey = paymentKey) + + every { + paymentRepository.findByPaymentKey(paymentKey) + } returns paymentEntity.also { + every { + paymentRepository.delete(it) + } just runs + } + + every { + canceledPaymentRepository.save(any()) + } returns PaymentFixture.createCanceled( + id = 1L, + paymentKey = paymentKey, + cancelAmount = paymentEntity.totalAmount, + ) + + val result: PaymentCancel.Request = paymentService.cancelPaymentByAdmin(reservationId) + + assertSoftly(result) { + this.paymentKey shouldBe paymentKey + this.amount shouldBe paymentEntity.totalAmount + this.cancelReason shouldBe "고객 요청" + } + } + } + } + + context("updateCanceledTime") { + val paymentKey = "test-payment-key" + val canceledAt = OffsetDateTime.now() + + test("paymentKey로 canceledPaymentEntity를 찾을 수 없으면 예외를 던진다.") { + every { + canceledPaymentRepository.findByPaymentKey(paymentKey) + } returns null + + val exception = shouldThrow { + paymentService.updateCanceledTime(paymentKey, canceledAt) + } + + assertSoftly(exception) { + this.errorType shouldBe ErrorType.PAYMENT_NOT_FOUND + this.httpStatus shouldBe HttpStatus.NOT_FOUND + } + } + + test("paymentKey로 canceledPaymentEntity를 찾고, canceledAt을 업데이트한다.") { + val canceledPaymentEntity = PaymentFixture.createCanceled( + paymentKey = paymentKey, + canceledAt = canceledAt.minusMinutes(1) + ) + + every { + canceledPaymentRepository.findByPaymentKey(paymentKey) + } returns canceledPaymentEntity + + paymentService.updateCanceledTime(paymentKey, canceledAt) + + assertSoftly(canceledPaymentEntity) { + this.canceledAt shouldBe canceledAt + } + } + } +}) -- 2.47.2