From bbed10dc72af6b52675b4acb6c454c2265e141da Mon Sep 17 00:00:00 2001 From: byulcode Date: Fri, 12 Jan 2024 10:36:51 +0900 Subject: [PATCH 1/3] =?UTF-8?q?test:=20=EC=B9=B4=EB=93=9C=20=EA=B2=B0?= =?UTF-8?q?=EC=A0=9C=20=EC=84=B1=EA=B3=B5=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 --- .../fake/TossPaymentServiceFake.java | 14 +- .../service/PaymentServiceTest.java | 458 +++++++++--------- 2 files changed, 224 insertions(+), 248 deletions(-) diff --git a/api/api-booking/src/test/java/com/pgms/apibooking/fake/TossPaymentServiceFake.java b/api/api-booking/src/test/java/com/pgms/apibooking/fake/TossPaymentServiceFake.java index d9da81b5..56d639ab 100644 --- a/api/api-booking/src/test/java/com/pgms/apibooking/fake/TossPaymentServiceFake.java +++ b/api/api-booking/src/test/java/com/pgms/apibooking/fake/TossPaymentServiceFake.java @@ -1,6 +1,7 @@ package com.pgms.apibooking.fake; -import java.time.LocalDateTime; +import java.time.OffsetDateTime; +import java.time.format.DateTimeFormatter; import java.util.List; import com.pgms.apibooking.domain.payment.dto.request.PaymentCancelRequest; @@ -25,7 +26,8 @@ @RequiredArgsConstructor public class TossPaymentServiceFake implements TossPaymentService { - private static final LocalDateTime NOW = LocalDateTime.now(); + OffsetDateTime NOW = OffsetDateTime.now(); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssXXX"); private final BookingRepository bookingRepository; private final PaymentRepository paymentRepository; @@ -44,8 +46,8 @@ public PaymentSuccessResponse requestTossPaymentConfirmation(PaymentConfirmReque booking.getPayment().getMethod().getDescription(), request.amount(), PaymentStatus.DONE.name(), - NOW.toString(), - NOW.toString(), + NOW.format(formatter), + NOW.format(formatter), new PaymentCardResponse( "61", "12341234****123*", @@ -63,8 +65,8 @@ public PaymentSuccessResponse requestTossPaymentConfirmation(PaymentConfirmReque booking.getPayment().getMethod().getDescription(), request.amount(), PaymentStatus.DONE.name(), - NOW.toString(), - NOW.toString(), + NOW.format(formatter), + NOW.format(formatter), null, new PaymentVirtualResponse( "X6505636518308", diff --git a/api/api-booking/src/test/java/com/pgms/apibooking/service/PaymentServiceTest.java b/api/api-booking/src/test/java/com/pgms/apibooking/service/PaymentServiceTest.java index b72d4b33..4443bc7d 100644 --- a/api/api-booking/src/test/java/com/pgms/apibooking/service/PaymentServiceTest.java +++ b/api/api-booking/src/test/java/com/pgms/apibooking/service/PaymentServiceTest.java @@ -1,242 +1,216 @@ -// package com.pgms.apibooking.service; -// -// import static org.assertj.core.api.Assertions.*; -// import static org.mockito.Mockito.*; -// -// import java.time.LocalDateTime; -// import java.util.List; -// -// import org.junit.jupiter.api.BeforeEach; -// import org.junit.jupiter.api.Test; -// import org.junit.jupiter.api.extension.ExtendWith; -// import org.mockito.Mock; -// import org.mockito.junit.jupiter.MockitoExtension; -// import org.springframework.beans.factory.annotation.Autowired; -// import org.springframework.boot.test.context.SpringBootTest; -// import org.springframework.boot.test.mock.mockito.MockBean; -// import org.springframework.web.client.RestTemplate; -// -// import com.pgms.apibooking.payment.dto.request.PaymentConfirmRequest; -// import com.pgms.apibooking.payment.dto.response.PaymentCardResponse; -// import com.pgms.apibooking.payment.dto.response.PaymentSuccessResponse; -// import com.pgms.coredomain.domain.booking.Booking; -// import com.pgms.coredomain.domain.booking.BookingStatus; -// import com.pgms.coredomain.domain.booking.Payment; -// import com.pgms.coredomain.domain.booking.PaymentMethod; -// import com.pgms.coredomain.domain.booking.PaymentStatus; -// import com.pgms.coredomain.domain.booking.ReceiptType; -// import com.pgms.coredomain.domain.booking.repository.BookingRepository; -// import com.pgms.coredomain.domain.booking.repository.PaymentRepository; -// import com.pgms.coredomain.domain.event.Event; -// import com.pgms.coredomain.domain.event.EventHall; -// import com.pgms.coredomain.domain.event.EventHallSeat; -// import com.pgms.coredomain.domain.event.EventSeat; -// import com.pgms.coredomain.domain.event.EventSeatArea; -// import com.pgms.coredomain.domain.event.EventSeatStatus; -// import com.pgms.coredomain.domain.event.EventTime; -// import com.pgms.coredomain.domain.event.GenreType; -// import com.pgms.coredomain.domain.event.SeatAreaType; -// import com.pgms.coredomain.domain.event.repository.EventHallRepository; -// import com.pgms.coredomain.domain.event.repository.EventRepository; -// import com.pgms.coredomain.domain.event.repository.EventSeatAreaRepository; -// import com.pgms.coredomain.domain.event.repository.EventSeatRepository; -// import com.pgms.coredomain.domain.event.repository.EventTimeRepository; -// -// @SpringBootTest -// @ExtendWith(MockitoExtension.class) -// class PaymentServiceTest { -// -// -// @Autowired -// private EventHallRepository eventHallRepository; -// -// @Autowired -// private EventTimeRepository eventTimeRepository; -// -// @Autowired -// private EventRepository eventRepository; -// -// @Autowired -// private EventSeatAreaRepository eventSeatAreaRepository; -// -// @Mock -// private RestTemplate restTemplate; -// -// private Booking booking; -// -// -// @Autowired -// private BookingRepository bookingRepository; -// -// @Autowired -// private PaymentRepository paymentRepository; -// @MockBean -// public TossPaymentService tossPaymentService; -// @Autowired -// private EventSeatRepository eventSeatRepository; -// -// @BeforeEach -// public void setUp() { -// EventHallSeat eventHallSeat = new EventHallSeat("A1"); -// EventHall eventHall = eventHallRepository.save(EventHall.builder() -// .name("예술공간서울") -// .address("서울특별시 종로구 명륜2가 성균관로4길 19") -// .eventHallSeats(List.of(eventHallSeat)) -// .build()); -// -// Event event = eventRepository.save(Event.builder() -// .title("공연 1") -// .description("공연1 입니다.") -// .runningTime(60) -// .startedAt(LocalDateTime.of(2023, 1, 1, 0, 0)) -// .endedAt(LocalDateTime.of(2023, 1, 1, 0, 0)) -// .viewRating("12세 이상") -// .genreType(GenreType.MUSICAL) -// .thumbnail("thumbnail.jpg") -// .bookingStartedAt(LocalDateTime.of(2023, 1, 1, 0, 0)) -// .bookingEndedAt(LocalDateTime.of(2023, 1, 1, 0, 0)) -// .eventHall(eventHall) -// .build()); -// -// EventTime time = eventTimeRepository.save(new EventTime( -// 1, -// LocalDateTime.of(2024, 1, 1, 0, 0), -// LocalDateTime.of(2024, 1, 1, 0, 0), -// event)); -// -// EventSeatArea eventSeatArea = eventSeatAreaRepository.save(new EventSeatArea( -// SeatAreaType.S, -// 100000, -// event)); -// -// eventSeatRepository.save( -// EventSeat.builder() -// .name("A1") -// .status(EventSeatStatus.AVAILABLE) -// .eventSeatArea(eventSeatArea) -// .eventTime(time).build()); -// -// booking = Booking.builder() -// .id("bookingTestId") -// .time(time) -// .bookingName("BLACKPINK WORLD TOUR [BORN PINK] FINALE IN SEOUL 1") -// .status(BookingStatus.WAITING_FOR_PAYMENT) -// .receiptType(ReceiptType.DELIVERY) -// .buyerName("김토스") -// .buyerPhoneNumber("010-123-456") -// .recipientName("Jane Doe") -// .recipientPhoneNumber("9876543210") -// .streetAddress("456 Oak St") -// .detailAddress("Apt 301") -// .zipCode("54321") -// .amount(180000) -// .build(); -// bookingRepository.save(booking); -// } -// -// @Test -// public void 카드_결제_성공_테스트() { -// // given -// String paymentKey = "paymentKey"; -// Payment payment = Payment.builder() -// .method(PaymentMethod.CARD) -// .amount(booking.getAmount()) -// .status(PaymentStatus.READY) -// .build(); -// booking.updatePayment(payment); -// paymentRepository.save(payment); -// -// PaymentSuccessResponse mockSuccessResponse = new PaymentSuccessResponse( -// paymentKey, -// booking.getId(), -// booking.getBookingName(), -// payment.getMethod().getDescription(), -// payment.getAmount(), -// "DONE", -// "2024-01-01T10:01:00+05:00", -// "2024-01-01T10:01:00+05:00", -// new PaymentCardResponse("card_num", 2, false), -// null -// ); -// -// PaymentService paymentService = new PaymentService(paymentRepository, bookingRepository, tossPaymentService); -// -// when(tossPaymentService.requestTossPaymentConfirmation(any(PaymentConfirmRequest.class))) -// .thenReturn(mockSuccessResponse); -// -// // when -// PaymentSuccessResponse response = paymentService.successPayment(paymentKey, booking.getId(), -// booking.getAmount()); -// -// -// // then -// verify(tossPaymentService, times(1)).requestTossPaymentConfirmation(any(PaymentConfirmRequest.class)); -// -// assertThat(response.status()).isEqualTo("DONE"); -// assertThat(payment.getCardNumber()).isEqualTo(response.card().number()); -// -// assertThat(booking.getStatus()).isEqualTo(BookingStatus.PAYMENT_COMPLETED); -// assertThat(payment.getAccountNumber()).isNull(); -// } -// -// // @Test -// // public void 결제_가격_불일치_오류_테스트() { -// // // given -// // String paymentKey = "paymentKey"; -// // Payment payment = Payment.builder() -// // .method(PaymentMethod.CARD) -// // .amount(booking.getAmount()) -// // .status(PaymentStatus.READY) -// // .build(); -// // booking.updatePayment(payment); -// // when(bookingRepository.findWithPaymentById(anyString())).thenReturn(Optional.of(booking)); -// // -// // // when & then -// // assertThatThrownBy(() -> paymentService.successPayment(paymentKey, booking.getId(), 170000)) -// // .isInstanceOf(BookingException.class) -// // .hasMessageContaining(BookingErrorCode.PAYMENT_AMOUNT_MISMATCH.getMessage()); -// // } -// // -// // @Test -// // public void 카드_결제_취소_테스트() { -// // String paymentKey = "paymentKey"; -// // Payment payment = Payment.builder() -// // .method(PaymentMethod.CARD) -// // .amount(booking.getAmount()) -// // .status(PaymentStatus.READY) -// // .build(); -// // booking.updatePayment(payment); -// // -// // BookingCancelRequest cancelRequest = new BookingCancelRequest( -// // "고객이 결제 취소", -// // 180000, -// // Optional.of(new RefundAccountRequest("20", "352-123", "김환불")) -// // ); -// // -// // PaymentCancelResponse mockCancelResponse = new PaymentCancelResponse( -// // paymentKey, -// // booking.getId(), -// // booking.getBookingName(), -// // payment.getMethod().getDescription(), -// // "180000", -// // "CANCELED", -// // "2024-01-01T10:01:00+05:00", -// // "2024-01-01T10:01:00+05:00", -// // new PaymentCardResponse("card_num", 2, false), -// // null, -// // List.of(new PaymentCancelDetailResponse(cancelRequest.cancelReason(), 180000, "2024-01-02T10:01:00+05:00")) -// // ); -// // -// // when(paymentRepository.findByPaymentKey(paymentKey)).thenReturn(Optional.of(payment)); -// // when(restTemplate.postForObject(any(), any(HttpEntity.class), eq(PaymentCancelResponse.class))) -// // .thenReturn(mockCancelResponse); -// // -// // // when -// // PaymentCancelResponse response = paymentService.cancelPayment(paymentKey, cancelRequest); -// // -// // // then -// // verify(restTemplate, times(1)).postForObject(any(), any(HttpEntity.class), eq(PaymentCancelResponse.class)); -// // assertThat(payment.getStatus()).isEqualTo(PaymentStatus.CANCELED); -// // assertThat(booking.getStatus()).isEqualTo(BookingStatus.CANCELED); -// // } -// } +package com.pgms.apibooking.service; + +import static org.assertj.core.api.Assertions.*; + +import java.time.LocalDateTime; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.annotation.Import; +import org.springframework.transaction.annotation.Transactional; + +import com.pgms.apibooking.config.TestConfig; +import com.pgms.apibooking.domain.payment.dto.response.PaymentSuccessResponse; +import com.pgms.apibooking.domain.payment.service.PaymentService; +import com.pgms.apibooking.factory.BookingFactory; +import com.pgms.apibooking.factory.EventFactory; +import com.pgms.apibooking.factory.EventHallFactory; +import com.pgms.apibooking.factory.EventSeatAreaFactory; +import com.pgms.apibooking.factory.EventSeatFactory; +import com.pgms.apibooking.factory.EventTimeFactory; +import com.pgms.apibooking.factory.PaymentFactory; +import com.pgms.apibooking.factory.TicketFactory; +import com.pgms.coredomain.domain.booking.Booking; +import com.pgms.coredomain.domain.booking.BookingStatus; +import com.pgms.coredomain.domain.booking.Payment; +import com.pgms.coredomain.domain.booking.PaymentMethod; +import com.pgms.coredomain.domain.booking.PaymentStatus; +import com.pgms.coredomain.domain.booking.Ticket; +import com.pgms.coredomain.domain.booking.repository.BookingRepository; +import com.pgms.coredomain.domain.booking.repository.PaymentRepository; +import com.pgms.coredomain.domain.event.Event; +import com.pgms.coredomain.domain.event.EventHall; +import com.pgms.coredomain.domain.event.EventSeat; +import com.pgms.coredomain.domain.event.EventSeatArea; +import com.pgms.coredomain.domain.event.EventSeatStatus; +import com.pgms.coredomain.domain.event.EventTime; +import com.pgms.coredomain.domain.event.SeatAreaType; +import com.pgms.coredomain.domain.event.repository.EventHallRepository; +import com.pgms.coredomain.domain.event.repository.EventRepository; +import com.pgms.coredomain.domain.event.repository.EventSeatAreaRepository; +import com.pgms.coredomain.domain.event.repository.EventSeatRepository; +import com.pgms.coredomain.domain.event.repository.EventTimeRepository; +import com.pgms.coredomain.domain.member.Member; +import com.pgms.coredomain.domain.member.enums.Provider; +import com.pgms.coredomain.domain.member.repository.MemberRepository; + +@SpringBootTest +@Import(TestConfig.class) +@Transactional +class PaymentServiceTest { + + private static final LocalDateTime NOW = LocalDateTime.now(); + + @Autowired + private EventHallRepository eventHallRepository; + + @Autowired + private EventTimeRepository eventTimeRepository; + + @Autowired + private EventRepository eventRepository; + + @Autowired + private EventSeatAreaRepository eventSeatAreaRepository; + + @Autowired + private BookingRepository bookingRepository; + + @Autowired + private PaymentRepository paymentRepository; + @Autowired + private EventSeatRepository eventSeatRepository; + @Autowired + private MemberRepository memberRepository; + + @Autowired + private PaymentService paymentService; + + private Member member; + private Booking booking; + private static final String PAYMENT_KEY = "paymentTestKey"; + + @BeforeEach + public void setUp() { + member = memberRepository.save(Member.builder() + .email("test@gmail.com") + .password("test1234!") + .name("홍길동") + .provider(Provider.KAKAO) + .phoneNumber("010-123-456") + .build()); + } + + @Test + public void 예매_카드_결제를_진행한다() { + // given + LocalDateTime eventStartedAt = NOW.plusDays(2); + LocalDateTime eventEndedAt = NOW.plusDays(2).plusMinutes(120); + LocalDateTime bookingStartedAt = NOW; + LocalDateTime bookingEndedAt = NOW.plusDays(1); + + EventHall hall = EventHallFactory.generate(); + eventHallRepository.save(hall); + + Event event = EventFactory.generate( + hall, + eventStartedAt, + eventEndedAt, + bookingStartedAt, + bookingEndedAt + ); + eventRepository.save(event); + + EventTime time = EventTimeFactory.generate(event, eventStartedAt, eventEndedAt); + eventTimeRepository.save(time); + + EventSeatArea area = EventSeatAreaFactory.generate(event, SeatAreaType.S); + eventSeatAreaRepository.save(area); + + EventSeat seat = EventSeatFactory.generate(time, area, "A1", EventSeatStatus.AVAILABLE); + eventSeatRepository.save(seat); + + Booking booking = BookingFactory.generate( + member, + time, + seat.getEventSeatArea().getPrice(), + BookingStatus.WAITING_FOR_PAYMENT + ); + Ticket ticket = TicketFactory.generate(seat); + booking.addTicket(ticket); + Payment payment = PaymentFactory.generate( + PaymentMethod.CARD, + booking.getAmount(), + PaymentStatus.READY + ); + booking.updatePayment(payment); + bookingRepository.save(booking); + + //when + PaymentSuccessResponse response = paymentService.successPayment(PAYMENT_KEY, booking.getId(), + payment.getAmount()); + + //then + payment = paymentRepository.findByPaymentKey(PAYMENT_KEY).get(); + booking = bookingRepository.findWithPaymentById(booking.getId()).get(); + + assertThat(response.status()).isEqualTo(PaymentStatus.DONE.toString()); + assertThat(response.card().number()).isEqualTo(payment.getCardNumber()); + + assertThat(booking.getStatus()).isEqualTo(BookingStatus.PAYMENT_COMPLETED); + assertThat(payment.getAccountNumber()).isNull(); + } + + // @Test + // public void 예매가격_결제가격_불일치시_결제를_진행할_수_없다() { + // // given + // String paymentKey = "paymentKey"; + // Payment payment = Payment.builder() + // .method(PaymentMethod.CARD) + // .amount(booking.getAmount()) + // .status(PaymentStatus.READY) + // .build(); + // booking.updatePayment(payment); + // when(bookingRepository.findWithPaymentById(anyString())).thenReturn(Optional.of(booking)); + // + // // when & then + // assertThatThrownBy(() -> paymentService.successPayment(paymentKey, booking.getId(), 170000)) + // .isInstanceOf(BookingException.class) + // .hasMessageContaining(BookingErrorCode.PAYMENT_AMOUNT_MISMATCH.getMessage()); + // } + // + // @Test + // public void 카드_결제_취소_테스트() { + // String paymentKey = "paymentKey"; + // Payment payment = Payment.builder() + // .method(PaymentMethod.CARD) + // .amount(booking.getAmount()) + // .status(PaymentStatus.READY) + // .build(); + // booking.updatePayment(payment); + // + // BookingCancelRequest cancelRequest = new BookingCancelRequest( + // "고객이 결제 취소", + // 180000, + // Optional.of(new RefundAccountRequest("20", "352-123", "김환불")) + // ); + // + // PaymentCancelResponse mockCancelResponse = new PaymentCancelResponse( + // paymentKey, + // booking.getId(), + // booking.getBookingName(), + // payment.getMethod().getDescription(), + // "180000", + // "CANCELED", + // "2024-01-01T10:01:00+05:00", + // "2024-01-01T10:01:00+05:00", + // new PaymentCardResponse("card_num", 2, false), + // null, + // List.of(new PaymentCancelDetailResponse(cancelRequest.cancelReason(), 180000, "2024-01-02T10:01:00+05:00")) + // ); + // + // when(paymentRepository.findByPaymentKey(paymentKey)).thenReturn(Optional.of(payment)); + // when(restTemplate.postForObject(any(), any(HttpEntity.class), eq(PaymentCancelResponse.class))) + // .thenReturn(mockCancelResponse); + // + // // when + // PaymentCancelResponse response = paymentService.cancelPayment(paymentKey, cancelRequest); + // + // // then + // verify(restTemplate, times(1)).postForObject(any(), any(HttpEntity.class), eq(PaymentCancelResponse.class)); + // assertThat(payment.getStatus()).isEqualTo(PaymentStatus.CANCELED); + // assertThat(booking.getStatus()).isEqualTo(BookingStatus.CANCELED); + // } +} From e8ccb97c946a107bcb1698e67f5f209953cecdee Mon Sep 17 00:00:00 2001 From: byulcode Date: Fri, 12 Jan 2024 22:55:19 +0900 Subject: [PATCH 2/3] =?UTF-8?q?test:=20=EA=B2=B0=EC=A0=9C=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=EC=BD=94=EB=93=9C=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../response/PaymentCancelDetailResponse.java | 2 +- .../dto/response/PaymentCancelResponse.java | 2 +- .../service/PaymentServiceTest.java | 265 +++++++++++++----- 3 files changed, 199 insertions(+), 70 deletions(-) diff --git a/api/api-booking/src/main/java/com/pgms/apibooking/domain/payment/dto/response/PaymentCancelDetailResponse.java b/api/api-booking/src/main/java/com/pgms/apibooking/domain/payment/dto/response/PaymentCancelDetailResponse.java index b338fdb1..286ee1c3 100644 --- a/api/api-booking/src/main/java/com/pgms/apibooking/domain/payment/dto/response/PaymentCancelDetailResponse.java +++ b/api/api-booking/src/main/java/com/pgms/apibooking/domain/payment/dto/response/PaymentCancelDetailResponse.java @@ -2,7 +2,7 @@ public record PaymentCancelDetailResponse( String cancelReason, - int cancelAmount, + Integer cancelAmount, String canceledAt ) { } diff --git a/api/api-booking/src/main/java/com/pgms/apibooking/domain/payment/dto/response/PaymentCancelResponse.java b/api/api-booking/src/main/java/com/pgms/apibooking/domain/payment/dto/response/PaymentCancelResponse.java index 790e8b68..62e3d713 100644 --- a/api/api-booking/src/main/java/com/pgms/apibooking/domain/payment/dto/response/PaymentCancelResponse.java +++ b/api/api-booking/src/main/java/com/pgms/apibooking/domain/payment/dto/response/PaymentCancelResponse.java @@ -5,7 +5,7 @@ public record PaymentCancelResponse( String paymentKey, String orderId, - String orderName, // 토스페이먼츠에서 제공해주는 값이라 booing으로 변경x + String orderName, // 토스페이먼츠에서 제공해주는 값이라 booking으로 변경x String method, int totalAmount, String status, diff --git a/api/api-booking/src/test/java/com/pgms/apibooking/service/PaymentServiceTest.java b/api/api-booking/src/test/java/com/pgms/apibooking/service/PaymentServiceTest.java index 4443bc7d..d1a42b7f 100644 --- a/api/api-booking/src/test/java/com/pgms/apibooking/service/PaymentServiceTest.java +++ b/api/api-booking/src/test/java/com/pgms/apibooking/service/PaymentServiceTest.java @@ -3,6 +3,7 @@ import static org.assertj.core.api.Assertions.*; import java.time.LocalDateTime; +import java.util.Optional; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -11,7 +12,9 @@ import org.springframework.context.annotation.Import; import org.springframework.transaction.annotation.Transactional; +import com.pgms.apibooking.common.exception.BookingException; import com.pgms.apibooking.config.TestConfig; +import com.pgms.apibooking.domain.payment.dto.request.PaymentCancelRequest; import com.pgms.apibooking.domain.payment.dto.response.PaymentSuccessResponse; import com.pgms.apibooking.domain.payment.service.PaymentService; import com.pgms.apibooking.factory.BookingFactory; @@ -22,14 +25,17 @@ import com.pgms.apibooking.factory.EventTimeFactory; import com.pgms.apibooking.factory.PaymentFactory; import com.pgms.apibooking.factory.TicketFactory; +import com.pgms.coredomain.domain.booking.BankCode; import com.pgms.coredomain.domain.booking.Booking; import com.pgms.coredomain.domain.booking.BookingStatus; +import com.pgms.coredomain.domain.booking.CardIssuer; import com.pgms.coredomain.domain.booking.Payment; import com.pgms.coredomain.domain.booking.PaymentMethod; import com.pgms.coredomain.domain.booking.PaymentStatus; import com.pgms.coredomain.domain.booking.Ticket; import com.pgms.coredomain.domain.booking.repository.BookingRepository; import com.pgms.coredomain.domain.booking.repository.PaymentRepository; +import com.pgms.coredomain.domain.common.BookingErrorCode; import com.pgms.coredomain.domain.event.Event; import com.pgms.coredomain.domain.event.EventHall; import com.pgms.coredomain.domain.event.EventSeat; @@ -70,8 +76,10 @@ class PaymentServiceTest { @Autowired private PaymentRepository paymentRepository; + @Autowired private EventSeatRepository eventSeatRepository; + @Autowired private MemberRepository memberRepository; @@ -79,7 +87,6 @@ class PaymentServiceTest { private PaymentService paymentService; private Member member; - private Booking booking; private static final String PAYMENT_KEY = "paymentTestKey"; @BeforeEach @@ -94,7 +101,7 @@ public void setUp() { } @Test - public void 예매_카드_결제를_진행한다() { + public void 카드_결제를_진행한다() { // given LocalDateTime eventStartedAt = NOW.plusDays(2); LocalDateTime eventEndedAt = NOW.plusDays(2).plusMinutes(120); @@ -138,79 +145,201 @@ public void setUp() { booking.updatePayment(payment); bookingRepository.save(booking); - //when + // when PaymentSuccessResponse response = paymentService.successPayment(PAYMENT_KEY, booking.getId(), - payment.getAmount()); + booking.getAmount()); - //then + // then payment = paymentRepository.findByPaymentKey(PAYMENT_KEY).get(); booking = bookingRepository.findWithPaymentById(booking.getId()).get(); - assertThat(response.status()).isEqualTo(PaymentStatus.DONE.toString()); - assertThat(response.card().number()).isEqualTo(payment.getCardNumber()); - + assertThat(payment.getStatus()).isEqualTo(PaymentStatus.DONE); assertThat(booking.getStatus()).isEqualTo(BookingStatus.PAYMENT_COMPLETED); + + assertThat(response.paymentKey()).isEqualTo(payment.getPaymentKey()); + assertThat(response.card().number()).isEqualTo(payment.getCardNumber()); + assertThat(CardIssuer.fromOfficialCode(response.card().issuerCode())).isEqualTo(payment.getCardIssuer()); assertThat(payment.getAccountNumber()).isNull(); } - // @Test - // public void 예매가격_결제가격_불일치시_결제를_진행할_수_없다() { - // // given - // String paymentKey = "paymentKey"; - // Payment payment = Payment.builder() - // .method(PaymentMethod.CARD) - // .amount(booking.getAmount()) - // .status(PaymentStatus.READY) - // .build(); - // booking.updatePayment(payment); - // when(bookingRepository.findWithPaymentById(anyString())).thenReturn(Optional.of(booking)); - // - // // when & then - // assertThatThrownBy(() -> paymentService.successPayment(paymentKey, booking.getId(), 170000)) - // .isInstanceOf(BookingException.class) - // .hasMessageContaining(BookingErrorCode.PAYMENT_AMOUNT_MISMATCH.getMessage()); - // } - // - // @Test - // public void 카드_결제_취소_테스트() { - // String paymentKey = "paymentKey"; - // Payment payment = Payment.builder() - // .method(PaymentMethod.CARD) - // .amount(booking.getAmount()) - // .status(PaymentStatus.READY) - // .build(); - // booking.updatePayment(payment); - // - // BookingCancelRequest cancelRequest = new BookingCancelRequest( - // "고객이 결제 취소", - // 180000, - // Optional.of(new RefundAccountRequest("20", "352-123", "김환불")) - // ); - // - // PaymentCancelResponse mockCancelResponse = new PaymentCancelResponse( - // paymentKey, - // booking.getId(), - // booking.getBookingName(), - // payment.getMethod().getDescription(), - // "180000", - // "CANCELED", - // "2024-01-01T10:01:00+05:00", - // "2024-01-01T10:01:00+05:00", - // new PaymentCardResponse("card_num", 2, false), - // null, - // List.of(new PaymentCancelDetailResponse(cancelRequest.cancelReason(), 180000, "2024-01-02T10:01:00+05:00")) - // ); - // - // when(paymentRepository.findByPaymentKey(paymentKey)).thenReturn(Optional.of(payment)); - // when(restTemplate.postForObject(any(), any(HttpEntity.class), eq(PaymentCancelResponse.class))) - // .thenReturn(mockCancelResponse); - // - // // when - // PaymentCancelResponse response = paymentService.cancelPayment(paymentKey, cancelRequest); - // - // // then - // verify(restTemplate, times(1)).postForObject(any(), any(HttpEntity.class), eq(PaymentCancelResponse.class)); - // assertThat(payment.getStatus()).isEqualTo(PaymentStatus.CANCELED); - // assertThat(booking.getStatus()).isEqualTo(BookingStatus.CANCELED); - // } + @Test + public void 예매가격_결제가격_불일치시_결제를_진행할_수_없다() { + // given + LocalDateTime eventStartedAt = NOW.plusDays(2); + LocalDateTime eventEndedAt = NOW.plusDays(2).plusMinutes(120); + LocalDateTime bookingStartedAt = NOW; + LocalDateTime paymentRequestedAt = NOW.plusMinutes(2); + LocalDateTime bookingEndedAt = NOW.plusDays(1); + + EventHall hall = EventHallFactory.generate(); + eventHallRepository.save(hall); + + Event event = EventFactory.generate( + hall, + eventStartedAt, + eventEndedAt, + bookingStartedAt, + bookingEndedAt + ); + eventRepository.save(event); + + EventTime time = EventTimeFactory.generate(event, eventStartedAt, eventEndedAt); + eventTimeRepository.save(time); + + EventSeatArea area = EventSeatAreaFactory.generate(event, SeatAreaType.S); + eventSeatAreaRepository.save(area); + + EventSeat seat = EventSeatFactory.generate(time, area, "A1", EventSeatStatus.AVAILABLE); + eventSeatRepository.save(seat); + + Booking booking = BookingFactory.generate( + member, + time, + seat.getEventSeatArea().getPrice(), + BookingStatus.WAITING_FOR_PAYMENT + ); + Ticket ticket = TicketFactory.generate(seat); + booking.addTicket(ticket); + + int actualAmount = booking.getAmount(); + int notSameAmount = 1000; + + Payment payment = PaymentFactory.generate( + PaymentMethod.CARD, + notSameAmount, + PaymentStatus.READY + ); + booking.updatePayment(payment); + bookingRepository.save(booking); + + // when & then + assertThatThrownBy(() -> paymentService.successPayment(PAYMENT_KEY, booking.getId(), actualAmount)) + .isInstanceOf(BookingException.class) + .hasMessageContaining(BookingErrorCode.PAYMENT_AMOUNT_MISMATCH.getMessage()); + } + + @Test + public void 카드_결제를_취소한다() { + // given + LocalDateTime eventStartedAt = NOW.plusDays(2); + LocalDateTime eventEndedAt = NOW.plusDays(2).plusMinutes(120); + LocalDateTime bookingStartedAt = NOW; + LocalDateTime bookingEndedAt = NOW.plusDays(1); + LocalDateTime paymentRequestedAt = NOW.plusMinutes(2); + + EventHall hall = EventHallFactory.generate(); + eventHallRepository.save(hall); + + Event event = EventFactory.generate( + hall, + eventStartedAt, + eventEndedAt, + bookingStartedAt, + bookingEndedAt + ); + eventRepository.save(event); + + EventTime time = EventTimeFactory.generate(event, eventStartedAt, eventEndedAt); + eventTimeRepository.save(time); + + EventSeatArea area = EventSeatAreaFactory.generate(event, SeatAreaType.S); + eventSeatAreaRepository.save(area); + + EventSeat seat = EventSeatFactory.generate(time, area, "A1", EventSeatStatus.AVAILABLE); + eventSeatRepository.save(seat); + + Booking booking = BookingFactory.generate( + member, + time, + seat.getEventSeatArea().getPrice(), + BookingStatus.PAYMENT_COMPLETED + ); + Ticket ticket = TicketFactory.generate(seat); + booking.addTicket(ticket); + + Payment payment = PaymentFactory.generate( + PaymentMethod.CARD, + booking.getAmount(), + PaymentStatus.DONE + ); + payment.updateConfirmInfo(PAYMENT_KEY, paymentRequestedAt); + booking.updatePayment(payment); + booking = bookingRepository.save(booking); + + // when + paymentService.cancelPayment(PAYMENT_KEY, new PaymentCancelRequest( + "단순 변심", + payment.getAmount(), + Optional.empty() + )); + + // then + booking = bookingRepository.findBookingInfoById(booking.getId()).get(); + payment = paymentRepository.findByPaymentKey(PAYMENT_KEY).get(); + + assertThat(payment.getStatus()).isEqualTo(PaymentStatus.CANCELED); + assertThat(booking.getStatus()).isEqualTo(BookingStatus.CANCELED); + assertThat(payment.getRefundAccountNumber()).isNull(); + } + + @Test + public void 가상계좌_결제를_진행한다() { + // given + LocalDateTime eventStartedAt = NOW.plusDays(2); + LocalDateTime eventEndedAt = NOW.plusDays(2).plusMinutes(120); + LocalDateTime bookingStartedAt = NOW; + LocalDateTime bookingEndedAt = NOW.plusDays(1); + + EventHall hall = EventHallFactory.generate(); + eventHallRepository.save(hall); + + Event event = EventFactory.generate( + hall, + eventStartedAt, + eventEndedAt, + bookingStartedAt, + bookingEndedAt + ); + eventRepository.save(event); + + EventTime time = EventTimeFactory.generate(event, eventStartedAt, eventEndedAt); + eventTimeRepository.save(time); + + EventSeatArea area = EventSeatAreaFactory.generate(event, SeatAreaType.S); + eventSeatAreaRepository.save(area); + + EventSeat seat = EventSeatFactory.generate(time, area, "A1", EventSeatStatus.AVAILABLE); + eventSeatRepository.save(seat); + + Booking booking = BookingFactory.generate( + member, + time, + seat.getEventSeatArea().getPrice(), + BookingStatus.WAITING_FOR_PAYMENT + ); + Ticket ticket = TicketFactory.generate(seat); + booking.addTicket(ticket); + Payment payment = PaymentFactory.generate( + PaymentMethod.VIRTUAL_ACCOUNT, + booking.getAmount(), + PaymentStatus.READY + ); + booking.updatePayment(payment); + bookingRepository.save(booking); + + // when + PaymentSuccessResponse response = paymentService.successPayment(PAYMENT_KEY, booking.getId(), + booking.getAmount()); + + // then + payment = paymentRepository.findByPaymentKey(PAYMENT_KEY).get(); + booking = bookingRepository.findWithPaymentById(booking.getId()).get(); + + assertThat(payment.getStatus()).isEqualTo(PaymentStatus.DONE); + assertThat(booking.getStatus()).isEqualTo(BookingStatus.WAITING_FOR_PAYMENT); + + assertThat(payment.getAccountNumber()).isEqualTo(response.virtualAccount().accountNumber()); + assertThat(payment.getBankCode()).isEqualTo(BankCode.getByBankNumCode(response.virtualAccount().bankCode())); + assertThat(payment.getDepositorName()).isEqualTo(response.virtualAccount().customerName()); + } } From 61728a7c66b4dd044c4f4dad47e7d3c852837023 Mon Sep 17 00:00:00 2001 From: byulcode Date: Sat, 13 Jan 2024 03:25:53 +0900 Subject: [PATCH 3/3] =?UTF-8?q?test:=20=EA=B2=B0=EC=A0=9C=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80=20?= =?UTF-8?q?=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../booking/service/BookingService.java | 10 +- .../payment/service/PaymentService.java | 5 +- .../fake/TossPaymentServiceFake.java | 2 +- .../service/BookingServiceTest.java | 2 +- .../service/PaymentServiceTest.java | 134 +++++++++++++++++- 5 files changed, 139 insertions(+), 14 deletions(-) diff --git a/api/api-booking/src/main/java/com/pgms/apibooking/domain/booking/service/BookingService.java b/api/api-booking/src/main/java/com/pgms/apibooking/domain/booking/service/BookingService.java index 0f5f6968..3055dfcc 100644 --- a/api/api-booking/src/main/java/com/pgms/apibooking/domain/booking/service/BookingService.java +++ b/api/api-booking/src/main/java/com/pgms/apibooking/domain/booking/service/BookingService.java @@ -230,14 +230,14 @@ private void validateRefundReceiveAccount(PaymentMethod paymentMethod, PaymentSt } public Integer calculateRefundAmount(Booking booking) { + Payment payment = booking.getPayment(); if (booking.isPaid()) return booking.getAmount(); - - Payment payment = booking.getPayment(); - return (payment.getMethod() == PaymentMethod.VIRTUAL_ACCOUNT && + else if (payment.getMethod() == PaymentMethod.VIRTUAL_ACCOUNT && payment.getStatus() == PaymentStatus.WAITING_FOR_DEPOSIT) - ? null - : 0; + return null; + else + throw new BookingException(BookingErrorCode.UNCANCELABLE_BOOKING); } private Member getMemberById(Long memberId) { diff --git a/api/api-booking/src/main/java/com/pgms/apibooking/domain/payment/service/PaymentService.java b/api/api-booking/src/main/java/com/pgms/apibooking/domain/payment/service/PaymentService.java index 4b60aee6..70d53996 100644 --- a/api/api-booking/src/main/java/com/pgms/apibooking/domain/payment/service/PaymentService.java +++ b/api/api-booking/src/main/java/com/pgms/apibooking/domain/payment/service/PaymentService.java @@ -76,7 +76,7 @@ public PaymentSuccessResponse successPayment(String paymentKey, String bookingId return response; } - public PaymentFailResponse failPayment(String errorCode, String errorMessage, String bookingId) {//TODO : booking 상태값 변경 + public PaymentFailResponse failPayment(String errorCode, String errorMessage, String bookingId) { Payment payment = getPaymentByBookingId(bookingId); payment.updateStatus(PaymentStatus.ABORTED); payment.updateFailedMsg(errorMessage); @@ -108,9 +108,8 @@ public void confirmVirtualAccountIncome(ConfirmVirtualIncomeRequest request) { } private Booking getBookingById(String bookingId) { - Booking booking = bookingRepository.findWithPaymentById(bookingId) + return bookingRepository.findWithPaymentById(bookingId) .orElseThrow(() -> new BookingException(BookingErrorCode.BOOKING_NOT_FOUND)); - return booking; } private Payment getPaymentByPaymentKey(String paymentKey) { diff --git a/api/api-booking/src/test/java/com/pgms/apibooking/fake/TossPaymentServiceFake.java b/api/api-booking/src/test/java/com/pgms/apibooking/fake/TossPaymentServiceFake.java index 56d639ab..6b30e5fb 100644 --- a/api/api-booking/src/test/java/com/pgms/apibooking/fake/TossPaymentServiceFake.java +++ b/api/api-booking/src/test/java/com/pgms/apibooking/fake/TossPaymentServiceFake.java @@ -72,7 +72,7 @@ public PaymentSuccessResponse requestTossPaymentConfirmation(PaymentConfirmReque "X6505636518308", "20", "박토스", - NOW.plusDays(7).toString() + NOW.plusDays(7).format(formatter) ) ); } diff --git a/api/api-booking/src/test/java/com/pgms/apibooking/service/BookingServiceTest.java b/api/api-booking/src/test/java/com/pgms/apibooking/service/BookingServiceTest.java index 8b1658e5..379cc64f 100644 --- a/api/api-booking/src/test/java/com/pgms/apibooking/service/BookingServiceTest.java +++ b/api/api-booking/src/test/java/com/pgms/apibooking/service/BookingServiceTest.java @@ -583,7 +583,7 @@ void setup() { } @Test - void 결제_수단이_가상게좌인데_환불계좌정보가_없다면_예매를_취소할_수_없다() { + void 결제_수단이_가상계좌이고_입금되었는데_환불계좌정보가_없다면_예매를_취소할_수_없다() { // given LocalDateTime eventStartedAt = NOW.plusDays(2); LocalDateTime eventEndedAt = NOW.plusDays(2).plusMinutes(120); diff --git a/api/api-booking/src/test/java/com/pgms/apibooking/service/PaymentServiceTest.java b/api/api-booking/src/test/java/com/pgms/apibooking/service/PaymentServiceTest.java index d1a42b7f..ff99f38c 100644 --- a/api/api-booking/src/test/java/com/pgms/apibooking/service/PaymentServiceTest.java +++ b/api/api-booking/src/test/java/com/pgms/apibooking/service/PaymentServiceTest.java @@ -15,6 +15,7 @@ import com.pgms.apibooking.common.exception.BookingException; import com.pgms.apibooking.config.TestConfig; import com.pgms.apibooking.domain.payment.dto.request.PaymentCancelRequest; +import com.pgms.apibooking.domain.payment.dto.request.RefundAccountRequest; import com.pgms.apibooking.domain.payment.dto.response.PaymentSuccessResponse; import com.pgms.apibooking.domain.payment.service.PaymentService; import com.pgms.apibooking.factory.BookingFactory; @@ -219,7 +220,7 @@ public void setUp() { } @Test - public void 카드_결제를_취소한다() { + public void 결제_수단이_카드_결제일때_결제를_취소한다() { // given LocalDateTime eventStartedAt = NOW.plusDays(2); LocalDateTime eventEndedAt = NOW.plusDays(2).plusMinutes(120); @@ -264,7 +265,7 @@ public void setUp() { ); payment.updateConfirmInfo(PAYMENT_KEY, paymentRequestedAt); booking.updatePayment(payment); - booking = bookingRepository.save(booking); + bookingRepository.save(booking); // when paymentService.cancelPayment(PAYMENT_KEY, new PaymentCancelRequest( @@ -274,12 +275,11 @@ public void setUp() { )); // then - booking = bookingRepository.findBookingInfoById(booking.getId()).get(); payment = paymentRepository.findByPaymentKey(PAYMENT_KEY).get(); + booking = payment.getBooking(); assertThat(payment.getStatus()).isEqualTo(PaymentStatus.CANCELED); assertThat(booking.getStatus()).isEqualTo(BookingStatus.CANCELED); - assertThat(payment.getRefundAccountNumber()).isNull(); } @Test @@ -342,4 +342,130 @@ public void setUp() { assertThat(payment.getBankCode()).isEqualTo(BankCode.getByBankNumCode(response.virtualAccount().bankCode())); assertThat(payment.getDepositorName()).isEqualTo(response.virtualAccount().customerName()); } + + @Test + public void 결제_수단이_가상계좌이고_입금전일때_결제를_취소한다() { + // given + LocalDateTime eventStartedAt = NOW.plusDays(2); + LocalDateTime eventEndedAt = NOW.plusDays(2).plusMinutes(120); + LocalDateTime bookingStartedAt = NOW; + LocalDateTime bookingEndedAt = NOW.plusDays(1); + LocalDateTime paymentRequestedAt = NOW.plusMinutes(2); + + EventHall hall = EventHallFactory.generate(); + eventHallRepository.save(hall); + + Event event = EventFactory.generate( + hall, + eventStartedAt, + eventEndedAt, + bookingStartedAt, + bookingEndedAt + ); + eventRepository.save(event); + + EventTime time = EventTimeFactory.generate(event, eventStartedAt, eventEndedAt); + eventTimeRepository.save(time); + + EventSeatArea area = EventSeatAreaFactory.generate(event, SeatAreaType.S); + eventSeatAreaRepository.save(area); + + EventSeat seat = EventSeatFactory.generate(time, area, "A1", EventSeatStatus.AVAILABLE); + eventSeatRepository.save(seat); + + Booking booking = BookingFactory.generate( + member, + time, + seat.getEventSeatArea().getPrice(), + BookingStatus.WAITING_FOR_PAYMENT + ); + Ticket ticket = TicketFactory.generate(seat); + booking.addTicket(ticket); + + Payment payment = PaymentFactory.generate( + PaymentMethod.VIRTUAL_ACCOUNT, + booking.getAmount(), + PaymentStatus.WAITING_FOR_DEPOSIT + ); + payment.updateConfirmInfo(PAYMENT_KEY, paymentRequestedAt); + booking.updatePayment(payment); + bookingRepository.save(booking); + + // when + paymentService.cancelPayment(PAYMENT_KEY, new PaymentCancelRequest( + "단순 변심", + null, + Optional.empty() + )); + + // then + payment = paymentRepository.findByPaymentKey(PAYMENT_KEY).get(); + booking = payment.getBooking(); + + assertThat(payment.getStatus()).isEqualTo(PaymentStatus.CANCELED); + assertThat(booking.getStatus()).isEqualTo(BookingStatus.CANCELED); + } + + @Test + public void 결제_수단이_가상계좌이고_입금되었을때_결제를_취소한다() { + // given + LocalDateTime eventStartedAt = NOW.plusDays(2); + LocalDateTime eventEndedAt = NOW.plusDays(2).plusMinutes(120); + LocalDateTime bookingStartedAt = NOW; + LocalDateTime bookingEndedAt = NOW.plusDays(1); + LocalDateTime paymentRequestedAt = NOW.plusMinutes(2); + + EventHall hall = EventHallFactory.generate(); + eventHallRepository.save(hall); + + Event event = EventFactory.generate( + hall, + eventStartedAt, + eventEndedAt, + bookingStartedAt, + bookingEndedAt + ); + eventRepository.save(event); + + EventTime time = EventTimeFactory.generate(event, eventStartedAt, eventEndedAt); + eventTimeRepository.save(time); + + EventSeatArea area = EventSeatAreaFactory.generate(event, SeatAreaType.S); + eventSeatAreaRepository.save(area); + + EventSeat seat = EventSeatFactory.generate(time, area, "A1", EventSeatStatus.AVAILABLE); + eventSeatRepository.save(seat); + + Booking booking = BookingFactory.generate( + member, + time, + seat.getEventSeatArea().getPrice(), + BookingStatus.PAYMENT_COMPLETED + ); + Ticket ticket = TicketFactory.generate(seat); + booking.addTicket(ticket); + + Payment payment = PaymentFactory.generate( + PaymentMethod.VIRTUAL_ACCOUNT, + booking.getAmount(), + PaymentStatus.DONE + ); + payment.updateConfirmInfo(PAYMENT_KEY, paymentRequestedAt); + booking.updatePayment(payment); + bookingRepository.save(booking); + + // when + paymentService.cancelPayment(PAYMENT_KEY, new PaymentCancelRequest( + "단순 변심", + payment.getAmount(), + Optional.of(new RefundAccountRequest("20", "X650583171835", "박토스")) + )); + + // then + payment = paymentRepository.findByPaymentKey(PAYMENT_KEY).get(); + booking = payment.getBooking(); + + assertThat(payment.getStatus()).isEqualTo(PaymentStatus.CANCELED); + assertThat(booking.getStatus()).isEqualTo(BookingStatus.CANCELED); + } }