Commit 0d6f16784d1 for woocommerce

commit 0d6f16784d14010b7b5d3d17ec3002d100ef0567
Author: Michal Iwanow <4765119+mcliwanow@users.noreply.github.com>
Date:   Mon Nov 2 08:58:15 2026 +0100

    Ignore doing_wp_cron when checking WooCommerce.com request signatures (#70401)

    * Ignore doing_wp_cron when checking WooCommerce.com request signatures

    With ALTERNATE_WP_CRON on, WordPress answers a GET request with a redirect
    to the same URL plus doing_wp_cron whenever cron is due. WooCommerce.com
    follows it, and the extra arg no longer matches the signed URL, so the
    request fails with request_verification_failed.

    Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

    * Cover pretty-permalink requests in the doing_wp_cron signature test

    Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

    ---------

    Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
    Co-authored-by: Herman <KokkieH@users.noreply.github.com>

diff --git a/plugins/woocommerce/changelog/wccom-3142-doing-wp-cron-signature b/plugins/woocommerce/changelog/wccom-3142-doing-wp-cron-signature
new file mode 100644
index 00000000000..a4340c34bea
--- /dev/null
+++ b/plugins/woocommerce/changelog/wccom-3142-doing-wp-cron-signature
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Fix WooCommerce.com requests such as connection status checks failing intermittently on stores that use ALTERNATE_WP_CRON.
diff --git a/plugins/woocommerce/includes/wccom-site/class-wc-wccom-site.php b/plugins/woocommerce/includes/wccom-site/class-wc-wccom-site.php
index 63b47249f88..076bf0604ae 100644
--- a/plugins/woocommerce/includes/wccom-site/class-wc-wccom-site.php
+++ b/plugins/woocommerce/includes/wccom-site/class-wc-wccom-site.php
@@ -229,7 +229,8 @@ class WC_WCCOM_Site {
 		// phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
 		$data = array(
 			'host'        => $_SERVER['HTTP_HOST'],
-			'request_uri' => urldecode( remove_query_arg( array( 'token', 'signature' ), $_SERVER['REQUEST_URI'] ) ),
+			// With ALTERNATE_WP_CRON, WordPress redirects a GET to itself plus doing_wp_cron when cron is due, and WooCommerce.com follows it.
+			'request_uri' => urldecode( remove_query_arg( array( 'token', 'signature', 'doing_wp_cron' ), $_SERVER['REQUEST_URI'] ) ),
 			'method'      => strtoupper( $_SERVER['REQUEST_METHOD'] ),
 		);
 		// phpcs:enable
diff --git a/plugins/woocommerce/tests/php/includes/wccom-site/class-wc-wccom-site-test.php b/plugins/woocommerce/tests/php/includes/wccom-site/class-wc-wccom-site-test.php
index a54a2f4b542..bff37c773cd 100644
--- a/plugins/woocommerce/tests/php/includes/wccom-site/class-wc-wccom-site-test.php
+++ b/plugins/woocommerce/tests/php/includes/wccom-site/class-wc-wccom-site-test.php
@@ -234,4 +234,27 @@ class WC_WCCOM_Site_Test extends WC_Unit_Test_Case {
 		$this->assertNull( self::with_rest_route_context( '/wccom-site/v3/status', fn() => WC_WCCOM_Site::authentication_fallback( null ) ) );
 		$this->assertSame( $current_user_id, get_current_user_id() );
 	}
+
+	/**
+	 * @testdox Should accept a signed request after the ALTERNATE_WP_CRON redirect adds doing_wp_cron, but no other change.
+	 *
+	 * @testWith ["/?rest_route=%2Fwccom-site%2Fv3%2Fstatus&doing_wp_cron=1761814536.1234567890123456789012", "/?rest_route=/wccom-site/v3/status", "/wccom-site/v3/status", true]
+	 *           ["/wp-json/wccom-site/v3/status?doing_wp_cron=1761814536.1234567890123456789012", "/wp-json/wccom-site/v3/status", "/wccom-site/v3/status", true]
+	 *           ["/?rest_route=/wccom-site/v3/ssr&doing_wp_cron=1761814536.1234567890123456789012", "/?rest_route=/wccom-site/v3/status", "/wccom-site/v3/ssr", false]
+	 *           ["/?rest_route=/wccom-site/v3/status&cron=1", "/?rest_route=/wccom-site/v3/status", "/wccom-site/v3/status", false]
+	 *
+	 * @param string $received_uri Request URI the site receives.
+	 * @param string $signed_uri   Request URI WooCommerce.com signed.
+	 * @param string $route        Route WordPress resolved.
+	 * @param bool   $expected     Whether the connected user is authenticated.
+	 */
+	public function test_authenticate_wccom_ignores_doing_wp_cron( string $received_uri, string $signed_uri, string $route, bool $expected ): void {
+		$user_id = self::factory()->user->create( array( 'role' => 'administrator' ) );
+		$this->simulate_request( $received_uri, true );
+		$this->sign_request_for_connected_site( $user_id, $signed_uri );
+
+		$user = self::with_rest_route_context( $route, fn() => WC_WCCOM_Site::authenticate_wccom( false ) );
+
+		$this->assertSame( $expected, $user instanceof WP_User && $user_id === $user->ID );
+	}
 }