Skip to content

Commit

Permalink
#1214 [Storefront & Backoffice] Payment was successfully processed, b…
Browse files Browse the repository at this point in the history
…ut the status still shows as 'pending payment' in both the back office and storefront
  • Loading branch information
Danh Nguyen Van committed Oct 23, 2024
1 parent 9e83bcd commit ce22db4
Show file tree
Hide file tree
Showing 11 changed files with 179 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,11 @@ public ResponseEntity<OrderVm> getOrderWithItemsById(@PathVariable long id) {
return ResponseEntity.ok(orderService.getOrderWithItemsById(id));
}

@GetMapping("/backoffice/orders/checkout/{id}")
public ResponseEntity<OrderGetVm> getOrderWithCheckoutId(@PathVariable String id) {
return ResponseEntity.ok(orderService.findOrderVmByCheckoutId(id));
}

@GetMapping("/backoffice/orders")
public ResponseEntity<OrderListVm> getOrders(
@RequestParam(value = "createdFrom", defaultValue = "#{new java.util.Date(1970-01-01)}", required = false)
Expand Down
5 changes: 5 additions & 0 deletions order/src/main/java/com/yas/order/service/OrderService.java
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,11 @@ public List<OrderGetVm> 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));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,13 @@ public record OrderVm(
DeliveryMethod deliveryMethod,
DeliveryStatus deliveryStatus,
PaymentStatus paymentStatus,
Set<OrderItemVm> orderItemVms
Set<OrderItemVm> orderItemVms,
String checkoutId

) {
public static OrderVm fromModel(Order order) {
Set<OrderItemVm> orderItemVms = order.getOrderItems().stream().map(
item -> OrderItemVm.fromModel(item))
OrderItemVm::fromModel)
.collect(Collectors.toSet());

return OrderVm.builder()
Expand All @@ -53,6 +54,7 @@ public static OrderVm fromModel(Order order) {
.deliveryStatus(order.getDeliveryStatus())
.paymentStatus(order.getPaymentStatus())
.orderItemVms(orderItemVms)
.checkoutId(order.getCheckoutId())
.build();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand All @@ -65,10 +66,10 @@ public PaypalRequestPayment createPayment(RequestPayment requestPayment) {
HttpResponse<Order> 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);
Expand All @@ -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;
Expand All @@ -110,4 +113,4 @@ public CapturedPaymentVm capturePayment(String token) {
}
return CapturedPaymentVm.builder().failureMessage("Something Wrong!").build();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.yas.paymentpaypal.viewmodel;

public record OrderVm(Long id) {
}
Original file line number Diff line number Diff line change
@@ -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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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");
}

Expand Down Expand Up @@ -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";
Expand Down Expand Up @@ -172,4 +179,4 @@ void testCapturePayment_whenIoException_returnCapturedPaymentVm() throws IOExcep
assertEquals("error message", result.failureMessage());
}

}
}
36 changes: 21 additions & 15 deletions payment/src/main/java/com/yas/payment/service/OrderService.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -24,36 +26,40 @@ 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);
}

@Retry(name = "restApi")
@CircuitBreaker(name = "restCircuitBreaker", fallbackMethod = "handlePaymentOrderStatusFallback")
public PaymentOrderStatusVm updateOrderStatus(PaymentOrderStatusVm orderPaymentStatusVm) {

final String jwt = ((Jwt) SecurityContextHolder.getContext().getAuthentication().getPrincipal()).getTokenValue();

Check warning on line 50 in payment/src/main/java/com/yas/payment/service/OrderService.java

View workflow job for this annotation

GitHub Actions / Checkstyle

com.puppycrawl.tools.checkstyle.checks.sizes.LineLengthCheck

Line is longer than 120 characters (found 121).
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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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);

Expand All @@ -78,6 +82,7 @@ void testUpdateCheckoutStatus_whenNormalCase_returnLong() {
@Test
void testUpdateOrderStatus_whenNormalCase_returnPaymentOrderStatusVm() {


PaymentOrderStatusVm statusVm = PaymentOrderStatusVm.builder()
.orderId(123456L)
.orderStatus("COMPLETED")
Expand All @@ -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);

Expand All @@ -104,4 +110,4 @@ void testUpdateOrderStatus_whenNormalCase_returnPaymentOrderStatusVm() {
assertThat(result.paymentId()).isEqualTo(78910L);
assertThat(result.paymentStatus()).isEqualTo("SUCCESS");
}
}
}

0 comments on commit ce22db4

Please sign in to comment.