Commit cb5f3fb0a8e for woocommerce

commit cb5f3fb0a8ef37d434451fc61b3f5e6015641bd8
Author: Alefe Souza <contact@alefesouza.com>
Date:   Tue Oct 6 15:05:10 2026 -0300

    Fix offline gateway shipping restrictions on the pay-for-order form with wc-ajax (#69312)

diff --git a/plugins/woocommerce/src/Gateways/ShippingMethodRestrictionsTrait.php b/plugins/woocommerce/src/Gateways/ShippingMethodRestrictionsTrait.php
index c7278faa448..4c26c50e1b5 100644
--- a/plugins/woocommerce/src/Gateways/ShippingMethodRestrictionsTrait.php
+++ b/plugins/woocommerce/src/Gateways/ShippingMethodRestrictionsTrait.php
@@ -103,9 +103,11 @@ trait ShippingMethodRestrictionsTrait {
 		$shipping_methods = array();

 		// Get shipping methods from the order being paid, or from the cart. The order-pay query var can be set by
-		// the client, and wc-ajax checkout requests always check out the cart, so ignore it on those requests.
-		$is_order_pay = is_wc_endpoint_url( 'order-pay' ) && ! Constants::is_true( 'WC_DOING_AJAX' );
-		$order        = $is_order_pay ? wc_get_order( absint( get_query_var( 'order-pay' ) ) ) : false;
+		// the client, and wc-ajax handlers always work on the cart, so ignore it while one is running. The pay form
+		// is processed on `wp`, before wc-ajax handlers run, so it keeps using the order even if wc-ajax is sent.
+		$wc_ajax_action = get_query_var( 'wc-ajax' );
+		$is_order_pay   = is_wc_endpoint_url( 'order-pay' ) && ! ( $wc_ajax_action && doing_action( 'wc_ajax_' . $wc_ajax_action ) );
+		$order          = $is_order_pay ? wc_get_order( absint( get_query_var( 'order-pay' ) ) ) : false;

 		if ( $order ) {
 			$shipping_methods = $order->get_shipping_methods();
diff --git a/plugins/woocommerce/tests/php/src/Gateways/ShippingMethodRestrictionsTraitTest.php b/plugins/woocommerce/tests/php/src/Gateways/ShippingMethodRestrictionsTraitTest.php
index 46cffe83382..3e086eb9c78 100644
--- a/plugins/woocommerce/tests/php/src/Gateways/ShippingMethodRestrictionsTraitTest.php
+++ b/plugins/woocommerce/tests/php/src/Gateways/ShippingMethodRestrictionsTraitTest.php
@@ -74,6 +74,8 @@ class ShippingMethodRestrictionsTraitTest extends WC_Unit_Test_Case {
 	public function tearDown(): void {
 		try {
 			$this->set_order_pay_query_var( null );
+			set_query_var( 'wc-ajax', '' );
+			remove_all_actions( 'wc_ajax_shipping_restrictions_test' );
 			Constants::clear_single_constant( 'WC_DOING_AJAX' );
 			WC()->session->set( 'chosen_shipping_methods', null );
 			WC_Cache_Helper::get_transient_version( 'shipping', true );
@@ -263,14 +265,14 @@ class ShippingMethodRestrictionsTraitTest extends WC_Unit_Test_Case {
 	}

 	/**
-	 * @testdox Should evaluate the cart's shipping method on wc-ajax requests, even when order-pay is an existing order.
+	 * @testdox Should evaluate the cart's shipping method inside a wc-ajax handler, even when order-pay is an existing order.
 	 * @testWith ["flat_rate_a", true]
 	 *           ["flat_rate_b", false]
 	 *
 	 * @param string $chosen_rate Symbolic name of the shipping rate selected in the cart.
 	 * @param bool   $expected    Expected availability.
 	 */
-	public function test_is_available_uses_the_cart_on_wc_ajax_requests_with_an_existing_order( string $chosen_rate, bool $expected ): void {
+	public function test_is_available_uses_the_cart_inside_wc_ajax_handlers_with_an_existing_order( string $chosen_rate, bool $expected ): void {
 		$gateway = $this->create_gateway(
 			WC_Gateway_COD::class,
 			array(
@@ -282,9 +284,40 @@ class ShippingMethodRestrictionsTraitTest extends WC_Unit_Test_Case {
 		$this->fill_cart( $chosen_rate );
 		// An order without shipping would count as virtual and skip the restriction if its context were used.
 		$this->set_order_pay_query_var( (string) $this->create_order_with_shipping( null )->get_id() );
+		set_query_var( 'wc-ajax', 'shipping_restrictions_test' );
+
+		$is_available = null;
+		add_action(
+			'wc_ajax_shipping_restrictions_test',
+			function () use ( $gateway, &$is_available ) {
+				$is_available = $gateway->is_available();
+			}
+		);
+		do_action( 'wc_ajax_shipping_restrictions_test' );
+
+		$this->assertSame( $expected, $is_available );
+	}
+
+	/**
+	 * @testdox Should evaluate the order's shipping methods when the pay form is submitted with a wc-ajax parameter.
+	 */
+	public function test_is_available_uses_the_order_when_the_pay_form_is_sent_with_wc_ajax(): void {
+		$gateway = $this->create_gateway(
+			WC_Gateway_COD::class,
+			array(
+				'enabled'            => 'yes',
+				'enable_for_methods' => array( $this->rate_ids['flat_rate_a'] ),
+				'enable_for_virtual' => 'yes',
+			)
+		);
+		// The cart would allow the gateway on its own, so a pass here proves the order context was used.
+		$this->fill_cart( 'flat_rate_a' );
+		$this->set_order_pay_query_var( (string) $this->create_order_with_shipping( 'flat_rate_b' )->get_id() );
+		// WC_AJAX::define_ajax() defines this on init for any wc-ajax value, before WC_Form_Handler::pay_action() runs on wp.
 		Constants::set_constant( 'WC_DOING_AJAX', true );
+		set_query_var( 'wc-ajax', 'checkout' );

-		$this->assertSame( $expected, $gateway->is_available() );
+		$this->assertFalse( $gateway->is_available() );
 	}

 	/**