Commit 27eb08ac34 for woocommerce

commit 27eb08ac344a9e1dae5294a69640d47977bd956f
Author: Leonardo Lopes de Albuquerque <leonardo.albuquerque@automattic.com>
Date:   Mon Jan 12 17:54:50 2026 -0300

    [Fraud Protection] Removed jetpack connection button (#62753)

    * Removed jetpack connection button

    * Added check to prevent accessing an empty variable

    * Fixed error message and phstan error

    * php lint fixes

    * lint fixes

    * Removed unecessary variable

    * Fixed translators comment

    * Fixed the way we try to connect to jetpack

    * Added comment to maybe_register_jetpack_connection

    * Added since and internal to method

    * added log when the automatic jetpack connection fails

diff --git a/plugins/woocommerce/includes/class-woocommerce.php b/plugins/woocommerce/includes/class-woocommerce.php
index c6b1796248..34cdd14483 100644
--- a/plugins/woocommerce/includes/class-woocommerce.php
+++ b/plugins/woocommerce/includes/class-woocommerce.php
@@ -375,7 +375,6 @@ final class WooCommerce {
 		$container->get( Automattic\WooCommerce\Internal\Orders\OrderAttributionBlocksController::class )->register();
 		$container->get( Automattic\WooCommerce\Internal\CostOfGoodsSold\CostOfGoodsSoldController::class )->register();
 		$container->get( Automattic\WooCommerce\Internal\FraudProtection\FraudProtectionController::class )->register();
-		$container->get( Automattic\WooCommerce\Internal\FraudProtection\AdminSettingsHandler::class )->register();
 		$container->get( Automattic\WooCommerce\Internal\Admin\Settings\PaymentsController::class )->register();
 		$container->get( Automattic\WooCommerce\Internal\Admin\Settings\PaymentsProviders\WooPayments\WooPaymentsController::class )->register();
 		$container->get( Automattic\WooCommerce\Internal\Utilities\LegacyRestApiStub::class )->register();
diff --git a/plugins/woocommerce/src/Internal/FraudProtection/AdminSettingsHandler.php b/plugins/woocommerce/src/Internal/FraudProtection/AdminSettingsHandler.php
deleted file mode 100644
index d89c2d22ef..0000000000
--- a/plugins/woocommerce/src/Internal/FraudProtection/AdminSettingsHandler.php
+++ /dev/null
@@ -1,158 +0,0 @@
-<?php
-/**
- * AdminSettingsHandler class file.
- */
-
-declare( strict_types=1 );
-
-namespace Automattic\WooCommerce\Internal\FraudProtection;
-
-defined( 'ABSPATH' ) || exit;
-
-/**
- * Handles admin settings for fraud protection.
- *
- * @since 10.5.0
- */
-class AdminSettingsHandler {
-
-	/**
-	 * Jetpack connection manager instance.
-	 *
-	 * @var JetpackConnectionManager
-	 */
-	private $connection_manager;
-
-	/**
-	 * Register hooks.
-	 */
-	public function register(): void {
-		add_filter( 'woocommerce_get_settings_advanced', array( $this, 'add_jetpack_connection_field' ), 100, 2 );
-		add_action( 'woocommerce_admin_field_jetpack_connection', array( $this, 'handle_output_jetpack_connection_field' ), 10, 1 );
-	}
-
-
-	/**
-	 * Initialize the class with dependencies.
-	 *
-	 * @internal
-	 *
-	 * @param JetpackConnectionManager $connection_manager Jetpack connection manager instance.
-	 * @return void
-	 */
-	final public function init( JetpackConnectionManager $connection_manager ): void {
-		$this->connection_manager = $connection_manager;
-	}
-
-	/**
-	 * Add Jetpack connection field to fraud protection settings.
-	 *
-	 * @internal
-	 *
-	 * @param array  $settings Existing settings.
-	 * @param string $current_section Current section name.
-	 * @return array Modified settings.
-	 */
-	public function add_jetpack_connection_field( $settings, $current_section ): array {
-		// Only add on the features section.
-		if ( 'features' !== $current_section ) {
-			return $settings;
-		}
-
-		// Check if field already exists to prevent duplicates.
-		foreach ( $settings as $setting ) {
-			if ( isset( $setting['id'] ) && 'woocommerce_fraud_protection_jetpack_connection' === $setting['id'] ) {
-				return $settings;
-			}
-		}
-
-		// Find the fraud_protection field and add Jetpack connection field after it.
-		$new_settings = array();
-		foreach ( $settings as $setting ) {
-			$new_settings[] = $setting;
-
-			// Add Jetpack connection field after fraud_protection checkbox.
-			if ( isset( $setting['id'] ) && 'woocommerce_feature_fraud_protection_enabled' === $setting['id'] ) {
-				$new_settings[] = array(
-					'id'    => 'woocommerce_fraud_protection_jetpack_connection',
-					'type'  => 'jetpack_connection',
-					'title' => __( 'Jetpack Connection', 'woocommerce' ),
-					'desc'  => __( 'Connect your site to Jetpack to enable fraud protection features.', 'woocommerce' ),
-				);
-			}
-		}
-
-		return $new_settings;
-	}
-
-	/**
-	 * Output the Jetpack connection field.
-	 *
-	 * @internal
-	 *
-	 * @param array $value Field configuration.
-	 * @return void
-	 */
-	public function handle_output_jetpack_connection_field( $value ): void { // phpcs:ignore Generic.CodeAnalysis.UnusedFunctionParameter.Found
-		// Only show Jetpack connection when fraud protection is enabled.
-		if ( 'yes' !== get_option( 'woocommerce_feature_fraud_protection_enabled', 'no' ) ) {
-			return;
-		}
-
-		$this->output_jetpack_connection_status();
-	}
-
-	/**
-	 * Output the Jetpack connection status and button.
-	 *
-	 * @internal
-	 *
-	 * @return void
-	 */
-	private function output_jetpack_connection_status(): void {
-		// Get connection status from connection manager.
-		$connection_status = $this->connection_manager->get_connection_status();
-		?>
-		<tr valign="top">
-			<th scope="row" class="titledesc">
-				<label><?php esc_html_e( 'Jetpack Connection', 'woocommerce' ); ?></label>
-			</th>
-			<td class="forminp forminp-button">
-				<?php if ( ! $connection_status['connected'] ) : ?>
-					<?php
-					// Get authorization URL for connecting.
-					$redirect_url   = admin_url( 'admin.php?page=wc-settings&tab=advanced&section=features' );
-					$connection_url = $this->connection_manager->get_authorization_url( $redirect_url );
-
-					// If we couldn't get authorization URL, show error message.
-					if ( ! $connection_url ) :
-						?>
-						<p class="description" style="color: #dc3232;">
-							<?php echo esc_html( $connection_status['error'] ); ?>
-						</p>
-					<?php else : ?>
-						<a href="<?php echo esc_url( $connection_url ); ?>" class="button button-secondary jetpack_connection_button">
-							<?php esc_html_e( 'Connect to Jetpack', 'woocommerce' ); ?>
-						</a>
-						<p class="description">
-							<?php esc_html_e( 'Connect your site to Jetpack to enable fraud protection features.', 'woocommerce' ); ?>
-						</p>
-					<?php endif; ?>
-				<?php else : ?>
-					<span class="dashicons dashicons-yes-alt" style="color: #46b450;"></span>
-					<span><?php esc_html_e( 'Connected to Jetpack', 'woocommerce' ); ?></span>
-					<p class="description">
-						<?php
-						printf(
-							/* translators: %d: Blog ID */
-							esc_html__( 'Site ID: %d', 'woocommerce' ),
-							(int) $connection_status['blog_id']
-						);
-						?>
-					</p>
-				<?php endif; ?>
-			</td>
-		</tr>
-		<?php
-	}
-}
diff --git a/plugins/woocommerce/src/Internal/FraudProtection/FraudProtectionController.php b/plugins/woocommerce/src/Internal/FraudProtection/FraudProtectionController.php
index 17634d4dad..1039e68e46 100644
--- a/plugins/woocommerce/src/Internal/FraudProtection/FraudProtectionController.php
+++ b/plugins/woocommerce/src/Internal/FraudProtection/FraudProtectionController.php
@@ -8,6 +8,7 @@ declare( strict_types=1 );
 namespace Automattic\WooCommerce\Internal\FraudProtection;

 use Automattic\WooCommerce\Internal\Features\FeaturesController;
+use Automattic\WooCommerce\Internal\Jetpack\JetpackConnection;
 use Automattic\WooCommerce\Internal\RegisterHooksInterface;

 defined( 'ABSPATH' ) || exit;
@@ -30,13 +31,6 @@ class FraudProtectionController implements RegisterHooksInterface {
 	 */
 	private FeaturesController $features_controller;

-	/**
-	 * Jetpack connection manager instance.
-	 *
-	 * @var JetpackConnectionManager
-	 */
-	private JetpackConnectionManager $connection_manager;
-
 	/**
 	 * Blocked session notice instance.
 	 *
@@ -50,6 +44,7 @@ class FraudProtectionController implements RegisterHooksInterface {
 	public function register(): void {
 		add_action( 'init', array( $this, 'on_init' ) );
 		add_action( 'admin_notices', array( $this, 'on_admin_notices' ) );
+		add_action( FeaturesController::FEATURE_ENABLED_CHANGED_ACTION, array( $this, 'maybe_register_jetpack_connection' ), 10, 2 );
 	}

 	/**
@@ -57,17 +52,14 @@ class FraudProtectionController implements RegisterHooksInterface {
 	 *
 	 * @internal
 	 *
-	 * @param FeaturesController       $features_controller      The instance of FeaturesController to use.
-	 * @param JetpackConnectionManager $connection_manager       The instance of JetpackConnectionManager to use.
-	 * @param BlockedSessionNotice     $blocked_session_notice   The instance of BlockedSessionNotice to use.
+	 * @param FeaturesController   $features_controller      The instance of FeaturesController to use.
+	 * @param BlockedSessionNotice $blocked_session_notice   The instance of BlockedSessionNotice to use.
 	 */
 	final public function init(
 		FeaturesController $features_controller,
-		JetpackConnectionManager $connection_manager,
 		BlockedSessionNotice $blocked_session_notice
 	): void {
 		$this->features_controller    = $features_controller;
-		$this->connection_manager     = $connection_manager;
 		$this->blocked_session_notice = $blocked_session_notice;
 	}

@@ -92,35 +84,25 @@ class FraudProtectionController implements RegisterHooksInterface {
 	 */
 	public function on_admin_notices(): void {
 		// Only show if feature is enabled.
-		if ( ! $this->feature_is_enabled() ) {
+		if ( ! $this->feature_is_enabled() || JetpackConnection::get_manager()->is_connected() ) {
 			return;
 		}

 		// Only show on WooCommerce settings page.
 		$screen = get_current_screen();
-		if ( ! $screen || 'woocommerce_page_wc-settings' !== $screen->id ) {
-			return;
-		}

-		$connection_status = $this->connection_manager->get_connection_status();
-		if ( $connection_status['connected'] ) {
+		if ( ! $screen || 'woocommerce_page_wc-settings' !== $screen->id ) {
 			return;
 		}

-		$settings_url = admin_url( 'admin.php?page=wc-settings&tab=advanced&section=features' );
-
 		?>
 		<div class="notice notice-warning is-dismissible">
-			<p>
-				<strong><?php esc_html_e( 'Fraud protection warning:', 'woocommerce' ); ?></strong>
-				<?php echo esc_html( $connection_status['error'] ); ?>
-			</p>
 			<p>
 				<?php
 				printf(
-					/* translators: %s: Settings page URL */
-					wp_kses_post( __( 'Fraud protection will fail open and allow all sessions until connected. <a href="%s">Connect to Jetpack</a>', 'woocommerce' ) ),
-					esc_url( $settings_url )
+					/* translators: %s: Getting Started with Jetpack documentation URL */
+					wp_kses_post( __( 'Your site failed to connect to Jetpack automatically. Fraud protection will fail open and allow all sessions until your site is connected to Jetpack. <a href="%s">How to connect to Jetpack</a>', 'woocommerce' ) ),
+					esc_url( 'https://jetpack.com/support/getting-started-with-jetpack/' )
 				);
 				?>
 			</p>
@@ -128,6 +110,40 @@ class FraudProtectionController implements RegisterHooksInterface {
 		<?php
 	}

+	/**
+	 * Maybe register Jetpack connection when fraud protection is enabled.
+	 *
+	 * Attempts to automatically register the site with Jetpack when the fraud protection
+	 * feature is enabled and the site is not already connected.
+	 *
+	 * @since 10.5.0
+	 *
+	 * @internal
+	 *
+	 * @param string $feature_id The feature ID being toggled.
+	 * @param bool   $is_enabled Whether the feature is being enabled or disabled.
+	 */
+	public function maybe_register_jetpack_connection( string $feature_id, bool $is_enabled ): void {
+		if ( 'fraud_protection' !== $feature_id || ! $is_enabled ) {
+			return;
+		}
+
+		$manager = JetpackConnection::get_manager();
+
+		if ( $manager->is_connected() ) {
+			return;
+		}
+
+		$result = $manager->try_registration();
+
+		if ( is_wp_error( $result ) ) {
+			$this->log( 'error', 'Failed to register Jetpack connection: ' . $result->get_error_message() );
+			return;
+		}
+
+		$this->log( 'info', 'Jetpack connection registered successfully' );
+	}
+
 	/**
 	 * Check if fraud protection feature is enabled.
 	 *
diff --git a/plugins/woocommerce/src/Internal/FraudProtection/JetpackConnectionManager.php b/plugins/woocommerce/src/Internal/FraudProtection/JetpackConnectionManager.php
deleted file mode 100644
index e3e904bd5a..0000000000
--- a/plugins/woocommerce/src/Internal/FraudProtection/JetpackConnectionManager.php
+++ /dev/null
@@ -1,105 +0,0 @@
-<?php
-/**
- * JetpackConnectionManager class file.
- */
-
-declare( strict_types=1 );
-
-namespace Automattic\WooCommerce\Internal\FraudProtection;
-
-use Automattic\WooCommerce\Internal\Jetpack\JetpackConnection;
-
-defined( 'ABSPATH' ) || exit;
-
-/**
- * Manages Jetpack connection status and validation for fraud protection.
- *
- * Provides centralized methods to check connection status, validate requirements,
- * and handle connection-related errors gracefully.
- *
- * @since 10.5.0
- */
-class JetpackConnectionManager {
-
-	/**
-	 * Get the Jetpack blog ID.
-	 *
-	 * @return int|null Blog ID if available, null otherwise.
-	 */
-	public function get_blog_id(): ?int {
-		// Get blog ID from Jetpack options.
-		$blog_id = \Jetpack_Options::get_option( 'id' );
-
-		return $blog_id ? (int) $blog_id : null;
-	}
-
-	/**
-	 * Get connection status with detailed error information.
-	 *
-	 * Returns an array with connection status and any error details.
-	 *
-	 * @return array {
-	 *     Connection status information.
-	 *
-	 *     @type bool   $connected    Whether the site is connected.
-	 *     @type string $error        Error message if not connected.
-	 *     @type string $error_code   Error code if not connected.
-	 *     @type int    $blog_id      Blog ID if available.
-	 * }
-	 */
-	public function get_connection_status(): array {
-		$status = array(
-			'connected'  => false,
-			'error'      => '',
-			'error_code' => '',
-			'blog_id'    => null,
-		);
-
-		// Check if connected.
-		if ( ! JetpackConnection::get_manager()->is_connected() ) {
-			$status['error']      = __( 'Site is not connected to WordPress.com. Please connect your site to enable fraud protection.', 'woocommerce' );
-			$status['error_code'] = 'not_connected';
-			return $status;
-		}
-
-		// Get blog ID.
-		$blog_id = $this->get_blog_id();
-		if ( ! $blog_id ) {
-			$status['error']      = __( 'Jetpack blog ID not found. Please reconnect your site to WordPress.com.', 'woocommerce' );
-			$status['error_code'] = 'no_blog_id';
-			return $status;
-		}
-
-		// All checks passed.
-		$status['connected'] = true;
-		$status['blog_id']   = $blog_id;
-
-		return $status;
-	}
-
-	/**
-	 * Get the Jetpack authorization URL for connecting the site.
-	 *
-	 * @param string $redirect_url URL to redirect to after authorization.
-	 * @return string|null Authorization URL or null on error.
-	 */
-	public function get_authorization_url( string $redirect_url = '' ): ?string {
-		// If no redirect URL provided, use current admin URL.
-		if ( empty( $redirect_url ) ) {
-			$redirect_url = admin_url( 'admin.php?page=wc-settings&tab=advanced&section=features' );
-		}
-
-		$authorization_data = JetpackConnection::get_authorization_url( $redirect_url, 'woocommerce-fraud-protection' );
-
-		if ( ! $authorization_data['success'] ) {
-			FraudProtectionController::log(
-				'error',
-				'Failed to get Jetpack authorization URL.',
-				$authorization_data['errors']
-			);
-			return null;
-		}
-
-		return $authorization_data['url'];
-	}
-}
diff --git a/plugins/woocommerce/tests/php/includes/class-wc-payment-gateways-test.php b/plugins/woocommerce/tests/php/includes/class-wc-payment-gateways-test.php
index 73e3ce43bc..16200b1cc3 100644
--- a/plugins/woocommerce/tests/php/includes/class-wc-payment-gateways-test.php
+++ b/plugins/woocommerce/tests/php/includes/class-wc-payment-gateways-test.php
@@ -21,6 +21,11 @@ class WC_Payment_Gateways_Test extends WC_Unit_Test_Case {
 	public function setUp(): void {
 		parent::setUp();
 		$this->reset_legacy_proxy_mocks();
+
+		// Set jetpack_activation_source option to prevent "Cannot use bool as array" error
+		// in Jetpack Connection Manager's apply_activation_source_to_args method.
+		update_option( 'jetpack_activation_source', array( '', '' ) );
+
 		$container = wc_get_container();
 		$container->reset_all_resolved();
 		$this->sut = new WC_Payment_Gateways();
@@ -33,6 +38,7 @@ class WC_Payment_Gateways_Test extends WC_Unit_Test_Case {
 	public function tearDown(): void {
 		parent::tearDown();
 		delete_option( 'woocommerce_feature_fraud_protection_enabled' );
+		delete_option( 'jetpack_activation_source' );
 		wc_get_container()->get( SessionClearanceManager::class )->reset_session();
 	}

diff --git a/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Routes/Checkout.php b/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Routes/Checkout.php
index 9aa32670d8..1c9e9757a0 100644
--- a/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Routes/Checkout.php
+++ b/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Routes/Checkout.php
@@ -35,6 +35,10 @@ class Checkout extends MockeryTestCase {
 	protected function setUp(): void {
 		parent::setUp();

+		// Set jetpack_activation_source option to prevent "Cannot use bool as array" error
+		// in Jetpack Connection Manager's apply_activation_source_to_args method.
+		update_option( 'jetpack_activation_source', array( '', '' ) );
+
 		add_filter( 'woocommerce_set_cookie_enabled', array( $this, 'filter_woocommerce_set_cookie_enabled' ), 10, 4 );

 		update_option( 'woocommerce_enable_guest_checkout', 'yes' );
@@ -128,6 +132,7 @@ class Checkout extends MockeryTestCase {
 		remove_all_actions( 'woocommerce_valid_order_statuses_for_payment' );

 		delete_option( 'woocommerce_feature_fraud_protection_enabled' );
+		delete_option( 'jetpack_activation_source' );
 		wc_get_container()->get( SessionClearanceManager::class )->reset_session();

 		update_option( 'woocommerce_ship_to_countries', 'all' );
diff --git a/plugins/woocommerce/tests/php/src/Internal/FraudProtection/AdminSettingsHandlerTest.php b/plugins/woocommerce/tests/php/src/Internal/FraudProtection/AdminSettingsHandlerTest.php
deleted file mode 100644
index f7256a7fc6..0000000000
--- a/plugins/woocommerce/tests/php/src/Internal/FraudProtection/AdminSettingsHandlerTest.php
+++ /dev/null
@@ -1,283 +0,0 @@
-<?php
-
-declare( strict_types=1 );
-
-namespace Automattic\WooCommerce\Tests\Internal\FraudProtection;
-
-use Automattic\WooCommerce\Internal\FraudProtection\AdminSettingsHandler;
-use Automattic\WooCommerce\Internal\FraudProtection\JetpackConnectionManager;
-
-/**
- * Tests for the AdminSettingsHandler class.
- */
-class AdminSettingsHandlerTest extends \WC_Unit_Test_Case {
-
-	/**
-	 * System under test.
-	 *
-	 * @var AdminSettingsHandler
-	 */
-	private $sut;
-
-	/**
-	 * Mock Jetpack connection manager.
-	 *
-	 * @var JetpackConnectionManager|\PHPUnit\Framework\MockObject\MockObject
-	 */
-	private $connection_manager_mock;
-
-	/**
-	 * Setup test.
-	 */
-	public function setUp(): void {
-		parent::setUp();
-
-		// Create mock connection manager.
-		$this->connection_manager_mock = $this->getMockBuilder( JetpackConnectionManager::class )
-			->disableOriginalConstructor()
-			->getMock();
-
-		// Get fresh instance from container.
-		$this->sut = wc_get_container()->get( AdminSettingsHandler::class );
-		$this->sut->init( $this->connection_manager_mock );
-	}
-
-	/**
-	 * Test that register method registers the expected hooks.
-	 */
-	public function test_register_registers_hooks(): void {
-		$this->sut->register();
-
-		// Check if the settings filter is registered.
-		$priority = has_filter( 'woocommerce_get_settings_advanced', array( $this->sut, 'add_jetpack_connection_field' ) );
-		$this->assertSame( 100, $priority, 'Settings filter should be registered with priority 100' );
-
-		// Check if the admin field action is registered.
-		$priority = has_action( 'woocommerce_admin_field_jetpack_connection', array( $this->sut, 'handle_output_jetpack_connection_field' ) );
-		$this->assertSame( 10, $priority, 'Admin field action should be registered with priority 10' );
-	}
-
-	/**
-	 * Test that add_jetpack_connection_field returns settings unchanged on non-features section.
-	 */
-	public function test_add_jetpack_connection_field_returns_unchanged_on_non_features_section(): void {
-		$this->sut->register();
-
-		$settings        = array(
-			array(
-				'id'   => 'some_setting',
-				'type' => 'text',
-			),
-		);
-		$current_section = 'general';
-		$result          = $this->sut->add_jetpack_connection_field( $settings, $current_section );
-
-		$this->assertSame( $settings, $result, 'Settings should be unchanged on non-features section' );
-	}
-
-	/**
-	 * Test that add_jetpack_connection_field adds field after fraud_protection on features section.
-	 */
-	public function test_add_jetpack_connection_field_adds_field_after_fraud_protection(): void {
-		$this->sut->register();
-
-		$settings = array(
-			array(
-				'id'   => 'some_other_feature',
-				'type' => 'checkbox',
-			),
-			array(
-				'id'   => 'woocommerce_feature_fraud_protection_enabled',
-				'type' => 'checkbox',
-			),
-			array(
-				'id'   => 'another_feature',
-				'type' => 'checkbox',
-			),
-		);
-
-		$result = $this->sut->add_jetpack_connection_field( $settings, 'features' );
-
-		// Should have one more setting.
-		$this->assertCount( 4, $result );
-
-		// The new field should be added after fraud_protection.
-		$this->assertSame( 'woocommerce_feature_fraud_protection_enabled', $result[1]['id'] );
-		$this->assertSame( 'woocommerce_fraud_protection_jetpack_connection', $result[2]['id'] );
-		$this->assertSame( 'jetpack_connection', $result[2]['type'] );
-		$this->assertSame( 'another_feature', $result[3]['id'] );
-	}
-
-	/**
-	 * Test that add_jetpack_connection_field doesn't add duplicate field.
-	 */
-	public function test_add_jetpack_connection_field_doesnt_duplicate(): void {
-		$this->sut->register();
-
-		$settings = array(
-			array(
-				'id'   => 'woocommerce_feature_fraud_protection_enabled',
-				'type' => 'checkbox',
-			),
-		);
-
-		// Call twice to check it doesn't duplicate.
-		$result1 = $this->sut->add_jetpack_connection_field( $settings, 'features' );
-		$result2 = $this->sut->add_jetpack_connection_field( $result1, 'features' );
-
-		// Should still only have 2 settings (fraud_protection + 1 jetpack_connection).
-		$this->assertCount( 2, $result2 );
-	}
-
-	/**
-	 * Test that handle_output_jetpack_connection_field does nothing when fraud protection disabled.
-	 */
-	public function test_handle_output_jetpack_connection_field_returns_early_when_disabled(): void {
-		// Disable fraud protection.
-		update_option( 'woocommerce_feature_fraud_protection_enabled', 'no' );
-
-		$this->sut->register();
-
-		// Mock connection manager should not be called.
-		$this->connection_manager_mock->expects( $this->never() )
-			->method( 'get_connection_status' );
-
-		// Capture output.
-		ob_start();
-		$this->sut->handle_output_jetpack_connection_field( array() );
-		$output = ob_get_clean();
-
-		// Should produce no output.
-		$this->assertEmpty( $output );
-	}
-
-	/**
-	 * Test that handle_output_jetpack_connection_field shows button when not connected.
-	 */
-	public function test_handle_output_jetpack_connection_field_shows_button_when_not_connected(): void {
-		// Enable fraud protection.
-		update_option( 'woocommerce_feature_fraud_protection_enabled', 'yes' );
-
-		$this->sut->register();
-
-		// Mock connection status - not connected.
-		$this->connection_manager_mock->expects( $this->once() )
-			->method( 'get_connection_status' )
-			->willReturn(
-				array(
-					'connected'  => false,
-					'error'      => 'Not connected',
-					'error_code' => 'not_connected',
-					'blog_id'    => null,
-				)
-			);
-
-		// Mock authorization URL.
-		$this->connection_manager_mock->expects( $this->once() )
-			->method( 'get_authorization_url' )
-			->willReturn( 'https://example.com/connect' );
-
-		// Capture output.
-		ob_start();
-		$this->sut->handle_output_jetpack_connection_field( array() );
-		$output = ob_get_clean();
-
-		// Should contain Connect button.
-		$this->assertStringContainsString( 'Connect to Jetpack', $output );
-		$this->assertStringContainsString( 'jetpack_connection_button', $output );
-		$this->assertStringContainsString( 'https://example.com/connect', $output );
-	}
-
-	/**
-	 * Test that handle_output_jetpack_connection_field shows connected status when connected.
-	 */
-	public function test_handle_output_jetpack_connection_field_shows_connected_status(): void {
-		// Enable fraud protection.
-		update_option( 'woocommerce_feature_fraud_protection_enabled', 'yes' );
-
-		$this->sut->register();
-
-		// Mock connection status - connected.
-		$this->connection_manager_mock->expects( $this->once() )
-			->method( 'get_connection_status' )
-			->willReturn(
-				array(
-					'connected'  => true,
-					'error'      => '',
-					'error_code' => '',
-					'blog_id'    => 12345,
-				)
-			);
-
-		// Should not call get_authorization_url when connected.
-		$this->connection_manager_mock->expects( $this->never() )
-			->method( 'get_authorization_url' );
-
-		// Capture output.
-		ob_start();
-		$this->sut->handle_output_jetpack_connection_field( array() );
-		$output = ob_get_clean();
-
-		// Should show connected status.
-		$this->assertStringContainsString( 'Connected to Jetpack', $output );
-		$this->assertStringContainsString( 'Site ID: 12345', $output );
-		$this->assertStringContainsString( 'dashicons-yes-alt', $output );
-	}
-
-	/**
-	 * Test that handle_output_jetpack_connection_field shows error when authorization URL fails.
-	 */
-	public function test_handle_output_jetpack_connection_field_shows_error_when_url_fails(): void {
-		// Enable fraud protection.
-		update_option( 'woocommerce_feature_fraud_protection_enabled', 'yes' );
-
-		$this->sut->register();
-
-		// Mock connection status - not connected.
-		$this->connection_manager_mock->expects( $this->once() )
-			->method( 'get_connection_status' )
-			->willReturn(
-				array(
-					'connected'  => false,
-					'error'      => 'Jetpack not available',
-					'error_code' => 'jetpack_not_available',
-					'blog_id'    => null,
-				)
-			);
-
-		// Mock authorization URL - returns null (failure).
-		$this->connection_manager_mock->expects( $this->once() )
-			->method( 'get_authorization_url' )
-			->willReturn( null );
-
-		// Capture output.
-		ob_start();
-		$this->sut->handle_output_jetpack_connection_field( array() );
-		$output = ob_get_clean();
-
-		// Should show error message.
-		$this->assertStringContainsString( 'Jetpack not available', $output );
-		$this->assertStringNotContainsString( 'Connect to Jetpack', $output );
-	}
-
-	/**
-	 * Cleanup after test.
-	 */
-	public function tearDown(): void {
-		parent::tearDown();
-
-		// Clean up options.
-		delete_option( 'woocommerce_feature_fraud_protection_enabled' );
-
-		// Remove hooks.
-		remove_all_filters( 'woocommerce_get_settings_advanced' );
-		remove_all_actions( 'woocommerce_admin_field_jetpack_connection' );
-		remove_all_actions( 'admin_enqueue_scripts' );
-
-		// Reset container.
-		wc_get_container()->reset_all_resolved();
-
-		// Clean up $_GET.
-		unset( $_GET['section'] );
-	}
-}
diff --git a/plugins/woocommerce/tests/php/src/Internal/FraudProtection/FraudProtectionControllerTest.php b/plugins/woocommerce/tests/php/src/Internal/FraudProtection/FraudProtectionControllerTest.php
index 1efd7c8b58..0a7cefda03 100644
--- a/plugins/woocommerce/tests/php/src/Internal/FraudProtection/FraudProtectionControllerTest.php
+++ b/plugins/woocommerce/tests/php/src/Internal/FraudProtection/FraudProtectionControllerTest.php
@@ -12,6 +12,17 @@ use Automattic\WooCommerce\Internal\FraudProtection\FraudProtectionController;
  */
 class FraudProtectionControllerTest extends \WC_Unit_Test_Case {

+	/**
+	 * Set up test fixtures.
+	 */
+	public function setUp(): void {
+		parent::setUp();
+
+		// Set jetpack_activation_source option to prevent "Cannot use bool as array" error
+		// in Jetpack Connection Manager's apply_activation_source_to_args method.
+		update_option( 'jetpack_activation_source', array( '', '' ) );
+	}
+
 	/**
 	 * Get a fresh controller instance with reset container.
 	 *
@@ -165,6 +176,7 @@ class FraudProtectionControllerTest extends \WC_Unit_Test_Case {
 		// Clean up any filters or options.
 		remove_all_filters( 'woocommerce_logging_class' );
 		delete_option( 'woocommerce_feature_fraud_protection_enabled' );
+		delete_option( 'jetpack_activation_source' );

 		// Remove any init hooks registered by the controller.
 		remove_all_actions( 'init' );
diff --git a/plugins/woocommerce/tests/php/src/Internal/FraudProtection/PaymentMethodEventTrackerTest.php b/plugins/woocommerce/tests/php/src/Internal/FraudProtection/PaymentMethodEventTrackerTest.php
index 5f7e787f36..eb32ea0450 100644
--- a/plugins/woocommerce/tests/php/src/Internal/FraudProtection/PaymentMethodEventTrackerTest.php
+++ b/plugins/woocommerce/tests/php/src/Internal/FraudProtection/PaymentMethodEventTrackerTest.php
@@ -39,6 +39,10 @@ class PaymentMethodEventTrackerTest extends \WC_Unit_Test_Case {
 	public function setUp(): void {
 		parent::setUp();

+		// Set jetpack_activation_source option to prevent "Cannot use bool as array" error
+		// in Jetpack Connection Manager's apply_activation_source_to_args method.
+		update_option( 'jetpack_activation_source', array( '', '' ) );
+
 		// Enable the fraud protection feature.
 		update_option( 'woocommerce_feature_fraud_protection_enabled', 'yes' );

@@ -121,6 +125,7 @@ class PaymentMethodEventTrackerTest extends \WC_Unit_Test_Case {

 		// Clean up options.
 		delete_option( 'woocommerce_feature_fraud_protection_enabled' );
+		delete_option( 'jetpack_activation_source' );

 		// Reset container.
 		wc_get_container()->reset_all_resolved();