Commit 75cc65a18c4 for woocommerce

commit 75cc65a18c48e2a583b4f29ce59c5715c87e6f36
Author: Chris Lilitsas <1105590+xristos3490@users.noreply.github.com>
Date:   Wed Sep 16 11:39:17 2026 +0300

    Remove account creation from Back in Stock guest sign-ups (#68587)

    * feat: remove account creation and unverified email linking from BIS sign-up

    * test: drop Back in Stock account-creation e2e coverage and stale PHPStan baseline entry

    * refactor: keep deprecated Back in Stock account-creation symbols for compatibility

diff --git a/plugins/woocommerce/changelog/wooplug-7718-bis-remove-account-creation b/plugins/woocommerce/changelog/wooplug-7718-bis-remove-account-creation
new file mode 100644
index 00000000000..e5ed35e6abb
--- /dev/null
+++ b/plugins/woocommerce/changelog/wooplug-7718-bis-remove-account-creation
@@ -0,0 +1,4 @@
+Significance: minor
+Type: update
+
+Remove account creation from Back in Stock guest sign-ups and stop linking guest sign-ups to existing accounts by email.
diff --git a/plugins/woocommerce/phpstan-baseline.neon b/plugins/woocommerce/phpstan-baseline.neon
index 6af33f889d8..a8882966d95 100644
--- a/plugins/woocommerce/phpstan-baseline.neon
+++ b/plugins/woocommerce/phpstan-baseline.neon
@@ -66493,12 +66493,6 @@ parameters:
 			count: 1
 			path: src/Internal/StockNotifications/Frontend/SignupService.php

-		-
-			message: '#^Parameter \#1 \$object_or_string of function is_a expects object, int\|WP_Error given\.$#'
-			identifier: argument.type
-			count: 1
-			path: src/Internal/StockNotifications/Frontend/SignupService.php
-
 		-
 			message: '#^Parameter \#1 \$string of function html_entity_decode expects string, array\|string given\.$#'
 			identifier: argument.type
diff --git a/plugins/woocommerce/src/Internal/StockNotifications/Admin/SettingsController.php b/plugins/woocommerce/src/Internal/StockNotifications/Admin/SettingsController.php
index d25f649086a..e9aaa7dc819 100644
--- a/plugins/woocommerce/src/Internal/StockNotifications/Admin/SettingsController.php
+++ b/plugins/woocommerce/src/Internal/StockNotifications/Admin/SettingsController.php
@@ -182,24 +182,12 @@ class SettingsController {
 				),

 				array(
-					'title'           => __( 'Guest sign-up', 'woocommerce' ),
-					'desc'            => __( 'Customers must be logged in to sign up for stock notifications.', 'woocommerce' ),
-					'id'              => 'woocommerce_customer_stock_notifications_require_account',
-					'default'         => 'no',
-					'type'            => 'checkbox',
-					'desc_tip'        => __( 'When enabled, guests will be redirected to a login page to complete the sign-up process.', 'woocommerce' ),
-					'checkboxgroup'   => 'start',
-					'hide_if_checked' => 'option',
-				),
-
-				array(
-					'desc'            => __( 'Create an account when guests sign up for stock notifications.', 'woocommerce' ),
-					'id'              => 'woocommerce_customer_stock_notifications_create_account_on_signup',
-					'default'         => 'no',
-					'type'            => 'checkbox',
-					'checkboxgroup'   => 'end',
-					'hide_if_checked' => 'yes',
-					'autoload'        => true,
+					'title'    => __( 'Guest sign-up', 'woocommerce' ),
+					'desc'     => __( 'Customers must be logged in to sign up for stock notifications.', 'woocommerce' ),
+					'id'       => 'woocommerce_customer_stock_notifications_require_account',
+					'default'  => 'no',
+					'type'     => 'checkbox',
+					'desc_tip' => __( 'When enabled, guests will be redirected to a login page to complete the sign-up process.', 'woocommerce' ),
 				),

 				array(
@@ -226,21 +214,6 @@ class SettingsController {
 			return;
 		}

-		if ( 'no' === get_option( 'woocommerce_registration_generate_password', 'no' ) && 'yes' === get_option( 'woocommerce_customer_stock_notifications_create_account_on_signup', 'no' ) ) {
-			wp_admin_notice(
-				sprintf(
-					/* translators: %s settings page link */
-					__( 'WooCommerce is currently <a href="%s">configured</a> to create new accounts without generating passwords automatically. Guests who sign up to receive stock notifications will need to reset their password before they can log into their new account.', 'woocommerce' ),
-					esc_url( admin_url( 'admin.php?page=wc-settings&tab=account' ) )
-				),
-				array(
-					'id'          => 'message',
-					'type'        => 'warning',
-					'dismissible' => false,
-				)
-			);
-		}
-
 		if ( 'yes' === get_option( 'woocommerce_hide_out_of_stock_items' ) && Config::allows_signups() ) {
 			wp_admin_notice(
 				sprintf(
diff --git a/plugins/woocommerce/src/Internal/StockNotifications/Config.php b/plugins/woocommerce/src/Internal/StockNotifications/Config.php
index e56a955af75..978c9f81d44 100644
--- a/plugins/woocommerce/src/Internal/StockNotifications/Config.php
+++ b/plugins/woocommerce/src/Internal/StockNotifications/Config.php
@@ -162,12 +162,17 @@ class Config {
 	}

 	/**
-	 * Check if an account is created on signup.
+	 * Whether an account is created on signup.
+	 *
+	 * Guest sign-ups no longer create accounts, so this always returns false.
+	 *
+	 * @deprecated 11.2.0 Account creation on sign-up was removed.
 	 *
 	 * @return bool
 	 */
 	public static function creates_account_on_signup(): bool {
-		return 'yes' === get_option( 'woocommerce_customer_stock_notifications_create_account_on_signup', 'no' );
+		wc_deprecated_function( __METHOD__, '11.2.0' );
+		return false;
 	}

 	/**
diff --git a/plugins/woocommerce/src/Internal/StockNotifications/Frontend/ProductPageIntegration.php b/plugins/woocommerce/src/Internal/StockNotifications/Frontend/ProductPageIntegration.php
index 3ee1a9e26cd..b769b61da48 100644
--- a/plugins/woocommerce/src/Internal/StockNotifications/Frontend/ProductPageIntegration.php
+++ b/plugins/woocommerce/src/Internal/StockNotifications/Frontend/ProductPageIntegration.php
@@ -244,7 +244,8 @@ class ProductPageIntegration {
 			'single-product/back-in-stock-form.php',
 			array(
 				'product_id'       => $product->get_parent_id() ? $product->get_parent_id() : $product->get_id(),
-				'show_checkbox'    => ! is_user_logged_in() && Config::creates_account_on_signup() && ! Config::requires_account(),
+				// Kept so template overrides that still read it don't raise an undefined-variable warning.
+				'show_checkbox'    => false,
 				'show_email_field' => ! is_user_logged_in() && ! Config::requires_account(),
 				'button_class'     => $button_class,
 				'is_visible'       => $is_visible,
diff --git a/plugins/woocommerce/src/Internal/StockNotifications/Frontend/SignupService.php b/plugins/woocommerce/src/Internal/StockNotifications/Frontend/SignupService.php
index 0c93f797f1b..2e04ce0897b 100644
--- a/plugins/woocommerce/src/Internal/StockNotifications/Frontend/SignupService.php
+++ b/plugins/woocommerce/src/Internal/StockNotifications/Frontend/SignupService.php
@@ -21,12 +21,10 @@ use Automattic\WooCommerce\Internal\StockNotifications\Utilities\EmailNormalizer
 class SignupService {

 	// phpcs:disable
-	public const SIGNUP_ALREADY_JOINED                        = 'already_joined';
-	public const SIGNUP_ALREADY_JOINED_DOUBLE_OPT_IN          = 'already_joined_double_opt_in';
-	public const SIGNUP_SUCCESS                               = 'success';
-	public const SIGNUP_SUCCESS_ACCOUNT_CREATED               = 'success_account_created';
-	public const SIGNUP_SUCCESS_ACCOUNT_CREATED_DOUBLE_OPT_IN = 'success_account_created_double_opt_in';
-	public const SIGNUP_SUCCESS_DOUBLE_OPT_IN                 = 'success_double_opt_in';
+	public const SIGNUP_ALREADY_JOINED               = 'already_joined';
+	public const SIGNUP_ALREADY_JOINED_DOUBLE_OPT_IN = 'already_joined_double_opt_in';
+	public const SIGNUP_SUCCESS                      = 'success';
+	public const SIGNUP_SUCCESS_DOUBLE_OPT_IN        = 'success_double_opt_in';

 	public const ERROR_FAILED           = 'failed_to_signup';
 	public const ERROR_INVALID_REQUEST  = 'invalid_request';
@@ -35,7 +33,21 @@ class SignupService {
 	public const ERROR_RATE_LIMITED     = 'rate_limited';
 	public const ERROR_INVALID_USER     = 'invalid_user';
 	public const ERROR_INVALID_EMAIL    = 'invalid_email';
-	public const ERROR_INVALID_OPT_IN   = 'invalid_opt_in';
+
+	/**
+	 * @deprecated 11.2.0 Guest sign-ups no longer create accounts. Never emitted.
+	 */
+	public const SIGNUP_SUCCESS_ACCOUNT_CREATED = 'success_account_created';
+
+	/**
+	 * @deprecated 11.2.0 Guest sign-ups no longer create accounts. Never emitted.
+	 */
+	public const SIGNUP_SUCCESS_ACCOUNT_CREATED_DOUBLE_OPT_IN = 'success_account_created_double_opt_in';
+
+	/**
+	 * @deprecated 11.2.0 The account-creation consent checkbox was removed. Never emitted.
+	 */
+	public const ERROR_INVALID_OPT_IN = 'invalid_opt_in';
 	// phpcs:enable

 	/**
@@ -176,9 +188,9 @@ class SignupService {
 			return new \WP_Error( self::ERROR_RATE_LIMITED );
 		}

-		// Claim the rate limit window before creating an account, storing a notification or
-		// sending mail. This narrows the window in which two near-simultaneous requests both
-		// get through; it does not close it.
+		// Claim the rate limit window before storing a notification or sending mail. This
+		// narrows the window in which two near-simultaneous requests both get through; it
+		// does not close it.
 		//
 		// A claim only fails when the rate limit table cannot be written to, which a shopper
 		// can neither cause nor resolve. Let the sign-up through rather than turn a broken
@@ -190,12 +202,6 @@ class SignupService {
 			);
 		}

-		$account_created = null;
-		if ( empty( $user_id ) && Config::creates_account_on_signup() ) {
-			$account_created = $this->create_customer( $user_email );
-			$user_id         = $account_created ? $account_created : $user_id;
-		}
-
 		$notification = new Notification();
 		$notification->set_status( NotificationStatus::ACTIVE );
 		$notification->set_product_id( $product_id );
@@ -228,14 +234,7 @@ class SignupService {
 			$this->email_manager->send_verify_email( $notification );
 		}

-		$signup_code = self::SIGNUP_SUCCESS;
-		if ( Config::requires_double_opt_in() ) {
-			$signup_code = $account_created
-				? self::SIGNUP_SUCCESS_ACCOUNT_CREATED_DOUBLE_OPT_IN
-				: self::SIGNUP_SUCCESS_DOUBLE_OPT_IN;
-		} elseif ( $account_created ) {
-			$signup_code = self::SIGNUP_SUCCESS_ACCOUNT_CREATED;
-		}
+		$signup_code = Config::requires_double_opt_in() ? self::SIGNUP_SUCCESS_DOUBLE_OPT_IN : self::SIGNUP_SUCCESS;
 		return new SignupResult( $signup_code, $notification );
 	}

@@ -324,37 +323,6 @@ class SignupService {
 		return true;
 	}

-	/**
-	 * Create a new customer.
-	 *
-	 * @param string $user_email The user email.
-	 * @return int|null The user ID if the customer was created, null otherwise.
-	 */
-	private function create_customer( string $user_email ) {
-
-		if ( empty( $user_email ) || ! is_email( $user_email ) ) {
-			return null;
-		}
-
-		try {
-			$username = wc_create_new_customer_username( $user_email );
-			$username = sanitize_user( $username );
-			if ( empty( $username ) || ! validate_username( $username ) ) {
-				return null;
-			}
-
-			$password = 'yes' === get_option( 'woocommerce_registration_generate_password' ) ? '' : wp_generate_password();
-			$user_id  = wc_create_new_customer( $user_email, $username, $password );
-			if ( is_a( $user_id, 'WP_Error' ) ) {
-				return null;
-			}
-		} catch ( \Throwable $e ) {
-			return null;
-		}
-
-		return $user_id;
-	}
-
 	/**
 	 * Parse the request data from a given source.
 	 *
@@ -406,14 +374,6 @@ class SignupService {
 			return new \WP_Error( self::ERROR_REQUIRES_ACCOUNT );
 		}

-		// Check for valid privacy terms.
-		if ( ! $is_logged_in && Config::creates_account_on_signup() && ! Config::requires_account() ) {
-			$opt_in = isset( $source['wc_bis_opt_in'] ) ? wc_clean( wp_unslash( $source['wc_bis_opt_in'] ) ) : false;
-			if ( 'on' !== $opt_in ) {
-				return new \WP_Error( self::ERROR_INVALID_OPT_IN );
-			}
-		}
-
 		if ( ! $is_logged_in ) {
 			$posted_email = isset( $source['wc_bis_email'] ) && is_string( $source['wc_bis_email'] ) ? sanitize_email( wp_unslash( $source['wc_bis_email'] ) ) : '';
 			$email        = is_email( $posted_email ) ? EmailNormalizer::normalize( $posted_email ) : '';
@@ -421,15 +381,10 @@ class SignupService {
 				return new \WP_Error( self::ERROR_INVALID_EMAIL );
 			}

+			// A guest sign-up stays unlinked until the customer verifies the email: an address typed
+			// into a form proves nothing about who owns the matching account.
 			$data['user_id']    = 0;
 			$data['user_email'] = $email;
-
-			// Look up the account with the letter case as entered: `wp_users.user_email` is never
-			// normalized, so on a case-sensitive collation the lowercased form would miss it.
-			$user = get_user_by( 'email', $posted_email );
-			if ( $user ) {
-				$data['user_id'] = $user->ID;
-			}
 		} else {
 			$user = wp_get_current_user();
 			if ( ! $user ) {
@@ -542,10 +497,10 @@ class SignupService {
 				return wp_kses_post( __( 'Invalid user.', 'woocommerce' ) );
 			case self::ERROR_INVALID_EMAIL:
 				return wp_kses_post( __( 'Invalid email address.', 'woocommerce' ) );
-			case self::ERROR_INVALID_OPT_IN:
-				return wp_kses_post( __( 'To proceed, please consent to the creation of a new account with your e-mail.', 'woocommerce' ) );
 			case self::ERROR_RATE_LIMITED:
 				return wp_kses_post( __( 'Please wait a moment before signing up again.', 'woocommerce' ) );
+			case self::ERROR_INVALID_OPT_IN: // Deprecated code kept for callers passing the old code.
+				return wp_kses_post( __( 'To proceed, please consent to the creation of a new account with your e-mail.', 'woocommerce' ) );
 			default:
 				return wp_kses_post( __( 'Failed to sign up. Please try again.', 'woocommerce' ) );
 		}
@@ -572,12 +527,12 @@ class SignupService {
 				$message = esc_html__( 'Thanks for signing up! Please complete the sign-up process by following the verification link sent to your e-mail.', 'woocommerce' );
 				break;

-			case self::SIGNUP_SUCCESS_ACCOUNT_CREATED:
+			case self::SIGNUP_SUCCESS_ACCOUNT_CREATED: // Deprecated code kept for callers passing the old code.
 				/* translators: Product name */
 				$message = sprintf( esc_html__( 'You have successfully signed up and will be notified when "%s" is back in stock! Note that a new account has been created for you; please check your e-mail for details.', 'woocommerce' ), $notification->get_product_name() );
 				break;

-			case self::SIGNUP_SUCCESS_ACCOUNT_CREATED_DOUBLE_OPT_IN:
+			case self::SIGNUP_SUCCESS_ACCOUNT_CREATED_DOUBLE_OPT_IN: // Deprecated code kept for callers passing the old code.
 				$message = esc_html__( 'Thanks for signing up! An account has been created for you. Please complete the sign-up process by following the verification link sent to your e-mail.', 'woocommerce' );
 				break;

diff --git a/plugins/woocommerce/templates/single-product/back-in-stock-form.php b/plugins/woocommerce/templates/single-product/back-in-stock-form.php
index 22b3af4f637..c53f934ce9e 100644
--- a/plugins/woocommerce/templates/single-product/back-in-stock-form.php
+++ b/plugins/woocommerce/templates/single-product/back-in-stock-form.php
@@ -14,7 +14,7 @@
  *
  * @see https://woocommerce.com/document/template-structure/
  * @package WooCommerce\Templates
- * @version 10.2.0
+ * @version 11.2.0
  */

 // Exit if accessed directly.
@@ -55,19 +55,6 @@ if ( ! defined( 'ABSPATH' ) ) {
 			</button>
 		</div>

-		<?php if ( $show_checkbox ) : ?>
-
-			<label for="wc_bis_opt_in_<?php echo absint( $product_id ); ?>" class="wc_bis_form__checkbox">
-				<input
-					type="checkbox"
-					name="wc_bis_opt_in"
-					id="wc_bis_opt_in_<?php echo absint( $product_id ); ?>"
-				/>
-				<?php echo wp_kses_post( wc_replace_policy_page_link_placeholders( wc_get_privacy_policy_text( 'registration' ) ) ); ?>
-			</label>
-
-		<?php endif; ?>
-
 		<?php wp_nonce_field( 'wc_bis_signup', 'wc_bis_nonce' ); ?>

 		<input type="hidden" name="wc_bis_product_id" value="<?php echo absint( $product_id ); ?>" />
diff --git a/plugins/woocommerce/tests/e2e/tests/back-in-stock-notifications/README.md b/plugins/woocommerce/tests/e2e/tests/back-in-stock-notifications/README.md
index 89fa56f9117..c7ccdfd34a6 100644
--- a/plugins/woocommerce/tests/e2e/tests/back-in-stock-notifications/README.md
+++ b/plugins/woocommerce/tests/e2e/tests/back-in-stock-notifications/README.md
@@ -5,8 +5,7 @@ Covers the scenarios from the original plugin test plan that have a target in co
 - `signing-up.spec.ts` — PDP form rendering + signup flow across the settings
   matrix: signups disabled, logged-in single opt-in (with the "Manage
   notifications" CTA and the nonce rejection), guest single and double opt-in,
-  account creation on signup (consent checkbox, welcome email), and the
-  requires-account prompt through to a logged-in signup. Also the server-side
+  and the requires-account prompt through to a logged-in signup. Also the server-side
   rejections for an invalid email and a tampered product id.
 - `receiving-confirmations.spec.ts` — verify email + verified email + unsubscribe
   flow (double opt-in), the frontend verify/unsubscribe notices, the logged-in
@@ -61,14 +60,9 @@ Covers the scenarios from the original plugin test plan that have a target in co
   customer and one IP. `signUpOnProductPage()` disables the limiter through the
   same cookie before every submit, so no spec has to opt in.
 - The email templates fork on `$is_guest`, which is "the signup has no
-  `WP_User`", not "the shopper was logged out": a guest signup with an email
-  that already belongs to an account, or one that created an account on signup,
-  also takes the logged-in branch. The specs cover it through the shared
-  `customer` account (`signUpAsCustomer()`).
-- Account-creation tests register a real customer for the guest's address. The
-  address comes from the `accountEmail` fixture, whose teardown looks it up and
-  deletes any account it finds, so a failed assertion (or a retry with a fresh
-  address) doesn't leave the account behind.
+  `WP_User`". A guest signup is never linked to an account by its email
+  address, so only a logged-in signup takes the other branch. The specs cover
+  it through the shared `customer` account (`signUpAsCustomer()`).

 ## Skipped scenarios

diff --git a/plugins/woocommerce/tests/e2e/tests/back-in-stock-notifications/receiving-confirmations.spec.ts b/plugins/woocommerce/tests/e2e/tests/back-in-stock-notifications/receiving-confirmations.spec.ts
index b71dd43740a..14aedcdec4c 100644
--- a/plugins/woocommerce/tests/e2e/tests/back-in-stock-notifications/receiving-confirmations.spec.ts
+++ b/plugins/woocommerce/tests/e2e/tests/back-in-stock-notifications/receiving-confirmations.spec.ts
@@ -39,7 +39,6 @@ test.describe(
 				allowSignups: true,
 				doubleOptIn: true,
 				requireAccount: false,
-				createAccountOnSignup: false,
 			} );
 		} );

diff --git a/plugins/woocommerce/tests/e2e/tests/back-in-stock-notifications/receiving-notifications.spec.ts b/plugins/woocommerce/tests/e2e/tests/back-in-stock-notifications/receiving-notifications.spec.ts
index 93a150238e9..54ee41c009d 100644
--- a/plugins/woocommerce/tests/e2e/tests/back-in-stock-notifications/receiving-notifications.spec.ts
+++ b/plugins/woocommerce/tests/e2e/tests/back-in-stock-notifications/receiving-notifications.spec.ts
@@ -41,7 +41,6 @@ test.describe(
 				allowSignups: true,
 				doubleOptIn: false,
 				requireAccount: false,
-				createAccountOnSignup: false,
 			} );
 		} );

diff --git a/plugins/woocommerce/tests/e2e/tests/back-in-stock-notifications/signing-up.spec.ts b/plugins/woocommerce/tests/e2e/tests/back-in-stock-notifications/signing-up.spec.ts
index 2b999f02add..06b8ca5737d 100644
--- a/plugins/woocommerce/tests/e2e/tests/back-in-stock-notifications/signing-up.spec.ts
+++ b/plugins/woocommerce/tests/e2e/tests/back-in-stock-notifications/signing-up.spec.ts
@@ -6,14 +6,11 @@ import { CUSTOMER_STATE_PATH } from '../../playwright.config';
 import { customer } from '../../test-data/data';
 import {
 	BIS_FEATURE_OPTION,
-	bisConsentCheckbox,
 	bisEmailSubject,
 	bisFormLocator,
 	bisNotice,
 	bisTargetProductInput,
 	expectEmailAsAdmin,
-	expectNoSignupAsAdmin,
-	findCustomerByEmail,
 	resetBISOptions,
 	setBISOptions,
 	signUpOnProductPage,
@@ -45,7 +42,6 @@ test.describe(
 					allowSignups: true,
 					doubleOptIn: false,
 					requireAccount: false,
-					createAccountOnSignup: false,
 				} );
 			} );

@@ -85,7 +81,6 @@ test.describe(
 					allowSignups: true,
 					doubleOptIn: false,
 					requireAccount: false,
-					createAccountOnSignup: false,
 				} );
 			} );

@@ -203,7 +198,6 @@ test.describe(
 					allowSignups: true,
 					doubleOptIn: false,
 					requireAccount: false,
-					createAccountOnSignup: false,
 				} );
 			} );

@@ -222,9 +216,11 @@ test.describe(
 					page.getByRole( 'button', { name: /Notify me/i } )
 				).toBeVisible();

-				// The consent checkbox only belongs to the account-creation
-				// setup, which is off here.
-				await expect( bisConsentCheckbox( page ) ).toHaveCount( 0 );
+				// Guest signups never register an account, so no consent
+				// checkbox is rendered.
+				await expect(
+					page.locator( 'input[name="wc_bis_opt_in"]' )
+				).toHaveCount( 0 );
 			} );

 			test( 'submitting the form surfaces a success notice', async ( {
@@ -284,7 +280,6 @@ test.describe(
 					allowSignups: true,
 					doubleOptIn: true,
 					requireAccount: false,
-					createAccountOnSignup: false,
 				} );
 			} );

@@ -312,119 +307,12 @@ test.describe(
 			} );
 		} );

-		test.describe( 'Guest — create account on signup', () => {
-			test.describe( 'Single opt-in', () => {
-				test.beforeAll( async ( { baseURL } ) => {
-					await setBISOptions( request, baseURL!, {
-						allowSignups: true,
-						doubleOptIn: false,
-						requireAccount: false,
-						createAccountOnSignup: true,
-					} );
-				} );
-
-				test( 'the consent checkbox renders and the signup is refused until it is ticked', async ( {
-					page,
-					product,
-					restApi,
-					browser,
-					accountEmail: email,
-				} ) => {
-					await page.goto( product.permalink );
-
-					const consent = bisConsentCheckbox( page );
-					await expect( consent ).toBeVisible();
-					await expect( consent ).not.toBeChecked();
-
-					await signUpOnProductPage( page, { email } );
-
-					await expect(
-						page.getByText( bisNotice.errors.missingConsent )
-					).toBeVisible();
-
-					// The refusal has to happen before anything is written:
-					// no account for the address, and no signup either.
-					expect(
-						await findCustomerByEmail( restApi, email )
-					).toBeUndefined();
-					await expectNoSignupAsAdmin( browser, product.id, email );
-				} );
-
-				test( 'ticking consent signs up, registers an account and sends the welcome email', async ( {
-					page,
-					product,
-					restApi,
-					browser,
-					accountEmail: email,
-				} ) => {
-					await page.goto( product.permalink );
-					await signUpOnProductPage( page, { email, consent: true } );
-
-					await expect(
-						page.getByText(
-							bisNotice.accountCreated( product.name )
-						)
-					).toBeVisible();
-
-					expect(
-						await findCustomerByEmail( restApi, email )
-					).toBeDefined();
-
-					// The "check your e-mail for details" in the notice is
-					// WooCommerce's own new-account email, sent with the
-					// generated password.
-					await expectEmailAsAdmin(
-						browser,
-						email,
-						/account has been created!/
-					);
-				} );
-			} );
-
-			test.describe( 'Double opt-in', () => {
-				test.beforeAll( async ( { baseURL } ) => {
-					await setBISOptions( request, baseURL!, {
-						allowSignups: true,
-						doubleOptIn: true,
-						requireAccount: false,
-						createAccountOnSignup: true,
-					} );
-				} );
-
-				test( 'ticking consent registers an account and still asks for email verification', async ( {
-					page,
-					product,
-					restApi,
-					browser,
-					accountEmail: email,
-				} ) => {
-					await page.goto( product.permalink );
-					await signUpOnProductPage( page, { email, consent: true } );
-
-					await expect(
-						page.getByText( bisNotice.accountCreatedDoubleOptIn )
-					).toBeVisible();
-
-					expect(
-						await findCustomerByEmail( restApi, email )
-					).toBeDefined();
-
-					await expectEmailAsAdmin(
-						browser,
-						email,
-						bisEmailSubject.verify( product.name )
-					);
-				} );
-			} );
-		} );
-
 		test.describe( 'Guest — requires account', () => {
 			test.beforeAll( async ( { baseURL } ) => {
 				await setBISOptions( request, baseURL!, {
 					allowSignups: true,
 					doubleOptIn: false,
 					requireAccount: true,
-					createAccountOnSignup: false,
 				} );
 			} );

diff --git a/plugins/woocommerce/tests/e2e/utils/back-in-stock-notifications.ts b/plugins/woocommerce/tests/e2e/utils/back-in-stock-notifications.ts
index 5bfffea5592..ed51b89c658 100644
--- a/plugins/woocommerce/tests/e2e/utils/back-in-stock-notifications.ts
+++ b/plugins/woocommerce/tests/e2e/utils/back-in-stock-notifications.ts
@@ -29,8 +29,6 @@ export const BIS_OPTIONS = {
 	doubleOptIn:
 		'woocommerce_customer_stock_notifications_require_double_opt_in',
 	requireAccount: 'woocommerce_customer_stock_notifications_require_account',
-	createAccountOnSignup:
-		'woocommerce_customer_stock_notifications_create_account_on_signup',
 } as const;

 /**
@@ -81,13 +79,12 @@ export async function assertBISEnvReady(): Promise< void > {
 /**
  * Configure the BIS feature options for a test. Omitted keys are left untouched.
  *
- * @param {APIRequest} request                         Playwright request fixture.
- * @param {string}     baseURL                         Test site base URL.
- * @param {Object}     options                         BIS option toggles.
- * @param {boolean}    [options.allowSignups]          Whether the signup form is rendered on product pages.
- * @param {boolean}    [options.doubleOptIn]           Whether signups require email verification before activating.
- * @param {boolean}    [options.requireAccount]        Whether signups are limited to logged-in users.
- * @param {boolean}    [options.createAccountOnSignup] Whether a guest signup also registers a customer account.
+ * @param {APIRequest} request                  Playwright request fixture.
+ * @param {string}     baseURL                  Test site base URL.
+ * @param {Object}     options                  BIS option toggles.
+ * @param {boolean}    [options.allowSignups]   Whether the signup form is rendered on product pages.
+ * @param {boolean}    [options.doubleOptIn]    Whether signups require email verification before activating.
+ * @param {boolean}    [options.requireAccount] Whether signups are limited to logged-in users.
  */
 export async function setBISOptions(
 	request: APIRequest,
@@ -96,7 +93,6 @@ export async function setBISOptions(
 		allowSignups?: boolean;
 		doubleOptIn?: boolean;
 		requireAccount?: boolean;
-		createAccountOnSignup?: boolean;
 	}
 ): Promise< void > {
 	const toYesNo = ( v: boolean | undefined ): string | undefined => {
@@ -110,10 +106,6 @@ export async function setBISOptions(
 		[ BIS_OPTIONS.allowSignups, toYesNo( options.allowSignups ) ],
 		[ BIS_OPTIONS.doubleOptIn, toYesNo( options.doubleOptIn ) ],
 		[ BIS_OPTIONS.requireAccount, toYesNo( options.requireAccount ) ],
-		[
-			BIS_OPTIONS.createAccountOnSignup,
-			toYesNo( options.createAccountOnSignup ),
-		],
 	];

 	for ( const [ name, value ] of entries ) {
@@ -482,18 +474,6 @@ export async function selectVariation(
 	);
 }

-/**
- * Locator for the account-creation consent checkbox on the PDP sign-up form.
- *
- * Its label is the store's registration privacy text, which the merchant can
- * edit, so it is located by name rather than by that label.
- *
- * @param {Page} page Playwright page on the product detail.
- */
-export function bisConsentCheckbox( page: Page ) {
-	return page.locator( 'input[name="wc_bis_opt_in"]' );
-}
-
 /**
  * Switch the sign-up rate limiter off for this page's context.
  *
@@ -518,16 +498,14 @@ export async function disableSignupRateLimit( page: Page ): Promise< void > {
  * The rate limiter is switched off for the submitting context first, so
  * back-to-back sign-ups within a spec are not refused.
  *
- * @param {Page}    page           Playwright page on the product detail.
- * @param {Object}  [opts]         Fill options.
- * @param {string}  [opts.email]   Email address to enter (guest flow only; logged-in PDP hides the field).
- * @param {boolean} [opts.consent] Tick the account-creation consent checkbox (only rendered with `createAccountOnSignup`).
+ * @param {Page}   page         Playwright page on the product detail.
+ * @param {Object} [opts]       Fill options.
+ * @param {string} [opts.email] Email address to enter (guest flow only; logged-in PDP hides the field).
  */
 export async function signUpOnProductPage(
 	page: Page,
 	opts: {
 		email?: string;
-		consent?: boolean;
 	} = {}
 ): Promise< void > {
 	await disableSignupRateLimit( page );
@@ -540,10 +518,6 @@ export async function signUpOnProductPage(
 			.fill( opts.email );
 	}

-	if ( opts.consent ) {
-		await bisConsentCheckbox( page ).check();
-	}
-
 	await page.getByRole( 'button', { name: /Notify me/i } ).click();
 }

@@ -558,7 +532,6 @@ export async function signUpOnProductPage(
  * @param {Object}  opts                             Signup options.
  * @param {Object}  [opts.storageState]              Storage state for the signup context; a logged-out guest by default.
  * @param {string}  [opts.email]                     Email to enter; omit for a logged-in signup, where the field isn't rendered.
- * @param {boolean} [opts.consent]                   Tick the account-creation consent checkbox before submitting.
  * @param {RegExp}  [opts.expectedNotice]            Notice to wait for after the post; a generic success match by default.
  * @param {Object}  [opts.selectVariation]           Variation to pick before submitting, for variable products.
  * @param {Object}  [opts.selectVariation.product]   The variable product handle.
@@ -570,7 +543,6 @@ export async function signUpInNewContext(
 	opts: {
 		storageState?: string | { cookies: []; origins: [] };
 		email?: string;
-		consent?: boolean;
 		expectedNotice?: RegExp;
 		selectVariation?: {
 			product: BISVariableProduct;
@@ -596,10 +568,7 @@ export async function signUpInNewContext(
 			);
 		}

-		await signUpOnProductPage( page, {
-			email: opts.email,
-			consent: opts.consent,
-		} );
+		await signUpOnProductPage( page, { email: opts.email } );

 		// The form posts and reloads the PDP with a notice. Wait for that notice
 		// before closing the context, or the submission can be aborted mid-flight
@@ -658,49 +627,6 @@ export async function signUpAsCustomer(
 	} );
 }

-/**
- * Find the customer account registered for an email address, if any.
- *
- * @param {ApiClient} restApi WP REST client.
- * @param {string}    email   The email address.
- */
-export async function findCustomerByEmail(
-	restApi: ApiClient,
-	email: string
-): Promise< BISCustomer | undefined > {
-	const response = await restApi.get< BISCustomer[] >(
-		`${ WC_API_PATH }/customers`,
-		{
-			email,
-			role: 'all',
-		}
-	);
-
-	return response.data.find(
-		( customer: BISCustomer ) => customer.email === email
-	);
-}
-
-/**
- * A customer account, as far as these specs need to know it.
- */
-type BISCustomer = { id: number; email: string; username: string };
-
-/**
- * Permanently delete a customer account created by a signup.
- *
- * @param {ApiClient} restApi    WP REST client.
- * @param {number}    customerId The customer id.
- */
-export async function deleteCustomer(
-	restApi: ApiClient,
-	customerId: number
-): Promise< void > {
-	await restApi.delete( `${ WC_API_PATH }/customers/${ customerId }`, {
-		force: true,
-	} );
-}
-
 /**
  * Make every verification link look expired to the server for this page's context.
  *
@@ -753,39 +679,6 @@ export function bisAdminListUrl( productId: number ): string {
 	return `wp-admin/admin.php?page=wc-customer-stock-notifications&customer_stock_notifications_product_filter=${ productId }`;
 }

-/**
- * Assert the product's notifications list holds no row for an email address.
- *
- * Opens its own admin context because the list is an admin-only screen, and
- * closes it in `finally` so a failure doesn't leak a context into the run.
- *
- * @param {Browser} browser   The test's browser fixture.
- * @param {number}  productId Product the list is filtered by.
- * @param {string}  email     The signup email address that must not be listed.
- */
-export async function expectNoSignupAsAdmin(
-	browser: Browser,
-	productId: number,
-	email: string
-): Promise< void > {
-	const adminContext = await browser.newContext( {
-		storageState: ADMIN_STATE_PATH,
-	} );
-
-	try {
-		const adminPage = await adminContext.newPage();
-		await adminPage.goto( bisAdminListUrl( productId ) );
-
-		await expect(
-			adminPage.getByRole( 'row' ).filter( {
-				has: adminPage.getByText( email, { exact: true } ),
-			} )
-		).toHaveCount( 0 );
-	} finally {
-		await adminContext.close();
-	}
-}
-
 /**
  * Generate a unique guest email address for a test so mail-log assertions don't collide.
  *
@@ -835,7 +728,6 @@ export const test = baseTest.extend<
 		product: BISProduct;
 		variableProduct: BISVariableProduct;
 		anyAttributeVariableProduct: BISVariableProduct;
-		accountEmail: string;
 	},
 	{ bisEnvReady: void }
 >( {
@@ -889,24 +781,6 @@ export const test = baseTest.extend<
 		await use( product );
 		productsToReap.push( product.id );
 	},
-
-	/**
-	 * A guest email address that a signup may register an account for.
-	 *
-	 * Teardown looks the address up and deletes the account if one exists, so
-	 * the customer is reaped whether the test failed on the notice, on the
-	 * lookup, or on the email — the test itself never has to hold the id.
-	 */
-	accountEmail: async ( { restApi }, use ) => {
-		const email = uniqueGuestEmail( 'bis-account' );
-		// eslint-disable-next-line react-hooks/rules-of-hooks -- Playwright's fixture `use`, not a React hook.
-		await use( email );
-
-		const account = await findCustomerByEmail( restApi, email );
-		if ( account ) {
-			await deleteCustomer( restApi, account.id );
-		}
-	},
 } );

 /**
@@ -982,19 +856,6 @@ export const bisNotice = {
 		),
 	doubleOptIn:
 		/Thanks for signing up! Please complete the sign-up process by following the verification link sent to your e-mail\./,
-	/**
-	 * Single opt-in success where the signup also registered an account.
-	 *
-	 * @param {string} productName The product name.
-	 */
-	accountCreated: ( productName: string ): RegExp =>
-		new RegExp(
-			`You have successfully signed up and will be notified when "${ escapeRegExp(
-				productName
-			) }" is back in stock! Note that a new account has been created for you; please check your e-mail for details\\.`
-		),
-	accountCreatedDoubleOptIn:
-		/Thanks for signing up! An account has been created for you\. Please complete the sign-up process by following the verification link sent to your e-mail\./,
 	alreadyJoined: /You have already joined this waitlist\./,
 	accountRequired: /Please log in to sign up for stock notifications\./,
 	/**
@@ -1025,8 +886,6 @@ export const bisNotice = {
 	errors: {
 		invalidEmail: /Invalid email address\./,
 		invalidProduct: /Invalid product\./,
-		missingConsent:
-			/To proceed, please consent to the creation of a new account with your e-mail\./,
 		failed: /Failed to sign up\. Please try again\./,
 	},
 } as const;
diff --git a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Admin/SettingsControllerTests.php b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Admin/SettingsControllerTests.php
index e62da777d4a..36ce2280c0c 100644
--- a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Admin/SettingsControllerTests.php
+++ b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Admin/SettingsControllerTests.php
@@ -67,7 +67,6 @@ class SettingsControllerTests extends \WC_Settings_Unit_Test_Case {
 			'woocommerce_customer_stock_notifications_allow_signups' => 'checkbox',
 			'woocommerce_customer_stock_notifications_require_double_opt_in' => 'checkbox',
 			'woocommerce_customer_stock_notifications_require_account' => 'checkbox',
-			'woocommerce_customer_stock_notifications_create_account_on_signup' => 'checkbox',
 			'woocommerce_customer_stock_notifications_unverified_deletions_days_threshold' => 'number',
 		);

diff --git a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Frontend/SignupServiceTests.php b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Frontend/SignupServiceTests.php
index 9c6e5121065..4b7ec32d5a4 100644
--- a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Frontend/SignupServiceTests.php
+++ b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/Frontend/SignupServiceTests.php
@@ -78,6 +78,7 @@ class SignupServiceTests extends \WC_Unit_Test_Case {

 		delete_option( 'woocommerce_customer_stock_notifications_allow_signups' );
 		delete_option( 'woocommerce_customer_stock_notifications_require_double_opt_in' );
+		delete_option( 'woocommerce_customer_stock_notifications_create_account_on_signup' );

 		// DELETE rather than TRUNCATE so the outer WP_UnitTestCase transaction can still roll back.
 		// TRUNCATE is DDL and implicitly commits the surrounding transaction.
@@ -486,7 +487,6 @@ class SignupServiceTests extends \WC_Unit_Test_Case {
 			array(
 				'wc_bis_product_id' => $product->get_id(),
 				'wc_bis_email'      => ' Guest@Example.COM ',
-				'wc_bis_opt_in'     => 'on',
 			)
 		);

@@ -495,41 +495,51 @@ class SignupServiceTests extends \WC_Unit_Test_Case {
 	}

 	/**
-	 * @testdox parse() should resolve a guest email to an existing account stored in mixed case and keep the canonical email.
+	 * @testdox parse() should leave a guest sign-up unlinked even when an account with that email exists.
 	 */
-	public function test_parse_resolves_mixed_case_account_email(): void {
-		global $wpdb;
-
+	public function test_parse_does_not_link_guest_to_existing_account(): void {
 		$product = $this->create_out_of_stock_product();
-		$user_id = $this->factory()->user->create( array( 'user_email' => 'Mixed.Case@Example.com' ) );
-
-		// The test database collation is case-insensitive, so capture the value the lookup actually
-		// sends instead of relying on the row being found.
-		$user_queries = array();
-		add_filter(
-			'query',
-			function ( $query ) use ( &$user_queries, $wpdb ) {
-				if ( false !== strpos( $query, "FROM {$wpdb->users} WHERE user_email" ) ) {
-					$user_queries[] = $query;
-				}
-				return $query;
-			}
-		);
-		wp_cache_flush();
+		$this->factory()->user->create( array( 'user_email' => 'existing@example.com' ) );

 		$data = $this->sut->parse(
 			array(
 				'wc_bis_product_id' => $product->get_id(),
-				'wc_bis_email'      => 'Mixed.Case@Example.com',
-				'wc_bis_opt_in'     => 'on',
+				'wc_bis_email'      => 'existing@example.com',
 			)
 		);

 		$this->assertIsArray( $data );
-		$this->assertSame( $user_id, $data['user_id'] );
-		$this->assertSame( 'mixed.case@example.com', $data['user_email'] );
-		$this->assertCount( 1, $user_queries, 'The account lookup should query the users table' );
-		$this->assertStringContainsString( "'Mixed.Case@Example.com'", $user_queries[0], 'The account lookup should keep the letter case as entered' );
+		$this->assertSame( 0, $data['user_id'] );
+		$this->assertSame( 'existing@example.com', $data['user_email'] );
+	}
+
+	/**
+	 * @testdox A guest signup should never create an account, even with the legacy option enabled.
+	 */
+	public function test_guest_signup_does_not_create_account(): void {
+		update_option( 'woocommerce_customer_stock_notifications_create_account_on_signup', 'yes' );
+		update_option( 'woocommerce_customer_stock_notifications_require_double_opt_in', 'no' );
+
+		$product = $this->create_out_of_stock_product();
+
+		$result = $this->sut->signup( $product->get_id(), 0, 'newguest@example.com' );
+
+		$this->assertSame( SignupService::SIGNUP_SUCCESS, $result->get_code() );
+		$this->assertSame( 0, $result->get_notification()->get_user_id() );
+		$this->assertFalse( get_user_by( 'email', 'newguest@example.com' ) );
+	}
+
+	/**
+	 * @testdox Deprecated account-creation codes should still map to a message.
+	 */
+	public function test_deprecated_account_created_codes_still_resolve(): void {
+		$product      = $this->create_out_of_stock_product();
+		$notification = new Notification();
+		$notification->set_product_id( $product->get_id() );
+
+		$this->assertStringContainsString( 'a new account has been created', $this->sut->get_signup_user_message( 'success_account_created', $notification ) );
+		$this->assertStringContainsString( 'An account has been created', $this->sut->get_signup_user_message( 'success_account_created_double_opt_in', $notification ) );
+		$this->assertStringContainsString( 'consent to the creation of a new account', $this->sut->get_error_message( 'invalid_opt_in' ) );
 	}

 	/**
@@ -548,7 +558,6 @@ class SignupServiceTests extends \WC_Unit_Test_Case {
 			array(
 				'wc_bis_product_id' => $product->get_id(),
 				'wc_bis_email'      => $posted_email,
-				'wc_bis_opt_in'     => 'on',
 			)
 		);

diff --git a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/StockNotificationsTests.php b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/StockNotificationsTests.php
index ce9b0150d3a..23fc3562725 100644
--- a/plugins/woocommerce/tests/php/src/Internal/StockNotifications/StockNotificationsTests.php
+++ b/plugins/woocommerce/tests/php/src/Internal/StockNotifications/StockNotificationsTests.php
@@ -109,7 +109,6 @@ class StockNotificationsTests extends \WC_Unit_Test_Case {
 		$this->assertContains( 'woocommerce_customer_stock_notifications_allow_signups', $setting_ids );
 		$this->assertContains( 'woocommerce_customer_stock_notifications_require_double_opt_in', $setting_ids );
 		$this->assertContains( 'woocommerce_customer_stock_notifications_require_account', $setting_ids );
-		$this->assertContains( 'woocommerce_customer_stock_notifications_create_account_on_signup', $setting_ids );
 		$this->assertContains( 'woocommerce_customer_stock_notifications_unverified_deletions_days_threshold', $setting_ids );
 	}