diff --git a/order/src/main/java/com/yas/order/controller/OrderController.java b/order/src/main/java/com/yas/order/controller/OrderController.java index e74f178db5..7b3973c74d 100644 --- a/order/src/main/java/com/yas/order/controller/OrderController.java +++ b/order/src/main/java/com/yas/order/controller/OrderController.java @@ -66,6 +66,11 @@ public ResponseEntity getOrderWithItemsById(@PathVariable long id) { return ResponseEntity.ok(orderService.getOrderWithItemsById(id)); } + @GetMapping("/backoffice/orders/checkout/{id}") + public ResponseEntity getOrderWithCheckoutId(@PathVariable String id) { + return ResponseEntity.ok(orderService.findOrderVmByCheckoutId(id)); + } + @GetMapping("/backoffice/orders") public ResponseEntity getOrders( @RequestParam(value = "createdFrom", defaultValue = "#{new java.util.Date(1970-01-01)}", required = false) diff --git a/order/src/main/java/com/yas/order/service/OrderService.java b/order/src/main/java/com/yas/order/service/OrderService.java index 2cfdb94c4a..638821dfe6 100644 --- a/order/src/main/java/com/yas/order/service/OrderService.java +++ b/order/src/main/java/com/yas/order/service/OrderService.java @@ -214,6 +214,11 @@ public List getMyOrders(String productName, OrderStatus orderStatus) return orders.stream().map(OrderGetVm::fromModel).toList(); } + public OrderGetVm findOrderVmByCheckoutId(String checkoutId) { + Order order = this.findOrderByCheckoutId(checkoutId); + return OrderGetVm.fromModel(order); + } + public Order findOrderByCheckoutId(String checkoutId) { return this.orderRepository.findByCheckoutId(checkoutId) .orElseThrow(() -> new NotFoundException(ORDER_NOT_FOUND, "of checkoutId " + checkoutId)); diff --git a/order/src/main/java/com/yas/order/viewmodel/order/OrderVm.java b/order/src/main/java/com/yas/order/viewmodel/order/OrderVm.java index 52aa9c4289..6edcfb6388 100644 --- a/order/src/main/java/com/yas/order/viewmodel/order/OrderVm.java +++ b/order/src/main/java/com/yas/order/viewmodel/order/OrderVm.java @@ -28,12 +28,13 @@ public record OrderVm( DeliveryMethod deliveryMethod, DeliveryStatus deliveryStatus, PaymentStatus paymentStatus, - Set orderItemVms + Set orderItemVms, + String checkoutId ) { public static OrderVm fromModel(Order order) { Set orderItemVms = order.getOrderItems().stream().map( - item -> OrderItemVm.fromModel(item)) + OrderItemVm::fromModel) .collect(Collectors.toSet()); return OrderVm.builder() @@ -53,6 +54,7 @@ public static OrderVm fromModel(Order order) { .deliveryStatus(order.getDeliveryStatus()) .paymentStatus(order.getPaymentStatus()) .orderItemVms(orderItemVms) + .checkoutId(order.getCheckoutId()) .build(); } -} \ No newline at end of file +} diff --git a/order/src/test/java/com/yas/order/controller/OrderControllerTest.java b/order/src/test/java/com/yas/order/controller/OrderControllerTest.java index a7364133ae..b4532edad3 100644 --- a/order/src/test/java/com/yas/order/controller/OrderControllerTest.java +++ b/order/src/test/java/com/yas/order/controller/OrderControllerTest.java @@ -39,6 +39,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.UUID; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; @@ -225,8 +226,7 @@ void testGetLatestOrders_whenRequestIsValid_thenReturnOrderListVm() throws Excep } @Test - void testExportCsv_whenRequestIsValid_thenReturnCsvFile() throws Exception - { + void testExportCsv_whenRequestIsValid_thenReturnCsvFile() throws Exception { ObjectMapper mapper = new ObjectMapper(); mapper.registerModule(new JavaTimeModule()); OrderRequest orderRequest = new OrderRequest(); @@ -296,7 +296,8 @@ private OrderVm getOrderVm() { DeliveryMethod.GRAB_EXPRESS, DeliveryStatus.PREPARING, PaymentStatus.COMPLETED, - items + items, + UUID.randomUUID().toString() ); } @@ -412,4 +413,4 @@ private List getOrderItemPostVms() { return List.of(item1, item2); } -} \ No newline at end of file +} diff --git a/order/src/test/java/com/yas/order/service/CartServiceTest.java b/order/src/test/java/com/yas/order/service/CartServiceTest.java index 0e9b686df9..265d08147e 100644 --- a/order/src/test/java/com/yas/order/service/CartServiceTest.java +++ b/order/src/test/java/com/yas/order/service/CartServiceTest.java @@ -19,6 +19,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; +import java.util.UUID; import org.jetbrains.annotations.NotNull; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -83,7 +84,8 @@ void testDeleteCartItems_ifNormalCase_shouldNoException() { DeliveryMethod.GRAB_EXPRESS, DeliveryStatus.CANCELLED, PaymentStatus.PENDING, - items + items, + UUID.randomUUID().toString() ); } @@ -118,4 +120,4 @@ void testDeleteCartItems_ifNormalCase_shouldNoException() { items.add(item2); return items; } -} \ No newline at end of file +} diff --git a/payment-paypal/src/main/java/com/yas/paymentpaypal/config/ServiceUrlConfig.java b/payment-paypal/src/main/java/com/yas/paymentpaypal/config/ServiceUrlConfig.java index 4b861f5d88..1d838a8632 100644 --- a/payment-paypal/src/main/java/com/yas/paymentpaypal/config/ServiceUrlConfig.java +++ b/payment-paypal/src/main/java/com/yas/paymentpaypal/config/ServiceUrlConfig.java @@ -3,6 +3,5 @@ import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = "yas.services") -public record ServiceUrlConfig( - String payment) { +public record ServiceUrlConfig(String payment, String order) { } diff --git a/payment-paypal/src/main/java/com/yas/paymentpaypal/service/OrderService.java b/payment-paypal/src/main/java/com/yas/paymentpaypal/service/OrderService.java new file mode 100644 index 0000000000..b8fac47202 --- /dev/null +++ b/payment-paypal/src/main/java/com/yas/paymentpaypal/service/OrderService.java @@ -0,0 +1,41 @@ +package com.yas.paymentpaypal.service; + +import com.yas.paymentpaypal.config.ServiceUrlConfig; +import com.yas.paymentpaypal.viewmodel.CapturedPaymentVm; +import com.yas.paymentpaypal.viewmodel.OrderVm; +import io.github.resilience4j.circuitbreaker.annotation.CircuitBreaker; +import io.github.resilience4j.retry.annotation.Retry; +import java.net.URI; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.jwt.Jwt; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestClient; +import org.springframework.web.util.UriComponentsBuilder; + +@Service +@Slf4j +@RequiredArgsConstructor +public class OrderService extends AbstractCircuitBreakFallbackHandler { + private final RestClient restClient; + private final ServiceUrlConfig serviceUrlConfig; + + @Retry(name = "restApi") + @CircuitBreaker(name = "restCircuitBreaker", fallbackMethod = "handleBodilessFallback") + public OrderVm getOrderByCheckoutId(String checkoutId) { + final String jwt = + ((Jwt) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getTokenValue(); + final URI url = UriComponentsBuilder + .fromHttpUrl(serviceUrlConfig.order()) + .path("/backoffice/orders/checkout/" + checkoutId) + .buildAndExpand() + .toUri(); + + return restClient.get() + .uri(url) + .headers(h -> h.setBearerAuth(jwt)) + .retrieve() + .body(OrderVm.class); + } +} diff --git a/payment-paypal/src/main/java/com/yas/paymentpaypal/service/PaypalService.java b/payment-paypal/src/main/java/com/yas/paymentpaypal/service/PaypalService.java index 9ffac107b5..961ca8b62e 100644 --- a/payment-paypal/src/main/java/com/yas/paymentpaypal/service/PaypalService.java +++ b/payment-paypal/src/main/java/com/yas/paymentpaypal/service/PaypalService.java @@ -30,6 +30,7 @@ public class PaypalService { private final PayPalHttpClient payPalHttpClient; private final PaymentService paymentService; + private final OrderService orderService; private final BigDecimal maxPay = BigDecimal.valueOf(1000); @Value("${yas.public.url}/capture") private String returnUrl; @@ -51,12 +52,12 @@ public PaypalRequestPayment createPayment(RequestPayment requestPayment) { PurchaseUnitRequest purchaseUnitRequest = new PurchaseUnitRequest().amountWithBreakdown(amountWithBreakdown); orderRequest.purchaseUnits(List.of(purchaseUnitRequest)); ApplicationContext applicationContext = new ApplicationContext() - .returnUrl(returnUrl) - .cancelUrl(cancelUrl) - .brandName(Constants.Yas.BRAND_NAME) - .landingPage("BILLING") - .userAction("PAY_NOW") - .shippingPreference("NO_SHIPPING"); + .returnUrl(returnUrl) + .cancelUrl(cancelUrl) + .brandName(Constants.Yas.BRAND_NAME) + .landingPage("BILLING") + .userAction("PAY_NOW") + .shippingPreference("NO_SHIPPING"); orderRequest.applicationContext(applicationContext); OrdersCreateRequest ordersCreateRequest = new OrdersCreateRequest().requestBody(orderRequest); @@ -65,10 +66,10 @@ public PaypalRequestPayment createPayment(RequestPayment requestPayment) { HttpResponse orderHttpResponse = payPalHttpClient.execute(ordersCreateRequest); Order order = orderHttpResponse.result(); String redirectUrl = order.links().stream() - .filter(link -> "approve".equals(link.rel())) - .findFirst() - .orElseThrow(NoSuchElementException::new) - .href(); + .filter(link -> "approve".equals(link.rel())) + .findFirst() + .orElseThrow(NoSuchElementException::new) + .href(); CheckoutIdHelper.setCheckoutId(requestPayment.checkoutId()); return new PaypalRequestPayment("success", order.id(), redirectUrl); @@ -91,15 +92,17 @@ public CapturedPaymentVm capturePayment(String token) { BigDecimal paymentFee = new BigDecimal(paypalFee); BigDecimal amount = new BigDecimal(capture.amount().value()); + var orderVm = orderService.getOrderByCheckoutId(CheckoutIdHelper.getCheckoutId()); CapturedPaymentVm capturedPayment = CapturedPaymentVm.builder() - .paymentFee(paymentFee) - .gatewayTransactionId(order.id()) - .amount(amount) - .paymentStatus(order.status()) - .paymentMethod("PAYPAL") - .checkoutId(CheckoutIdHelper.getCheckoutId()) - .build(); + .orderId(orderVm.id()) + .paymentFee(paymentFee) + .gatewayTransactionId(order.id()) + .amount(amount) + .paymentStatus(order.status()) + .paymentMethod("PAYPAL") + .checkoutId(CheckoutIdHelper.getCheckoutId()) + .build(); paymentService.capturePayment(capturedPayment); return capturedPayment; @@ -110,4 +113,4 @@ public CapturedPaymentVm capturePayment(String token) { } return CapturedPaymentVm.builder().failureMessage("Something Wrong!").build(); } -} \ No newline at end of file +} diff --git a/payment-paypal/src/main/java/com/yas/paymentpaypal/viewmodel/OrderVm.java b/payment-paypal/src/main/java/com/yas/paymentpaypal/viewmodel/OrderVm.java new file mode 100644 index 0000000000..1336ef92b4 --- /dev/null +++ b/payment-paypal/src/main/java/com/yas/paymentpaypal/viewmodel/OrderVm.java @@ -0,0 +1,4 @@ +package com.yas.paymentpaypal.viewmodel; + +public record OrderVm(Long id) { +} diff --git a/payment-paypal/src/main/resources/application.properties b/payment-paypal/src/main/resources/application.properties index 9c28774bab..eec0d9e324 100644 --- a/payment-paypal/src/main/resources/application.properties +++ b/payment-paypal/src/main/resources/application.properties @@ -13,6 +13,7 @@ logging.pattern.level=%5p [${spring.application.name:},%X{traceId:-},%X{spanId:- spring.security.oauth2.resourceserver.jwt.issuer-uri=http://identity/realms/Yas yas.services.payment=http://api.yas.local/payment +yas.services.order=http://api.yas.local/order yas.public.url=http://storefront/complete-payment diff --git a/payment-paypal/src/test/java/com/yas/paymentpaypal/service/OrderServiceTest.java b/payment-paypal/src/test/java/com/yas/paymentpaypal/service/OrderServiceTest.java new file mode 100644 index 0000000000..f004dfe3c8 --- /dev/null +++ b/payment-paypal/src/test/java/com/yas/paymentpaypal/service/OrderServiceTest.java @@ -0,0 +1,60 @@ +package com.yas.paymentpaypal.service; + +import static com.yas.paymentpaypal.utils.SecurityContextUtils.setUpSecurityContext; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.yas.paymentpaypal.config.ServiceUrlConfig; +import java.net.URI; +import java.util.UUID; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; +import org.springframework.web.client.RestClient; +import org.springframework.web.util.UriComponentsBuilder; + +public class OrderServiceTest { + private RestClient restClient; + + private ServiceUrlConfig serviceUrlConfig; + + private OrderService orderService; + + private RestClient.ResponseSpec responseSpec; + + private static final String ORDER_URL = "http://api.yas.local/order"; + + @BeforeEach + void setUp() { + restClient = mock(RestClient.class); + serviceUrlConfig = mock(ServiceUrlConfig.class); + orderService = new OrderService(restClient, serviceUrlConfig); + responseSpec = Mockito.mock(RestClient.ResponseSpec.class); + setUpSecurityContext("test"); + when(serviceUrlConfig.order()).thenReturn(ORDER_URL); + } + + @Test + void testGetOrderByCheckoutId_ifNormalCase_returnOrderVm() { + String checkoutId = UUID.randomUUID().toString(); + + final URI url = UriComponentsBuilder + .fromHttpUrl(serviceUrlConfig.order()) + .path("/backoffice/orders/checkout/" + checkoutId) + .buildAndExpand() + .toUri(); + + RestClient.RequestHeadersUriSpec requestBodyUriSpec = mock(RestClient.RequestHeadersUriSpec.class); + when(restClient.get()).thenReturn(requestBodyUriSpec); + when(requestBodyUriSpec.uri(url)).thenReturn(requestBodyUriSpec); + when(requestBodyUriSpec.headers(any())).thenReturn(requestBodyUriSpec); + when(requestBodyUriSpec.retrieve()).thenReturn(responseSpec); + + orderService.getOrderByCheckoutId(checkoutId); + + verify(restClient, times(1)).get(); + } +} diff --git a/payment-paypal/src/test/java/com/yas/paymentpaypal/service/PaypalServiceTest.java b/payment-paypal/src/test/java/com/yas/paymentpaypal/service/PaypalServiceTest.java index d40ecd8164..5d0c0f707e 100644 --- a/payment-paypal/src/test/java/com/yas/paymentpaypal/service/PaypalServiceTest.java +++ b/payment-paypal/src/test/java/com/yas/paymentpaypal/service/PaypalServiceTest.java @@ -23,6 +23,7 @@ import com.paypal.orders.PurchaseUnit; import com.yas.paymentpaypal.model.CheckoutIdHelper; import com.yas.paymentpaypal.viewmodel.CapturedPaymentVm; +import com.yas.paymentpaypal.viewmodel.OrderVm; import com.yas.paymentpaypal.viewmodel.PaypalRequestPayment; import com.yas.paymentpaypal.viewmodel.RequestPayment; import java.io.IOException; @@ -41,11 +42,14 @@ class PaypalServiceTest { private PaypalService paypalService; + private OrderService orderService; + @BeforeEach void setUp() { payPalHttpClient = mock(PayPalHttpClient.class); paymentService = mock(PaymentService.class); - paypalService = new PaypalService(payPalHttpClient, paymentService); + orderService = mock(OrderService.class); + paypalService = new PaypalService(payPalHttpClient, paymentService, orderService); CheckoutIdHelper.setCheckoutId("test-checkout-id"); } @@ -136,8 +140,11 @@ void testCapturePayment_whenStatusNotNull_returnCapturedPaymentVm() throws IOExc .status("COMPLETED") .purchaseUnits(purchaseUnitList); + OrderVm orderVmRes = new OrderVm(12L); + HttpResponse mockResponse = mock(HttpResponse.class); when(payPalHttpClient.execute(any(OrdersCaptureRequest.class))).thenReturn(mockResponse); + when(orderService.getOrderByCheckoutId(any(String.class))).thenReturn(orderVmRes); when(mockResponse.result()).thenReturn(mockOrder); String token = "test-token-1"; @@ -172,4 +179,4 @@ void testCapturePayment_whenIoException_returnCapturedPaymentVm() throws IOExcep assertEquals("error message", result.failureMessage()); } -} \ No newline at end of file +} diff --git a/payment/src/main/java/com/yas/payment/service/OrderService.java b/payment/src/main/java/com/yas/payment/service/OrderService.java index feb51463b3..62e9322fa1 100644 --- a/payment/src/main/java/com/yas/payment/service/OrderService.java +++ b/payment/src/main/java/com/yas/payment/service/OrderService.java @@ -9,6 +9,8 @@ import java.net.URI; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.stereotype.Service; import org.springframework.web.client.RestClient; import org.springframework.web.util.UriComponentsBuilder; @@ -24,17 +26,20 @@ public class OrderService extends AbstractCircuitBreakFallbackHandler { @Retry(name = "restApi") @CircuitBreaker(name = "restCircuitBreaker", fallbackMethod = "handleLongFallback") public Long updateCheckoutStatus(CapturedPayment capturedPayment) { + final String jwt = + ((Jwt) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getTokenValue(); final URI url = UriComponentsBuilder - .fromHttpUrl(serviceUrlConfig.order()) - .path("/storefront/checkouts/status") - .buildAndExpand() - .toUri(); + .fromHttpUrl(serviceUrlConfig.order()) + .path("/storefront/checkouts/status") + .buildAndExpand() + .toUri(); CheckoutStatusVm checkoutStatusVm = new CheckoutStatusVm(capturedPayment.checkoutId(), capturedPayment.paymentStatus().name()); return restClient.put() - .uri(url) - .body(checkoutStatusVm) + .uri(url) + .headers(h -> h.setBearerAuth(jwt)) + .body(checkoutStatusVm) .retrieve() .body(Long.class); } @@ -42,18 +47,20 @@ public Long updateCheckoutStatus(CapturedPayment capturedPayment) { @Retry(name = "restApi") @CircuitBreaker(name = "restCircuitBreaker", fallbackMethod = "handlePaymentOrderStatusFallback") public PaymentOrderStatusVm updateOrderStatus(PaymentOrderStatusVm orderPaymentStatusVm) { - + final String jwt = + ((Jwt) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getTokenValue(); final URI url = UriComponentsBuilder - .fromHttpUrl(serviceUrlConfig.order()) - .path("/storefront/orders/status") - .buildAndExpand() - .toUri(); + .fromHttpUrl(serviceUrlConfig.order()) + .path("/storefront/orders/status") + .buildAndExpand() + .toUri(); return restClient.put() - .uri(url) - .body(orderPaymentStatusVm) - .retrieve() - .body(PaymentOrderStatusVm.class); + .uri(url) + .headers(h -> h.setBearerAuth(jwt)) + .body(orderPaymentStatusVm) + .retrieve() + .body(PaymentOrderStatusVm.class); } protected Long handleLongFallback(Throwable throwable) throws Throwable { diff --git a/payment/src/test/java/com/yas/payment/service/OrderServiceTest.java b/payment/src/test/java/com/yas/payment/service/OrderServiceTest.java index c7b485da37..ea9d16d4ab 100644 --- a/payment/src/test/java/com/yas/payment/service/OrderServiceTest.java +++ b/payment/src/test/java/com/yas/payment/service/OrderServiceTest.java @@ -17,6 +17,9 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.Mockito; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.web.client.RestClient; import org.springframework.web.util.UriComponentsBuilder; @@ -66,6 +69,7 @@ void testUpdateCheckoutStatus_whenNormalCase_returnLong() { when(restClient.put()).thenReturn(requestBodyUriSpec); when(requestBodyUriSpec.uri(url)).thenReturn(requestBodyUriSpec); when(requestBodyUriSpec.body(any(CheckoutStatusVm.class))).thenReturn(requestBodyUriSpec); + when(requestBodyUriSpec.headers(any())).thenReturn(requestBodyUriSpec); when(requestBodyUriSpec.retrieve()).thenReturn(responseSpec); when(responseSpec.body(Long.class)).thenReturn(1L); @@ -78,6 +82,7 @@ void testUpdateCheckoutStatus_whenNormalCase_returnLong() { @Test void testUpdateOrderStatus_whenNormalCase_returnPaymentOrderStatusVm() { + PaymentOrderStatusVm statusVm = PaymentOrderStatusVm.builder() .orderId(123456L) .orderStatus("COMPLETED") @@ -95,6 +100,7 @@ void testUpdateOrderStatus_whenNormalCase_returnPaymentOrderStatusVm() { when(restClient.put()).thenReturn(requestBodyUriSpec); when(requestBodyUriSpec.uri(url)).thenReturn(requestBodyUriSpec); when(requestBodyUriSpec.body(statusVm)).thenReturn(requestBodyUriSpec); + when(requestBodyUriSpec.headers(any())).thenReturn(requestBodyUriSpec); when(requestBodyUriSpec.retrieve()).thenReturn(responseSpec); when(responseSpec.body(PaymentOrderStatusVm.class)).thenReturn(statusVm); @@ -104,4 +110,4 @@ void testUpdateOrderStatus_whenNormalCase_returnPaymentOrderStatusVm() { assertThat(result.paymentId()).isEqualTo(78910L); assertThat(result.paymentStatus()).isEqualTo("SUCCESS"); } -} \ No newline at end of file +}