diff --git a/controllers/front/fail.php b/controllers/front/fail.php index 6161fbba..ab47fa36 100644 --- a/controllers/front/fail.php +++ b/controllers/front/fail.php @@ -21,8 +21,10 @@ *@license SIX Payment Services */ +use Invertus\SaferPay\Config\SaferPayConfig; use Invertus\SaferPay\Controller\AbstractSaferPayController; use Invertus\SaferPay\Factory\OrderPresenterFactory; +use Invertus\SaferPay\Repository\SaferPayOrderRepository; use Invertus\SaferPay\Service\CartDuplicationService; use Invertus\SaferPay\Logger\LoggerInterface; @@ -84,6 +86,8 @@ public function initContent() $logger->debug(sprintf('%s - Controller called', self::FILE_NAME)); + $this->markOrderAsFailed($logger); + $this->warning[] = $this->module->l('We couldn\'t authorize your payment. Please try again.', self::FILE_NAME); $logger->debug(sprintf('%s - Controller action ended', self::FILE_NAME)); @@ -102,4 +106,79 @@ public function initContent() ) ); } + + /** + * When the "Order creation rule" is "Before authorization", the order row already exists by the time + * the customer lands here after a rejected/failed authorization. Transition it to the failed state and + * flag the Saferpay order as canceled so it does not stay stuck on "Awaiting Saferpay payment" forever + * (and so the awaiting-status poller stops spinning). Mirrors the failure handling in notify.php. + * + * @param LoggerInterface $logger + * + * @return void + */ + private function markOrderAsFailed($logger) + { + /** @var SaferPayOrderRepository $orderRepo */ + $orderRepo = $this->module->getService(SaferPayOrderRepository::class); + + $saferPayOrderId = (int) $orderRepo->getIdByCartId($this->id_cart); + + if (!$saferPayOrderId) { + // "After authorization" mode: no order was created for a failed payment, nothing to update. + return; + } + + $saferPayOrder = new SaferPayOrder($saferPayOrderId); + + if (!Validate::isLoadedObject($saferPayOrder)) { + return; + } + + // Payment already succeeded through another path (e.g. the notify webhook); never override it. + if ($saferPayOrder->authorized || $saferPayOrder->captured) { + return; + } + + $orderId = (int) Order::getIdByCartId($this->id_cart); + $failedStatus = (int) _SAFERPAY_PAYMENT_AUTHORIZATION_FAILED_; + + if ($orderId && $failedStatus) { + $order = new Order($orderId); + + if (Validate::isLoadedObject($order)) { + $currentState = (int) $order->current_state; + + $authorizedStatus = (int) Configuration::get(SaferPayConfig::SAFERPAY_PAYMENT_AUTHORIZED); + $capturedStatus = (int) Configuration::get(SaferPayConfig::SAFERPAY_PAYMENT_COMPLETED); + + // Do not override a success state, and avoid duplicate history entries if already failed. + if ($currentState !== $authorizedStatus + && $currentState !== $capturedStatus + && $currentState !== $failedStatus + ) { + $order->setCurrentState($failedStatus); + + $logger->debug(sprintf('%s - Order transitioned to authorization failed', self::FILE_NAME), [ + 'context' => [ + 'id_order' => $orderId, + 'id_cart' => $this->id_cart, + ], + ]); + } + } + } + + if (!$saferPayOrder->canceled) { + $saferPayOrder->authorized = false; + $saferPayOrder->pending = false; + $saferPayOrder->canceled = true; + + if ($orderId) { + $saferPayOrder->id_order = $orderId; + } + + $saferPayOrder->update(); + } + } }