Commit 949aeea68d for woocommerce

commit 949aeea68d787268ae43b489edb7288e8871fcf4
Author: Michal Iwanow <4765119+mcliwanow@users.noreply.github.com>
Date:   Wed Feb 25 13:12:05 2026 +0100

    Short-circuit payment gateway availability checks when disabled (#63344)

    * Short-circuit payment gateway availability when disabled

    * Add @since 10.7.0 docs and harden COD disabled-gateway test cleanup

    - Move WC()->cart and order-pay query var mutations inside try/finally in COD unit test.
    - Add @since 10.7.0 annotations for is_available() behavior changes in abstract and COD gateways.


    ---------

    Co-authored-by: Herman <KokkieH@users.noreply.github.com>

diff --git a/plugins/woocommerce/includes/abstracts/abstract-wc-payment-gateway.php b/plugins/woocommerce/includes/abstracts/abstract-wc-payment-gateway.php
index 85d91f4d75..d1d23508ec 100644
--- a/plugins/woocommerce/includes/abstracts/abstract-wc-payment-gateway.php
+++ b/plugins/woocommerce/includes/abstracts/abstract-wc-payment-gateway.php
@@ -340,10 +340,15 @@ abstract class WC_Payment_Gateway extends WC_Settings_API {
 	/**
 	 * Check if the gateway is available for use.
 	 *
+	 * @since 10.7.0 Added early return when gateway is disabled.
 	 * @return bool
 	 */
 	public function is_available() {
-		$is_available = ( 'yes' === $this->enabled );
+		if ( 'yes' !== $this->enabled ) {
+			return false;
+		}
+
+		$is_available = true;

 		if ( WC()->cart && 0 < $this->get_order_total() && 0 < $this->max_amount && $this->max_amount < $this->get_order_total() ) {
 			$is_available = false;
diff --git a/plugins/woocommerce/includes/gateways/cod/class-wc-gateway-cod.php b/plugins/woocommerce/includes/gateways/cod/class-wc-gateway-cod.php
index b48c3721bb..7cf0c6fbbb 100644
--- a/plugins/woocommerce/includes/gateways/cod/class-wc-gateway-cod.php
+++ b/plugins/woocommerce/includes/gateways/cod/class-wc-gateway-cod.php
@@ -149,9 +149,14 @@ class WC_Gateway_COD extends WC_Payment_Gateway {
 	/**
 	 * Check If The Gateway Is Available For Use.
 	 *
+	 * @since 10.7.0 Added early return when gateway is disabled.
 	 * @return bool
 	 */
 	public function is_available() {
+		if ( 'yes' !== $this->enabled ) {
+			return false;
+		}
+
 		$is_virtual       = true;
 		$shipping_methods = array();

diff --git a/plugins/woocommerce/tests/legacy/unit-tests/gateways/gateways.php b/plugins/woocommerce/tests/legacy/unit-tests/gateways/gateways.php
index 065787e87a..ef1a545927 100644
--- a/plugins/woocommerce/tests/legacy/unit-tests/gateways/gateways.php
+++ b/plugins/woocommerce/tests/legacy/unit-tests/gateways/gateways.php
@@ -85,5 +85,42 @@ class WC_Tests_Gateways extends WC_Unit_Test_Case {

 		$this->assertEquals( $gateway->pay_button_id, $gateway->get_pay_button_id() );
 	}
-}

+	/**
+	 * Test WC_Payment_Gateway::is_available() returns early when gateway is disabled.
+	 */
+	public function test_is_available_does_not_calculate_order_total_when_disabled() {
+		$cart = WC()->cart;
+
+		WC()->cart = new stdClass();
+
+		$gateway = new class() extends WC_Mock_Payment_Gateway {
+			/**
+			 * Number of times get_order_total() is called.
+			 *
+			 * @var int
+			 */
+			public $get_order_total_call_count = 0;
+
+			/**
+			 * Get the order total and track how many times this method is called.
+			 *
+			 * @return float
+			 */
+			protected function get_order_total() {
+				++$this->get_order_total_call_count;
+				return 10.0;
+			}
+		};
+
+		$gateway->enabled    = 'no';
+		$gateway->max_amount = 100;
+
+		try {
+			$this->assertFalse( $gateway->is_available() );
+			$this->assertSame( 0, $gateway->get_order_total_call_count );
+		} finally {
+			WC()->cart = $cart;
+		}
+	}
+}
diff --git a/plugins/woocommerce/tests/legacy/unit-tests/payment-gateways/cod.php b/plugins/woocommerce/tests/legacy/unit-tests/payment-gateways/cod.php
index fdc256b740..4041ad03f1 100644
--- a/plugins/woocommerce/tests/legacy/unit-tests/payment-gateways/cod.php
+++ b/plugins/woocommerce/tests/legacy/unit-tests/payment-gateways/cod.php
@@ -84,4 +84,47 @@ class WC_Tests_Payment_Gateway_COD extends WC_Unit_Test_Case {
 		$this->assertArrayHasKey( 'enable_for_methods', $form_fields );
 		$this->assertNotEmpty( $form_fields['enable_for_methods']['options'] );
 	}
+
+	/**
+	 * Make sure is_available() returns early for disabled gateways.
+	 */
+	public function test_is_available_returns_early_when_disabled() {
+		$gateway          = new WC_Gateway_COD();
+		$gateway->enabled = 'no';
+
+		$cart                    = WC()->cart;
+		$has_order_pay_query_var = array_key_exists( 'order-pay', $GLOBALS['wp']->query_vars );
+		$order_pay_query_var     = $has_order_pay_query_var ? $GLOBALS['wp']->query_vars['order-pay'] : null;
+
+		try {
+			WC()->cart = new class() {
+				/**
+				 * Number of times needs_shipping() is called.
+				 *
+				 * @var int
+				 */
+				public $needs_shipping_call_count = 0;
+
+				/**
+				 * Track calls to needs_shipping().
+				 *
+				 * @return bool
+				 */
+				public function needs_shipping() {
+					++$this->needs_shipping_call_count;
+					return false;
+				}
+			};
+			unset( $GLOBALS['wp']->query_vars['order-pay'] );
+
+			$this->assertFalse( $gateway->is_available() );
+			$this->assertSame( 0, WC()->cart->needs_shipping_call_count );
+		} finally {
+			WC()->cart = $cart;
+
+			if ( $has_order_pay_query_var ) {
+				$GLOBALS['wp']->query_vars['order-pay'] = $order_pay_query_var;
+			}
+		}
+	}
 }