From c46a4fce8c82b15422a5871e340119d53b69af10 Mon Sep 17 00:00:00 2001 From: Kochnov Denis Date: Fri, 28 Feb 2025 13:28:50 +0300 Subject: [PATCH 1/3] Add observer to hold placed orders before processing --- Model/Observer/HoldPlacedOrderObserver.php | 27 ++++++++++++++++++++++ Service/PlacedOrderHolder.php | 26 +++++++++++++++++++++ etc/events.xml | 4 ++++ 3 files changed, 57 insertions(+) create mode 100644 Model/Observer/HoldPlacedOrderObserver.php create mode 100644 Service/PlacedOrderHolder.php diff --git a/Model/Observer/HoldPlacedOrderObserver.php b/Model/Observer/HoldPlacedOrderObserver.php new file mode 100644 index 00000000..f5a1e4bd --- /dev/null +++ b/Model/Observer/HoldPlacedOrderObserver.php @@ -0,0 +1,27 @@ +getEvent()->getOrder(); + + $this->placedOrderHolder->hold($order); + } +} diff --git a/Service/PlacedOrderHolder.php b/Service/PlacedOrderHolder.php new file mode 100644 index 00000000..d55710af --- /dev/null +++ b/Service/PlacedOrderHolder.php @@ -0,0 +1,26 @@ +order) { + throw new \DomainException('Order is already held'); + } + + $this->order = $order; + } + + public function retrieve(): ?OrderInterface + { + return $this->order; + } +} diff --git a/etc/events.xml b/etc/events.xml index 808bddb8..3b0508d9 100644 --- a/etc/events.xml +++ b/etc/events.xml @@ -16,4 +16,8 @@ + + + From 24d546d1148b695676e3a5f8234f150bf21153d4 Mon Sep 17 00:00:00 2001 From: Kochnov Denis Date: Fri, 28 Feb 2025 13:29:20 +0300 Subject: [PATCH 2/3] Add OrderCancellation plugin to handle order payment cancellation logic for case when order was not saved yet --- Plugin/OrderCancellation.php | 127 +++++++++++++++++++++++++++++++++++ etc/di.xml | 4 ++ 2 files changed, 131 insertions(+) create mode 100644 Plugin/OrderCancellation.php diff --git a/Plugin/OrderCancellation.php b/Plugin/OrderCancellation.php new file mode 100644 index 00000000..10655c04 --- /dev/null +++ b/Plugin/OrderCancellation.php @@ -0,0 +1,127 @@ +quoteRepository->get((int) $cartId); + + $payment = $quote->getPayment(); + + // Abort if the payment method is not relevant. + if ($payment->getMethod() !== 'affirm_gateway') { + throw $e; + } + + $errorMessagePrefix = 'Unable to cancel payment: '; + + /** @var \Magento\Sales\Model\Order|null */ + $order = $this->placedOrderHolder->retrieve(); + + // Abort if the order object is not available + if (!$order) { + throw new RuntimeException( + $errorMessagePrefix . "Order data unavailable. Reserved order ID: {$quote->getReservedOrderId()}", + $e->getCode(), + $e + ); + } + + // Abort if the order object is not relevant for transaction. + if ($order->getIncrementId() !== $quote->getReservedOrderId()) { + throw new RuntimeException( + $errorMessagePrefix . "Available order data ({$order->getIncrementId()}, {$order->getId()}) doesn't match the quote value: {$quote->getReservedOrderId()}", + $e->getCode(), + $e + ); + } + + // Cancel the order in case when it was saved. + if ($order->getId()) { + $order->cancel(); + + $this->orderRepository->save($order); + + throw $e; + } + + /** @var \Magento\Sales\Model\Order\Payment|null */ + $orderPayment = $order->getPayment(); + + // Abort if the order lacks payment information. + if (!$orderPayment) { + throw $e; + } + + $methodInstance = $orderPayment->getMethodInstance(); + $methodInstance->setStore($order->getStoreId()); + + $errorMessage = ""; + + if (!$methodInstance->canRefund()) { + $errorMessage = "Transaction can not be refunded."; + } + + if (!$orderPayment->getCreatedTransaction()) { + $errorMessage = "Transaction information is missing."; + } + + if (!$orderPayment->getCreatedInvoice()) { + $errorMessage = "Invoice is missing."; + } + + if ($errorMessage) { + throw new RuntimeException( + $errorMessagePrefix . $errorMessage, + $e->getCode(), + $e + ); + } + + $creditmemo = $this->creditmemoFactory->createByOrder($order); + $creditmemo->setInvoice($orderPayment->getCreatedInvoice()); + + $orderPayment->setCreditmemo($creditmemo); + $orderPayment->setParentTransactionId($orderPayment->getCreatedTransaction()->getTxnId()); + + $methodInstance->refund($orderPayment, $orderPayment->getAmountPaid()); + + throw $e; + } + } +} diff --git a/etc/di.xml b/etc/di.xml index b0402027..ac5965bf 100644 --- a/etc/di.xml +++ b/etc/di.xml @@ -321,6 +321,10 @@ + + + + From 312ff9e2a43d70f25c8595263310dfb9eafe592a Mon Sep 17 00:00:00 2001 From: Andrey Gorbunov Date: Fri, 28 Feb 2025 22:34:40 +0100 Subject: [PATCH 3/3] OrderCancellation - fix PHPDoc --- Plugin/OrderCancellation.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Plugin/OrderCancellation.php b/Plugin/OrderCancellation.php index 10655c04..99d070aa 100644 --- a/Plugin/OrderCancellation.php +++ b/Plugin/OrderCancellation.php @@ -17,10 +17,10 @@ class OrderCancellation { /** - * @param Magento\Quote\Api\CartRepositoryInterface $quoteRepository - * @param Magento\Sales\Api\OrderRepositoryInterface $orderRepository - * @param PlacedOrderHolder $placedOrderHolder - * @param Magento\Sales\Model\Order\CreditmemoFactory $creditmemoFactory + * @param \Magento\Quote\Api\CartRepositoryInterface $quoteRepository + * @param \Magento\Sales\Api\OrderRepositoryInterface $orderRepository + * @param \Astound\Affirm\Service\PlacedOrderHolder\PlacedOrderHolder $placedOrderHolder + * @param \Magento\Sales\Model\Order\CreditmemoFactory $creditmemoFactory */ public function __construct( private CartRepositoryInterface $quoteRepository,