Commit c4b415a7ce7 for woocommerce

commit c4b415a7ce712926380a06161948d1713b207ccf
Author: Tom Cafferkey <tjcafferkey@gmail.com>
Date:   Tue Sep 29 10:14:07 2026 +0100

    Add guidance after order withdrawal activation (#68687)

    * Add enabled order withdrawal inbox guidance

    * Add changelog entry for order withdrawal guidance

    * Update plugins/woocommerce/src/Internal/OrderWithdrawal/OrderWithdrawalFeatureHighlightNotification.php

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

    * Use controller for consts

    * More useful messaging in note

    * Order withdrawal endpoint class

    * Convert to testwith

    * Dismiss previous note

    * Just rely on get_notes_with_name

    * update get_url

    ---------

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

diff --git a/plugins/woocommerce/changelog/add-order-withdrawal-enabled-notification b/plugins/woocommerce/changelog/add-order-withdrawal-enabled-notification
new file mode 100644
index 00000000000..b36fafc5db8
--- /dev/null
+++ b/plugins/woocommerce/changelog/add-order-withdrawal-enabled-notification
@@ -0,0 +1,4 @@
+Significance: minor
+Type: add
+
+Add guidance after the order withdrawal feature is enabled.
diff --git a/plugins/woocommerce/src/Internal/OrderWithdrawal/OrderWithdrawalController.php b/plugins/woocommerce/src/Internal/OrderWithdrawal/OrderWithdrawalController.php
index 8cdab00f4ea..7bf27610d38 100644
--- a/plugins/woocommerce/src/Internal/OrderWithdrawal/OrderWithdrawalController.php
+++ b/plugins/woocommerce/src/Internal/OrderWithdrawal/OrderWithdrawalController.php
@@ -15,10 +15,7 @@ use Automattic\WooCommerce\Utilities\FeaturesUtil;
  */
 final class OrderWithdrawalController implements RegisterHooksInterface {

-	private const FEATURE_ID      = 'order_withdrawal';
-	private const ENDPOINT_KEY    = 'order-withdrawal';
-	private const ENDPOINT_SLUG   = 'withdraw-order';
-	private const ENDPOINT_OPTION = 'woocommerce_myaccount_order_withdrawal_endpoint';
+	private const FEATURE_ID = 'order_withdrawal';

 	/**
 	 * Form processor.
@@ -48,6 +45,13 @@ final class OrderWithdrawalController implements RegisterHooksInterface {
 	 */
 	private OrderWithdrawalEmailPreview $email_preview;

+	/**
+	 * Order withdrawal endpoint.
+	 *
+	 * @var OrderWithdrawalEndpoint
+	 */
+	private OrderWithdrawalEndpoint $endpoint;
+
 	/**
 	 * Initialize dependencies.
 	 *
@@ -55,15 +59,17 @@ final class OrderWithdrawalController implements RegisterHooksInterface {
 	 * @param OrderWithdrawalFormView                     $form_view                      Form view.
 	 * @param OrderWithdrawalFeatureHighlightNotification $feature_highlight_notification Feature highlight notification.
 	 * @param OrderWithdrawalEmailPreview                 $email_preview                  Email preview handler.
+	 * @param OrderWithdrawalEndpoint                     $endpoint                       Order withdrawal endpoint.
 	 * @internal
 	 *
 	 * @since 11.1.0
 	 */
-	final public function init( OrderWithdrawalFormProcessor $form_processor, OrderWithdrawalFormView $form_view, OrderWithdrawalFeatureHighlightNotification $feature_highlight_notification, OrderWithdrawalEmailPreview $email_preview ): void { // phpcs:ignore Generic.CodeAnalysis.UnnecessaryFinalModifier.Found -- Required by WooCommerce injection method rules.
+	final public function init( OrderWithdrawalFormProcessor $form_processor, OrderWithdrawalFormView $form_view, OrderWithdrawalFeatureHighlightNotification $feature_highlight_notification, OrderWithdrawalEmailPreview $email_preview, OrderWithdrawalEndpoint $endpoint ): void { // phpcs:ignore Generic.CodeAnalysis.UnnecessaryFinalModifier.Found -- Required by WooCommerce injection method rules.
 		$this->form_processor                 = $form_processor;
 		$this->form_view                      = $form_view;
 		$this->feature_highlight_notification = $feature_highlight_notification;
 		$this->email_preview                  = $email_preview;
+		$this->endpoint                       = $endpoint;
 	}

 	/**
@@ -73,6 +79,7 @@ final class OrderWithdrawalController implements RegisterHooksInterface {
 	 */
 	public function register(): void {
 		add_action( FeaturesController::FEATURE_ENABLED_CHANGED_ACTION, array( $this, 'maybe_flush_rewrite_rules' ), 10, 1 );
+		add_action( FeaturesController::FEATURE_ENABLED_CHANGED_ACTION, array( $this->feature_highlight_notification, 'possibly_add_enabled_note' ), 10, 2 );
 		add_action( 'init', array( $this, 'register_feature_hooks' ), 0, 0 );
 	}

@@ -92,9 +99,9 @@ final class OrderWithdrawalController implements RegisterHooksInterface {
 		}

 		add_filter( 'woocommerce_get_query_vars', array( $this, 'add_query_var' ), 10, 1 );
-		add_filter( 'woocommerce_endpoint_' . self::ENDPOINT_KEY . '_title', array( $this, 'get_endpoint_title' ), 10, 1 );
+		add_filter( 'woocommerce_endpoint_' . OrderWithdrawalEndpoint::ENDPOINT_KEY . '_title', array( $this, 'get_endpoint_title' ), 10, 1 );
 		add_filter( 'woocommerce_settings_pages', array( $this, 'add_endpoint_setting' ), 10, 1 );
-		add_action( 'woocommerce_account_' . self::ENDPOINT_KEY . '_endpoint', array( $this, 'render_view' ) );
+		add_action( 'woocommerce_account_' . OrderWithdrawalEndpoint::ENDPOINT_KEY . '_endpoint', array( $this, 'render_view' ) );

 		$this->email_preview->register();
 	}
@@ -117,8 +124,8 @@ final class OrderWithdrawalController implements RegisterHooksInterface {
 		global $wp;

 		return $this->is_enabled()
-			&& isset( $wp->query_vars[ self::ENDPOINT_KEY ] )
-			&& self::ENDPOINT_KEY === WC()->query->get_current_endpoint();
+			&& isset( $wp->query_vars[ OrderWithdrawalEndpoint::ENDPOINT_KEY ] )
+			&& OrderWithdrawalEndpoint::ENDPOINT_KEY === WC()->query->get_current_endpoint();
 	}

 	/**
@@ -147,10 +154,10 @@ final class OrderWithdrawalController implements RegisterHooksInterface {
 			return array();
 		}

-		$endpoint = (string) get_option( self::ENDPOINT_OPTION, self::ENDPOINT_SLUG );
+		$endpoint = $this->endpoint->get_slug();

 		if ( ! empty( $endpoint ) ) {
-			$query_vars[ self::ENDPOINT_KEY ] = $endpoint;
+			$query_vars[ OrderWithdrawalEndpoint::ENDPOINT_KEY ] = $endpoint;
 		}

 		return $query_vars;
@@ -184,9 +191,9 @@ final class OrderWithdrawalController implements RegisterHooksInterface {
 		$endpoint_setting = array(
 			'title'    => __( 'Order withdrawal', 'woocommerce' ),
 			'desc'     => __( 'Endpoint for the order withdrawal page.', 'woocommerce' ),
-			'id'       => self::ENDPOINT_OPTION,
+			'id'       => OrderWithdrawalEndpoint::ENDPOINT_OPTION,
 			'type'     => 'text',
-			'default'  => self::ENDPOINT_SLUG,
+			'default'  => OrderWithdrawalEndpoint::ENDPOINT_SLUG,
 			'desc_tip' => true,
 		);

@@ -194,7 +201,7 @@ final class OrderWithdrawalController implements RegisterHooksInterface {
 		$added        = false;

 		foreach ( $settings as $key => $setting ) {
-			if ( is_array( $setting ) && self::ENDPOINT_OPTION === ( $setting['id'] ?? '' ) ) {
+			if ( is_array( $setting ) && OrderWithdrawalEndpoint::ENDPOINT_OPTION === ( $setting['id'] ?? '' ) ) {
 				return $settings;
 			}

@@ -204,15 +211,15 @@ final class OrderWithdrawalController implements RegisterHooksInterface {
 				'sectionend' === ( $setting['type'] ?? '' ) &&
 				'account_endpoint_options' === ( $setting['id'] ?? '' )
 			) {
-				$new_settings[ self::ENDPOINT_OPTION ] = $endpoint_setting;
-				$added                                 = true;
+				$new_settings[ OrderWithdrawalEndpoint::ENDPOINT_OPTION ] = $endpoint_setting;
+				$added = true;
 			}

 			$new_settings[ $key ] = $setting;
 		}

 		if ( ! $added ) {
-			$new_settings[ self::ENDPOINT_OPTION ] = $endpoint_setting;
+			$new_settings[ OrderWithdrawalEndpoint::ENDPOINT_OPTION ] = $endpoint_setting;
 		}

 		return $new_settings;
@@ -238,7 +245,7 @@ final class OrderWithdrawalController implements RegisterHooksInterface {
 	private function get_template_args(): array {
 		return $this->form_view->get_template_args(
 			$this->form_processor->process_current_request(),
-			$this->get_form_action_url(),
+			$this->endpoint->get_url(),
 			$this->get_shop_url()
 		);
 	}
@@ -251,13 +258,4 @@ final class OrderWithdrawalController implements RegisterHooksInterface {

 		return $shop_url ? $shop_url : home_url( '/' );
 	}
-
-	/**
-	 * Get the form action URL.
-	 */
-	private function get_form_action_url(): string {
-		$account_url = wc_get_page_permalink( 'myaccount' );
-
-		return wc_get_endpoint_url( self::ENDPOINT_KEY, '', $account_url ? $account_url : home_url( '/' ) );
-	}
 }
diff --git a/plugins/woocommerce/src/Internal/OrderWithdrawal/OrderWithdrawalEndpoint.php b/plugins/woocommerce/src/Internal/OrderWithdrawal/OrderWithdrawalEndpoint.php
new file mode 100644
index 00000000000..c0145191907
--- /dev/null
+++ b/plugins/woocommerce/src/Internal/OrderWithdrawal/OrderWithdrawalEndpoint.php
@@ -0,0 +1,51 @@
+<?php
+declare( strict_types = 1 );
+
+namespace Automattic\WooCommerce\Internal\OrderWithdrawal;
+
+/**
+ * Provides configuration and URLs for the order withdrawal endpoint.
+ *
+ * @internal Just for internal use.
+ *
+ * @since 11.3.0
+ */
+final class OrderWithdrawalEndpoint {
+
+	/**
+	 * Internal endpoint key.
+	 */
+	public const ENDPOINT_KEY = 'order-withdrawal';
+
+	/**
+	 * Default endpoint slug.
+	 */
+	public const ENDPOINT_SLUG = 'withdraw-order';
+
+	/**
+	 * Option containing the configured endpoint slug.
+	 */
+	public const ENDPOINT_OPTION = 'woocommerce_myaccount_order_withdrawal_endpoint';
+
+	/**
+	 * Get the configured endpoint slug.
+	 *
+	 * @since 11.3.0
+	 */
+	public function get_slug(): string {
+		return (string) get_option( self::ENDPOINT_OPTION, self::ENDPOINT_SLUG );
+	}
+
+	/**
+	 * Get the configured public order withdrawal page URL.
+	 *
+	 * @since 11.3.0
+	 */
+	public function get_url(): string {
+		$account_url = wc_get_page_permalink( 'myaccount' );
+		$query_vars  = WC()->query->get_query_vars();
+		$endpoint    = ! empty( $query_vars[ self::ENDPOINT_KEY ] ) ? self::ENDPOINT_KEY : $this->get_slug();
+
+		return wc_get_endpoint_url( $endpoint, '', $account_url ? $account_url : home_url( '/' ) );
+	}
+}
diff --git a/plugins/woocommerce/src/Internal/OrderWithdrawal/OrderWithdrawalFeatureHighlightNotification.php b/plugins/woocommerce/src/Internal/OrderWithdrawal/OrderWithdrawalFeatureHighlightNotification.php
index d563cce8f0e..62351e8c458 100644
--- a/plugins/woocommerce/src/Internal/OrderWithdrawal/OrderWithdrawalFeatureHighlightNotification.php
+++ b/plugins/woocommerce/src/Internal/OrderWithdrawal/OrderWithdrawalFeatureHighlightNotification.php
@@ -16,13 +16,30 @@ use Exception;
  */
 final class OrderWithdrawalFeatureHighlightNotification implements RegisterHooksInterface {

-	public const NOTE_NAME      = 'wc-admin-order-withdrawal-feature';
-	public const CREATED_OPTION = 'woocommerce_order_withdrawal_inbox_notification_created';
+	/**
+	 * Order withdrawal endpoint.
+	 *
+	 * @var OrderWithdrawalEndpoint
+	 */
+	private OrderWithdrawalEndpoint $endpoint;
+
+	public const NOTE_NAME         = 'wc-admin-order-withdrawal-feature';
+	public const ENABLED_NOTE_NAME = 'wc-admin-order-withdrawal-enabled';

 	private const COMING_SOON_OPTION    = 'woocommerce_coming_soon';
 	private const FEATURES_SETTINGS_URL = 'admin.php?page=wc-settings&tab=advanced&section=features';
 	private const DOCUMENTATION_URL     = 'https://woocommerce.com/document/customer-order-withdrawal/';

+	/**
+	 * Initialize dependencies.
+	 *
+	 * @param OrderWithdrawalEndpoint $endpoint Order withdrawal endpoint.
+	 * @internal
+	 */
+	final public function init( OrderWithdrawalEndpoint $endpoint ): void { // phpcs:ignore Generic.CodeAnalysis.UnnecessaryFinalModifier.Found -- Required by WooCommerce injection method rules.
+		$this->endpoint = $endpoint;
+	}
+
 	/**
 	 * Register hooks.
 	 *
@@ -62,7 +79,7 @@ final class OrderWithdrawalFeatureHighlightNotification implements RegisterHooks
 	 */
 	public function possibly_add_note(): void {
 		try {
-			if ( $this->has_note_been_created() ) {
+			if ( $this->note_exists( self::NOTE_NAME ) ) {
 				return;
 			}

@@ -70,15 +87,42 @@ final class OrderWithdrawalFeatureHighlightNotification implements RegisterHooks
 				return;
 			}

-			if ( ! add_option( self::CREATED_OPTION, 'yes', '', false ) ) {
+			$this->get_feature_highlight_note()->save();
+		} catch ( Exception $exception ) {
+			wc_get_logger()->error(
+				'Unable to create the order withdrawal inbox notification.',
+				array(
+					'source'    => 'order-withdrawal',
+					'exception' => $exception,
+				)
+			);
+		}
+	}
+
+	/**
+	 * Add a note when the order withdrawal feature is enabled.
+	 *
+	 * @param mixed $feature_id Feature being toggled.
+	 * @param mixed $enabled    Whether the feature was enabled.
+	 *
+	 * @since 11.3.0
+	 */
+	public function possibly_add_enabled_note( $feature_id, $enabled ): void {
+		if ( 'order_withdrawal' !== $feature_id || true !== $enabled ) {
+			return;
+		}
+
+		try {
+			if ( $this->note_exists( self::ENABLED_NOTE_NAME ) ) {
+				$this->dismiss_note( self::NOTE_NAME );
 				return;
 			}

-			$this->get_note()->save();
+			$this->get_enabled_note()->save();
+			$this->dismiss_note( self::NOTE_NAME );
 		} catch ( Exception $exception ) {
-			delete_option( self::CREATED_OPTION );
 			wc_get_logger()->error(
-				'Unable to create the order withdrawal inbox notification.',
+				'Unable to create the order withdrawal enabled inbox notification.',
 				array(
 					'source'    => 'order-withdrawal',
 					'exception' => $exception,
@@ -87,6 +131,32 @@ final class OrderWithdrawalFeatureHighlightNotification implements RegisterHooks
 		}
 	}

+	/**
+	 * Dismiss notes with the given name.
+	 *
+	 * @param string $note_name Note name stored in the data store.
+	 */
+	private function dismiss_note( string $note_name ): void {
+		/**
+		 * Data store instance.
+		 *
+		 * @var NotesDataStore $data_store
+		 */
+		$data_store = Notes::load_data_store();
+		$note_ids   = $data_store->get_notes_with_name( $note_name );
+
+		foreach ( $note_ids as $note_id ) {
+			$note = Notes::get_note( $note_id );
+
+			if ( ! $note instanceof Note || Note::E_WC_ADMIN_NOTE_ACTIONED === $note->get_status() ) {
+				continue;
+			}
+
+			$note->set_status( Note::E_WC_ADMIN_NOTE_ACTIONED );
+			$note->save();
+		}
+	}
+
 	/**
 	 * Whether the notification is relevant for the current store settings.
 	 */
@@ -98,7 +168,7 @@ final class OrderWithdrawalFeatureHighlightNotification implements RegisterHooks
 	/**
 	 * Get the inbox note.
 	 */
-	private function get_note(): Note {
+	private function get_feature_highlight_note(): Note {
 		$note = new Note();

 		$note->set_title(
@@ -132,28 +202,51 @@ final class OrderWithdrawalFeatureHighlightNotification implements RegisterHooks
 	}

 	/**
-	 * Whether this note has already been created, including soft-deleted notes.
+	 * Get the inbox note shown after the feature is enabled.
 	 */
-	private function has_note_been_created(): bool {
-		if ( 'yes' === get_option( self::CREATED_OPTION, 'no' ) ) {
-			return true;
-		}
+	private function get_enabled_note(): Note {
+		$note = new Note();

+		$note->set_title( __( 'The order withdrawal feature is enabled', 'woocommerce' ) );
+		$note->set_content(
+			__( 'Your order withdrawal page is ready. Share its URL wherever customers need access to the withdrawal form, such as your store\'s terms and conditions.', 'woocommerce' )
+		);
+		$note->set_content_data( (object) array() );
+		$note->set_type( Note::E_WC_ADMIN_NOTE_INFORMATIONAL );
+		$note->set_name( self::ENABLED_NOTE_NAME );
+		$note->set_source( 'woocommerce-admin' );
+		$note->add_action(
+			'view-page',
+			__( 'View page', 'woocommerce' ),
+			$this->endpoint->get_url(),
+			Note::E_WC_ADMIN_NOTE_ACTIONED,
+			true
+		);
+		$note->add_action(
+			'learn-more',
+			__( 'Learn more', 'woocommerce' ),
+			self::DOCUMENTATION_URL,
+			Note::E_WC_ADMIN_NOTE_UNACTIONED
+		);
+
+		return $note;
+	}
+
+	/**
+	 * Whether a note exists, including soft-deleted notes.
+	 *
+	 * @param string $note_name Note name stored in the data store.
+	 */
+	private function note_exists( string $note_name ): bool {
 		/**
 		 * Data store instance.
 		 *
 		 * @var NotesDataStore $data_store
 		 */
 		$data_store = Notes::load_data_store();
-		$note_ids   = $data_store->get_notes_with_name( self::NOTE_NAME );
-
-		if ( empty( $note_ids ) ) {
-			return false;
-		}
-
-		update_option( self::CREATED_OPTION, 'yes', false );
+		$note_ids   = $data_store->get_notes_with_name( $note_name );

-		return true;
+		return ! empty( $note_ids );
 	}

 	/**
diff --git a/plugins/woocommerce/tests/php/src/Internal/OrderWithdrawal/OrderWithdrawalFeatureHighlightNotificationTest.php b/plugins/woocommerce/tests/php/src/Internal/OrderWithdrawal/OrderWithdrawalFeatureHighlightNotificationTest.php
index 5df32561c51..e05bc0a480d 100644
--- a/plugins/woocommerce/tests/php/src/Internal/OrderWithdrawal/OrderWithdrawalFeatureHighlightNotificationTest.php
+++ b/plugins/woocommerce/tests/php/src/Internal/OrderWithdrawal/OrderWithdrawalFeatureHighlightNotificationTest.php
@@ -5,6 +5,7 @@ namespace Automattic\WooCommerce\Tests\Internal\OrderWithdrawal;

 use Automattic\WooCommerce\Admin\Notes\Note;
 use Automattic\WooCommerce\Admin\Notes\Notes;
+use Automattic\WooCommerce\Internal\OrderWithdrawal\OrderWithdrawalEndpoint;
 use Automattic\WooCommerce\Internal\OrderWithdrawal\OrderWithdrawalFeatureHighlightNotification;
 use WC_Unit_Test_Case;

@@ -26,7 +27,7 @@ class OrderWithdrawalFeatureHighlightNotificationTest extends WC_Unit_Test_Case
 		self::ALLOWED_COUNTRIES_OPTION,
 		self::ALL_EXCEPT_COUNTRIES_OPTION,
 		self::SPECIFIC_COUNTRIES_OPTION,
-		OrderWithdrawalFeatureHighlightNotification::CREATED_OPTION,
+		OrderWithdrawalEndpoint::ENDPOINT_OPTION,
 	);

 	/**
@@ -50,6 +51,7 @@ class OrderWithdrawalFeatureHighlightNotificationTest extends WC_Unit_Test_Case
 		parent::setUp();

 		$this->sut = new OrderWithdrawalFeatureHighlightNotification();
+		$this->sut->init( new OrderWithdrawalEndpoint() );
 		$this->store_original_options();
 		$this->delete_notification_state();
 		$this->set_live_eu_store_defaults();
@@ -78,11 +80,6 @@ class OrderWithdrawalFeatureHighlightNotificationTest extends WC_Unit_Test_Case
 			$note_ids,
 			'An eligible existing live store should receive the notification.'
 		);
-		$this->assertSame(
-			'yes',
-			get_option( OrderWithdrawalFeatureHighlightNotification::CREATED_OPTION ),
-			'Creating the note should persist the one-time creation flag.'
-		);
 	}

 	/**
@@ -114,6 +111,74 @@ class OrderWithdrawalFeatureHighlightNotificationTest extends WC_Unit_Test_Case
 		);
 	}

+	/**
+	 * @testdox Should add a notification with the configured withdrawal page when the feature is enabled.
+	 */
+	public function test_possibly_add_enabled_note_adds_notification_with_configured_page_url(): void {
+		update_option( OrderWithdrawalEndpoint::ENDPOINT_OPTION, 'request-withdrawal' );
+
+		$this->sut->possibly_add_enabled_note( 'order_withdrawal', true );
+
+		$note = Notes::get_note_by_name( OrderWithdrawalFeatureHighlightNotification::ENABLED_NOTE_NAME );
+
+		$this->assertInstanceOf( Note::class, $note, 'Enabling order withdrawal should create an inbox notification.' );
+		$this->assertSame( 'The order withdrawal feature is enabled', $note->get_title(), 'The notification should have the expected title.' );
+		$this->assertSame( 'Your order withdrawal page is ready. Share its URL wherever customers need access to the withdrawal form, such as your store\'s terms and conditions.', $note->get_content(), 'The notification should explain how to share the withdrawal page.' );
+
+		$actions = $note->get_actions();
+
+		$this->assertCount( 2, $actions, 'The notification should include view page and learn more actions.' );
+		$this->assertSame( 'view-page', $actions[0]->name, 'The primary action should view the withdrawal page.' );
+		$this->assertSame( wc_get_endpoint_url( 'request-withdrawal', '', wc_get_page_permalink( 'myaccount' ) ), $actions[0]->query, 'The view page action should use the configured endpoint.' );
+		$this->assertSame( 'learn-more', $actions[1]->name, 'The secondary action should link to documentation.' );
+		$this->assertSame( 'https://woocommerce.com/document/customer-order-withdrawal/', $actions[1]->query, 'The learn more action should use the order withdrawal documentation URL.' );
+	}
+
+	/**
+	 * @testdox Should only create the enabled notification once.
+	 */
+	public function test_possibly_add_enabled_note_prevents_duplicates(): void {
+		$this->sut->possibly_add_enabled_note( 'order_withdrawal', true );
+		$this->sut->possibly_add_enabled_note( 'order_withdrawal', true );
+
+		$this->assertCount(
+			1,
+			$this->get_enabled_notification_note_ids(),
+			'Repeated enable events should not create duplicate notifications.'
+		);
+	}
+
+	/**
+	 * @testdox Should dismiss the feature highlight notification when the enabled notification is created.
+	 */
+	public function test_possibly_add_enabled_note_dismisses_feature_highlight_notification(): void {
+		$this->sut->possibly_add_note();
+
+		$this->sut->possibly_add_enabled_note( 'order_withdrawal', true );
+
+		$feature_highlight_note = Notes::get_note_by_name( OrderWithdrawalFeatureHighlightNotification::NOTE_NAME );
+		$enabled_note           = Notes::get_note_by_name( OrderWithdrawalFeatureHighlightNotification::ENABLED_NOTE_NAME );
+
+		$this->assertInstanceOf( Note::class, $feature_highlight_note, 'The feature highlight notification should exist.' );
+		$this->assertSame( Note::E_WC_ADMIN_NOTE_ACTIONED, $feature_highlight_note->get_status(), 'The feature highlight notification should be dismissed.' );
+		$this->assertInstanceOf( Note::class, $enabled_note, 'The enabled notification should be created.' );
+		$this->assertSame( Note::E_WC_ADMIN_NOTE_UNACTIONED, $enabled_note->get_status(), 'The enabled notification should remain visible.' );
+	}
+
+	/**
+	 * @testdox Should not add the enabled notification for unrelated or disabled features.
+	 * @testWith ["order_withdrawal", false]
+	 *           ["other_feature", true]
+	 *
+	 * @param string $feature_id Feature being toggled.
+	 * @param bool   $enabled    Whether the feature was enabled.
+	 */
+	public function test_possibly_add_enabled_note_ignores_other_feature_changes( string $feature_id, bool $enabled ): void {
+		$this->sut->possibly_add_enabled_note( $feature_id, $enabled );
+
+		$this->assertFalse( Notes::get_note_by_name( OrderWithdrawalFeatureHighlightNotification::ENABLED_NOTE_NAME ), 'An irrelevant feature change should not create the notification.' );
+	}
+
 	/**
 	 * @testdox Should match stores selling to EU countries.
 	 * @dataProvider provide_country_settings
@@ -205,9 +270,12 @@ class OrderWithdrawalFeatureHighlightNotificationTest extends WC_Unit_Test_Case
 	 * Delete notification state created by tests.
 	 */
 	private function delete_notification_state(): void {
-		delete_option( OrderWithdrawalFeatureHighlightNotification::CREATED_OPTION );
+		$note_ids = array_merge(
+			$this->get_notification_note_ids(),
+			$this->get_enabled_notification_note_ids()
+		);

-		foreach ( $this->get_notification_note_ids() as $note_id ) {
+		foreach ( $note_ids as $note_id ) {
 			$note = Notes::get_note( $note_id );

 			if ( $note instanceof Note ) {
@@ -230,6 +298,20 @@ class OrderWithdrawalFeatureHighlightNotificationTest extends WC_Unit_Test_Case
 		);
 	}

+	/**
+	 * Get enabled notification note IDs.
+	 *
+	 * @return int[]
+	 */
+	private function get_enabled_notification_note_ids(): array {
+		$data_store = Notes::load_data_store();
+
+		return array_map(
+			'absint',
+			$data_store->get_notes_with_name( OrderWithdrawalFeatureHighlightNotification::ENABLED_NOTE_NAME )
+		);
+	}
+
 	/**
 	 * Restore an option to its original state.
 	 *
diff --git a/plugins/woocommerce/tests/php/src/Internal/OrderWithdrawal/OrderWithdrawalTest.php b/plugins/woocommerce/tests/php/src/Internal/OrderWithdrawal/OrderWithdrawalTest.php
index 6176add9230..a737b7c09e3 100644
--- a/plugins/woocommerce/tests/php/src/Internal/OrderWithdrawal/OrderWithdrawalTest.php
+++ b/plugins/woocommerce/tests/php/src/Internal/OrderWithdrawal/OrderWithdrawalTest.php
@@ -9,6 +9,7 @@ use Automattic\WooCommerce\Internal\Admin\EmailPreview\EmailPreview;
 use Automattic\WooCommerce\Internal\Features\FeaturesController;
 use Automattic\WooCommerce\Internal\OrderWithdrawal\Emails\OrderWithdrawalEmailPreview;
 use Automattic\WooCommerce\Internal\OrderWithdrawal\OrderWithdrawalController;
+use Automattic\WooCommerce\Internal\OrderWithdrawal\OrderWithdrawalEndpoint;
 use Automattic\WooCommerce\Internal\OrderWithdrawal\OrderWithdrawalFormProcessor;
 use Automattic\WooCommerce\Internal\OrderWithdrawal\OrderWithdrawalFormState;
 use Automattic\WooCommerce\Internal\OrderWithdrawal\OrderWithdrawalFormView;
@@ -25,7 +26,6 @@ use WC_Unit_Test_Case;
 class OrderWithdrawalTest extends WC_Unit_Test_Case {

 	private const FEATURE_OPTION                      = 'woocommerce_feature_order_withdrawal_enabled';
-	private const ENDPOINT_OPTION                     = 'woocommerce_myaccount_order_withdrawal_endpoint';
 	private const FLUSH_QUEUE_OPTION                  = 'woocommerce_queue_flush_rewrite_rules';
 	private const MISSING_OPTION_MARK                 = '__woocommerce_order_withdrawal_missing_option__';
 	private const ORDER_WITHDRAWAL_REQUESTED_META_KEY = '_order_withdrawal_requested';
@@ -128,7 +128,7 @@ class OrderWithdrawalTest extends WC_Unit_Test_Case {
 		$this->original_remote_addr        = $this->had_remote_addr ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : null;
 		$this->original_session            = WC()->session;
 		$this->original_feature_option     = get_option( self::FEATURE_OPTION, self::MISSING_OPTION_MARK );
-		$this->original_endpoint_option    = get_option( self::ENDPOINT_OPTION, self::MISSING_OPTION_MARK );
+		$this->original_endpoint_option    = get_option( OrderWithdrawalEndpoint::ENDPOINT_OPTION, self::MISSING_OPTION_MARK );
 		$this->original_flush_queue_option = get_option( self::FLUSH_QUEUE_OPTION, self::MISSING_OPTION_MARK );

 		if ( ! WC()->session ) {
@@ -140,7 +140,7 @@ class OrderWithdrawalTest extends WC_Unit_Test_Case {
 		$_SERVER['REMOTE_ADDR']    = '203.0.113.10';
 		$this->clear_order_withdrawal_rate_limits();
 		$this->disable_feature();
-		delete_option( self::ENDPOINT_OPTION );
+		delete_option( OrderWithdrawalEndpoint::ENDPOINT_OPTION );
 		delete_option( self::FLUSH_QUEUE_OPTION );
 		wc_clear_notices();
 	}
@@ -164,7 +164,7 @@ class OrderWithdrawalTest extends WC_Unit_Test_Case {
 		}

 		$this->restore_option( self::FEATURE_OPTION, $this->original_feature_option );
-		$this->restore_option( self::ENDPOINT_OPTION, $this->original_endpoint_option );
+		$this->restore_option( OrderWithdrawalEndpoint::ENDPOINT_OPTION, $this->original_endpoint_option );
 		$this->restore_option( self::FLUSH_QUEUE_OPTION, $this->original_flush_queue_option );
 		wc_clear_notices();
 		$this->clear_order_withdrawal_rate_limits();
@@ -680,8 +680,11 @@ class OrderWithdrawalTest extends WC_Unit_Test_Case {
 	 */
 	public function test_controller_registers_order_deletion_cleanup_hooks(): void {
 		$controller    = new OrderWithdrawalController();
+		$notification  = new OrderWithdrawalFeatureHighlightNotification();
 		$email_preview = new OrderWithdrawalEmailPreview();
-		$controller->init( $this->sut, new OrderWithdrawalFormView(), new OrderWithdrawalFeatureHighlightNotification(), $email_preview );
+		$endpoint      = new OrderWithdrawalEndpoint();
+		$notification->init( $endpoint );
+		$controller->init( $this->sut, new OrderWithdrawalFormView(), $notification, $email_preview, $endpoint );

 		try {
 			$controller->register();
@@ -689,10 +692,12 @@ class OrderWithdrawalTest extends WC_Unit_Test_Case {

 			$this->assertNotFalse( has_action( 'woocommerce_before_delete_order', array( $this->sut, 'delete_order_withdrawal_inbox_note_for_order' ) ) );
 			$this->assertNotFalse( has_action( 'before_delete_post', array( $this->sut, 'delete_order_withdrawal_inbox_note_for_order' ) ) );
+			$this->assertNotFalse( has_action( FeaturesController::FEATURE_ENABLED_CHANGED_ACTION, array( $notification, 'possibly_add_enabled_note' ) ) );
 			$this->assertFalse( has_filter( 'woocommerce_prepare_email_for_preview', array( $email_preview, 'prepare_email_for_preview' ) ) );
 		} finally {
 			remove_action( 'init', array( $controller, 'register_feature_hooks' ), 0 );
 			remove_action( FeaturesController::FEATURE_ENABLED_CHANGED_ACTION, array( $controller, 'maybe_flush_rewrite_rules' ), 10 );
+			remove_action( FeaturesController::FEATURE_ENABLED_CHANGED_ACTION, array( $notification, 'possibly_add_enabled_note' ), 10 );
 			remove_filter( 'woocommerce_prepare_email_for_preview', array( $email_preview, 'prepare_email_for_preview' ), 10 );
 			remove_filter( 'woocommerce_get_query_vars', array( $controller, 'add_query_var' ), 10 );
 			remove_filter( 'woocommerce_endpoint_order-withdrawal_title', array( $controller, 'get_endpoint_title' ), 10 );
@@ -748,8 +753,10 @@ class OrderWithdrawalTest extends WC_Unit_Test_Case {
 		$controller    = new OrderWithdrawalController();
 		$notification  = new OrderWithdrawalFeatureHighlightNotification();
 		$email_preview = new OrderWithdrawalEmailPreview();
+		$endpoint      = new OrderWithdrawalEndpoint();

-		$controller->init( $this->sut, new OrderWithdrawalFormView(), $notification, $email_preview );
+		$notification->init( $endpoint );
+		$controller->init( $this->sut, new OrderWithdrawalFormView(), $notification, $email_preview, $endpoint );

 		try {
 			$controller->register();
@@ -764,6 +771,7 @@ class OrderWithdrawalTest extends WC_Unit_Test_Case {
 		} finally {
 			remove_action( 'init', array( $controller, 'register_feature_hooks' ), 0 );
 			remove_action( FeaturesController::FEATURE_ENABLED_CHANGED_ACTION, array( $controller, 'maybe_flush_rewrite_rules' ), 10 );
+			remove_action( FeaturesController::FEATURE_ENABLED_CHANGED_ACTION, array( $notification, 'possibly_add_enabled_note' ), 10 );
 			remove_filter( 'woocommerce_prepare_email_for_preview', array( $email_preview, 'prepare_email_for_preview' ), 10 );
 			remove_filter( 'woocommerce_get_query_vars', array( $controller, 'add_query_var' ), 10 );
 			remove_filter( 'woocommerce_endpoint_order-withdrawal_title', array( $controller, 'get_endpoint_title' ), 10 );
@@ -785,12 +793,15 @@ class OrderWithdrawalTest extends WC_Unit_Test_Case {
 		$controller    = new OrderWithdrawalController();
 		$notification  = new OrderWithdrawalFeatureHighlightNotification();
 		$email_preview = new OrderWithdrawalEmailPreview();
+		$endpoint      = new OrderWithdrawalEndpoint();

+		$notification->init( $endpoint );
 		$controller->init(
 			$this->sut,
 			new OrderWithdrawalFormView(),
 			$notification,
-			$email_preview
+			$email_preview,
+			$endpoint
 		);

 		try {
@@ -808,6 +819,7 @@ class OrderWithdrawalTest extends WC_Unit_Test_Case {
 		} finally {
 			remove_action( 'init', array( $controller, 'register_feature_hooks' ), 0 );
 			remove_action( FeaturesController::FEATURE_ENABLED_CHANGED_ACTION, array( $controller, 'maybe_flush_rewrite_rules' ), 10 );
+			remove_action( FeaturesController::FEATURE_ENABLED_CHANGED_ACTION, array( $notification, 'possibly_add_enabled_note' ), 10 );
 			remove_action( 'update_option_woocommerce_coming_soon', array( $notification, 'maybe_add_note_when_store_goes_live' ), 10 );
 			remove_action( 'wc_admin_daily', array( $notification, 'possibly_add_note' ), 10 );
 			remove_action( 'woocommerce_before_delete_order', array( $this->sut, 'delete_order_withdrawal_inbox_note_for_order' ), 10 );