From 3a8e162508d3679b54b8bc780a2e31cd7e3e4d98 Mon Sep 17 00:00:00 2001
From: tuannguyenh1
Date: Mon, 18 Nov 2024 22:00:45 +0700
Subject: [PATCH] #1268 Step3 Customer Execute Payment
---
.../order/controller/CheckoutController.java | 6 +
.../CheckoutConfirmedStatusConsumer.java | 160 ++++++++++++++++++
....java => CheckoutFulfillmentConsumer.java} | 37 ++--
...> PaymentPaypalOrderIdUpdateConsumer.java} | 35 ++--
.../consumer/PaymentStatusUpdateConsumer.java | 95 +++++++++++
.../java/com/yas/order/model/Checkout.java | 14 +-
.../main/java/com/yas/order/model/Order.java | 4 +-
.../order/model/enumeration/OrderStatus.java | 12 +-
.../model/enumeration/PaymentStatus.java | 3 +
.../order/service/CheckoutItemService.java | 32 ++++
.../yas/order/service/CheckoutService.java | 12 ++
.../order/service/OrderAddressService.java | 24 +++
.../yas/order/service/OrderItemService.java | 29 ++++
.../com/yas/order/service/OrderService.java | 8 +
.../java/com/yas/order/utils/Constants.java | 8 +-
.../java/com/yas/order/utils/JsonUtils.java | 31 +++-
.../order/viewmodel/order/OrderPostVm.java | 4 +-
.../yas/order/viewmodel/order/OrderVm.java | 4 +-
.../db/changelog/ddl/changelog-0016.sql | 2 +
.../db/changelog/ddl/changelog-0017.sql | 1 +
.../resources/messages/messages.properties | 1 +
.../order/controller/OrderControllerTest.java | 8 +-
.../consumer/OrderStatusConsumerTest.java | 75 +++-----
... => PaymentOrderIdUpdateConsumerTest.java} | 8 +-
.../yas/order/service/CartServiceTest.java | 8 +-
.../src/test/resources/application.properties | 6 +-
.../paypal/model/PaypalPaymentAmount.java | 19 +++
.../model/PaypalPaymentPurchaseUnit.java | 22 +++
.../paypal/model/PaypalPaymentResource.java | 29 ++++
.../paypal/model/PaypalWebhookEvent.java | 32 ++++
.../enumeration/PaypalPaymentStatus.java | 39 +++++
.../payment/paypal/service/PaypalService.java | 6 +-
.../paypal/service/PaypalServiceTest.java | 32 ++--
.../yas/payment/config/SecurityConfig.java | 1 +
.../controller/event/EventController.java | 31 ++++
.../kafka/consumer/PaymentCreateConsumer.java | 22 ++-
.../model/enumeration/PaymentStatus.java | 1 +
.../payment/repository/PaymentRepository.java | 3 +
.../yas/payment/service/PaymentService.java | 8 +
.../order/handler/CompleteCaptureHandler.java | 36 ++++
.../order/handler/DeclinedCaptureHandler.java | 37 ++++
.../order/handler/RefundedCaptureHandler.java | 37 ++++
.../order/handler/ReversedCaptureHandler.java | 37 ++++
.../handler/VoidedAuthorizationHandler.java | 37 ++++
.../handler/base/IPaypalWebhookHandler.java | 14 ++
.../base/IPaypalWebhookHandlerRegistry.java | 10 ++
.../handler/base/PaypalWebhookHandler.java | 41 +++++
.../base/PaypalWebhookHandlerRegistry.java | 27 +++
.../java/com/yas/payment/utils/JsonUtils.java | 15 ++
.../consumer/PaymentCreateConsumerTest.java | 4 +-
.../order/components/CheckOutDetail.tsx | 15 +-
storefront/pages/checkout/[id].tsx | 17 +-
.../pages/complete-payment/[capture].tsx | 114 -------------
.../pages/complete-payment/[success].tsx | 48 ++++++
54 files changed, 1057 insertions(+), 304 deletions(-)
create mode 100644 order/src/main/java/com/yas/order/kafka/consumer/CheckoutConfirmedStatusConsumer.java
rename order/src/main/java/com/yas/order/kafka/consumer/{OrderStatusConsumer.java => CheckoutFulfillmentConsumer.java} (83%)
rename order/src/main/java/com/yas/order/kafka/consumer/{PaymentUpdateConsumer.java => PaymentPaypalOrderIdUpdateConsumer.java} (74%)
create mode 100644 order/src/main/java/com/yas/order/kafka/consumer/PaymentStatusUpdateConsumer.java
create mode 100644 order/src/main/java/com/yas/order/service/CheckoutItemService.java
create mode 100644 order/src/main/java/com/yas/order/service/OrderAddressService.java
create mode 100644 order/src/main/java/com/yas/order/service/OrderItemService.java
create mode 100644 order/src/main/resources/db/changelog/ddl/changelog-0016.sql
create mode 100644 order/src/main/resources/db/changelog/ddl/changelog-0017.sql
rename order/src/test/java/com/yas/order/kafka/consumer/{PaymentUpdateConsumerTest.java => PaymentOrderIdUpdateConsumerTest.java} (95%)
create mode 100644 payment-paypal/src/main/java/com/yas/payment/paypal/model/PaypalPaymentAmount.java
create mode 100644 payment-paypal/src/main/java/com/yas/payment/paypal/model/PaypalPaymentPurchaseUnit.java
create mode 100644 payment-paypal/src/main/java/com/yas/payment/paypal/model/PaypalPaymentResource.java
create mode 100644 payment-paypal/src/main/java/com/yas/payment/paypal/model/PaypalWebhookEvent.java
create mode 100644 payment-paypal/src/main/java/com/yas/payment/paypal/model/enumeration/PaypalPaymentStatus.java
create mode 100644 payment/src/main/java/com/yas/payment/controller/event/EventController.java
create mode 100644 payment/src/main/java/com/yas/payment/service/order/handler/CompleteCaptureHandler.java
create mode 100644 payment/src/main/java/com/yas/payment/service/order/handler/DeclinedCaptureHandler.java
create mode 100644 payment/src/main/java/com/yas/payment/service/order/handler/RefundedCaptureHandler.java
create mode 100644 payment/src/main/java/com/yas/payment/service/order/handler/ReversedCaptureHandler.java
create mode 100644 payment/src/main/java/com/yas/payment/service/order/handler/VoidedAuthorizationHandler.java
create mode 100644 payment/src/main/java/com/yas/payment/service/order/handler/base/IPaypalWebhookHandler.java
create mode 100644 payment/src/main/java/com/yas/payment/service/order/handler/base/IPaypalWebhookHandlerRegistry.java
create mode 100644 payment/src/main/java/com/yas/payment/service/order/handler/base/PaypalWebhookHandler.java
create mode 100644 payment/src/main/java/com/yas/payment/service/order/handler/base/PaypalWebhookHandlerRegistry.java
delete mode 100644 storefront/pages/complete-payment/[capture].tsx
create mode 100644 storefront/pages/complete-payment/[success].tsx
diff --git a/order/src/main/java/com/yas/order/controller/CheckoutController.java b/order/src/main/java/com/yas/order/controller/CheckoutController.java
index 96a6bf0993..5f364e22ac 100644
--- a/order/src/main/java/com/yas/order/controller/CheckoutController.java
+++ b/order/src/main/java/com/yas/order/controller/CheckoutController.java
@@ -32,6 +32,12 @@ public ResponseEntity createCheckout(@Valid @RequestBody CheckoutPos
return ResponseEntity.ok(checkoutService.createCheckout(checkoutPostVm));
}
+ @PostMapping("/storefront/checkouts/{id}/process-payment")
+ public ResponseEntity processPayment(@PathVariable String id) {
+ checkoutService.processPayment(id);
+ return ResponseEntity.ok().build();
+ }
+
@PutMapping("/storefront/checkouts/status")
public ResponseEntity updateCheckoutStatus(@Valid @RequestBody CheckoutStatusPutVm checkoutStatusPutVm) {
return ResponseEntity.ok(checkoutService.updateCheckoutStatus(checkoutStatusPutVm));
diff --git a/order/src/main/java/com/yas/order/kafka/consumer/CheckoutConfirmedStatusConsumer.java b/order/src/main/java/com/yas/order/kafka/consumer/CheckoutConfirmedStatusConsumer.java
new file mode 100644
index 0000000000..ef1e879705
--- /dev/null
+++ b/order/src/main/java/com/yas/order/kafka/consumer/CheckoutConfirmedStatusConsumer.java
@@ -0,0 +1,160 @@
+package com.yas.order.kafka.consumer;
+
+import static com.yas.order.utils.JsonUtils.getJsonNodeByValue;
+import static com.yas.order.utils.JsonUtils.getJsonValueOrNull;
+import static com.yas.order.utils.JsonUtils.getJsonValueOrThrow;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.yas.commonlibrary.exception.BadRequestException;
+import com.yas.order.model.Checkout;
+import com.yas.order.model.CheckoutItem;
+import com.yas.order.model.Order;
+import com.yas.order.model.OrderItem;
+import com.yas.order.model.enumeration.CheckoutState;
+import com.yas.order.model.enumeration.DeliveryStatus;
+import com.yas.order.model.enumeration.OrderStatus;
+import com.yas.order.service.CheckoutItemService;
+import com.yas.order.service.CheckoutService;
+import com.yas.order.service.OrderAddressService;
+import com.yas.order.service.OrderItemService;
+import com.yas.order.service.OrderService;
+import com.yas.order.utils.Constants;
+import java.util.List;
+import java.util.Objects;
+import java.util.Optional;
+import lombok.RequiredArgsConstructor;
+import org.apache.kafka.clients.consumer.ConsumerRecord;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.kafka.annotation.KafkaListener;
+import org.springframework.kafka.annotation.RetryableTopic;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * After the Checkout Status is set to PAYMENT_CONFIRMED, an order will be created.
+ */
+@Service
+@Transactional
+@RequiredArgsConstructor
+public class CheckoutConfirmedStatusConsumer {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(CheckoutConfirmedStatusConsumer.class);
+ private final OrderService orderService;
+ private final OrderItemService orderItemService;
+ private final CheckoutService checkoutService;
+ private final CheckoutItemService checkoutItemService;
+ private final OrderAddressService orderAddressService;
+ private final ObjectMapper objectMapper;
+
+ @KafkaListener(
+ topics = "${cdc.event.checkout.status.topic-name}",
+ groupId = "${cdc.event.checkout.confirmed.status.group-id}"
+ )
+ @RetryableTopic
+ public void listen(ConsumerRecord, ?> consumerRecord) {
+
+ if (Objects.isNull(consumerRecord)) {
+ LOGGER.info("ConsumerRecord is null");
+ return;
+ }
+ String jsonValue = (String) consumerRecord.value();
+ JsonNode valueObject = getJsonNodeByValue(objectMapper, jsonValue, LOGGER);
+ processCheckoutEvent(valueObject);
+ }
+
+ private void processCheckoutEvent(JsonNode valueObject) {
+ Optional.ofNullable(valueObject)
+ .filter(value -> value.has("op") && "u".equals(value.get("op").asText()))
+ .filter(value -> value.has("before") && value.has("after"))
+ .ifPresentOrElse(
+ this::handleJsonForUpdateCheckout,
+ () -> LOGGER.warn("Message does not match expected update structure: {}", valueObject)
+ );
+ }
+
+ private void handleJsonForUpdateCheckout(JsonNode valueObject) {
+
+ JsonNode before = valueObject.get("before");
+ JsonNode after = valueObject.get("after");
+
+ String id = getJsonValueOrThrow(after, Constants.Column.ID_COLUMN, Constants.ErrorCode.ID_NOT_EXISTED);
+ String beforeStatus = getJsonValueOrNull(before, Constants.Column.STATUS_COLUMN);
+ String afterStatus = getJsonValueOrNull(after, Constants.Column.STATUS_COLUMN);
+
+ if (Objects.isNull(afterStatus)
+ || afterStatus.equals(beforeStatus)
+ || !CheckoutState.PAYMENT_CONFIRMED.name().equals(afterStatus)
+ ) {
+ LOGGER.info("It's not an event to create Order with Checkout Id {}", id);
+ return;
+ }
+
+ LOGGER.info("Checkout record with ID {} has the status 'PAYMENT_CONFIRMED'", id);
+
+ Checkout checkout = checkoutService.findCheckoutById(id);
+ List checkoutItemList = checkoutItemService.getAllByCheckoutId(checkout.getId());
+
+ Order order = createOrder(checkout, checkoutItemList);
+ createOrderItems(order, checkoutItemList);
+ updateCheckoutStatus(checkout);
+ }
+
+ private Order createOrder(Checkout checkout, List checkoutItemList) {
+
+ Order order = Order.builder()
+ .email(checkout.getEmail())
+ .numberItem(checkoutItemList.size())
+ .note(checkout.getNote())
+ .tax(checkout.getTotalTax())
+ .discount(checkout.getTotalDiscountAmount())
+ .totalPrice(checkout.getTotalAmount())
+ .couponCode(checkout.getCouponCode())
+ .orderStatus(OrderStatus.PAYMENT_CONFIRMED)
+ .deliveryFee(checkout.getTotalShipmentFee())
+ .deliveryMethod(checkout.getShipmentMethodId())
+ .deliveryStatus(DeliveryStatus.PREPARING)
+ .totalShipmentTax(checkout.getTotalShipmentTax())
+ .customerId(checkout.getCustomerId())
+ .shippingAddressId(
+ Optional.ofNullable(checkout.getShippingAddressId())
+ .map(orderAddressService::findOrderAddressById)
+ .orElseThrow(() -> new BadRequestException("Shipping Address Id is not existed: {}",
+ checkout.getShippingAddressId()))
+ )
+ .billingAddressId(
+ Optional.ofNullable(checkout.getBillingAddressId())
+ .map(orderAddressService::findOrderAddressById)
+ .orElseThrow(() -> new BadRequestException("Billing Address Id is not existed: {}",
+ checkout.getBillingAddressId()))
+ )
+ .checkoutId(checkout.getId())
+ .build();
+
+ return orderService.updateOrder(order);
+
+ }
+
+ private void createOrderItems(Order order, List checkoutItemList) {
+
+ List orderItems = checkoutItemList.stream()
+ .map(item -> OrderItem.builder()
+ .productId(item.getProductId())
+ .productName(item.getProductName())
+ .quantity(item.getQuantity())
+ .productPrice(item.getProductPrice())
+ .note(item.getNote())
+ .orderId(order.getId())
+ .build())
+ .toList();
+
+ orderItemService.saveAll(orderItems);
+ }
+
+ private void updateCheckoutStatus(Checkout checkout) {
+ checkout.setCheckoutState(CheckoutState.FULFILLED);
+ checkoutService.updateCheckout(checkout);
+ }
+
+}
diff --git a/order/src/main/java/com/yas/order/kafka/consumer/OrderStatusConsumer.java b/order/src/main/java/com/yas/order/kafka/consumer/CheckoutFulfillmentConsumer.java
similarity index 83%
rename from order/src/main/java/com/yas/order/kafka/consumer/OrderStatusConsumer.java
rename to order/src/main/java/com/yas/order/kafka/consumer/CheckoutFulfillmentConsumer.java
index 0c78375085..cca23bc4a9 100644
--- a/order/src/main/java/com/yas/order/kafka/consumer/OrderStatusConsumer.java
+++ b/order/src/main/java/com/yas/order/kafka/consumer/CheckoutFulfillmentConsumer.java
@@ -3,18 +3,17 @@
import static com.yas.order.utils.JsonUtils.convertObjectToString;
import static com.yas.order.utils.JsonUtils.createJsonErrorObject;
import static com.yas.order.utils.JsonUtils.getAttributesNode;
+import static com.yas.order.utils.JsonUtils.getJsonNodeByValue;
import static com.yas.order.utils.JsonUtils.getJsonValueOrThrow;
+import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
-import com.google.gson.Gson;
-import com.google.gson.JsonObject;
import com.yas.commonlibrary.exception.BadRequestException;
-import com.yas.commonlibrary.exception.NotFoundException;
import com.yas.order.model.Checkout;
import com.yas.order.model.enumeration.CheckoutProgress;
import com.yas.order.model.enumeration.CheckoutState;
-import com.yas.order.repository.CheckoutRepository;
+import com.yas.order.service.CheckoutService;
import com.yas.order.service.PaymentService;
import com.yas.order.utils.Constants;
import com.yas.order.viewmodel.payment.CheckoutPaymentVm;
@@ -28,19 +27,21 @@
import org.springframework.kafka.annotation.RetryableTopic;
import org.springframework.stereotype.Service;
+/**
+ * After fulfillment, process payment and create order in PayPal.
+ */
@Service
@RequiredArgsConstructor
-public class OrderStatusConsumer {
+public class CheckoutFulfillmentConsumer {
- private static final Logger LOGGER = LoggerFactory.getLogger(OrderStatusConsumer.class);
+ private static final Logger LOGGER = LoggerFactory.getLogger(CheckoutFulfillmentConsumer.class);
private final PaymentService paymentService;
- private final CheckoutRepository checkoutRepository;
+ private final CheckoutService checkoutService;
private final ObjectMapper objectMapper;
- private final Gson gson;
@KafkaListener(
topics = "${cdc.event.checkout.status.topic-name}",
- groupId = "${cdc.event.checkout.status.group-id}"
+ groupId = "${cdc.event.checkout.fulfillment.group-id}"
)
@RetryableTopic(
attempts = "1"
@@ -51,21 +52,21 @@ public void listen(ConsumerRecord, ?> consumerRecord) {
LOGGER.info("ConsumerRecord is null");
return;
}
- JsonObject valueObject = gson.fromJson((String) consumerRecord.value(), JsonObject.class);
+ String jsonValue = (String) consumerRecord.value();
+ JsonNode valueObject = getJsonNodeByValue(objectMapper, jsonValue, LOGGER);
processCheckoutEvent(valueObject);
-
}
- private void processCheckoutEvent(JsonObject valueObject) {
+ private void processCheckoutEvent(JsonNode valueObject) {
Optional.ofNullable(valueObject)
.filter(
- value -> value.has("op") && "u".equals(value.get("op").getAsString())
+ value -> value.has("op") && "u".equals(value.get("op").asText())
)
- .map(value -> value.getAsJsonObject("after"))
+ .map(value -> value.get("after"))
.ifPresent(this::handleAfterJson);
}
- private void handleAfterJson(JsonObject after) {
+ private void handleAfterJson(JsonNode after) {
String id = getJsonValueOrThrow(after, Constants.Column.ID_COLUMN,
Constants.ErrorCode.ID_NOT_EXISTED);
@@ -83,9 +84,7 @@ private void handleAfterJson(JsonObject after) {
LOGGER.info("Checkout record with ID {} has the status 'PAYMENT_PROCESSING' and the process 'STOCK_LOCKED'",
id);
- Checkout checkout = checkoutRepository
- .findById(id)
- .orElseThrow(() -> new NotFoundException(Constants.ErrorCode.CHECKOUT_NOT_FOUND, id));
+ Checkout checkout = checkoutService.findCheckoutById(id);
processPaymentAndUpdateCheckout(checkout);
}
@@ -117,7 +116,7 @@ private void processPaymentAndUpdateCheckout(Checkout checkout) {
throw new BadRequestException(Constants.ErrorCode.PROCESS_CHECKOUT_FAILED, checkout.getId());
} finally {
- checkoutRepository.save(checkout);
+ checkoutService.updateCheckout(checkout);
}
}
diff --git a/order/src/main/java/com/yas/order/kafka/consumer/PaymentUpdateConsumer.java b/order/src/main/java/com/yas/order/kafka/consumer/PaymentPaypalOrderIdUpdateConsumer.java
similarity index 74%
rename from order/src/main/java/com/yas/order/kafka/consumer/PaymentUpdateConsumer.java
rename to order/src/main/java/com/yas/order/kafka/consumer/PaymentPaypalOrderIdUpdateConsumer.java
index 0f78037556..5527d634dd 100644
--- a/order/src/main/java/com/yas/order/kafka/consumer/PaymentUpdateConsumer.java
+++ b/order/src/main/java/com/yas/order/kafka/consumer/PaymentPaypalOrderIdUpdateConsumer.java
@@ -2,13 +2,13 @@
import static com.yas.order.utils.JsonUtils.convertObjectToString;
import static com.yas.order.utils.JsonUtils.getAttributesNode;
+import static com.yas.order.utils.JsonUtils.getJsonNodeByValue;
import static com.yas.order.utils.JsonUtils.getJsonValueOrNull;
import static com.yas.order.utils.JsonUtils.getJsonValueOrThrow;
+import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
-import com.google.gson.Gson;
-import com.google.gson.JsonObject;
import com.yas.order.model.Checkout;
import com.yas.order.model.enumeration.CheckoutProgress;
import com.yas.order.model.enumeration.CheckoutState;
@@ -24,18 +24,21 @@
import org.springframework.kafka.annotation.RetryableTopic;
import org.springframework.stereotype.Service;
+/**
+ * After the PayPal Order id is updated in payment_provider_checkout_id column.
+ * Update Checkout state is PAYMENT_PROCESSING, progress is PAYMENT_CREATED
+ */
@Service
@RequiredArgsConstructor
-public class PaymentUpdateConsumer {
+public class PaymentPaypalOrderIdUpdateConsumer {
- private static final Logger LOGGER = LoggerFactory.getLogger(PaymentUpdateConsumer.class);
+ private static final Logger LOGGER = LoggerFactory.getLogger(PaymentPaypalOrderIdUpdateConsumer.class);
private final CheckoutService checkoutService;
private final ObjectMapper objectMapper;
- private final Gson gson;
@KafkaListener(
topics = "${cdc.event.payment.topic-name}",
- groupId = "${cdc.event.payment.update.group-id}"
+ groupId = "${cdc.event.payment.order.id.update.group-id}"
)
@RetryableTopic
public void listen(ConsumerRecord, ?> consumerRecord) {
@@ -44,32 +47,32 @@ public void listen(ConsumerRecord, ?> consumerRecord) {
LOGGER.info("Consumer Record is null");
return;
}
- JsonObject valueObject = gson.fromJson((String) consumerRecord.value(), JsonObject.class);
+ String jsonValue = (String) consumerRecord.value();
+ JsonNode valueObject = getJsonNodeByValue(objectMapper, jsonValue, LOGGER);
processPaymentEvent(valueObject);
-
}
- private void processPaymentEvent(JsonObject valueObject) {
+ private void processPaymentEvent(JsonNode valueObject) {
Optional.ofNullable(valueObject)
.filter(
- value -> value.has("op") && "u".equals(value.get("op").getAsString())
+ value -> value.has("op") && "u".equals(value.get("op").asText())
)
.filter(value -> value.has("before") && value.has("after"))
.ifPresent(this::handleJsonForUpdateCheckout);
}
- private void handleJsonForUpdateCheckout(JsonObject valueObject) {
+ private void handleJsonForUpdateCheckout(JsonNode valueObject) {
- JsonObject before = valueObject.getAsJsonObject("before");
- JsonObject after = valueObject.getAsJsonObject("after");
+ JsonNode before = valueObject.get("before");
+ JsonNode after = valueObject.get("after");
String id = getJsonValueOrThrow(after, Constants.Column.ID_COLUMN,
Constants.ErrorCode.ID_NOT_EXISTED);
String beforePaypalOrderId = getJsonValueOrNull(before,
- Constants.Column.CHECKOUT_ATTRIBUTES_PAYMENT_PROVIDER_CHECKOUT_ID_FIELD);
+ Constants.Column.PAYMENT_ATTRIBUTES_PAYMENT_PROVIDER_CHECKOUT_ID_FIELD);
String afterPaypalOrderId = getJsonValueOrNull(after,
- Constants.Column.CHECKOUT_ATTRIBUTES_PAYMENT_PROVIDER_CHECKOUT_ID_FIELD);
+ Constants.Column.PAYMENT_ATTRIBUTES_PAYMENT_PROVIDER_CHECKOUT_ID_FIELD);
if (!Objects.isNull(afterPaypalOrderId) && !afterPaypalOrderId.equals(beforePaypalOrderId)) {
@@ -90,7 +93,7 @@ private void updateCheckOut(String checkoutId, String paymentProviderCheckoutId)
checkout.setProgress(CheckoutProgress.PAYMENT_CREATED);
ObjectNode attributesNode = getAttributesNode(objectMapper, checkout.getAttributes());
- attributesNode.put(Constants.Column.CHECKOUT_ATTRIBUTES_PAYMENT_PROVIDER_CHECKOUT_ID_FIELD,
+ attributesNode.put(Constants.Column.PAYMENT_ATTRIBUTES_PAYMENT_PROVIDER_CHECKOUT_ID_FIELD,
paymentProviderCheckoutId);
checkout.setAttributes(convertObjectToString(objectMapper, attributesNode));
diff --git a/order/src/main/java/com/yas/order/kafka/consumer/PaymentStatusUpdateConsumer.java b/order/src/main/java/com/yas/order/kafka/consumer/PaymentStatusUpdateConsumer.java
new file mode 100644
index 0000000000..6c52f0fbc8
--- /dev/null
+++ b/order/src/main/java/com/yas/order/kafka/consumer/PaymentStatusUpdateConsumer.java
@@ -0,0 +1,95 @@
+package com.yas.order.kafka.consumer;
+
+import static com.yas.order.utils.JsonUtils.getJsonNodeByValue;
+import static com.yas.order.utils.JsonUtils.getJsonValueOrNull;
+import static com.yas.order.utils.JsonUtils.getJsonValueOrThrow;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.yas.order.model.Checkout;
+import com.yas.order.model.enumeration.CheckoutState;
+import com.yas.order.model.enumeration.PaymentStatus;
+import com.yas.order.service.CheckoutService;
+import com.yas.order.utils.Constants;
+import java.util.Objects;
+import java.util.Optional;
+import lombok.RequiredArgsConstructor;
+import org.apache.kafka.clients.consumer.ConsumerRecord;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.kafka.annotation.KafkaListener;
+import org.springframework.kafka.annotation.RetryableTopic;
+import org.springframework.stereotype.Service;
+
+/**
+ * After Status Payment is changed to COMPLETED/FAILURE.
+ * Update Status Checkout to PAYMENT_CONFIRMED
+ */
+@Service
+@RequiredArgsConstructor
+public class PaymentStatusUpdateConsumer {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(PaymentStatusUpdateConsumer.class);
+ private final CheckoutService checkoutService;
+ private final ObjectMapper objectMapper;
+
+ @KafkaListener(
+ topics = "${cdc.event.payment.topic-name}",
+ groupId = "${cdc.event.payment.status.update.group-id}"
+ )
+ @RetryableTopic
+ public void listen(ConsumerRecord, ?> consumerRecord) {
+
+ if (Objects.isNull(consumerRecord)) {
+ LOGGER.info("Consumer Record is null");
+ return;
+ }
+ String jsonValue = (String) consumerRecord.value();
+ JsonNode valueObject = getJsonNodeByValue(objectMapper, jsonValue, LOGGER);
+ processPaymentEvent(valueObject);
+ }
+
+ private void processPaymentEvent(JsonNode valueObject) {
+ Optional.ofNullable(valueObject)
+ .filter(
+ value -> value.has("op") && "u".equals(value.get("op").asText())
+ )
+ .filter(value -> value.has("before") && value.has("after"))
+ .ifPresent(this::handleJsonForUpdateCheckout);
+ }
+
+ private void handleJsonForUpdateCheckout(JsonNode valueObject) {
+
+ JsonNode before = valueObject.get("before");
+ JsonNode after = valueObject.get("after");
+
+ String id = getJsonValueOrThrow(after, Constants.Column.ID_COLUMN,
+ Constants.ErrorCode.ID_NOT_EXISTED);
+
+ String beforePaymentStatus = getJsonValueOrNull(before,
+ Constants.Column.PAYMENT_STATUS_FIELD);
+ String afterPaymentStatus = getJsonValueOrNull(after,
+ Constants.Column.PAYMENT_STATUS_FIELD);
+
+ if ((PaymentStatus.COMPLETED.name().equals(afterPaymentStatus)
+ || PaymentStatus.FAILURE.name().equals(afterPaymentStatus))
+ && !afterPaymentStatus.equals(beforePaymentStatus)) {
+
+ LOGGER.info("Update Checkout Payment Status to PAYMENT_CONFIRMED with Payment {}", id);
+
+ String checkoutId = getJsonValueOrThrow(after, Constants.Column.CHECKOUT_ID_COLUMN,
+ Constants.ErrorCode.CHECKOUT_ID_NOT_EXISTED);
+ updateCheckoutPaymentStatus(checkoutId);
+ } else {
+ LOGGER.warn("It is not an event to confirm payment with Payment ID {}", id);
+ }
+ }
+
+ private void updateCheckoutPaymentStatus(String checkoutId) {
+
+ Checkout checkout = checkoutService.findCheckoutById(checkoutId);
+ checkout.setCheckoutState(CheckoutState.PAYMENT_CONFIRMED);
+ checkoutService.updateCheckout(checkout);
+ }
+
+}
\ No newline at end of file
diff --git a/order/src/main/java/com/yas/order/model/Checkout.java b/order/src/main/java/com/yas/order/model/Checkout.java
index 4478783238..3023c46082 100644
--- a/order/src/main/java/com/yas/order/model/Checkout.java
+++ b/order/src/main/java/com/yas/order/model/Checkout.java
@@ -2,6 +2,7 @@
import com.yas.order.model.enumeration.CheckoutProgress;
import com.yas.order.model.enumeration.CheckoutState;
+import com.yas.order.model.enumeration.DeliveryMethod;
import com.yas.order.model.enumeration.PaymentMethod;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
@@ -44,40 +45,33 @@ public class Checkout extends AbstractAuditEntity {
@Enumerated(EnumType.STRING)
private CheckoutProgress progress;
- @SuppressWarnings("unused")
private Long customerId;
- @SuppressWarnings("unused")
- private String shipmentMethodId;
+ private DeliveryMethod shipmentMethodId;
@Enumerated(EnumType.STRING)
private PaymentMethod paymentMethodId;
- @SuppressWarnings("unused")
private Long shippingAddressId;
+ private Long billingAddressId;
+
@JdbcTypeCode(SqlTypes.JSON)
@Column(name = "last_error", columnDefinition = "jsonb")
private String lastError;
- @SuppressWarnings("unused")
@JdbcTypeCode(SqlTypes.JSON)
@Column(name = "attributes", columnDefinition = "jsonb")
private String attributes;
- @SuppressWarnings("unused")
private BigDecimal totalAmount;
- @SuppressWarnings("unused")
private BigDecimal totalShipmentFee;
- @SuppressWarnings("unused")
private BigDecimal totalShipmentTax;
- @SuppressWarnings("unused")
private BigDecimal totalTax;
- @SuppressWarnings("unused")
private BigDecimal totalDiscountAmount;
}
\ No newline at end of file
diff --git a/order/src/main/java/com/yas/order/model/Order.java b/order/src/main/java/com/yas/order/model/Order.java
index 834d684f49..009e4d41fd 100644
--- a/order/src/main/java/com/yas/order/model/Order.java
+++ b/order/src/main/java/com/yas/order/model/Order.java
@@ -52,10 +52,10 @@ public class Order extends AbstractAuditEntity {
private String note;
@Column(name = "total_tax")
- private float tax;
+ private BigDecimal tax;
@Column(name = "total_discount_amount")
- private float discount;
+ private BigDecimal discount;
private int numberItem;
diff --git a/order/src/main/java/com/yas/order/model/enumeration/OrderStatus.java b/order/src/main/java/com/yas/order/model/enumeration/OrderStatus.java
index 045d4bdd69..652d03b301 100644
--- a/order/src/main/java/com/yas/order/model/enumeration/OrderStatus.java
+++ b/order/src/main/java/com/yas/order/model/enumeration/OrderStatus.java
@@ -8,9 +8,17 @@ public enum OrderStatus {
SHIPPING("SHIPPING"),
COMPLETED("COMPLETED"),
REFUND("REFUND"),
- CANCELLED("CANCELLED"),
+ REJECT("REJECT"),
- REJECT("REJECT");
+ PAYMENT_CONFIRMED("PAYMENT_CONFIRMED"),
+ READY_TO_SHIP("READY_TO_SHIP"),
+ SHIPPED("SHIPPED"),
+ CANCEL_REQUESTED("CANCEL_REQUESTED"),
+ RECEIVED("RECEIVED"),
+ SHIPMENT_FAILED("SHIPMENT_FAILED"),
+ REFUND_PROCESSING("REFUND_PROCESSING"),
+ CONFIRMED("CONFIRMED"),
+ CANCELLED("CANCELLED");
private final String name;
diff --git a/order/src/main/java/com/yas/order/model/enumeration/PaymentStatus.java b/order/src/main/java/com/yas/order/model/enumeration/PaymentStatus.java
index 422d3ad6a6..480f2dc029 100644
--- a/order/src/main/java/com/yas/order/model/enumeration/PaymentStatus.java
+++ b/order/src/main/java/com/yas/order/model/enumeration/PaymentStatus.java
@@ -1,7 +1,10 @@
package com.yas.order.model.enumeration;
public enum PaymentStatus {
+ NEW,
+ PROCESSING,
PENDING,
+ FAILURE,
COMPLETED,
CANCELLED
}
diff --git a/order/src/main/java/com/yas/order/service/CheckoutItemService.java b/order/src/main/java/com/yas/order/service/CheckoutItemService.java
new file mode 100644
index 0000000000..923c51a719
--- /dev/null
+++ b/order/src/main/java/com/yas/order/service/CheckoutItemService.java
@@ -0,0 +1,32 @@
+package com.yas.order.service;
+
+import com.yas.order.model.CheckoutItem;
+import com.yas.order.repository.CheckoutItemRepository;
+import java.util.List;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class CheckoutItemService {
+ private final CheckoutItemRepository checkoutItemRepository;
+
+ public List getAllByCheckoutId(String checkoutId) {
+ List checkoutItemList = checkoutItemRepository.findAllByCheckoutId(checkoutId);
+ if (CollectionUtils.isEmpty(checkoutItemList)) {
+ return List.of();
+ }
+ return checkoutItemList;
+ }
+
+ public void saveAll(List items) {
+ if (CollectionUtils.isEmpty(items)) {
+ return;
+ }
+ checkoutItemRepository.saveAll(items);
+ }
+
+}
diff --git a/order/src/main/java/com/yas/order/service/CheckoutService.java b/order/src/main/java/com/yas/order/service/CheckoutService.java
index a66897c26f..b37ee96cf9 100644
--- a/order/src/main/java/com/yas/order/service/CheckoutService.java
+++ b/order/src/main/java/com/yas/order/service/CheckoutService.java
@@ -9,6 +9,7 @@
import com.yas.order.model.Checkout;
import com.yas.order.model.CheckoutItem;
import com.yas.order.model.Order;
+import com.yas.order.model.enumeration.CheckoutProgress;
import com.yas.order.model.enumeration.CheckoutState;
import com.yas.order.model.enumeration.PaymentMethod;
import com.yas.order.repository.CheckoutItemRepository;
@@ -43,6 +44,17 @@ public class CheckoutService {
private final OrderService orderService;
private final CheckoutMapper checkoutMapper;
+ public void processPayment(String checkoutId) {
+ Checkout checkout = checkoutRepository.findById(checkoutId)
+ .orElseThrow(() -> new NotFoundException(CHECKOUT_NOT_FOUND, checkoutId));
+ checkout.setCheckoutState(CheckoutState.PAYMENT_PROCESSING);
+ checkout.setProgress(CheckoutProgress.STOCK_LOCKED);
+ checkout.setPaymentMethodId(PaymentMethod.PAYPAL);
+ checkout.setShippingAddressId(1L);
+ checkout.setBillingAddressId(1L);
+ checkoutRepository.save(checkout);
+ }
+
public CheckoutVm createCheckout(CheckoutPostVm checkoutPostVm) {
Checkout checkout = checkoutMapper.toModel(checkoutPostVm);
diff --git a/order/src/main/java/com/yas/order/service/OrderAddressService.java b/order/src/main/java/com/yas/order/service/OrderAddressService.java
new file mode 100644
index 0000000000..e07f18a8b7
--- /dev/null
+++ b/order/src/main/java/com/yas/order/service/OrderAddressService.java
@@ -0,0 +1,24 @@
+package com.yas.order.service;
+
+import static com.yas.order.utils.Constants.ErrorCode.ORDER_ADDRESS_NOT_FOUND;
+
+import com.yas.commonlibrary.exception.NotFoundException;
+import com.yas.order.model.OrderAddress;
+import com.yas.order.repository.OrderAddressRepository;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class OrderAddressService {
+
+ private final OrderAddressRepository orderAddressRepository;
+
+ public OrderAddress findOrderAddressById(Long id) {
+ return this.orderAddressRepository.findById(id)
+ .orElseThrow(() -> new NotFoundException(ORDER_ADDRESS_NOT_FOUND, id));
+ }
+}
+
diff --git a/order/src/main/java/com/yas/order/service/OrderItemService.java b/order/src/main/java/com/yas/order/service/OrderItemService.java
new file mode 100644
index 0000000000..38c6e7b37f
--- /dev/null
+++ b/order/src/main/java/com/yas/order/service/OrderItemService.java
@@ -0,0 +1,29 @@
+package com.yas.order.service;
+
+import com.yas.order.model.OrderItem;
+import com.yas.order.repository.OrderItemRepository;
+import java.util.List;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Service;
+import org.springframework.util.CollectionUtils;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class OrderItemService {
+
+ private final OrderItemRepository orderItemRepository;
+
+ public List saveAll(List items) {
+ if (CollectionUtils.isEmpty(items)) {
+ return List.of();
+ }
+ return orderItemRepository.saveAll(items);
+ }
+
+ public void findAllByOrderId(Long orderId) {
+ orderItemRepository.findAllByOrderId(orderId);
+ }
+
+}
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 47cab4d04b..61a929edd4 100644
--- a/order/src/main/java/com/yas/order/service/OrderService.java
+++ b/order/src/main/java/com/yas/order/service/OrderService.java
@@ -4,6 +4,7 @@
import com.yas.commonlibrary.csv.BaseCsv;
import com.yas.commonlibrary.csv.CsvExporter;
+import com.yas.commonlibrary.exception.BadRequestException;
import com.yas.commonlibrary.exception.NotFoundException;
import com.yas.order.mapper.OrderMapper;
import com.yas.order.model.Order;
@@ -288,6 +289,13 @@ public void acceptOrder(Long orderId) {
this.orderRepository.save(order);
}
+ public Order updateOrder(Order order) {
+ if (Objects.isNull(order)) {
+ throw new BadRequestException("Order is not existed.");
+ }
+ return orderRepository.save(order);
+ }
+
public byte[] exportCsv(OrderRequest orderRequest) throws IOException {
ZonedDateTime createdFrom = orderRequest.getCreatedFrom();
ZonedDateTime createdTo = orderRequest.getCreatedTo();
diff --git a/order/src/main/java/com/yas/order/utils/Constants.java b/order/src/main/java/com/yas/order/utils/Constants.java
index 3e2f18f55c..f8b5b7538e 100644
--- a/order/src/main/java/com/yas/order/utils/Constants.java
+++ b/order/src/main/java/com/yas/order/utils/Constants.java
@@ -7,6 +7,7 @@ private ErrorCode() {}
public static final String ORDER_NOT_FOUND = "ORDER_NOT_FOUND";
public static final String CHECKOUT_NOT_FOUND = "CHECKOUT_NOT_FOUND";
+ public static final String ORDER_ADDRESS_NOT_FOUND = "ORDER_ADDRESS_NOT_FOUND";
public static final String SIGN_IN_REQUIRED = "SIGN_IN_REQUIRED";
public static final String FORBIDDEN = "FORBIDDEN";
public static final String CANNOT_CONVERT_TO_STRING = "CANNOT_CONVERT_TO_STRING";
@@ -44,8 +45,11 @@ private Column() {}
public static final String CHECKOUT_PROGRESS_COLUMN = "progress";
public static final String CHECKOUT_ATTRIBUTES_PAYMENT_ID_FIELD = "payment_id";
public static final String CHECKOUT_ID_COLUMN = "checkout_id";
- public static final String CHECKOUT_STATUS_COLUMN = "status";
- public static final String CHECKOUT_ATTRIBUTES_PAYMENT_PROVIDER_CHECKOUT_ID_FIELD
+
+ // Column name of Payment table
+ public static final String PAYMENT_ATTRIBUTES_PAYMENT_PROVIDER_CHECKOUT_ID_FIELD
= "payment_provider_checkout_id";
+ public static final String PAYMENT_STATUS_FIELD
+ = "payment_status";
}
}
diff --git a/order/src/main/java/com/yas/order/utils/JsonUtils.java b/order/src/main/java/com/yas/order/utils/JsonUtils.java
index 1143c70339..271a8e2f71 100644
--- a/order/src/main/java/com/yas/order/utils/JsonUtils.java
+++ b/order/src/main/java/com/yas/order/utils/JsonUtils.java
@@ -1,12 +1,12 @@
package com.yas.order.utils;
import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
import com.yas.commonlibrary.exception.BadRequestException;
import java.util.Optional;
+import org.slf4j.Logger;
public class JsonUtils {
@@ -42,25 +42,38 @@ public static ObjectNode createJsonErrorObject(ObjectMapper objectMapper, String
}
public static String getJsonValueOrThrow(
- JsonObject jsonObject,
+ JsonNode jsonObject,
String columnName,
String errorCode,
Object... errorParams
) {
return Optional.ofNullable(jsonObject.get(columnName))
- .filter(jsonElement -> !jsonElement.isJsonNull())
- .map(JsonElement::getAsString)
+ .map(JsonNode::asText)
.orElseThrow(() -> new BadRequestException(errorCode, errorParams));
}
public static String getJsonValueOrNull(
- JsonObject jsonObject,
+ JsonNode jsonObject,
String columnName
) {
- JsonElement jsonElement = jsonObject.get(columnName);
- if (jsonElement != null && !jsonElement.isJsonNull()) {
- return jsonElement.getAsString();
+ JsonNode jsonElement = jsonObject.get(columnName);
+ if (jsonElement != null && !jsonElement.isNull()) {
+ return jsonElement.asText();
}
return null;
}
+
+
+ public static JsonNode getJsonNodeByValue(
+ ObjectMapper objectMapper,
+ String jsonValue,
+ Logger logger
+ ) {
+ try {
+ return objectMapper.readTree(jsonValue);
+ } catch (JsonProcessingException e) {
+ logger.error("Failed to parse message as JSON. Message: {}", jsonValue, e);
+ throw new BadRequestException("Failed to parse message as JSON. Message: {}", jsonValue);
+ }
+ }
}
\ No newline at end of file
diff --git a/order/src/main/java/com/yas/order/viewmodel/order/OrderPostVm.java b/order/src/main/java/com/yas/order/viewmodel/order/OrderPostVm.java
index 8d2e74a3ce..942fbcdb65 100644
--- a/order/src/main/java/com/yas/order/viewmodel/order/OrderPostVm.java
+++ b/order/src/main/java/com/yas/order/viewmodel/order/OrderPostVm.java
@@ -17,8 +17,8 @@ public record OrderPostVm(
@NotNull OrderAddressPostVm shippingAddressPostVm,
@NotNull OrderAddressPostVm billingAddressPostVm,
String note,
- float tax,
- float discount,
+ BigDecimal tax,
+ BigDecimal discount,
int numberItem,
@NotNull BigDecimal totalPrice,
BigDecimal deliveryFee,
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 ab54204e0f..7ce16fea03 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
@@ -20,8 +20,8 @@ public record OrderVm(
OrderAddressVm shippingAddressVm,
OrderAddressVm billingAddressVm,
String note,
- float tax,
- float discount,
+ BigDecimal tax,
+ BigDecimal discount,
int numberItem,
BigDecimal totalPrice,
BigDecimal deliveryFee,
diff --git a/order/src/main/resources/db/changelog/ddl/changelog-0016.sql b/order/src/main/resources/db/changelog/ddl/changelog-0016.sql
new file mode 100644
index 0000000000..52563b9e22
--- /dev/null
+++ b/order/src/main/resources/db/changelog/ddl/changelog-0016.sql
@@ -0,0 +1,2 @@
+ALTER TABLE IF EXISTS "checkout"
+ADD COLUMN billing_address_id bigint;
\ No newline at end of file
diff --git a/order/src/main/resources/db/changelog/ddl/changelog-0017.sql b/order/src/main/resources/db/changelog/ddl/changelog-0017.sql
new file mode 100644
index 0000000000..c9e2188575
--- /dev/null
+++ b/order/src/main/resources/db/changelog/ddl/changelog-0017.sql
@@ -0,0 +1 @@
+ALTER TABLE public.checkout REPLICA IDENTITY FULL;
\ No newline at end of file
diff --git a/order/src/main/resources/messages/messages.properties b/order/src/main/resources/messages/messages.properties
index 414c0feeca..19ecd57b80 100644
--- a/order/src/main/resources/messages/messages.properties
+++ b/order/src/main/resources/messages/messages.properties
@@ -1,5 +1,6 @@
ORDER_NOT_FOUND=Order {} is not found
CHECKOUT_NOT_FOUND=Checkout {} is not found
+ORDER_ADDRESS_NOT_FOUND=Order Address {} is not found
SUCCESS_MESSAGE=Success
SIGN_IN_REQUIRED=Authentication required
FORBIDDEN=You don't have permission to access this page
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 ad92143799..fc8a69d1da 100644
--- a/order/src/test/java/com/yas/order/controller/OrderControllerTest.java
+++ b/order/src/test/java/com/yas/order/controller/OrderControllerTest.java
@@ -281,8 +281,8 @@ private OrderVm getOrderVm() {
shippingAddress,
billingAddress,
"Please deliver by next week.",
- 7.50f,
- 15.00f,
+ new BigDecimal("7.50"),
+ new BigDecimal("15.00"),
3,
new BigDecimal("159.97"),
new BigDecimal("7.99"),
@@ -371,8 +371,8 @@ private OrderPostVm getOrderPostVm() {
shippingAddress,
billingAddress,
"Please handle with care.",
- 5.00f,
- 10.00f,
+ new BigDecimal("5.00"),
+ new BigDecimal("10.00"),
2,
new BigDecimal("89.97"),
new BigDecimal("5.00"),
diff --git a/order/src/test/java/com/yas/order/kafka/consumer/OrderStatusConsumerTest.java b/order/src/test/java/com/yas/order/kafka/consumer/OrderStatusConsumerTest.java
index 1e95152491..dab9354331 100644
--- a/order/src/test/java/com/yas/order/kafka/consumer/OrderStatusConsumerTest.java
+++ b/order/src/test/java/com/yas/order/kafka/consumer/OrderStatusConsumerTest.java
@@ -9,16 +9,13 @@
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.google.gson.Gson;
-import com.google.gson.JsonObject;
import com.yas.commonlibrary.exception.BadRequestException;
-import com.yas.commonlibrary.exception.NotFoundException;
import com.yas.order.model.Checkout;
import com.yas.order.model.enumeration.CheckoutProgress;
-import com.yas.order.repository.CheckoutRepository;
+import com.yas.order.service.CheckoutService;
import com.yas.order.service.PaymentService;
-import java.util.Optional;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
@@ -30,9 +27,9 @@ class OrderStatusConsumerTest {
private PaymentService paymentService;
- private CheckoutRepository checkoutRepository;
+ private CheckoutService checkoutService;
- private OrderStatusConsumer orderStatusConsumer;
+ private CheckoutFulfillmentConsumer orderStatusConsumer;
private final String jsonRecord = "{\"op\": \"u\", \"after\": {"
+ " \"status\": \"PAYMENT_PROCESSING\","
@@ -45,10 +42,9 @@ class OrderStatusConsumerTest {
@BeforeEach
void setUp() {
paymentService = Mockito.mock(PaymentService.class);
- checkoutRepository = Mockito.mock(CheckoutRepository.class);
+ checkoutService = Mockito.mock(CheckoutService.class);
ObjectMapper objectMapper = new ObjectMapper();
- Gson gson = new Gson();
- orderStatusConsumer = new OrderStatusConsumer(paymentService, checkoutRepository, objectMapper, gson);
+ orderStatusConsumer = new CheckoutFulfillmentConsumer(paymentService, checkoutService, objectMapper);
}
@@ -57,9 +53,9 @@ void testListen_whenConsumerRecordIsNull_shouldNotThing() {
orderStatusConsumer.listen(null);
- verify(checkoutRepository, never()).findById(any());
+ verify(checkoutService, never()).findCheckoutById(any());
verify(paymentService, never()).createPaymentFromEvent(any());
- verify(checkoutRepository, never()).save(any());
+ verify(checkoutService, never()).updateCheckout(any());
}
@Test
@@ -71,8 +67,8 @@ void testListen_whenHaveNoAfter_shouldNotThing() {
orderStatusConsumer.listen(consumerRecord);
- verify(checkoutRepository, never()).save(any());
- verify(checkoutRepository, never()).findById(any());
+ verify(checkoutService, never()).updateCheckout(any());
+ verify(checkoutService, never()).findCheckoutById(any());
verify(paymentService, never()).createPaymentFromEvent(any());
}
@@ -93,8 +89,8 @@ void testListen_whenIsNotPaymentProcess_shouldNotThing() {
orderStatusConsumer.listen(consumerRecord);
- verify(checkoutRepository, never()).findById(any());
- verify(checkoutRepository, never()).save(any());
+ verify(checkoutService, never()).findCheckoutById(any());
+ verify(checkoutService, never()).updateCheckout(any());
verify(paymentService, never()).createPaymentFromEvent(any());
}
@@ -115,31 +111,8 @@ void testListen_whenProgressIsNotStockLocked_shouldNotThing() {
orderStatusConsumer.listen(consumerRecord);
- verify(checkoutRepository, never()).findById(any());
- verify(checkoutRepository, never()).save(any());
- verify(paymentService, never()).createPaymentFromEvent(any());
- }
-
- @Test
- void testListen_whenCheckoutIsNotFound_shouldNotSave() {
-
- ConsumerRecord, String> consumerRecord = mock(ConsumerRecord.class);
-
- when(consumerRecord.value()).thenReturn(jsonRecord);
- JsonObject jsonObject = mock(JsonObject.class);
- when(jsonObject.has("after")).thenReturn(true);
- when(jsonObject.getAsJsonObject("after")).thenReturn(mock(JsonObject.class));
-
- when(checkoutRepository.findById(anyString())).thenReturn(Optional.empty());
-
- when(paymentService.createPaymentFromEvent(any())).thenReturn(1L);
-
- NotFoundException notFoundException
- = Assertions.assertThrows(NotFoundException.class, () -> orderStatusConsumer.listen(consumerRecord));
-
- assertThat(notFoundException.getMessage()).isEqualTo("Checkout 12345 is not found");
- verify(checkoutRepository, atLeastOnce()).findById(any());
- verify(checkoutRepository, never()).save(any());
+ verify(checkoutService, never()).findCheckoutById(any());
+ verify(checkoutService, never()).updateCheckout(any());
verify(paymentService, never()).createPaymentFromEvent(any());
}
@@ -149,21 +122,21 @@ void testListen_whenCreatePaymentSuccess_shouldProcessCheckoutEvent() {
ConsumerRecord, String> consumerRecord = mock(ConsumerRecord.class);
when(consumerRecord.value()).thenReturn(jsonRecord);
- JsonObject jsonObject = mock(JsonObject.class);
+ JsonNode jsonObject = mock(JsonNode.class);
when(jsonObject.has("after")).thenReturn(true);
- when(jsonObject.getAsJsonObject("after")).thenReturn(mock(JsonObject.class));
+ when(jsonObject.get("after")).thenReturn(mock(JsonNode.class));
ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Checkout.class);
- when(checkoutRepository.findById(anyString())).thenReturn(Optional.of(new Checkout()));
+ when(checkoutService.findCheckoutById(anyString())).thenReturn(new Checkout());
when(paymentService.createPaymentFromEvent(any())).thenReturn(2L);
orderStatusConsumer.listen(consumerRecord);
- verify(checkoutRepository, atLeastOnce()).findById(any());
+ verify(checkoutService, atLeastOnce()).findCheckoutById(any());
verify(paymentService, atLeastOnce()).createPaymentFromEvent(any());
- verify(checkoutRepository, atLeastOnce()).save(argumentCaptor.capture());
+ verify(checkoutService, atLeastOnce()).updateCheckout(argumentCaptor.capture());
Checkout actual = argumentCaptor.getValue();
assertThat(actual.getProgress()).isEqualTo(CheckoutProgress.PAYMENT_CREATED);
@@ -176,13 +149,13 @@ void testListen_whenCreatePaymentFailure_shouldThrowException() {
ConsumerRecord, String> consumerRecord = mock(ConsumerRecord.class);
when(consumerRecord.value()).thenReturn(jsonRecord);
- JsonObject jsonObject = mock(JsonObject.class);
+ JsonNode jsonObject = mock(JsonNode.class);
when(jsonObject.has("after")).thenReturn(true);
- when(jsonObject.getAsJsonObject("after")).thenReturn(mock(JsonObject.class));
+ when(jsonObject.get("after")).thenReturn(mock(JsonNode.class));
Checkout checkout = new Checkout();
checkout.setId("12345");
- when(checkoutRepository.findById(anyString())).thenReturn(Optional.of(checkout));
+ when(checkoutService.findCheckoutById(anyString())).thenReturn(checkout);
BadRequestException badRequestException = new BadRequestException("test exception");
when(paymentService.createPaymentFromEvent(any())).thenThrow(badRequestException);
@@ -192,8 +165,8 @@ void testListen_whenCreatePaymentFailure_shouldThrowException() {
assertThat(badRequest.getMessage()).isEqualTo("Failed to process checkout event for ID 12345");
ArgumentCaptor argumentCaptor = ArgumentCaptor.forClass(Checkout.class);
- verify(checkoutRepository, atLeastOnce()).findById(any());
- verify(checkoutRepository, atLeastOnce()).save(argumentCaptor.capture());
+ verify(checkoutService, atLeastOnce()).findCheckoutById(any());
+ verify(checkoutService, atLeastOnce()).updateCheckout(argumentCaptor.capture());
verify(paymentService, atLeastOnce()).createPaymentFromEvent(any());
Checkout actual = argumentCaptor.getValue();
diff --git a/order/src/test/java/com/yas/order/kafka/consumer/PaymentUpdateConsumerTest.java b/order/src/test/java/com/yas/order/kafka/consumer/PaymentOrderIdUpdateConsumerTest.java
similarity index 95%
rename from order/src/test/java/com/yas/order/kafka/consumer/PaymentUpdateConsumerTest.java
rename to order/src/test/java/com/yas/order/kafka/consumer/PaymentOrderIdUpdateConsumerTest.java
index 9a858e0ad0..b9dc185fdf 100644
--- a/order/src/test/java/com/yas/order/kafka/consumer/PaymentUpdateConsumerTest.java
+++ b/order/src/test/java/com/yas/order/kafka/consumer/PaymentOrderIdUpdateConsumerTest.java
@@ -9,7 +9,6 @@
import static org.mockito.Mockito.when;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.google.gson.Gson;
import com.yas.order.model.Checkout;
import com.yas.order.model.enumeration.CheckoutProgress;
import com.yas.order.model.enumeration.CheckoutState;
@@ -19,11 +18,11 @@
import org.junit.jupiter.api.Test;
import org.mockito.ArgumentCaptor;
-class PaymentUpdateConsumerTest {
+class PaymentOrderIdUpdateConsumerTest {
private CheckoutService checkoutService;
- private PaymentUpdateConsumer paymentUpdateConsumer;
+ private PaymentPaypalOrderIdUpdateConsumer paymentUpdateConsumer;
private final String jsonRecord = "{\"op\":\"u\"," +
" \"before\":{\"payment_provider_checkout_id\":\"OLD\"}," +
@@ -35,8 +34,7 @@ class PaymentUpdateConsumerTest {
void setUp() {
checkoutService = mock(CheckoutService.class);
ObjectMapper objectMapper = new ObjectMapper();
- Gson gson = new Gson();
- paymentUpdateConsumer = new PaymentUpdateConsumer(checkoutService, objectMapper, gson);
+ paymentUpdateConsumer = new PaymentPaypalOrderIdUpdateConsumer(checkoutService, objectMapper);
}
@Test
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 265d08147e..1696ce775b 100644
--- a/order/src/test/java/com/yas/order/service/CartServiceTest.java
+++ b/order/src/test/java/com/yas/order/service/CartServiceTest.java
@@ -4,7 +4,6 @@
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import com.yas.order.config.ServiceUrlConfig;
@@ -17,16 +16,13 @@
import java.math.BigDecimal;
import java.net.URI;
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;
import org.mockito.Mockito;
-import org.springframework.core.ParameterizedTypeReference;
import org.springframework.web.client.RestClient;
-import org.springframework.web.util.UriComponentsBuilder;
class CartServiceTest {
@@ -74,8 +70,8 @@ void testDeleteCartItems_ifNormalCase_shouldNoException() {
null,
null,
"Please deliver between 9 AM and 5 PM",
- 5.0f,
- 10.0f,
+ new BigDecimal("5.0"),
+ new BigDecimal("10.0"),
3,
new BigDecimal("89.97"),
new BigDecimal("5.00"),
diff --git a/order/src/test/resources/application.properties b/order/src/test/resources/application.properties
index 5605601269..57af33b025 100644
--- a/order/src/test/resources/application.properties
+++ b/order/src/test/resources/application.properties
@@ -11,10 +11,12 @@ springdoc.oauthflow.token-url=test
cors.allowed-origins=*
cdc.event.checkout.status.topic-name=dbcheckout-status.public.checkout
-cdc.event.checkout.status.group-id=checkout-status
+cdc.event.checkout.fulfillment.group-id=checkout-fulfillment
+cdc.event.checkout.confirmed.status.group-id=checkout-confirmed-status
cdc.event.payment.topic-name=dbpayment.public.payment
-cdc.event.payment.update.group-id=payment-update
+cdc.event.payment.order.id.update.group-id=payment-order-id-update
+cdc.event.payment.status.update.group-id=payment-status-update
kafka.version=7.0.9
diff --git a/payment-paypal/src/main/java/com/yas/payment/paypal/model/PaypalPaymentAmount.java b/payment-paypal/src/main/java/com/yas/payment/paypal/model/PaypalPaymentAmount.java
new file mode 100644
index 0000000000..0a95de96bf
--- /dev/null
+++ b/payment-paypal/src/main/java/com/yas/payment/paypal/model/PaypalPaymentAmount.java
@@ -0,0 +1,19 @@
+package com.yas.payment.paypal.model;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class PaypalPaymentAmount {
+ private String currencyCode;
+ private String value;
+}
diff --git a/payment-paypal/src/main/java/com/yas/payment/paypal/model/PaypalPaymentPurchaseUnit.java b/payment-paypal/src/main/java/com/yas/payment/paypal/model/PaypalPaymentPurchaseUnit.java
new file mode 100644
index 0000000000..e81efcaed7
--- /dev/null
+++ b/payment-paypal/src/main/java/com/yas/payment/paypal/model/PaypalPaymentPurchaseUnit.java
@@ -0,0 +1,22 @@
+package com.yas.payment.paypal.model;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class PaypalPaymentPurchaseUnit {
+
+ @JsonProperty("reference_id")
+ private String referenceId;
+ private PaypalPaymentAmount amount;
+}
diff --git a/payment-paypal/src/main/java/com/yas/payment/paypal/model/PaypalPaymentResource.java b/payment-paypal/src/main/java/com/yas/payment/paypal/model/PaypalPaymentResource.java
new file mode 100644
index 0000000000..7ad006de93
--- /dev/null
+++ b/payment-paypal/src/main/java/com/yas/payment/paypal/model/PaypalPaymentResource.java
@@ -0,0 +1,29 @@
+package com.yas.payment.paypal.model;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import jakarta.validation.constraints.NotNull;
+import java.util.List;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class PaypalPaymentResource {
+
+ @NotNull
+ private String id;
+
+ @NotNull
+ private String status;
+
+ @JsonProperty("purchase_units")
+ private List purchaseUnits;
+}
diff --git a/payment-paypal/src/main/java/com/yas/payment/paypal/model/PaypalWebhookEvent.java b/payment-paypal/src/main/java/com/yas/payment/paypal/model/PaypalWebhookEvent.java
new file mode 100644
index 0000000000..2e7a1819d2
--- /dev/null
+++ b/payment-paypal/src/main/java/com/yas/payment/paypal/model/PaypalWebhookEvent.java
@@ -0,0 +1,32 @@
+package com.yas.payment.paypal.model;
+
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonProperty;
+import com.yas.payment.paypal.model.enumeration.PaypalPaymentStatus;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.NotNull;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+@JsonIgnoreProperties(ignoreUnknown = true)
+public class PaypalWebhookEvent {
+
+ @NotNull
+ private String id;
+
+ @NotNull
+ @JsonProperty("event_type")
+ private PaypalPaymentStatus eventType;
+
+ @NotNull
+ @Valid
+ private PaypalPaymentResource resource;
+}
diff --git a/payment-paypal/src/main/java/com/yas/payment/paypal/model/enumeration/PaypalPaymentStatus.java b/payment-paypal/src/main/java/com/yas/payment/paypal/model/enumeration/PaypalPaymentStatus.java
new file mode 100644
index 0000000000..839b58882a
--- /dev/null
+++ b/payment-paypal/src/main/java/com/yas/payment/paypal/model/enumeration/PaypalPaymentStatus.java
@@ -0,0 +1,39 @@
+package com.yas.payment.paypal.model.enumeration;
+
+import com.fasterxml.jackson.annotation.JsonCreator;
+import com.fasterxml.jackson.annotation.JsonValue;
+import com.yas.commonlibrary.exception.BadRequestException;
+import lombok.Getter;
+
+@Getter
+public enum PaypalPaymentStatus {
+
+ PAYMENT_AUTHORIZATION_CREATED("PAYMENT.AUTHORIZATION.CREATED"),
+ PAYMENT_AUTHORIZATION_VOIDED("PAYMENT.AUTHORIZATION.VOIDED"),
+ PAYMENT_CAPTURE_DECLINED("PAYMENT.CAPTURE.DECLINED"),
+ PAYMENT_CAPTURE_COMPLETED("PAYMENT.CAPTURE.COMPLETED"),
+ PAYMENT_CAPTURE_PENDING("PAYMENT.CAPTURE.PENDING"),
+ PAYMENT_CAPTURE_REFUNDED("PAYMENT.CAPTURE.REFUNDED"),
+ PAYMENT_CAPTURE_REVERSED("PAYMENT.CAPTURE.REVERSED");
+
+ private final String status;
+
+ PaypalPaymentStatus(String status) {
+ this.status = status;
+ }
+
+ @JsonCreator
+ public static PaypalPaymentStatus fromString(String status) {
+ for (PaypalPaymentStatus paymentStatus : PaypalPaymentStatus.values()) {
+ if (paymentStatus.getStatus().equals(status)) {
+ return paymentStatus;
+ }
+ }
+ throw new BadRequestException("Unknown enum value: {}", status);
+ }
+
+ @JsonValue
+ public String getStatus() {
+ return status;
+ }
+}
diff --git a/payment-paypal/src/main/java/com/yas/payment/paypal/service/PaypalService.java b/payment-paypal/src/main/java/com/yas/payment/paypal/service/PaypalService.java
index 5116c37453..d0f7f14e31 100644
--- a/payment-paypal/src/main/java/com/yas/payment/paypal/service/PaypalService.java
+++ b/payment-paypal/src/main/java/com/yas/payment/paypal/service/PaypalService.java
@@ -31,7 +31,7 @@
public class PaypalService {
private final PayPalHttpClientInitializer payPalHttpClientInitializer;
private final BigDecimal maxPay = BigDecimal.valueOf(1000);
- @Value("${yas.public.url}/capture")
+ @Value("${yas.public.url}/success")
private String returnUrl;
@Value("${yas.public.url}/cancel")
private String cancelUrl;
@@ -52,10 +52,8 @@ public PaypalCreatePaymentResponse createPayment(PaypalCreatePaymentRequest crea
.value(totalPrice.toString());
PurchaseUnitRequest purchaseUnitRequest = new PurchaseUnitRequest().amountWithBreakdown(amountWithBreakdown);
orderRequest.purchaseUnits(List.of(purchaseUnitRequest));
- String paymentMethodReturnUrl
- = String.format("%s?paymentMethod=%s", returnUrl, createPaymentRequest.paymentMethod());
ApplicationContext applicationContext = new ApplicationContext()
- .returnUrl(paymentMethodReturnUrl)
+ .returnUrl(returnUrl)
.cancelUrl(cancelUrl)
.brandName(Constants.Yas.BRAND_NAME)
.landingPage("BILLING")
diff --git a/payment-paypal/src/test/java/com/yas/payment/paypal/service/PaypalServiceTest.java b/payment-paypal/src/test/java/com/yas/payment/paypal/service/PaypalServiceTest.java
index fb1718003b..7a2fc88ef6 100644
--- a/payment-paypal/src/test/java/com/yas/payment/paypal/service/PaypalServiceTest.java
+++ b/payment-paypal/src/test/java/com/yas/payment/paypal/service/PaypalServiceTest.java
@@ -1,26 +1,38 @@
package com.yas.payment.paypal.service;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
import com.paypal.core.PayPalHttpClient;
import com.paypal.http.HttpResponse;
-import com.paypal.orders.*;
-import com.yas.payment.paypal.model.*;
+import com.paypal.orders.Capture;
+import com.paypal.orders.LinkDescription;
+import com.paypal.orders.MerchantReceivableBreakdown;
+import com.paypal.orders.Money;
+import com.paypal.orders.Order;
+import com.paypal.orders.OrdersCaptureRequest;
+import com.paypal.orders.OrdersCreateRequest;
+import com.paypal.orders.PaymentCollection;
+import com.paypal.orders.PurchaseUnit;
+import com.yas.payment.paypal.model.CheckoutIdHelper;
import com.yas.payment.paypal.viewmodel.PaypalCapturePaymentRequest;
import com.yas.payment.paypal.viewmodel.PaypalCapturePaymentResponse;
import com.yas.payment.paypal.viewmodel.PaypalCreatePaymentRequest;
import com.yas.payment.paypal.viewmodel.PaypalCreatePaymentResponse;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;
-
-import static org.junit.jupiter.api.Assertions.*;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
class PaypalServiceTest {
diff --git a/payment/src/main/java/com/yas/payment/config/SecurityConfig.java b/payment/src/main/java/com/yas/payment/config/SecurityConfig.java
index cd140341d9..21ef1730b3 100644
--- a/payment/src/main/java/com/yas/payment/config/SecurityConfig.java
+++ b/payment/src/main/java/com/yas/payment/config/SecurityConfig.java
@@ -25,6 +25,7 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
.authorizeHttpRequests(auth -> auth
.requestMatchers("/actuator/prometheus", "/actuator/health/**",
"/swagger-ui", "/swagger-ui/**", "/error", "/v3/api-docs/**").permitAll()
+ .requestMatchers("/webhook/paypal/payment-events").permitAll()
.requestMatchers("/storefront/**").permitAll()
.requestMatchers("/backoffice/**").hasRole("ADMIN")
.requestMatchers("/payment-providers/**").permitAll()
diff --git a/payment/src/main/java/com/yas/payment/controller/event/EventController.java b/payment/src/main/java/com/yas/payment/controller/event/EventController.java
new file mode 100644
index 0000000000..5e1ed96c08
--- /dev/null
+++ b/payment/src/main/java/com/yas/payment/controller/event/EventController.java
@@ -0,0 +1,31 @@
+package com.yas.payment.controller.event;
+
+import com.yas.payment.paypal.model.PaypalWebhookEvent;
+import com.yas.payment.service.order.handler.base.IPaypalWebhookHandlerRegistry;
+import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RestController;
+
+@RestController
+@RequiredArgsConstructor
+public class EventController {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(EventController.class);
+
+ private final IPaypalWebhookHandlerRegistry registry;
+
+ @PostMapping("/webhook/paypal/payment-events")
+ public ResponseEntity handlePaypalPaymentEvent(@RequestBody @Valid PaypalWebhookEvent payload) {
+
+ LOGGER.info("Received webhook from PayPal: {}", payload);
+
+ registry.get(payload.getEventType().getStatus()).handle(payload);
+
+ return ResponseEntity.ok("Paypal webhook handled successfully");
+ }
+}
diff --git a/payment/src/main/java/com/yas/payment/kafka/consumer/PaymentCreateConsumer.java b/payment/src/main/java/com/yas/payment/kafka/consumer/PaymentCreateConsumer.java
index 81931a607f..bbdc923921 100644
--- a/payment/src/main/java/com/yas/payment/kafka/consumer/PaymentCreateConsumer.java
+++ b/payment/src/main/java/com/yas/payment/kafka/consumer/PaymentCreateConsumer.java
@@ -2,12 +2,11 @@
import static com.yas.payment.utils.JsonUtils.convertObjectToString;
import static com.yas.payment.utils.JsonUtils.getAttributesNode;
+import static com.yas.payment.utils.JsonUtils.getJsonNodeByValue;
+import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
-import com.google.gson.Gson;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
import com.yas.commonlibrary.exception.BadRequestException;
import com.yas.payment.model.Payment;
import com.yas.payment.model.enumeration.PaymentMethod;
@@ -33,7 +32,6 @@ public class PaymentCreateConsumer {
private static final Logger LOGGER = LoggerFactory.getLogger(PaymentCreateConsumer.class);
private final PaymentService paymentService;
private final ObjectMapper objectMapper;
- private final Gson gson;
@KafkaListener(
topics = "${cdc.event.payment.topic-name}",
@@ -46,26 +44,26 @@ public void listen(ConsumerRecord, ?> consumerRecord) {
LOGGER.info("Consumer Record is null");
return;
}
- JsonObject valueObject = gson.fromJson((String) consumerRecord.value(), JsonObject.class);
+ String jsonValue = (String) consumerRecord.value();
+ JsonNode valueObject = getJsonNodeByValue(objectMapper, jsonValue, LOGGER);
processCheckoutEvent(valueObject);
-
}
- private void processCheckoutEvent(JsonObject valueObject) {
+ private void processCheckoutEvent(JsonNode valueObject) {
Optional.ofNullable(valueObject)
.filter(
- value -> value.has("op") && "c".equals(value.get("op").getAsString())
+ value -> value.has("op") && "c".equals(value.get("op").asText())
)
.filter(value -> value.has("after"))
- .map(value -> value.getAsJsonObject("after"))
+ .map(value -> value.get("after"))
.ifPresent(this::handleAfterJsonForCreatingOrder);
}
- private void handleAfterJsonForCreatingOrder(JsonObject after) {
+ private void handleAfterJsonForCreatingOrder(JsonNode after) {
Long id = Optional.ofNullable(after.get(Constants.Column.ID_COLUMN))
- .filter(jsonElement -> !jsonElement.isJsonNull())
- .map(JsonElement::getAsLong)
+ .filter(jsonElement -> !jsonElement.isNull())
+ .map(JsonNode::asLong)
.orElseThrow(() -> new BadRequestException(Constants.ErrorCode.ID_NOT_EXISTED));
LOGGER.info("Handle after json for creating order Payment ID {}", id);
diff --git a/payment/src/main/java/com/yas/payment/model/enumeration/PaymentStatus.java b/payment/src/main/java/com/yas/payment/model/enumeration/PaymentStatus.java
index d3b3280ef0..6858f48a96 100644
--- a/payment/src/main/java/com/yas/payment/model/enumeration/PaymentStatus.java
+++ b/payment/src/main/java/com/yas/payment/model/enumeration/PaymentStatus.java
@@ -4,6 +4,7 @@ public enum PaymentStatus {
NEW,
PROCESSING,
PENDING,
+ FAILURE,
COMPLETED,
CANCELLED
}
diff --git a/payment/src/main/java/com/yas/payment/repository/PaymentRepository.java b/payment/src/main/java/com/yas/payment/repository/PaymentRepository.java
index f802eb9334..f52939891d 100644
--- a/payment/src/main/java/com/yas/payment/repository/PaymentRepository.java
+++ b/payment/src/main/java/com/yas/payment/repository/PaymentRepository.java
@@ -1,9 +1,12 @@
package com.yas.payment.repository;
import com.yas.payment.model.Payment;
+import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface PaymentRepository extends JpaRepository {
+
+ Optional findByPaymentProviderCheckoutId(String paymentProviderCheckoutId);
}
\ No newline at end of file
diff --git a/payment/src/main/java/com/yas/payment/service/PaymentService.java b/payment/src/main/java/com/yas/payment/service/PaymentService.java
index e7f519aa2a..cfb56576c5 100644
--- a/payment/src/main/java/com/yas/payment/service/PaymentService.java
+++ b/payment/src/main/java/com/yas/payment/service/PaymentService.java
@@ -133,6 +133,14 @@ public Payment findPaymentById(Long id) {
() -> new NotFoundException(Constants.ErrorCode.PAYMENT_NOT_FOUND, id));
}
+ public Payment findPaymentByPaypalOrderId(String id) {
+
+ return paymentRepository
+ .findByPaymentProviderCheckoutId(id)
+ .orElseThrow(
+ () -> new NotFoundException(Constants.ErrorCode.PAYMENT_NOT_FOUND, id));
+ }
+
public void updatePayment(Payment payment) {
if (Objects.isNull(payment.getId())) {
diff --git a/payment/src/main/java/com/yas/payment/service/order/handler/CompleteCaptureHandler.java b/payment/src/main/java/com/yas/payment/service/order/handler/CompleteCaptureHandler.java
new file mode 100644
index 0000000000..909a221297
--- /dev/null
+++ b/payment/src/main/java/com/yas/payment/service/order/handler/CompleteCaptureHandler.java
@@ -0,0 +1,36 @@
+package com.yas.payment.service.order.handler;
+
+import static com.yas.payment.paypal.model.enumeration.PaypalPaymentStatus.PAYMENT_CAPTURE_COMPLETED;
+
+import com.yas.payment.model.enumeration.PaymentStatus;
+import com.yas.payment.paypal.model.PaypalWebhookEvent;
+import com.yas.payment.paypal.model.enumeration.PaypalPaymentStatus;
+import com.yas.payment.service.PaymentService;
+import com.yas.payment.service.order.handler.base.IPaypalWebhookHandlerRegistry;
+import com.yas.payment.service.order.handler.base.PaypalWebhookHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+@Component
+public class CompleteCaptureHandler extends PaypalWebhookHandler {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(CompleteCaptureHandler.class);
+
+ public CompleteCaptureHandler(
+ IPaypalWebhookHandlerRegistry registry,
+ PaymentService paymentService
+ ) {
+ super(registry, paymentService, LOGGER);
+ }
+
+ @Override
+ protected PaypalPaymentStatus providePaypalPaymentStatus() {
+ return PAYMENT_CAPTURE_COMPLETED;
+ }
+
+ @Override
+ protected PaymentStatus getPaymentStatus() {
+ return PaymentStatus.COMPLETED;
+ }
+}
diff --git a/payment/src/main/java/com/yas/payment/service/order/handler/DeclinedCaptureHandler.java b/payment/src/main/java/com/yas/payment/service/order/handler/DeclinedCaptureHandler.java
new file mode 100644
index 0000000000..99362f1475
--- /dev/null
+++ b/payment/src/main/java/com/yas/payment/service/order/handler/DeclinedCaptureHandler.java
@@ -0,0 +1,37 @@
+package com.yas.payment.service.order.handler;
+
+import static com.yas.payment.paypal.model.enumeration.PaypalPaymentStatus.PAYMENT_CAPTURE_DECLINED;
+
+import com.yas.payment.model.enumeration.PaymentStatus;
+import com.yas.payment.paypal.model.PaypalWebhookEvent;
+import com.yas.payment.paypal.model.enumeration.PaypalPaymentStatus;
+import com.yas.payment.service.PaymentService;
+import com.yas.payment.service.order.handler.base.IPaypalWebhookHandlerRegistry;
+import com.yas.payment.service.order.handler.base.PaypalWebhookHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+@Component
+public class DeclinedCaptureHandler extends PaypalWebhookHandler {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(DeclinedCaptureHandler.class);
+
+ public DeclinedCaptureHandler(
+ IPaypalWebhookHandlerRegistry registry,
+ PaymentService paymentService
+ ) {
+ super(registry, paymentService, LOGGER);
+ }
+
+ @Override
+ protected PaypalPaymentStatus providePaypalPaymentStatus() {
+ return PAYMENT_CAPTURE_DECLINED;
+ }
+
+ @Override
+ protected PaymentStatus getPaymentStatus() {
+ return PaymentStatus.FAILURE;
+ }
+
+}
diff --git a/payment/src/main/java/com/yas/payment/service/order/handler/RefundedCaptureHandler.java b/payment/src/main/java/com/yas/payment/service/order/handler/RefundedCaptureHandler.java
new file mode 100644
index 0000000000..fa59ce0e95
--- /dev/null
+++ b/payment/src/main/java/com/yas/payment/service/order/handler/RefundedCaptureHandler.java
@@ -0,0 +1,37 @@
+package com.yas.payment.service.order.handler;
+
+import static com.yas.payment.paypal.model.enumeration.PaypalPaymentStatus.PAYMENT_CAPTURE_REFUNDED;
+
+import com.yas.payment.model.enumeration.PaymentStatus;
+import com.yas.payment.paypal.model.PaypalWebhookEvent;
+import com.yas.payment.paypal.model.enumeration.PaypalPaymentStatus;
+import com.yas.payment.service.PaymentService;
+import com.yas.payment.service.order.handler.base.IPaypalWebhookHandlerRegistry;
+import com.yas.payment.service.order.handler.base.PaypalWebhookHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+@Component
+public class RefundedCaptureHandler extends PaypalWebhookHandler {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(RefundedCaptureHandler.class);
+
+ public RefundedCaptureHandler(
+ IPaypalWebhookHandlerRegistry registry,
+ PaymentService paymentService
+ ) {
+ super(registry, paymentService, LOGGER);
+ }
+
+ @Override
+ protected PaypalPaymentStatus providePaypalPaymentStatus() {
+ return PAYMENT_CAPTURE_REFUNDED;
+ }
+
+ @Override
+ protected PaymentStatus getPaymentStatus() {
+ return PaymentStatus.FAILURE;
+ }
+
+}
diff --git a/payment/src/main/java/com/yas/payment/service/order/handler/ReversedCaptureHandler.java b/payment/src/main/java/com/yas/payment/service/order/handler/ReversedCaptureHandler.java
new file mode 100644
index 0000000000..931ea189d5
--- /dev/null
+++ b/payment/src/main/java/com/yas/payment/service/order/handler/ReversedCaptureHandler.java
@@ -0,0 +1,37 @@
+package com.yas.payment.service.order.handler;
+
+import static com.yas.payment.paypal.model.enumeration.PaypalPaymentStatus.PAYMENT_CAPTURE_REVERSED;
+
+import com.yas.payment.model.enumeration.PaymentStatus;
+import com.yas.payment.paypal.model.PaypalWebhookEvent;
+import com.yas.payment.paypal.model.enumeration.PaypalPaymentStatus;
+import com.yas.payment.service.PaymentService;
+import com.yas.payment.service.order.handler.base.IPaypalWebhookHandlerRegistry;
+import com.yas.payment.service.order.handler.base.PaypalWebhookHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+@Component
+public class ReversedCaptureHandler extends PaypalWebhookHandler {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(ReversedCaptureHandler.class);
+
+ public ReversedCaptureHandler(
+ IPaypalWebhookHandlerRegistry registry,
+ PaymentService paymentService
+ ) {
+ super(registry, paymentService, LOGGER);
+ }
+
+ @Override
+ protected PaypalPaymentStatus providePaypalPaymentStatus() {
+ return PAYMENT_CAPTURE_REVERSED;
+ }
+
+ @Override
+ protected PaymentStatus getPaymentStatus() {
+ return PaymentStatus.FAILURE;
+ }
+
+}
diff --git a/payment/src/main/java/com/yas/payment/service/order/handler/VoidedAuthorizationHandler.java b/payment/src/main/java/com/yas/payment/service/order/handler/VoidedAuthorizationHandler.java
new file mode 100644
index 0000000000..16fb67e7c0
--- /dev/null
+++ b/payment/src/main/java/com/yas/payment/service/order/handler/VoidedAuthorizationHandler.java
@@ -0,0 +1,37 @@
+package com.yas.payment.service.order.handler;
+
+import static com.yas.payment.paypal.model.enumeration.PaypalPaymentStatus.PAYMENT_AUTHORIZATION_VOIDED;
+
+import com.yas.payment.model.enumeration.PaymentStatus;
+import com.yas.payment.paypal.model.PaypalWebhookEvent;
+import com.yas.payment.paypal.model.enumeration.PaypalPaymentStatus;
+import com.yas.payment.service.PaymentService;
+import com.yas.payment.service.order.handler.base.IPaypalWebhookHandlerRegistry;
+import com.yas.payment.service.order.handler.base.PaypalWebhookHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+@Component
+public class VoidedAuthorizationHandler extends PaypalWebhookHandler {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(VoidedAuthorizationHandler.class);
+
+ public VoidedAuthorizationHandler(
+ IPaypalWebhookHandlerRegistry registry,
+ PaymentService paymentService
+ ) {
+ super(registry, paymentService, LOGGER);
+ }
+
+ @Override
+ protected PaypalPaymentStatus providePaypalPaymentStatus() {
+ return PAYMENT_AUTHORIZATION_VOIDED;
+ }
+
+ @Override
+ protected PaymentStatus getPaymentStatus() {
+ return PaymentStatus.FAILURE;
+ }
+
+}
diff --git a/payment/src/main/java/com/yas/payment/service/order/handler/base/IPaypalWebhookHandler.java b/payment/src/main/java/com/yas/payment/service/order/handler/base/IPaypalWebhookHandler.java
new file mode 100644
index 0000000000..06713ba81b
--- /dev/null
+++ b/payment/src/main/java/com/yas/payment/service/order/handler/base/IPaypalWebhookHandler.java
@@ -0,0 +1,14 @@
+package com.yas.payment.service.order.handler.base;
+
+import com.yas.payment.paypal.model.PaypalWebhookEvent;
+
+public interface IPaypalWebhookHandler {
+
+ /**
+ * Processes the webhook payload.
+ *
+ * @param payload the webhook payload of type PaypalWebhookEvent
+ */
+ void handle(T payload);
+
+}
diff --git a/payment/src/main/java/com/yas/payment/service/order/handler/base/IPaypalWebhookHandlerRegistry.java b/payment/src/main/java/com/yas/payment/service/order/handler/base/IPaypalWebhookHandlerRegistry.java
new file mode 100644
index 0000000000..711d61bbda
--- /dev/null
+++ b/payment/src/main/java/com/yas/payment/service/order/handler/base/IPaypalWebhookHandlerRegistry.java
@@ -0,0 +1,10 @@
+package com.yas.payment.service.order.handler.base;
+
+import com.yas.payment.paypal.model.PaypalWebhookEvent;
+
+public interface IPaypalWebhookHandlerRegistry {
+
+ IPaypalWebhookHandler get(String type);
+
+ void register(String type, IPaypalWebhookHandler handler);
+}
diff --git a/payment/src/main/java/com/yas/payment/service/order/handler/base/PaypalWebhookHandler.java b/payment/src/main/java/com/yas/payment/service/order/handler/base/PaypalWebhookHandler.java
new file mode 100644
index 0000000000..e42d675b59
--- /dev/null
+++ b/payment/src/main/java/com/yas/payment/service/order/handler/base/PaypalWebhookHandler.java
@@ -0,0 +1,41 @@
+package com.yas.payment.service.order.handler.base;
+
+import com.yas.payment.model.Payment;
+import com.yas.payment.model.enumeration.PaymentStatus;
+import com.yas.payment.paypal.model.PaypalWebhookEvent;
+import com.yas.payment.paypal.model.enumeration.PaypalPaymentStatus;
+import com.yas.payment.service.PaymentService;
+import org.slf4j.Logger;
+
+public abstract class PaypalWebhookHandler implements IPaypalWebhookHandler {
+
+ private final Logger logger;
+ protected final PaymentService paymentService;
+
+ protected PaypalWebhookHandler(
+ IPaypalWebhookHandlerRegistry registry,
+ PaymentService paymentService,
+ Logger logger
+ ) {
+ registry.register(this.providePaypalPaymentStatus().getStatus(), this);
+ this.paymentService = paymentService;
+ this.logger = logger;
+ }
+
+ protected abstract PaypalPaymentStatus providePaypalPaymentStatus();
+
+ protected abstract PaymentStatus getPaymentStatus();
+
+ @Override
+ public void handle(PaypalWebhookEvent payload) {
+
+ logger.info("Start handling for status {}, Update Payment Status to {}",
+ providePaypalPaymentStatus(), getPaymentStatus());
+ Payment payment = paymentService.findPaymentByPaypalOrderId(payload.getResource().getId());
+ payment.setPaymentStatus(getPaymentStatus());
+ paymentService.updatePayment(payment);
+ logger.info("End webhook from PayPal: {}", providePaypalPaymentStatus());
+
+ }
+
+}
diff --git a/payment/src/main/java/com/yas/payment/service/order/handler/base/PaypalWebhookHandlerRegistry.java b/payment/src/main/java/com/yas/payment/service/order/handler/base/PaypalWebhookHandlerRegistry.java
new file mode 100644
index 0000000000..7d4a502b29
--- /dev/null
+++ b/payment/src/main/java/com/yas/payment/service/order/handler/base/PaypalWebhookHandlerRegistry.java
@@ -0,0 +1,27 @@
+package com.yas.payment.service.order.handler.base;
+
+import com.yas.commonlibrary.exception.NotFoundException;
+import com.yas.payment.paypal.model.PaypalWebhookEvent;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
+import org.springframework.stereotype.Component;
+
+@Component
+public class PaypalWebhookHandlerRegistry implements IPaypalWebhookHandlerRegistry {
+
+ private final Map> registry = new HashMap<>();
+
+ public IPaypalWebhookHandler get(String type) {
+ IPaypalWebhookHandler handler = this.registry.get(type);
+ if (Objects.isNull(handler)) {
+ throw new NotFoundException("MessageType {} is not supported", type);
+ }
+ return handler;
+ }
+
+ @Override
+ public void register(String type, IPaypalWebhookHandler handler) {
+ this.registry.put(type, handler);
+ }
+}
diff --git a/payment/src/main/java/com/yas/payment/utils/JsonUtils.java b/payment/src/main/java/com/yas/payment/utils/JsonUtils.java
index 4e99823cbd..76061a8a5e 100644
--- a/payment/src/main/java/com/yas/payment/utils/JsonUtils.java
+++ b/payment/src/main/java/com/yas/payment/utils/JsonUtils.java
@@ -1,9 +1,11 @@
package com.yas.payment.utils;
import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.yas.commonlibrary.exception.BadRequestException;
+import org.slf4j.Logger;
public class JsonUtils {
@@ -30,4 +32,17 @@ public static ObjectNode getAttributesNode(ObjectMapper objectMapper, String att
throw new BadRequestException("Invalid Json: {}", attributes);
}
}
+
+ public static JsonNode getJsonNodeByValue(
+ ObjectMapper objectMapper,
+ String jsonValue,
+ Logger logger
+ ) {
+ try {
+ return objectMapper.readTree(jsonValue);
+ } catch (JsonProcessingException e) {
+ logger.error("Invalid Json. Message: {}", jsonValue, e);
+ throw new BadRequestException("Failed to parse message as JSON. Message: {}", jsonValue);
+ }
+ }
}
\ No newline at end of file
diff --git a/payment/src/test/java/com/yas/payment/kafka/consumer/PaymentCreateConsumerTest.java b/payment/src/test/java/com/yas/payment/kafka/consumer/PaymentCreateConsumerTest.java
index 7bf3c6a9d2..b0c9b3b396 100644
--- a/payment/src/test/java/com/yas/payment/kafka/consumer/PaymentCreateConsumerTest.java
+++ b/payment/src/test/java/com/yas/payment/kafka/consumer/PaymentCreateConsumerTest.java
@@ -9,7 +9,6 @@
import static org.mockito.Mockito.when;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.google.gson.Gson;
import com.yas.commonlibrary.exception.BadRequestException;
import com.yas.payment.model.Payment;
import com.yas.payment.model.enumeration.PaymentMethod;
@@ -33,8 +32,7 @@ class PaymentCreateConsumerTest {
void setUp() {
paymentService = mock(PaymentService.class);
ObjectMapper objectMapper = new ObjectMapper();
- Gson gson = new Gson();
- paymentCreateConsumer = new PaymentCreateConsumer(paymentService, objectMapper, gson);
+ paymentCreateConsumer = new PaymentCreateConsumer(paymentService, objectMapper);
}
@Test
diff --git a/storefront/modules/order/components/CheckOutDetail.tsx b/storefront/modules/order/components/CheckOutDetail.tsx
index 74e29628b4..a3a9957556 100644
--- a/storefront/modules/order/components/CheckOutDetail.tsx
+++ b/storefront/modules/order/components/CheckOutDetail.tsx
@@ -137,7 +137,7 @@ const CheckOutDetail = ({ orderItems, disablePaymentProcess, setPaymentMethod }:
-
>
);
diff --git a/storefront/pages/checkout/[id].tsx b/storefront/pages/checkout/[id].tsx
index 710e88a297..179c66043b 100644
--- a/storefront/pages/checkout/[id].tsx
+++ b/storefront/pages/checkout/[id].tsx
@@ -10,7 +10,6 @@ import { Input } from 'common/items/Input';
import { Address } from '@/modules/address/models/AddressModel';
import AddressForm from '@/modules/address/components/AddressForm';
import {
- createOrder,
getCheckoutById,
processPayment,
} from '@/modules/order/services/OrderService';
@@ -222,20 +221,8 @@ const Checkout = () => {
setIsShowSpinner(true);
setDisableProcessPayment(true);
- if (clickedButtonId === 'newProcessToPaymentButton') {
- processPayment(id as string);
- handlePaymentProcess(order);
- } else {
- createOrder(order)
- .then(() => {
- handleCheckOutProcess(order);
- })
- .catch(() => {
- setIsShowSpinner(false);
- setDisableProcessPayment(false);
- toast.error('Place order failed');
- });
- }
+ processPayment(id as string);
+ handlePaymentProcess(order);
}
};
diff --git a/storefront/pages/complete-payment/[capture].tsx b/storefront/pages/complete-payment/[capture].tsx
deleted file mode 100644
index 0c9443c55e..0000000000
--- a/storefront/pages/complete-payment/[capture].tsx
+++ /dev/null
@@ -1,114 +0,0 @@
-import { BreadcrumbModel } from '@/modules/breadcrumb/model/BreadcrumbModel';
-import { Button, Container } from 'react-bootstrap';
-import BreadcrumbComponent from '../../common/components/BreadcrumbComponent';
-import { useRouter } from 'next/router';
-import { useEffect, useState } from 'react';
-import { capturePaymentPaypal } from '@/modules/paymentPaypal/services/PaymentPaypalService';
-import SpinnerComponent from '@/common/components/SpinnerComponent';
-import Link from 'next/link';
-import { CapturePaymentPaypalResponse } from '@/modules/paymentPaypal/models/CapturePaymentPaypalResponse';
-import { PaymentPaypalFailureMessage } from '@/modules/paymentPaypal/models/PaymentPaypalFailureMesasge';
-import { CapturePaymentRequest } from '@/modules/paymentPaypal/models/CapturePaymentRequest';
-
-const crumb: BreadcrumbModel[] = [
- {
- pageName: 'Home',
- url: '/',
- },
- {
- pageName: 'Complete Payment',
- url: '/complete-payment',
- },
-];
-
-const CompletePayment = () => {
- const router = useRouter();
- const { token, paymentMethod } = router.query;
- const [isPaymentSuccess, setIsPaymentSuccess] = useState(false);
- const [isAlreadyPaid, setIsAlreadyPaid] = useState(false);
- const [isCancelPayment, setIsCancelPayment] = useState(false);
- const [isPaymentUnsuccessful, setIsPaymentUnsuccessful] = useState(false);
- const [isShowSpinner, setIsShowSpinner] = useState(false);
- useEffect(() => {
- if (token) {
- const fetchCapturePaymentPaypal = async (capturePaymentRequestVM: CapturePaymentRequest) => {
- setIsShowSpinner(true);
- const res = await capturePaymentPaypal(capturePaymentRequestVM);
- if (res.paymentStatus == 'COMPLETED') {
- setIsPaymentSuccess(true);
- } else {
- extractPaymentPaypalFailure(res);
- }
- setIsShowSpinner(false);
- };
-
- const capturePaymentRequestVM: CapturePaymentRequest = {
- token: token as string,
- paymentMethod: paymentMethod as string,
- };
- fetchCapturePaymentPaypal(capturePaymentRequestVM).then();
- }
- }, [router.query, token, paymentMethod]);
-
- const extractPaymentPaypalFailure = (res: CapturePaymentPaypalResponse) => {
- const failureMessage: PaymentPaypalFailureMessage = JSON.parse(res.failureMessage!!);
- const details = failureMessage.details;
- const issue = details[0].issue;
- switch (issue) {
- case 'ORDER_NOT_APPROVED':
- setIsCancelPayment(true);
- break;
- case 'ORDER_ALREADY_CAPTURED':
- setIsAlreadyPaid(true);
- break;
- default:
- setIsPaymentUnsuccessful(true);
- }
- };
-
- return (
- <>
-
-
-
-
-
-
-
-
-
- YOUR ORDER PAID SUCCESSFUL
-
-
-
-
- YOUR PAYMENT IS CANCELED
-
-
-
-
- YOUR ORDER ALREADY PAID
-
-
-
-
- YOUR ORDER PURCHASE UNSUCCESSFUL{' '}
-
-
-
-
-
-
-
-
-
-
-
-
- >
- );
-};
-
-export default CompletePayment;
diff --git a/storefront/pages/complete-payment/[success].tsx b/storefront/pages/complete-payment/[success].tsx
new file mode 100644
index 0000000000..8c05b69026
--- /dev/null
+++ b/storefront/pages/complete-payment/[success].tsx
@@ -0,0 +1,48 @@
+import { BreadcrumbModel } from '@/modules/breadcrumb/model/BreadcrumbModel';
+import { Button, Container } from 'react-bootstrap';
+import BreadcrumbComponent from '../../common/components/BreadcrumbComponent';
+import Link from 'next/link';
+
+const crumb: BreadcrumbModel[] = [
+ {
+ pageName: 'Home',
+ url: '/',
+ },
+ {
+ pageName: 'Complete Payment',
+ url: '/complete-payment',
+ },
+];
+
+const CompletePayment = () => {
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+ YOUR ORDER PAID SUCCESSFUL
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default CompletePayment;