Extension: Payment Failed Email

  • Extension Name: ext.magento2.cleverzoeger.payment-failed-email (CleverZoeger_PaymentFailedEmail)

  • Magento Compatibility: 2.4.6, 2.4.7, 2.4.8, 2.4.9 (PHP 8.1, 8.2, 8.3, 8.4)

Magento core sends the "Payment Failed" email (checkout/payment_failed/*) unconditionally — unlike the New Order, Invoice, Shipment and Credit Memo emails, it has no Enabled toggle in Stores > Configuration > Sales > Checkout > Payment Failed Emails. This extension adds that missing toggle.

Features

  • Adds an Enabled field (default: Yes, matches previous behavior) to Stores > Configuration > Sales > Checkout > Payment Failed Emails.

  • When disabled, the Payment Failed email is not sent; all other Payment Failed Email settings (sender, receiver, template, copy to, copy method) are hidden via depends until re-enabled.

  • Store-view scoped, like the other sales email Enabled settings.

Background: why this extension exists

While reviewing var/log/exception.log on production, we found repeated, bursty main.CRITICAL entries (multiple hits within the same minute, recurring across separate days) for:

Magento\Framework\Exception\NoSuchEntityException: Keine Eintrag mit cartId = 0

Every one of these traces goes through the same call path:

Magento\Paypal\Controller\Transparent\Response->execute()
  -> Magento\Sales\Model\Service\PaymentFailuresService->handle()
    -> Magento\Quote\Model\QuoteRepository->get()   [throws NoSuchEntityException, uncaught]

Magento\Paypal\Controller\Transparent\Response (PayPal Payflow "Transparent Redirect") is a HttpPostActionInterface controller that explicitly disables CSRF validation (validateForCsrf() returns true unconditionally, createCsrfValidationException() returns null), so it accepts POST requests without a referrer/form-key check. On any LocalizedException while processing the posted payment response, it calls:

$this->paymentFailures->handle((int) $this->sessionTransparent->getQuoteId(), $parameters['error_msg']);

getQuoteId() comes from the transparent-redirect session, not from request input. When this controller is called directly/out of the normal checkout flow — as happened here, in bursts consistent with automated scanning rather than real checkout attempts — no transparent-redirect session exists yet, so the quote ID is 0. PaymentFailuresService::handle() then does $this→cartRepository→get(0), which throws NoSuchEntityException. Magento core does not catch this, so every such request produces an uncaught, logged CRITICAL exception. Across the observed period (2026-09-11 to 2026-09-15) this alone accounted for 189 log entries and contributed to exception.log growing to roughly 99.5 MB.

Because Magento core has no Enabled toggle for Payment Failed Emails, there was also no supported way to mute or rate-limit the resulting noise (log growth, and on any request that did resolve to a real, payment-less quote, an actual "Payment Failed" email) without patching core. This extension’s config toggle lets this be switched off per store view without a core patch or a code deployment.

Screenshots

Backend

Payment Failed Emails configuration with the Enabled field set to Yes
Figure 1. Stores > Configuration > Sales > Checkout > Payment Failed Emails with the added Enabled field
Payment Failed Emails configuration with the Enabled field set to No
Figure 2. Set to No the Payment Failed Email is suppressed and the remaining settings are hidden

Frontend

The extension has no storefront output of its own; what it switches on and off is the transactional Payment Failed email.

Payment Failed email
Figure 3. The Payment Failed email that is sent while the toggle is set to Yes

This documentation is brought to you by the clever+zöger GmbH. We are developing Magento projects since 2008 and since 2020 we are Adobe Solution partner. If you have any issues, don’t hesitate to contact us support@clever-zoeger.de.