diff --git a/Model/Observer/HoldPlacedOrderObserver.php b/Model/Observer/HoldPlacedOrderObserver.php
new file mode 100644
index 0000000..f5a1e4b
--- /dev/null
+++ b/Model/Observer/HoldPlacedOrderObserver.php
@@ -0,0 +1,27 @@
+getEvent()->getOrder();
+
+ $this->placedOrderHolder->hold($order);
+ }
+}
diff --git a/Plugin/OrderCancellation.php b/Plugin/OrderCancellation.php
new file mode 100644
index 0000000..99d070a
--- /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/Service/PlacedOrderHolder.php b/Service/PlacedOrderHolder.php
new file mode 100644
index 0000000..d55710a
--- /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/di.xml b/etc/di.xml
index b040202..ac5965b 100644
--- a/etc/di.xml
+++ b/etc/di.xml
@@ -321,6 +321,10 @@
+
+
+
+
diff --git a/etc/events.xml b/etc/events.xml
index 808bddb..3b0508d 100644
--- a/etc/events.xml
+++ b/etc/events.xml
@@ -16,4 +16,8 @@
+
+
+