Commit 0ebae5fc26 for woocommerce

commit 0ebae5fc26fd6d75e8ba7c27227c2cc0a103a54d
Author: Eric Binnion <ericbinnion@gmail.com>
Date:   Tue Jan 20 14:21:50 2026 -0600

    Agentic Commerce: Enforce Jetpack blog token as sole authentication for checkout sessions (#62814)

    * Enforce Jetpack blog token as sole authentication for checkout sessions

    Replace bearer token authentication on agentic checkout endpoints with
    Jetpack blog token authentication. This ensures all agentic commerce
    traffic flows through WordPress.com gateway, providing centralized
    control, observability, and rapid protocol iteration.

    Changes:
    - Replace is_authorized() bearer token validation with validate_jetpack_request()
      using Jetpack Connection's REST_Authentication::is_signed_with_blog_token()
    - Remove AgenticSettingsPage.php (bearer token settings UI)
    - Remove AgenticCommerceIntegration.php (WooCommerce integration wrapper)
    - Remove integration registration from AgenticController
    - Update tests to reflect new Jetpack authentication
    - Clean up phpstan-baseline.neon for deleted files

    * Add Jetpack blog token authentication tests for checkout sessions

    - Add Jetpack auth mocking methods to AgenticTestHelpers trait
    - Update CheckoutSessions integration tests to use Jetpack auth
    - Update CheckoutSessionsComplete integration tests to use Jetpack auth
    - Add AgenticCheckoutUtilsTest unit tests for validate_jetpack_request()
    - Add auth failure and recovery test scenarios

    * Fix Jetpack class casing and update PHPStan baseline

    - Change REST_Authentication to Rest_Authentication to match Jetpack's
      actual class naming
    - Regenerate phpstan-baseline.neon to remove stale entries for the
      old is_authorized($request) method signature

    * Fix equals sign alignment in AgenticTestHelpers

    Align equals signs with surrounding assignments to satisfy
    PHPCS MultipleStatementAlignment rule.

    * Remove redundant method_exists check in validate_jetpack_request

    The method_exists() check for is_signed_with_blog_token is always true
    when the class exists since automattic/jetpack-connection is a required
    dependency. The class_exists() check alone sufficiently guards against
    environments where the package might not be installed.

    * Add @since tag to validate_jetpack_request docblock

    * Fix flaky refund order test in AgenticWebhookPayloadBuilderTest

    The test assumed refunds would be returned in creation order, but
    WC_Order::get_refunds() does not guarantee ordering. Changed to
    order-independent assertions using array_column to extract and
    verify amounts and types exist regardless of array position.

diff --git a/plugins/woocommerce/phpstan-baseline.neon b/plugins/woocommerce/phpstan-baseline.neon
index fb1be5cf94..0914682a92 100644
--- a/plugins/woocommerce/phpstan-baseline.neon
+++ b/plugins/woocommerce/phpstan-baseline.neon
@@ -63339,42 +63339,12 @@ parameters:
 			count: 1
 			path: src/Internal/Admin/ActivityPanels.php

-		-
-			message: '#^Action callback returns bool but should not return anything\.$#'
-			identifier: return.void
-			count: 1
-			path: src/Internal/Admin/Agentic/AgenticCommerceIntegration.php
-
-		-
-			message: '#^Method Automattic\\WooCommerce\\Internal\\Admin\\Agentic\\AgenticCommerceIntegration\:\:admin_options\(\) has no return type specified\.$#'
-			identifier: missingType.return
-			count: 1
-			path: src/Internal/Admin/Agentic/AgenticCommerceIntegration.php
-
-		-
-			message: '#^Method Automattic\\WooCommerce\\Internal\\Admin\\Agentic\\AgenticCommerceIntegration\:\:process_admin_options\(\) should return bool but return statement is missing\.$#'
-			identifier: return.missing
-			count: 1
-			path: src/Internal/Admin/Agentic/AgenticCommerceIntegration.php
-
 		-
 			message: '#^Method Automattic\\WooCommerce\\Internal\\Admin\\Agentic\\AgenticController\:\:on_init\(\) has no return type specified\.$#'
 			identifier: missingType.return
 			count: 1
 			path: src/Internal/Admin/Agentic/AgenticController.php

-		-
-			message: '#^Method Automattic\\WooCommerce\\Internal\\Admin\\Agentic\\AgenticSettingsPage\:\:save_settings\(\) has no return type specified\.$#'
-			identifier: missingType.return
-			count: 1
-			path: src/Internal/Admin/Agentic/AgenticSettingsPage.php
-
-		-
-			message: '#^Parameter \#1 \$url of function esc_url expects string, string\|false given\.$#'
-			identifier: argument.type
-			count: 2
-			path: src/Internal/Admin/Agentic/AgenticSettingsPage.php
-
 		-
 			message: '#^@param WC_Order \$order does not accept actual type of parameter\: WC_Order\|WC_Order_Refund\.$#'
 			identifier: parameter.phpDocType
@@ -78774,12 +78744,6 @@ parameters:
 			count: 1
 			path: src/StoreApi/Routes/V1/Agentic/CheckoutSessions.php

-		-
-			message: '#^Method Automattic\\WooCommerce\\StoreApi\\Routes\\V1\\Agentic\\CheckoutSessions\:\:is_authorized\(\) has parameter \$request with generic class WP_REST_Request but does not specify its types\: T$#'
-			identifier: missingType.generics
-			count: 1
-			path: src/StoreApi/Routes/V1/Agentic/CheckoutSessions.php
-
 		-
 			message: '#^Method Automattic\\WooCommerce\\StoreApi\\Routes\\V1\\Agentic\\CheckoutSessions\:\:requires_nonce\(\) has parameter \$request with generic class WP_REST_Request but does not specify its types\: T$#'
 			identifier: missingType.generics
@@ -80922,12 +80886,6 @@ parameters:
 			count: 1
 			path: src/StoreApi/Utilities/AgenticCheckoutUtils.php

-		-
-			message: '#^Method Automattic\\WooCommerce\\StoreApi\\Utilities\\AgenticCheckoutUtils\:\:is_authorized\(\) has parameter \$request with generic class WP_REST_Request but does not specify its types\: T$#'
-			identifier: missingType.generics
-			count: 1
-			path: src/StoreApi/Utilities/AgenticCheckoutUtils.php
-
 		-
 			message: '#^Method Automattic\\WooCommerce\\StoreApi\\Utilities\\AgenticCheckoutUtils\:\:set_billing_address\(\) has no return type specified\.$#'
 			identifier: missingType.return
diff --git a/plugins/woocommerce/src/Internal/Admin/Agentic/AgenticCommerceIntegration.php b/plugins/woocommerce/src/Internal/Admin/Agentic/AgenticCommerceIntegration.php
deleted file mode 100644
index f402b0e4ff..0000000000
--- a/plugins/woocommerce/src/Internal/Admin/Agentic/AgenticCommerceIntegration.php
+++ /dev/null
@@ -1,53 +0,0 @@
-<?php
-declare(strict_types=1);
-
-namespace Automattic\WooCommerce\Internal\Admin\Agentic;
-
-/**
- * Agentic Commerce Integration class
- *
- * Registers the Agentic Commerce Protocol as a WooCommerce integration.
- * Manages settings for various AI agent providers (OpenAI, Anthropic, etc.)
- *
- * @since 10.4.0
- */
-class AgenticCommerceIntegration extends \WC_Integration {
-
-	/**
-	 * Settings page instance.
-	 *
-	 * @var AgenticSettingsPage
-	 */
-	private $settings_page;
-
-	/**
-	 * Constructor.
-	 */
-	public function __construct() {
-		$this->id                 = 'agentic_commerce';
-		$this->method_title       = __( 'Agentic Commerce', 'woocommerce' );
-		$this->method_description = __( 'Configure settings to allow AI agents to purchase from your store.', 'woocommerce' );
-
-		// Initialize settings page helper.
-		$this->settings_page = new AgenticSettingsPage();
-
-		// Bind to the save action for the settings.
-		add_action( 'woocommerce_update_options_integration_' . $this->id, array( $this, 'process_admin_options' ) );
-	}
-
-	/**
-	 * Admin options output.
-	 */
-	public function admin_options() {
-		$settings = $this->settings_page->get_settings( array(), $this->id );
-		\WC_Admin_Settings::output_fields( $settings );
-	}
-
-	/**
-	 * Process and save options.
-	 */
-	public function process_admin_options() {
-		// Let AgenticSettingsPage handle saving.
-		$this->settings_page->save_settings();
-	}
-}
diff --git a/plugins/woocommerce/src/Internal/Admin/Agentic/AgenticController.php b/plugins/woocommerce/src/Internal/Admin/Agentic/AgenticController.php
index 7ca5edf54c..639a6c4719 100644
--- a/plugins/woocommerce/src/Internal/Admin/Agentic/AgenticController.php
+++ b/plugins/woocommerce/src/Internal/Admin/Agentic/AgenticController.php
@@ -46,22 +46,5 @@ class AgenticController implements RegisterHooksInterface {

 		// Resolve webhook manager from container.
 		wc_get_container()->get( AgenticWebhookManager::class )->register();
-
-		// Register Agentic Commerce integration.
-		add_filter( 'woocommerce_integrations', array( $this, 'add_agentic_commerce_integration' ) );
-	}
-
-	/**
-	 * Add Agentic Commerce integration to WooCommerce integrations.
-	 *
-	 * @param array $integrations Existing integrations.
-	 * @return array Modified integrations.
-	 */
-	public function add_agentic_commerce_integration( $integrations ): array {
-		if ( ! is_array( $integrations ) ) {
-			$integrations = array();
-		}
-		$integrations[] = AgenticCommerceIntegration::class;
-		return $integrations;
 	}
 }
diff --git a/plugins/woocommerce/src/Internal/Admin/Agentic/AgenticSettingsPage.php b/plugins/woocommerce/src/Internal/Admin/Agentic/AgenticSettingsPage.php
deleted file mode 100644
index c7a12908df..0000000000
--- a/plugins/woocommerce/src/Internal/Admin/Agentic/AgenticSettingsPage.php
+++ /dev/null
@@ -1,304 +0,0 @@
-<?php
-declare(strict_types=1);
-
-namespace Automattic\WooCommerce\Internal\Admin\Agentic;
-
-/**
- * AgenticSettingsPage class
- *
- * Adds Agentic Commerce settings to WooCommerce > Settings > Integration.
- * Uses a provider-based system to allow multiple AI agent integrations.
- *
- * @since 10.4.0
- */
-class AgenticSettingsPage {
-
-	/**
-	 * Registry option name.
-	 */
-	const REGISTRY_OPTION = 'woocommerce_agentic_agent_registry';
-
-	/**
-	 * Constructor.
-	 */
-	public function __construct() {
-		// No hooks needed - used by AgenticCommerceIntegration class.
-	}
-
-	/**
-	 * Get the agent registry with default values.
-	 *
-	 * @return array Agent registry.
-	 */
-	private function get_registry() {
-		return get_option( self::REGISTRY_OPTION, array() );
-	}
-
-	/**
-	 * Get registered providers.
-	 *
-	 * Each provider should return an array with:
-	 * - id: string (unique identifier, e.g., 'openai')
-	 * - name: string (display name, e.g., 'OpenAI')
-	 * - description: string (optional description)
-	 * - fields: array (settings fields configuration)
-	 *
-	 * @return array Array of registered providers.
-	 */
-	private function get_providers() {
-		$registry = $this->get_registry();
-
-		// Register built-in OpenAI provider.
-		$providers = array(
-			array(
-				'id'          => 'openai',
-				'name'        => __( 'ChatGPT', 'woocommerce' ),
-				'description' => sprintf(
-					/* translators: %s: URL to ChatGPT merchants application page */
-					__( 'To get started, <a href="%s" target="_blank">apply to ChatGPT</a>. Once approved, ChatGPT will provide the credentials below.', 'woocommerce' ),
-					'https://chatgpt.com/merchants'
-				),
-				'fields'      => $this->get_openai_fields(),
-			),
-		);
-
-		/**
-		 * Filter to register additional AI agent providers.
-		 *
-		 * Allows extensions to add their own AI agent provider settings.
-		 * Each provider should return an array with id, name, description, and fields.
-		 *
-		 * @since 10.4.0
-		 *
-		 * @internal This filter is experimental and behind a non-visible feature flag. Backwards compatibility not guaranted.
-		 *
-		 * @param array $providers Array of provider configurations.
-		 * @param array $registry  Current registry data.
-		 */
-		$providers = apply_filters( 'woocommerce_agentic_commerce_providers', $providers, $registry );
-
-		// Validate provider structure.
-		$validated = array();
-		foreach ( $providers as $provider ) {
-			if (
-				! is_array( $provider )
-				|| empty( $provider['id'] )
-				|| empty( $provider['name'] )
-				|| ! is_array( $provider['fields'] ?? null )
-			) {
-				continue;
-			}
-
-			// Sanitize text fields.
-			$provider['id']   = sanitize_key( $provider['id'] );
-			$provider['name'] = sanitize_text_field( $provider['name'] );
-			if ( ! empty( $provider['description'] ) ) {
-				$provider['description'] = wp_kses_post( $provider['description'] );
-			}
-
-			$validated[] = $provider;
-		}
-
-		return $validated;
-	}
-
-	/**
-	 * Get general Agentic Commerce settings.
-	 *
-	 * @param array $config Current general configuration.
-	 * @return array Settings fields.
-	 */
-	private function get_general_settings( $config ) {
-		return array(
-			array(
-				'title' => __( 'Agentic commerce', 'woocommerce' ),
-				'type'  => 'title',
-				'desc'  => '',
-				'id'    => 'agentic_commerce_general_settings',
-			),
-			array(
-				'title'   => __( 'Enable product visibility', 'woocommerce' ),
-				'desc'    => __( 'Allow products to be visible by default to the AI agents you integrate with. Can be overridden per product.', 'woocommerce' ),
-				'id'      => 'woocommerce_agentic_enable_products_default',
-				'type'    => 'checkbox',
-				'default' => ( ! empty( $config['enable_products_default'] ) && 'yes' === $config['enable_products_default'] ) ? 'yes' : 'no',
-			),
-			array(
-				'type' => 'sectionend',
-				'id'   => 'agentic_commerce_general_settings',
-			),
-		);
-	}
-
-	/**
-	 * Get store policies settings.
-	 *
-	 * @return array Settings fields.
-	 */
-	private function get_store_policies_settings() {
-		// Get URLs from WooCommerce/WordPress settings.
-		$terms_page_id   = wc_terms_and_conditions_page_id();
-		$privacy_page_id = get_option( 'wp_page_for_privacy_policy' );
-
-		$terms_url   = $terms_page_id ? get_permalink( $terms_page_id ) : '';
-		$privacy_url = $privacy_page_id ? get_permalink( $privacy_page_id ) : '';
-
-		// Build admin URLs for configuration links.
-		$advanced_settings_url = admin_url( 'admin.php?page=wc-settings&tab=advanced' );
-		$privacy_settings_url  = admin_url( 'options-privacy.php' );
-
-		return array(
-			array(
-				'title' => __( 'Store policies', 'woocommerce' ),
-				'type'  => 'title',
-				'desc'  => '',
-				'id'    => 'agentic_commerce_store_policies',
-			),
-			array(
-				'title'             => __( 'Privacy Policy URL', 'woocommerce' ),
-				'desc'              => sprintf(
-					/* translators: %s: URL to WordPress privacy settings */
-					__( 'Configure your Privacy Policy page in <a href="%s">Settings &gt; Privacy</a>.', 'woocommerce' ),
-					esc_url( $privacy_settings_url )
-				),
-				'id'                => 'woocommerce_agentic_privacy_url_display',
-				'type'              => 'text',
-				'default'           => esc_url( $privacy_url ),
-				'custom_attributes' => array(
-					'disabled' => 'disabled',
-					'readonly' => 'readonly',
-				),
-			),
-			array(
-				'title'             => __( 'Terms and Conditions URL', 'woocommerce' ),
-				'desc'              => sprintf(
-					/* translators: %s: URL to WooCommerce advanced settings */
-					__( 'Configure your Terms and Conditions page in <a href="%s">WooCommerce &gt; Settings &gt; Advanced &gt; Page setup</a>.', 'woocommerce' ),
-					esc_url( $advanced_settings_url )
-				),
-				'id'                => 'woocommerce_agentic_terms_url_display',
-				'type'              => 'text',
-				'default'           => esc_url( $terms_url ),
-				'custom_attributes' => array(
-					'disabled' => 'disabled',
-					'readonly' => 'readonly',
-				),
-			),
-			array(
-				'type' => 'sectionend',
-				'id'   => 'agentic_commerce_store_policies',
-			),
-		);
-	}
-
-	/**
-	 * Get OpenAI provider fields.
-	 *
-	 * @return array Fields configuration.
-	 */
-	private function get_openai_fields() {
-		return array(
-			array(
-				'title'   => __( 'Authorization Token', 'woocommerce' ),
-				'desc'    => __( 'The bearer token that ChatGPT uses to authenticate checkout requests.', 'woocommerce' ),
-				'id'      => 'woocommerce_agentic_openai_bearer_token',
-				'type'    => 'password',
-				'default' => '',
-			),
-		);
-	}
-
-	/**
-	 * Get settings for Agentic Commerce integration.
-	 *
-	 * @param array  $settings Current settings.
-	 * @param string $current_section Current section ID.
-	 * @return array Settings array.
-	 */
-	public function get_settings( $settings, $current_section ) {
-		if ( 'agentic_commerce' !== $current_section ) {
-			return $settings;
-		}
-
-		$agentic_settings = array();
-		$registry         = $this->get_registry();
-
-		// Add general Agentic Commerce settings section.
-		$agentic_settings = array_merge( $agentic_settings, $this->get_general_settings( $registry['general'] ?? array() ) );
-
-		// Build settings for each provider.
-		$providers = $this->get_providers();
-		foreach ( $providers as $provider ) {
-			// Provider section header.
-			$agentic_settings[] = array(
-				'title' => $provider['name'],
-				'type'  => 'title',
-				'desc'  => $provider['description'] ?? '',
-				'id'    => 'agentic_commerce_' . $provider['id'] . '_settings',
-			);
-
-			// Add provider fields.
-			foreach ( $provider['fields'] as $field ) {
-				$agentic_settings[] = $field;
-			}
-
-			// Provider section end.
-			$agentic_settings[] = array(
-				'type' => 'sectionend',
-				'id'   => 'agentic_commerce_' . $provider['id'] . '_settings',
-			);
-		}
-
-		// Add store policies section.
-		$agentic_settings = array_merge( $agentic_settings, $this->get_store_policies_settings() );
-
-		return $agentic_settings;
-	}
-
-	/**
-	 * Save settings to registry structure.
-	 */
-	public function save_settings() {
-		check_admin_referer( 'woocommerce-settings' );
-
-		$registry = $this->get_registry();
-
-		// Update general settings.
-		$registry['general'] = array(
-			'enable_products_default' => isset( $_POST['woocommerce_agentic_enable_products_default'] ) && '1' === $_POST['woocommerce_agentic_enable_products_default']
-				? 'yes'
-				: 'no',
-		);
-
-		// Update OpenAI settings.
-		$new_token = isset( $_POST['woocommerce_agentic_openai_bearer_token'] )
-			? sanitize_text_field( wp_unslash( $_POST['woocommerce_agentic_openai_bearer_token'] ) )
-			: '';
-
-		// Only update if a new token was provided; otherwise keep existing.
-		if ( ! empty( $new_token ) ) {
-			$registry['openai']['bearer_token'] = wp_hash_password( $new_token );
-		} elseif ( ! isset( $registry['openai']['bearer_token'] ) ) {
-			$registry['openai']['bearer_token'] = '';
-		}
-
-		/**
-		 * Filter registry before saving.
-		 *
-		 * Allows extensions to save their own agent provider settings.
-		 * Extensions can access $_POST directly for their settings but MUST sanitize all input
-		 * using appropriate WordPress sanitization functions (sanitize_text_field, esc_url_raw, etc.)
-		 * and call wp_unslash() on POST data.
-		 *
-		 * @since 10.4.0
-		 *
-		 * @internal This filter is experimental and behind a non-visible feature flag. Backwards compatibility not guaranted.
-		 *
-		 * @param array $registry Registry data to save. Extensions should add their provider settings to this array.
-		 */
-		$registry = apply_filters( 'woocommerce_agentic_commerce_save_settings', $registry );
-
-		// Save registry (don't autoload to prevent performance issues).
-		update_option( self::REGISTRY_OPTION, $registry, false );
-	}
-}
diff --git a/plugins/woocommerce/src/StoreApi/Routes/V1/Agentic/CheckoutSessions.php b/plugins/woocommerce/src/StoreApi/Routes/V1/Agentic/CheckoutSessions.php
index e62a270251..c640c1729c 100644
--- a/plugins/woocommerce/src/StoreApi/Routes/V1/Agentic/CheckoutSessions.php
+++ b/plugins/woocommerce/src/StoreApi/Routes/V1/Agentic/CheckoutSessions.php
@@ -107,23 +107,22 @@ class CheckoutSessions extends AbstractCartRoute {
 	/**
 	 * Check if the request is authorized.
 	 *
-	 * Delegates to the AgenticCheckoutUtils helper.
+	 * Validates that the request is signed with Jetpack blog token.
 	 *
-	 * @param \WP_REST_Request $request Request object.
 	 * @return bool|\WP_Error True if authorized, WP_Error otherwise.
 	 */
-	public function is_authorized( \WP_REST_Request $request ) {
-		return AgenticCheckoutUtils::is_authorized( $request );
+	public function is_authorized() {
+		return AgenticCheckoutUtils::validate_jetpack_request();
 	}

 	/**
 	 * Check if a nonce is required for the route.
 	 *
 	 * @param \WP_REST_Request $request Request object.
-	 * @return bool False, Bearer token auth used instead.
+	 * @return bool False, Jetpack blog token auth used instead.
 	 */
 	protected function requires_nonce( \WP_REST_Request $request ) {
-		// Should use `is_authorized` to validate Bearer token authentication.
+		// Uses Jetpack blog token authentication via is_authorized().
 		return false;
 	}

diff --git a/plugins/woocommerce/src/StoreApi/Routes/V1/Agentic/CheckoutSessionsComplete.php b/plugins/woocommerce/src/StoreApi/Routes/V1/Agentic/CheckoutSessionsComplete.php
index ac8030d8c8..9a66251da8 100644
--- a/plugins/woocommerce/src/StoreApi/Routes/V1/Agentic/CheckoutSessionsComplete.php
+++ b/plugins/woocommerce/src/StoreApi/Routes/V1/Agentic/CheckoutSessionsComplete.php
@@ -149,14 +149,14 @@ class CheckoutSessionsComplete extends AbstractCartRoute {
 	/**
 	 * Check if the request is authorized.
 	 *
-	 * Checks feature enablement and cart token validity.
+	 * Validates Jetpack blog token and cart token validity.
 	 *
 	 * @param \WP_REST_Request $request Request object.
 	 * @return bool|\WP_Error True if authorized, WP_Error otherwise.
 	 */
 	public function is_authorized( \WP_REST_Request $request ) {
-		// Check if feature is enabled using helper.
-		$auth_check = AgenticCheckoutUtils::is_authorized( $request );
+		// Check Jetpack blog token authentication.
+		$auth_check = AgenticCheckoutUtils::validate_jetpack_request();
 		if ( is_wp_error( $auth_check ) ) {
 			return $auth_check;
 		}
@@ -198,10 +198,10 @@ class CheckoutSessionsComplete extends AbstractCartRoute {
 	 * Check if a nonce is required for the route.
 	 *
 	 * @param \WP_REST_Request $request Request object.
-	 * @return bool False, Bearer token auth used instead.
+	 * @return bool False, Jetpack blog token auth used instead.
 	 */
 	protected function requires_nonce( \WP_REST_Request $request ) {
-		// Should use `is_authorized` to validate Bearer token authentication.
+		// Uses Jetpack blog token authentication via is_authorized().
 		return false;
 	}

diff --git a/plugins/woocommerce/src/StoreApi/Routes/V1/Agentic/CheckoutSessionsUpdate.php b/plugins/woocommerce/src/StoreApi/Routes/V1/Agentic/CheckoutSessionsUpdate.php
index b4d926ea90..bfb83385f7 100644
--- a/plugins/woocommerce/src/StoreApi/Routes/V1/Agentic/CheckoutSessionsUpdate.php
+++ b/plugins/woocommerce/src/StoreApi/Routes/V1/Agentic/CheckoutSessionsUpdate.php
@@ -116,14 +116,14 @@ class CheckoutSessionsUpdate extends AbstractCartRoute {
 	/**
 	 * Check if the request is authorized.
 	 *
-	 * Checks feature enablement and cart token validity.
+	 * Validates Jetpack blog token and cart token validity.
 	 *
 	 * @param \WP_REST_Request $request Request object.
 	 * @return bool|\WP_Error True if authorized, WP_Error otherwise.
 	 */
 	public function is_authorized( \WP_REST_Request $request ) {
-		// Check if feature is enabled using helper.
-		$auth_check = AgenticCheckoutUtils::is_authorized( $request );
+		// Check Jetpack blog token authentication.
+		$auth_check = AgenticCheckoutUtils::validate_jetpack_request();
 		if ( is_wp_error( $auth_check ) ) {
 			return $auth_check;
 		}
diff --git a/plugins/woocommerce/src/StoreApi/Utilities/AgenticCheckoutUtils.php b/plugins/woocommerce/src/StoreApi/Utilities/AgenticCheckoutUtils.php
index 45fe4b77aa..496db75af8 100644
--- a/plugins/woocommerce/src/StoreApi/Utilities/AgenticCheckoutUtils.php
+++ b/plugins/woocommerce/src/StoreApi/Utilities/AgenticCheckoutUtils.php
@@ -332,82 +332,23 @@ class AgenticCheckoutUtils {
 	}

 	/**
-	 * Check if the Agentic Checkout feature is enabled and request is authorized.
+	 * Validate that the request is signed with Jetpack blog token.
 	 *
-	 * Validates bearer token against registered agents in the agent registry.
+	 * @since 10.6.0
 	 *
-	 * @param \WP_REST_Request $request Request object.
-	 * @return bool|\WP_Error True if authorized, WP_Error otherwise.
+	 * @return true|\WP_Error True if valid, WP_Error otherwise.
 	 */
-	public static function is_authorized( $request = null ) {
-		if ( null === $request ) {
-			return new \WP_Error(
-				'invalid_request',
-				__( 'Invalid request object.', 'woocommerce' ),
-				array(
-					'status' => 400,
-					'type'   => 'invalid_request',
-					'code'   => 'invalid_request',
-				)
-			);
-		}
-
-		$auth_header = $request->get_header( 'Authorization' );
-		if ( empty( $auth_header ) || 0 !== stripos( $auth_header, 'Bearer ' ) ) {
-			return new \WP_Error(
-				'invalid_request',
-				__( 'Invalid authorization.', 'woocommerce' ),
-				array(
-					'status' => 400,
-					'type'   => 'invalid_request',
-					'code'   => 'invalid_authorization_format',
-				)
-			);
-		}
-
-		$provided_token = trim( substr( $auth_header, 7 ) ); // "Bearer " is 7 characters
-		if ( empty( $provided_token ) ) {
-			return new \WP_Error(
-				'invalid_request',
-				__( 'Invalid authorization.', 'woocommerce' ),
-				array(
-					'status' => 400,
-					'type'   => 'invalid_request',
-					'code'   => 'invalid_authorization_format',
-				)
-			);
-		}
-
-		$registry               = get_option( \Automattic\WooCommerce\Internal\Admin\Agentic\AgenticSettingsPage::REGISTRY_OPTION, array() );
-		$authenticated_provider = null;
-
-		// Check each provider's bearer token.
-		foreach ( $registry as $provider_id => $provider_config ) {
-			if ( ! is_array( $provider_config ) || empty( $provider_config['bearer_token'] ) ) {
-				continue;
-			}
-
-			if ( wp_check_password( $provided_token, $provider_config['bearer_token'] ) ) {
-				// Store and continue checking to minimize timing attack.
-				$authenticated_provider = $provider_id;
-			}
-		}
-
-		if ( null !== $authenticated_provider ) {
-			if ( WC()->session ) {
-				WC()->session->set( SessionKey::AGENTIC_CHECKOUT_PROVIDER_ID, $authenticated_provider );
+	public static function validate_jetpack_request() {
+		if ( class_exists( 'Automattic\Jetpack\Connection\Rest_Authentication' ) ) {
+			if ( \Automattic\Jetpack\Connection\Rest_Authentication::is_signed_with_blog_token() ) {
+				return true;
 			}
-			return true;
 		}

 		return new \WP_Error(
-			'invalid_request',
-			__( 'Invalid authorization.', 'woocommerce' ),
-			array(
-				'status' => 400,
-				'type'   => 'invalid_request',
-				'code'   => 'authentication_failed',
-			)
+			'rest_forbidden',
+			__( 'This endpoint requires Jetpack blog token authentication.', 'woocommerce' ),
+			array( 'status' => 401 )
 		);
 	}

diff --git a/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Routes/CheckoutSessions.php b/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Routes/CheckoutSessions.php
index b264b58d28..34afa0c1d8 100644
--- a/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Routes/CheckoutSessions.php
+++ b/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Routes/CheckoutSessions.php
@@ -14,11 +14,14 @@ use Automattic\WooCommerce\Tests\Blocks\Helpers\FixtureData;
 use Automattic\WooCommerce\Enums\ProductStockStatus;
 use Automattic\WooCommerce\Internal\Agentic\Enums\Specs\ErrorCode;
 use Automattic\WooCommerce\StoreApi\RoutesController;
+use Automattic\WooCommerce\Tests\Internal\Admin\Agentic\AgenticTestHelpers;

 /**
  * CheckoutSessions Controller Tests.
  */
 class CheckoutSessions extends ControllerTestCase {
+	use AgenticTestHelpers;
+
 	/**
 	 * Products created for tests.
 	 *
@@ -26,13 +29,6 @@ class CheckoutSessions extends ControllerTestCase {
 	 */
 	protected $products = array();

-	/**
-	 * Test bearer token for authorization.
-	 *
-	 * @var string
-	 */
-	protected $test_bearer_token;
-
 	/**
 	 * Setup test product data. Called before every test.
 	 */
@@ -51,17 +47,8 @@ class CheckoutSessions extends ControllerTestCase {
 		// Enable the agentic_checkout feature.
 		update_option( 'woocommerce_feature_agentic_checkout_enabled', 'yes' );

-		// Set up registry with test bearer token for authorization.
-		$this->test_bearer_token = 'test_token_' . uniqid();
-		update_option(
-			'woocommerce_agentic_agent_registry',
-			array(
-				'openai' => array(
-					'bearer_token' => wp_hash_password( $this->test_bearer_token ),
-				),
-			),
-			false
-		);
+		// Set up Jetpack blog token authentication.
+		$this->mock_jetpack_blog_token_auth();

 		$fixtures = new FixtureData();
 		$fixtures->shipping_add_flat_rate();
@@ -102,13 +89,15 @@ class CheckoutSessions extends ControllerTestCase {
 	protected function tearDown(): void {
 		parent::tearDown();
 		delete_option( 'woocommerce_feature_agentic_checkout_enabled' );
-		delete_option( 'woocommerce_agentic_agent_registry' );

 		// Clear session data.
 		WC()->session->set( SessionKey::CHOSEN_SHIPPING_METHODS, null );

 		// Reset customer state to clean state.
 		$this->reset_customer_state();
+
+		// Reset Jetpack auth state.
+		$this->reset_jetpack_auth_state();
 	}

 	/**
@@ -206,7 +195,6 @@ class CheckoutSessions extends ControllerTestCase {
 	 */
 	private function create_session( $body_params ) {
 		$request = new \WP_REST_Request( 'POST', '/wc/agentic/v1/checkout_sessions' );
-		$request->set_header( 'Authorization', 'Bearer ' . $this->test_bearer_token );
 		$request->set_body_params( $body_params );
 		return rest_get_server()->dispatch( $request );
 	}
@@ -588,7 +576,6 @@ class CheckoutSessions extends ControllerTestCase {
 	 */
 	private function update_session( $session_id, $body_params ) {
 		$request = new \WP_REST_Request( 'POST', '/wc/agentic/v1/checkout_sessions/' . $session_id );
-		$request->set_header( 'Authorization', 'Bearer ' . $this->test_bearer_token );
 		$request->set_body_params( $body_params );
 		return rest_get_server()->dispatch( $request );
 	}
@@ -987,7 +974,6 @@ class CheckoutSessions extends ControllerTestCase {
 		$idempotency_key = 'test-idempotency-123';
 		$request         = new \WP_REST_Request( 'POST', '/wc/agentic/v1/checkout_sessions' );
 		$request->set_body_params( $this->create_checkout_request() );
-		$request->set_header( 'Authorization', 'Bearer ' . $this->test_bearer_token );
 		$request->set_header( 'Idempotency-Key', $idempotency_key );

 		$response = rest_get_server()->dispatch( $request );
@@ -1004,7 +990,6 @@ class CheckoutSessions extends ControllerTestCase {
 		$request_id = 'req_' . uniqid();
 		$request    = new \WP_REST_Request( 'POST', '/wc/agentic/v1/checkout_sessions' );
 		$request->set_body_params( $this->create_checkout_request() );
-		$request->set_header( 'Authorization', 'Bearer ' . $this->test_bearer_token );
 		$request->set_header( 'Request-Id', $request_id );

 		$response = rest_get_server()->dispatch( $request );
@@ -1352,4 +1337,75 @@ class CheckoutSessions extends ControllerTestCase {
 		$this->assertArrayHasKey( 'message', $data );
 		$this->assertStringContainsString( 'cannot be updated', $data['message'] );
 	}
+
+	/**
+	 * Test that creating a checkout session without Jetpack authentication fails.
+	 */
+	public function test_create_session_without_jetpack_auth_fails() {
+		// Clear Jetpack authentication to simulate unauthenticated request.
+		$this->mock_jetpack_auth_failure();
+
+		$request = new \WP_REST_Request( 'POST', '/wc/agentic/v1/checkout_sessions' );
+		$request->set_body_params( $this->create_checkout_request() );
+		$response = rest_get_server()->dispatch( $request );
+
+		// Should return 401 error.
+		$this->assertEquals( 401, $response->get_status() );
+
+		$data = $response->get_data();
+		$this->assertArrayHasKey( 'code', $data );
+		$this->assertEquals( 'rest_forbidden', $data['code'] );
+		$this->assertStringContainsString( 'Jetpack blog token', $data['message'] );
+	}
+
+	/**
+	 * Test that updating a checkout session without Jetpack authentication fails.
+	 */
+	public function test_update_session_without_jetpack_auth_fails() {
+		// Create a session first with valid auth.
+		$create_response = $this->create_session( $this->create_checkout_request() );
+		$create_data     = $create_response->get_data();
+		$session_id      = $create_data['id'];
+
+		// Clear Jetpack authentication to simulate unauthenticated request.
+		$this->mock_jetpack_auth_failure();
+
+		$request = new \WP_REST_Request( 'POST', '/wc/agentic/v1/checkout_sessions/' . $session_id );
+		$request->set_body_params(
+			array(
+				'buyer' => array(
+					'first_name' => 'Test',
+				),
+			)
+		);
+		$response = rest_get_server()->dispatch( $request );
+
+		// Should return 401 error.
+		$this->assertEquals( 401, $response->get_status() );
+
+		$data = $response->get_data();
+		$this->assertArrayHasKey( 'code', $data );
+		$this->assertEquals( 'rest_forbidden', $data['code'] );
+	}
+
+	/**
+	 * Test that Jetpack authentication works when re-established after failure.
+	 */
+	public function test_jetpack_auth_recovery() {
+		// First request without auth should fail.
+		$this->mock_jetpack_auth_failure();
+
+		$request = new \WP_REST_Request( 'POST', '/wc/agentic/v1/checkout_sessions' );
+		$request->set_body_params( $this->create_checkout_request() );
+		$response = rest_get_server()->dispatch( $request );
+
+		$this->assertEquals( 401, $response->get_status() );
+
+		// Re-establish Jetpack authentication.
+		$this->mock_jetpack_blog_token_auth();
+
+		// Second request with auth should succeed.
+		$response = $this->create_session( $this->create_checkout_request() );
+		$this->assertEquals( 200, $response->get_status() );
+	}
 }
diff --git a/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Routes/CheckoutSessionsComplete.php b/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Routes/CheckoutSessionsComplete.php
index 094900d244..dea405c4b8 100644
--- a/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Routes/CheckoutSessionsComplete.php
+++ b/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Routes/CheckoutSessionsComplete.php
@@ -13,11 +13,14 @@ use Automattic\WooCommerce\StoreApi\Routes\V1\Agentic\Enums\SessionKey;
 use Automattic\WooCommerce\Tests\Blocks\Helpers\FixtureData;
 use Automattic\WooCommerce\Enums\ProductStockStatus;
 use Automattic\WooCommerce\StoreApi\RoutesController;
+use Automattic\WooCommerce\Tests\Internal\Admin\Agentic\AgenticTestHelpers;

 /**
  * CheckoutSessionsComplete Controller Tests.
  */
 class CheckoutSessionsComplete extends ControllerTestCase {
+	use AgenticTestHelpers;
+
 	/**
 	 * Products created for tests.
 	 *
@@ -32,13 +35,6 @@ class CheckoutSessionsComplete extends ControllerTestCase {
 	 */
 	protected $mock_gateway;

-	/**
-	 * Test bearer token for authorization.
-	 *
-	 * @var string
-	 */
-	protected $test_bearer_token;
-
 	/**
 	 * Setup test product data. Called before every test.
 	 */
@@ -57,17 +53,8 @@ class CheckoutSessionsComplete extends ControllerTestCase {
 		// Enable the agentic_checkout feature.
 		update_option( 'woocommerce_feature_agentic_checkout_enabled', 'yes' );

-		// Set up registry with test bearer token for authorization.
-		$this->test_bearer_token = 'test_token_' . uniqid();
-		update_option(
-			'woocommerce_agentic_agent_registry',
-			array(
-				'openai' => array(
-					'bearer_token' => wp_hash_password( $this->test_bearer_token ),
-				),
-			),
-			false
-		);
+		// Set up Jetpack blog token authentication.
+		$this->mock_jetpack_blog_token_auth();

 		$fixtures = new FixtureData();
 		$fixtures->shipping_add_flat_rate();
@@ -105,7 +92,6 @@ class CheckoutSessionsComplete extends ControllerTestCase {
 	protected function tearDown(): void {
 		parent::tearDown();
 		delete_option( 'woocommerce_feature_agentic_checkout_enabled' );
-		delete_option( 'woocommerce_agentic_agent_registry' );

 		// Clear session data.
 		WC()->session->set( SessionKey::CHOSEN_SHIPPING_METHODS, null );
@@ -113,6 +99,9 @@ class CheckoutSessionsComplete extends ControllerTestCase {

 		// Reset customer state to clean state.
 		$this->reset_customer_state();
+
+		// Reset Jetpack auth state.
+		$this->reset_jetpack_auth_state();
 	}

 	/**
@@ -221,7 +210,6 @@ class CheckoutSessionsComplete extends ControllerTestCase {
 	 */
 	private function create_session( $body_params ) {
 		$request = new \WP_REST_Request( 'POST', '/wc/agentic/v1/checkout_sessions' );
-		$request->set_header( 'Authorization', 'Bearer ' . $this->test_bearer_token );
 		$request->set_body_params( $body_params );
 		return rest_get_server()->dispatch( $request );
 	}
@@ -235,7 +223,6 @@ class CheckoutSessionsComplete extends ControllerTestCase {
 	 */
 	private function update_session( $session_id, $body_params ) {
 		$request = new \WP_REST_Request( 'POST', '/wc/agentic/v1/checkout_sessions/' . $session_id );
-		$request->set_header( 'Authorization', 'Bearer ' . $this->test_bearer_token );
 		$request->set_body_params( $body_params );
 		return rest_get_server()->dispatch( $request );
 	}
@@ -249,7 +236,6 @@ class CheckoutSessionsComplete extends ControllerTestCase {
 	 */
 	private function complete_session( $session_id, $body_params ) {
 		$request = new \WP_REST_Request( 'POST', '/wc/agentic/v1/checkout_sessions/' . $session_id . '/complete' );
-		$request->set_header( 'Authorization', 'Bearer ' . $this->test_bearer_token );
 		$request->set_body_params( $body_params );
 		return rest_get_server()->dispatch( $request );
 	}
@@ -758,6 +744,105 @@ class CheckoutSessionsComplete extends ControllerTestCase {
 		$total = reset( $total_obj );
 		$this->assertGreaterThan( 0, $total['amount'] );
 	}
+
+	/**
+	 * Test that completing a checkout session without Jetpack authentication fails.
+	 */
+	public function test_complete_session_without_jetpack_auth_fails() {
+		// Create a ready-for-payment session first with valid auth.
+		$create_response = $this->create_session(
+			$this->create_checkout_request(
+				array(
+					'fulfillment_address' => $this->get_test_address(),
+					'buyer'               => $this->get_test_buyer(),
+				)
+			)
+		);
+
+		$create_data        = $create_response->get_data();
+		$session_id         = $create_data['id'];
+		$shipping_method_id = $create_data['fulfillment_options'][0]['id'];
+
+		$this->update_session(
+			$session_id,
+			array(
+				'fulfillment_option_id' => $shipping_method_id,
+			)
+		);
+
+		// Clear Jetpack authentication to simulate unauthenticated request.
+		$this->mock_jetpack_auth_failure();
+
+		$request = new \WP_REST_Request( 'POST', '/wc/agentic/v1/checkout_sessions/' . $session_id . '/complete' );
+		$request->set_body_params(
+			array(
+				'payment_data' => $this->get_payment_data(),
+			)
+		);
+		$response = rest_get_server()->dispatch( $request );
+
+		// Should return 401 error.
+		$this->assertEquals( 401, $response->get_status() );
+
+		$data = $response->get_data();
+		$this->assertArrayHasKey( 'code', $data );
+		$this->assertEquals( 'rest_forbidden', $data['code'] );
+		$this->assertStringContainsString( 'Jetpack blog token', $data['message'] );
+	}
+
+	/**
+	 * Test that checkout completion works after Jetpack auth is re-established.
+	 */
+	public function test_complete_session_jetpack_auth_recovery() {
+		// Create a ready-for-payment session first with valid auth.
+		$create_response = $this->create_session(
+			$this->create_checkout_request(
+				array(
+					'fulfillment_address' => $this->get_test_address(),
+					'buyer'               => $this->get_test_buyer(),
+				)
+			)
+		);
+
+		$create_data        = $create_response->get_data();
+		$session_id         = $create_data['id'];
+		$shipping_method_id = $create_data['fulfillment_options'][0]['id'];
+
+		$this->update_session(
+			$session_id,
+			array(
+				'fulfillment_option_id' => $shipping_method_id,
+			)
+		);
+
+		// Clear Jetpack authentication - complete should fail.
+		$this->mock_jetpack_auth_failure();
+
+		$request = new \WP_REST_Request( 'POST', '/wc/agentic/v1/checkout_sessions/' . $session_id . '/complete' );
+		$request->set_body_params(
+			array(
+				'payment_data' => $this->get_payment_data(),
+			)
+		);
+		$response = rest_get_server()->dispatch( $request );
+
+		$this->assertEquals( 401, $response->get_status() );
+
+		// Re-establish Jetpack authentication.
+		$this->mock_jetpack_blog_token_auth();
+
+		// Complete should now succeed.
+		$complete_response = $this->complete_session(
+			$session_id,
+			array(
+				'payment_data' => $this->get_payment_data(),
+			)
+		);
+
+		$this->assertEquals( 200, $complete_response->get_status() );
+		$complete_data = $complete_response->get_data();
+		$this->assertEquals( 'completed', $complete_data['status'] );
+	}
 }

 // phpcs:disable Generic.Files.OneObjectStructurePerFile.MultipleFound, Squiz.Classes.ClassFileName.NoMatch, Suin.Classes.PSR4.IncorrectClassName
diff --git a/plugins/woocommerce/tests/php/src/Internal/Admin/Agentic/AgenticTestHelpers.php b/plugins/woocommerce/tests/php/src/Internal/Admin/Agentic/AgenticTestHelpers.php
index 07da071553..b6d9b7b304 100644
--- a/plugins/woocommerce/tests/php/src/Internal/Admin/Agentic/AgenticTestHelpers.php
+++ b/plugins/woocommerce/tests/php/src/Internal/Admin/Agentic/AgenticTestHelpers.php
@@ -3,6 +3,8 @@ declare(strict_types=1);

 namespace Automattic\WooCommerce\Tests\Internal\Admin\Agentic;

+use Automattic\Jetpack\Connection\Manager as JetpackConnectionManager;
+use Automattic\Jetpack\Connection\Rest_Authentication as JetpackRestAuthentication;
 use Automattic\WooCommerce\Internal\Admin\Agentic\AgenticWebhookManager;
 use Automattic\WooCommerce\StoreApi\Routes\V1\Agentic\Enums\OrderMetaKey;
 use WC_Order;
@@ -12,6 +14,118 @@ use WC_Webhook;
  * Shared test helpers for Agentic tests.
  */
 trait AgenticTestHelpers {
+
+	/**
+	 * Mock for Jetpack Connection Manager.
+	 *
+	 * @var JetpackConnectionManager|\PHPUnit\Framework\MockObject\MockObject
+	 */
+	protected $jetpack_manager_mock;
+
+	/**
+	 * Clear the Jetpack REST_Authentication singleton instance.
+	 *
+	 * This allows tests to reset the authentication state between test cases.
+	 */
+	protected function clear_jetpack_auth_singleton(): void {
+		if ( ! class_exists( JetpackRestAuthentication::class ) ) {
+			return;
+		}
+
+		$reflection_class  = new \ReflectionClass( JetpackRestAuthentication::class );
+		$instance_property = $reflection_class->getProperty( 'instance' );
+		$instance_property->setAccessible( true );
+		$instance_property->setValue( null, null );
+	}
+
+	/**
+	 * Set up Jetpack authentication to simulate a valid blog token request.
+	 *
+	 * This mocks the Jetpack Connection Manager's verify_xml_rpc_signature method
+	 * to return a successful blog token verification result.
+	 */
+	protected function mock_jetpack_blog_token_auth(): void {
+		if ( ! class_exists( JetpackRestAuthentication::class ) ) {
+			$this->markTestSkipped( 'Jetpack Connection package not available.' );
+			return;
+		}
+
+		// Clear any existing singleton.
+		$this->clear_jetpack_auth_singleton();
+
+		// Initialize the REST Authentication singleton.
+		$rest_auth = JetpackRestAuthentication::init();
+
+		// Create a mock for the Connection Manager.
+		$this->jetpack_manager_mock = $this->getMockBuilder( JetpackConnectionManager::class )
+			->disableOriginalConstructor()
+			->onlyMethods( array( 'verify_xml_rpc_signature', 'reset_saved_auth_state' ) )
+			->getMock();
+
+		// Configure the mock to return successful blog token verification.
+		$this->jetpack_manager_mock->expects( $this->any() )
+			->method( 'verify_xml_rpc_signature' )
+			->willReturn(
+				array(
+					'type'      => 'blog',
+					'token_key' => 'test_blog_token',
+					'user_id'   => 0,
+				)
+			);
+
+		$this->jetpack_manager_mock->expects( $this->any() )
+			->method( 'reset_saved_auth_state' )
+			->willReturn( null );
+
+		// Inject the mock into the REST Authentication instance using reflection.
+		$reflection_class = new \ReflectionClass( JetpackRestAuthentication::class );
+		$manager_property = $reflection_class->getProperty( 'connection_manager' );
+		$manager_property->setAccessible( true );
+		$manager_property->setValue( $rest_auth, $this->jetpack_manager_mock );
+
+		// Set up the $_GET parameters and $_SERVER to simulate a Jetpack-signed request.
+		$_GET['_for']              = 'jetpack';
+		$_GET['token']             = 'test_token';
+		$_GET['signature']         = 'test_signature';
+		$_SERVER['REQUEST_METHOD'] = 'POST';
+
+		// Trigger the authentication.
+		$rest_auth->wp_rest_authenticate( '' );
+	}
+
+	/**
+	 * Set up Jetpack authentication to simulate a failed/missing authentication.
+	 *
+	 * This ensures that is_signed_with_blog_token() returns false.
+	 */
+	protected function mock_jetpack_auth_failure(): void {
+		if ( ! class_exists( JetpackRestAuthentication::class ) ) {
+			return;
+		}
+
+		// Clear any existing singleton - this ensures no authentication is set.
+		$this->clear_jetpack_auth_singleton();
+
+		// Clear Jetpack-related $_GET parameters.
+		unset( $_GET['_for'], $_GET['token'], $_GET['signature'] );
+	}
+
+	/**
+	 * Reset Jetpack authentication state after tests.
+	 *
+	 * Call this in tearDown() to ensure clean state between tests.
+	 */
+	protected function reset_jetpack_auth_state(): void {
+		if ( class_exists( JetpackRestAuthentication::class ) ) {
+			$this->clear_jetpack_auth_singleton();
+		}
+
+		// Clear Jetpack-related $_GET parameters.
+		unset( $_GET['_for'], $_GET['token'], $_GET['signature'] );
+
+		// Reset request method if it was set.
+		unset( $_SERVER['REQUEST_METHOD'] );
+	}
 	/**
 	 * Create an order with Agentic session ID.
 	 *
diff --git a/plugins/woocommerce/tests/php/src/Internal/Admin/Agentic/AgenticWebhookPayloadBuilderTest.php b/plugins/woocommerce/tests/php/src/Internal/Admin/Agentic/AgenticWebhookPayloadBuilderTest.php
index 5de99b8969..037b9482b3 100644
--- a/plugins/woocommerce/tests/php/src/Internal/Admin/Agentic/AgenticWebhookPayloadBuilderTest.php
+++ b/plugins/woocommerce/tests/php/src/Internal/Admin/Agentic/AgenticWebhookPayloadBuilderTest.php
@@ -124,11 +124,13 @@ class AgenticWebhookPayloadBuilderTest extends \WC_Unit_Test_Case {

 		// Both refunds should default to original_payment.
 		// Amounts should be in cents (integer).
-		$this->assertEquals( 'original_payment', $payload['data']['refunds'][0]['type'] );
-		$this->assertEquals( 1000, $payload['data']['refunds'][0]['amount'] );
+		// Use order-independent assertions since refund order is not guaranteed.
+		$amounts = array_column( $payload['data']['refunds'], 'amount' );
+		$types   = array_column( $payload['data']['refunds'], 'type' );

-		$this->assertEquals( 'original_payment', $payload['data']['refunds'][1]['type'] );
-		$this->assertEquals( 500, $payload['data']['refunds'][1]['amount'] );
+		$this->assertContains( 1000, $amounts );
+		$this->assertContains( 500, $amounts );
+		$this->assertEquals( array( 'original_payment', 'original_payment' ), $types );
 	}

 	/**
diff --git a/plugins/woocommerce/tests/php/src/StoreApi/Utilities/AgenticCheckoutUtilsTest.php b/plugins/woocommerce/tests/php/src/StoreApi/Utilities/AgenticCheckoutUtilsTest.php
index 99ad1ccedf..e71f60549c 100644
--- a/plugins/woocommerce/tests/php/src/StoreApi/Utilities/AgenticCheckoutUtilsTest.php
+++ b/plugins/woocommerce/tests/php/src/StoreApi/Utilities/AgenticCheckoutUtilsTest.php
@@ -7,11 +7,14 @@ use Automattic\WooCommerce\StoreApi\Routes\V1\Agentic\AgenticCheckoutSession;
 use Automattic\WooCommerce\StoreApi\Utilities\AgenticCheckoutUtils;
 use Automattic\WooCommerce\StoreApi\Routes\V1\Agentic\Enums\SessionKey;
 use Automattic\WooCommerce\Internal\Agentic\Enums\Specs\CheckoutSessionStatus;
+use Automattic\WooCommerce\Tests\Internal\Admin\Agentic\AgenticTestHelpers;

 /**
  * Tests for AgenticCheckoutUtils class.
  */
 class AgenticCheckoutUtilsTest extends \WC_Unit_Test_Case {
+	use AgenticTestHelpers;
+
 	/**
 	 * Setup cart and session data.
 	 */
@@ -35,6 +38,9 @@ class AgenticCheckoutUtilsTest extends \WC_Unit_Test_Case {

 		// Clear session data.
 		WC()->session->set( SessionKey::AGENTIC_CHECKOUT_PAYMENT_IN_PROGRESS, null );
+
+		// Reset Jetpack auth state.
+		$this->reset_jetpack_auth_state();
 	}

 	/**
@@ -142,221 +148,51 @@ class AgenticCheckoutUtilsTest extends \WC_Unit_Test_Case {
 	}

 	/**
-	 * Test authorization with valid OpenAI token.
+	 * Test validate_jetpack_request returns error when Jetpack authentication is not present.
 	 */
-	public function test_is_authorized_with_valid_token() {
-		// Enable the feature.
-		update_option( 'woocommerce_feature_agentic_checkout_enabled', 'yes' );
-
-		// Set up registry with OpenAI token (hashed).
-		$test_token = 'test_bearer_token_12345';
-		update_option(
-			'woocommerce_agentic_agent_registry',
-			array(
-				'openai' => array(
-					'bearer_token' => wp_hash_password( $test_token ),
-				),
-			),
-			false
-		);
-
-		// Create mock request with Authorization header (plaintext).
-		$request = new \WP_REST_Request();
-		$request->set_header( 'Authorization', 'Bearer ' . $test_token );
-
-		// Test authorization.
-		$result = AgenticCheckoutUtils::is_authorized( $request );
-
-		// Assert authorization succeeds.
-		$this->assertTrue( $result );
+	public function test_validate_jetpack_request_returns_error_without_authentication() {
+		// Ensure no Jetpack authentication is set.
+		$this->mock_jetpack_auth_failure();

-		// Assert provider ID is stored in session.
-		$this->assertEquals( 'openai', WC()->session->get( SessionKey::AGENTIC_CHECKOUT_PROVIDER_ID ) );
-	}
+		// Test validation.
+		$result = AgenticCheckoutUtils::validate_jetpack_request();

-	/**
-	 * Test authorization with invalid token.
-	 */
-	public function test_is_authorized_with_invalid_token() {
-		// Enable the feature.
-		update_option( 'woocommerce_feature_agentic_checkout_enabled', 'yes' );
-
-		// Set up registry with OpenAI token (hashed).
-		update_option(
-			'woocommerce_agentic_agent_registry',
-			array(
-				'openai' => array(
-					'bearer_token' => wp_hash_password( 'correct_token' ),
-				),
-			),
-			false
-		);
-
-		// Create mock request with wrong token.
-		$request = new \WP_REST_Request();
-		$request->set_header( 'Authorization', 'Bearer wrong_token' );
-
-		// Test authorization.
-		$result = AgenticCheckoutUtils::is_authorized( $request );
-
-		// Assert authorization fails with ACP error format.
+		// Assert authorization fails with 401 error.
 		$this->assertWPError( $result );
-		$this->assertEquals( 'invalid_request', $result->get_error_code() );
-		$this->assertEquals( 400, $result->get_error_data()['status'] );
-		$this->assertEquals( 'invalid_request', $result->get_error_data()['type'] );
-		$this->assertEquals( 'authentication_failed', $result->get_error_data()['code'] );
+		$this->assertEquals( 'rest_forbidden', $result->get_error_code() );
+		$this->assertEquals( 401, $result->get_error_data()['status'] );
+		$this->assertStringContainsString( 'Jetpack blog token', $result->get_error_message() );
 	}

 	/**
-	 * Test authorization with missing Authorization header.
+	 * Test validate_jetpack_request returns true with valid blog token authentication.
 	 */
-	public function test_is_authorized_with_missing_header() {
-		// Enable the feature.
-		update_option( 'woocommerce_feature_agentic_checkout_enabled', 'yes' );
+	public function test_validate_jetpack_request_returns_true_with_valid_blog_token() {
+		// Mock successful Jetpack blog token authentication.
+		$this->mock_jetpack_blog_token_auth();

-		// Create mock request without Authorization header.
-		$request = new \WP_REST_Request();
-
-		// Test authorization.
-		$result = AgenticCheckoutUtils::is_authorized( $request );
-
-		// Assert authorization fails with ACP error format.
-		$this->assertWPError( $result );
-		$this->assertEquals( 'invalid_request', $result->get_error_code() );
-		$this->assertEquals( 400, $result->get_error_data()['status'] );
-		$this->assertEquals( 'invalid_request', $result->get_error_data()['type'] );
-		$this->assertEquals( 'invalid_authorization_format', $result->get_error_data()['code'] );
-	}
-
-	/**
-	 * Test authorization with malformed Bearer token format.
-	 */
-	public function test_is_authorized_with_malformed_header() {
-		// Enable the feature.
-		update_option( 'woocommerce_feature_agentic_checkout_enabled', 'yes' );
-
-		// Test various malformed formats.
-		$malformed_headers = array(
-			'token_without_bearer',
-			'Basic token123',
-			'Bearertoken123', // Missing space.
-			'Bearer',         // No token.
-		);
+		// Test validation.
+		$result = AgenticCheckoutUtils::validate_jetpack_request();

-		foreach ( $malformed_headers as $header ) {
-			$request = new \WP_REST_Request();
-			$request->set_header( 'Authorization', $header );
-
-			$result = AgenticCheckoutUtils::is_authorized( $request );
-
-			$this->assertWPError( $result, "Failed for header: $header" );
-			$this->assertEquals( 'invalid_request', $result->get_error_code() );
-			$this->assertEquals( 400, $result->get_error_data()['status'] );
-			$this->assertEquals( 'invalid_request', $result->get_error_data()['type'] );
-			$this->assertEquals( 'invalid_authorization_format', $result->get_error_data()['code'] );
-		}
+		// Assert authorization succeeds.
+		$this->assertTrue( $result );
 	}

 	/**
-	 * Test authorization with empty provider tokens.
+	 * Test validate_jetpack_request error message is user-friendly.
 	 */
-	public function test_is_authorized_with_empty_provider_tokens() {
-		// Enable the feature.
-		update_option( 'woocommerce_feature_agentic_checkout_enabled', 'yes' );
-
-		// Set up registry with empty token.
-		update_option(
-			'woocommerce_agentic_agent_registry',
-			array(
-				'openai' => array(
-					'bearer_token' => '',
-				),
-			),
-			false
-		);
+	public function test_validate_jetpack_request_error_message() {
+		// Ensure no Jetpack authentication is set.
+		$this->mock_jetpack_auth_failure();

-		// Create mock request with token.
-		$request = new \WP_REST_Request();
-		$request->set_header( 'Authorization', 'Bearer some_token' );
+		// Test validation.
+		$result = AgenticCheckoutUtils::validate_jetpack_request();

-		// Test authorization.
-		$result = AgenticCheckoutUtils::is_authorized( $request );
-
-		// Assert authorization fails with ACP error format (empty tokens are skipped).
+		// Assert the error message is helpful for debugging.
 		$this->assertWPError( $result );
-		$this->assertEquals( 'invalid_request', $result->get_error_code() );
-		$this->assertEquals( 400, $result->get_error_data()['status'] );
-		$this->assertEquals( 'invalid_request', $result->get_error_data()['type'] );
-		$this->assertEquals( 'authentication_failed', $result->get_error_data()['code'] );
-	}
-
-	/**
-	 * Test authorization with multiple providers.
-	 */
-	public function test_is_authorized_with_multiple_providers() {
-		// Enable the feature.
-		update_option( 'woocommerce_feature_agentic_checkout_enabled', 'yes' );
-
-		// Set up registry with multiple providers (hashed tokens).
-		$token_a = 'provider_a_token';
-		$token_b = 'provider_b_token';
-		update_option(
-			'woocommerce_agentic_agent_registry',
-			array(
-				'general'    => array(
-					'enable_products_default' => 'yes',
-				),
-				'provider_a' => array(
-					'bearer_token' => wp_hash_password( $token_a ),
-				),
-				'provider_b' => array(
-					'bearer_token' => wp_hash_password( $token_b ),
-				),
-			),
-			false
+		$this->assertEquals(
+			'This endpoint requires Jetpack blog token authentication.',
+			$result->get_error_message()
 		);
-
-		// Test with provider A token (plaintext).
-		$request = new \WP_REST_Request();
-		$request->set_header( 'Authorization', 'Bearer ' . $token_a );
-		$result = AgenticCheckoutUtils::is_authorized( $request );
-		$this->assertTrue( $result );
-		$this->assertEquals( 'provider_a', WC()->session->get( SessionKey::AGENTIC_CHECKOUT_PROVIDER_ID ) );
-
-		// Test with provider B token (plaintext).
-		$request = new \WP_REST_Request();
-		$request->set_header( 'Authorization', 'Bearer ' . $token_b );
-		$result = AgenticCheckoutUtils::is_authorized( $request );
-		$this->assertTrue( $result );
-		$this->assertEquals( 'provider_b', WC()->session->get( SessionKey::AGENTIC_CHECKOUT_PROVIDER_ID ) );
-	}
-
-	/**
-	 * Test authorization with case-insensitive Bearer keyword.
-	 */
-	public function test_is_authorized_with_case_insensitive_bearer() {
-		// Enable the feature.
-		update_option( 'woocommerce_feature_agentic_checkout_enabled', 'yes' );
-
-		// Set up registry (hashed token).
-		$test_token = 'test_token';
-		update_option(
-			'woocommerce_agentic_agent_registry',
-			array(
-				'openai' => array(
-					'bearer_token' => wp_hash_password( $test_token ),
-				),
-			),
-			false
-		);
-
-		// Test with different casings of "Bearer" (plaintext token).
-		$casings = array( 'Bearer', 'bearer', 'BEARER', 'BeArEr' );
-		foreach ( $casings as $casing ) {
-			$request = new \WP_REST_Request();
-			$request->set_header( 'Authorization', $casing . ' ' . $test_token );
-			$result = AgenticCheckoutUtils::is_authorized( $request );
-			$this->assertTrue( $result, "Failed for casing: $casing" );
-		}
 	}
 }