Commit 28e21afa3c4 for woocommerce

commit 28e21afa3c4a4ba80699656bb17603e231105478
Author: Tom Cafferkey <tjcafferkey@gmail.com>
Date:   Mon Aug 24 13:23:23 2026 +0100

    Fix email verification prompt for guest checkout (#67960)

    * Fix email verification prompt for guest checkout

    * Simplify guest checkout prompt logic and fix filter type safety

    * Stop the guest checkout prompt filter from overriding verified or logged-out users

    ---------

    Co-authored-by: Jorge Torres <jorge.torres@automattic.com>

diff --git a/plugins/woocommerce/changelog/fix-email-verification-guest-checkout-prompt b/plugins/woocommerce/changelog/fix-email-verification-guest-checkout-prompt
new file mode 100644
index 00000000000..307e1b9ba17
--- /dev/null
+++ b/plugins/woocommerce/changelog/fix-email-verification-guest-checkout-prompt
@@ -0,0 +1,4 @@
+Significance: patch
+Type: fix
+
+Suppress the customer email verification prompt when guest checkout is disabled.
diff --git a/plugins/woocommerce/src/Internal/CustomerEmailVerification/VerificationController.php b/plugins/woocommerce/src/Internal/CustomerEmailVerification/VerificationController.php
index fae1a841c7d..1d2e4b97408 100644
--- a/plugins/woocommerce/src/Internal/CustomerEmailVerification/VerificationController.php
+++ b/plugins/woocommerce/src/Internal/CustomerEmailVerification/VerificationController.php
@@ -210,9 +210,7 @@ class VerificationController {
 	/**
 	 * Return whether the verification prompt should be shown for the current user.
 	 *
-	 * True for a logged-in, unverified customer, except one still using a temporary password (those
-	 * confirm via their set-password link, so the temporary-password notice already covers it). This
-	 * must not depend on whether matching guest orders exist, because that would disclose order
+	 * This cannot depend on whether matching guest orders exist, since that would disclose order
 	 * existence before the customer proves they control the email address.
 	 *
 	 * @since 11.0.0
@@ -222,21 +220,25 @@ class VerificationController {
 	public function should_show_prompt(): bool {
 		$user_id = get_current_user_id();

-		if ( ! $user_id ) {
+		if ( ! $user_id || $this->service->is_verified( $user_id ) ) {
 			return false;
 		}

-		if ( $this->service->is_verified( $user_id ) ) {
-			return false;
-		}
+		$should_show = wc_string_to_bool( get_option( 'woocommerce_enable_guest_checkout' ) );

 		// A temporary-password account already has a set-password link (which also verifies on use),
-		// surfaced by the temporary-password notice — don't show a second prompt alongside it.
-		if ( get_user_option( 'default_password_nag', $user_id ) ) {
-			return false;
-		}
+		// surfaced by the temporary-password notice, so skip a second prompt alongside it.
+		$should_show = $should_show && ! get_user_option( 'default_password_nag', $user_id );

-		return true;
+		/**
+		 * Filter whether to show the verification prompt for an unverified user.
+		 *
+		 * @since 11.1.0
+		 *
+		 * @param bool $should_show Whether to show the prompt, before this filter runs.
+		 * @param int  $user_id     The WordPress user ID of the customer.
+		 */
+		return (bool) apply_filters( 'woocommerce_customer_email_verification_should_show_prompt', $should_show, $user_id );
 	}

 	/**
diff --git a/plugins/woocommerce/tests/php/src/Internal/CustomerEmailVerification/MyAccountPromptTest.php b/plugins/woocommerce/tests/php/src/Internal/CustomerEmailVerification/MyAccountPromptTest.php
index 337bf3faae7..7838c0a440f 100644
--- a/plugins/woocommerce/tests/php/src/Internal/CustomerEmailVerification/MyAccountPromptTest.php
+++ b/plugins/woocommerce/tests/php/src/Internal/CustomerEmailVerification/MyAccountPromptTest.php
@@ -26,13 +26,22 @@ class MyAccountPromptTest extends WC_Unit_Test_Case {
 	 */
 	private $service;

+	/**
+	 * Previous guest checkout option value.
+	 *
+	 * @var mixed
+	 */
+	private $previous_guest_checkout_option;
+
 	/**
 	 * Set up test fixtures.
 	 */
 	public function setUp(): void {
 		parent::setUp();
-		$this->service = wc_get_container()->get( EmailVerificationService::class );
-		$this->sut     = wc_get_container()->get( VerificationController::class );
+		$this->service                        = wc_get_container()->get( EmailVerificationService::class );
+		$this->sut                            = wc_get_container()->get( VerificationController::class );
+		$this->previous_guest_checkout_option = get_option( 'woocommerce_enable_guest_checkout', null );
+		update_option( 'woocommerce_enable_guest_checkout', 'yes' );
 	}

 	/**
@@ -41,9 +50,25 @@ class MyAccountPromptTest extends WC_Unit_Test_Case {
 	public function tearDown(): void {
 		wp_set_current_user( 0 );
 		wc_clear_notices();
+		$this->restore_option( 'woocommerce_enable_guest_checkout', $this->previous_guest_checkout_option );
 		parent::tearDown();
 	}

+	/**
+	 * Restore an option to its previous value.
+	 *
+	 * @param string $option_name    Option name.
+	 * @param mixed  $previous_value Previous option value, or null if it did not exist.
+	 */
+	private function restore_option( string $option_name, $previous_value ): void {
+		if ( null === $previous_value ) {
+			delete_option( $option_name );
+			return;
+		}
+
+		update_option( $option_name, $previous_value );
+	}
+
 	/**
 	 * Render the My Account prompt and return its HTML.
 	 *
@@ -114,6 +139,29 @@ class MyAccountPromptTest extends WC_Unit_Test_Case {
 		$this->assertTrue( $this->sut->should_show_prompt(), 'Prompt visibility must not reveal whether matching guest orders exist' );
 	}

+	/**
+	 * @testdox should_show_prompt returns false when guest checkout is disabled.
+	 */
+	public function test_should_show_prompt_returns_false_when_guest_checkout_is_disabled(): void {
+		$user_id = wc_create_new_customer( 'prompt-guest-disabled@example.com', 'promptguestdisabled', 'pw' );
+		wp_set_current_user( $user_id );
+		update_option( 'woocommerce_enable_guest_checkout', 'no' );
+
+		$this->assertFalse( $this->sut->should_show_prompt(), 'The prompt should not show when guest checkout is disabled.' );
+	}
+
+	/**
+	 * @testdox should_show_prompt allows filters to override the guest checkout default.
+	 */
+	public function test_should_show_prompt_allows_filter_to_override_guest_checkout_default(): void {
+		$user_id = wc_create_new_customer( 'prompt-guest-disabled-filtered@example.com', 'promptguestdisabledfiltered', 'pw' );
+		wp_set_current_user( $user_id );
+		update_option( 'woocommerce_enable_guest_checkout', 'no' );
+
+		add_filter( 'woocommerce_customer_email_verification_should_show_prompt', '__return_true' );
+		$this->assertTrue( $this->sut->should_show_prompt(), 'The prompt default should be overrideable by filter.' );
+	}
+
 	/**
 	 * @testdox should_show_prompt returns false for an account using a temporary password.
 	 */
@@ -137,6 +185,18 @@ class MyAccountPromptTest extends WC_Unit_Test_Case {
 		$this->assertFalse( $this->sut->should_show_prompt(), 'Verified customers should not see the prompt' );
 	}

+	/**
+	 * @testdox should_show_prompt never applies the filter for a verified customer.
+	 */
+	public function test_should_show_prompt_ignores_filter_for_verified_customer(): void {
+		$user_id = wc_create_new_customer( 'prompt-verified-filtered@example.com', 'promptverifiedfiltered', 'pw' );
+		wp_set_current_user( $user_id );
+		$this->service->mark_verified( $user_id );
+
+		add_filter( 'woocommerce_customer_email_verification_should_show_prompt', '__return_true' );
+		$this->assertFalse( $this->sut->should_show_prompt(), 'A verified customer should never see the prompt, even if a filter tries to force it on.' );
+	}
+
 	// -------------------------------------------------------------------------
 	// render_prompt()
 	// -------------------------------------------------------------------------