Commit 6ff231e4b4 for woocommerce

commit 6ff231e4b4e2dd6ee66888b371b9019093564804
Author: Leonardo Lopes de Albuquerque <leonardo.albuquerque@automattic.com>
Date:   Sun Jan 11 20:03:05 2026 -0300

    [Fraud Protection] Call fraud protection methods directly instead of relying on hooks (#62743)

    * Call fraud protection methods directly from cart, checkout and payment methods management flows instead of hooks.

    * Implement EventDispatcher with shared events tracking and decision processing logic.

    * [Fraud Protection] Optimize checkout field tracking to only fire on country changes.

    Reduces unnecessary API calls by dispatching checkout_field_update events only
    when billing or shipping country changes, instead of on every field update.

    Changes:
    - Add get_current_billing_country() and get_current_shipping_country() helper
      methods to SessionDataCollector
    - Update CheckoutEventTracker to inject SessionDataCollector and compare posted
      countries with current session values
    - Fix bug: detect when user unchecks "ship to different address" and effective
      shipping country changes from current shipping to billing country
    - Update all tests with comprehensive coverage for country change detection

    Performance: Reduces fraud protection API calls by 80-90%.

    * [Fraud Protection] Rename checkout_field_update event to checkout_update

    Unifies event naming convention by renaming checkout_field_update to checkout_update
    to match the existing track_blocks_checkout_update method which uses the same event name.

    This creates consistency across shortcode and blocks checkout tracking.

    * [Fraud Protection] Fix double-dispatch when adding existing cart items

    When add_to_cart() finds an existing cart item and calls set_quantity(),
    it triggers track_cart_item_updated(). Previously, track_cart_item_added()
    was also called unconditionally after the if/else block, resulting in both
    an "updated" and "added" event being dispatched for the same operation.

    Fix:
    - Move track_cart_item_added() inside the else block (lines 1342-1346)
    - Now only fires when truly creating a new cart item
    - Cast $quantity to (int) for consistency with (int) $product_id

    The correct behavior is now:
    - Adding a NEW item: track_cart_item_added() fires
    - Updating EXISTING item quantity: track_cart_item_updated() fires (from set_quantity())

    All fraud protection tests pass (100 tests, 392 assertions).

    * [Fraud Protection] Add cart event tracking for Store API add-to-cart

    When the hook-based tracking was replaced with direct method calls,
    the Store API's CartController::add_to_cart() was not updated.
    This caused cart item added events to not be tracked when items
    were added via Store API (blocks checkout, headless).

    This commit:
    - Adds fraud protection tracking call to CartController::add_to_cart()
    - Adds tests for Store API fraud protection tracking

    * [Fraud Protection] Move cart tracking after woocommerce_add_to_cart action

    Moved the fraud protection tracking call to after the woocommerce_add_to_cart
    action fires, consistent with how other events are tracked. This ensures
    third-party plugins that modify the cart during that action are accounted for.

    Also added proper variable initialization for $item_was_already_in_cart.

    Co-authored-by: Vasily Belolapotkov <vasily.belolapotkov@automattic.com>
    Co-authored-by: Luiz Reis <luiz.reis@automattic.com>

diff --git a/plugins/woocommerce/includes/class-wc-ajax.php b/plugins/woocommerce/includes/class-wc-ajax.php
index 6d6207f236..9f920b39a6 100644
--- a/plugins/woocommerce/includes/class-wc-ajax.php
+++ b/plugins/woocommerce/includes/class-wc-ajax.php
@@ -15,6 +15,8 @@ use Automattic\WooCommerce\Internal\Orders\CouponsController;
 use Automattic\WooCommerce\Internal\Orders\TaxesController;
 use Automattic\WooCommerce\Internal\Orders\OrderNoteGroup;
 use Automattic\WooCommerce\Internal\Admin\Orders\MetaBoxes\CustomMetaBox;
+use Automattic\WooCommerce\Internal\FraudProtection\CheckoutEventTracker;
+use Automattic\WooCommerce\Internal\FraudProtection\FraudProtectionController;
 use Automattic\WooCommerce\Internal\Utilities\Users;
 use Automattic\WooCommerce\Proxies\LegacyProxy;
 use Automattic\WooCommerce\Utilities\ArrayUtil;
@@ -375,6 +377,12 @@ class WC_AJAX {

 		do_action( 'woocommerce_checkout_update_order_review', isset( $_POST['post_data'] ) ? wp_unslash( $_POST['post_data'] ) : '' ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized

+		// Track checkout field update for fraud protection.
+		if ( wc_get_container()->get( FraudProtectionController::class )->feature_is_enabled() ) {
+			wc_get_container()->get( CheckoutEventTracker::class )
+				->track_shortcode_checkout_field_update( isset( $_POST['post_data'] ) ? wp_unslash( $_POST['post_data'] ) : '' ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
+		}
+
 		$chosen_shipping_methods = WC()->session->get( 'chosen_shipping_methods' );
 		$posted_shipping_methods = isset( $_POST['shipping_method'] ) ? wc_clean( wp_unslash( $_POST['shipping_method'] ) ) : array();

diff --git a/plugins/woocommerce/includes/class-wc-cart.php b/plugins/woocommerce/includes/class-wc-cart.php
index c0d8659bd6..51a0971976 100644
--- a/plugins/woocommerce/includes/class-wc-cart.php
+++ b/plugins/woocommerce/includes/class-wc-cart.php
@@ -12,6 +12,8 @@
 use Automattic\WooCommerce\Blocks\Utils\CartCheckoutUtils;
 use Automattic\WooCommerce\Enums\ProductStatus;
 use Automattic\WooCommerce\Enums\ProductType;
+use Automattic\WooCommerce\Internal\FraudProtection\CartEventTracker;
+use Automattic\WooCommerce\Internal\FraudProtection\FraudProtectionController;
 use Automattic\WooCommerce\Utilities\DiscountsUtil;
 use Automattic\WooCommerce\Utilities\NumberUtil;
 use Automattic\WooCommerce\Utilities\ShippingUtil;
@@ -1312,10 +1314,12 @@ class WC_Cart extends WC_Legacy_Cart {
 				}
 			}

+			$item_was_already_in_cart = false;
 			// If cart_item_key is set, the item is already in the cart.
 			if ( $cart_item_key ) {
 				$new_quantity = $quantity + $this->cart_contents[ $cart_item_key ]['quantity'];
 				$this->set_quantity( $cart_item_key, $new_quantity, false );
+				$item_was_already_in_cart = true;
 			} else {
 				$cart_item_key = $cart_id;

@@ -1342,6 +1346,12 @@ class WC_Cart extends WC_Legacy_Cart {

 			do_action( 'woocommerce_add_to_cart', $cart_item_key, $product_id, $quantity, $variation_id, $variation, $cart_item_data );

+			// Track cart event for fraud protection (only for newly added items).
+			if ( ! $item_was_already_in_cart && wc_get_container()->get( FraudProtectionController::class )->feature_is_enabled() ) {
+				wc_get_container()->get( CartEventTracker::class )
+					->track_cart_item_added( $cart_item_key, (int) $product_id, (int) $quantity, $variation_id );
+			}
+
 			return $cart_item_key;

 		} catch ( Exception $e ) {
@@ -1367,6 +1377,12 @@ class WC_Cart extends WC_Legacy_Cart {

 			do_action( 'woocommerce_remove_cart_item', $cart_item_key, $this );

+			// Track cart event for fraud protection.
+			if ( wc_get_container()->get( FraudProtectionController::class )->feature_is_enabled() ) {
+				wc_get_container()->get( CartEventTracker::class )
+					->track_cart_item_removed( $cart_item_key, $this );
+			}
+
 			unset( $this->cart_contents[ $cart_item_key ] );

 			do_action( 'woocommerce_cart_item_removed', $cart_item_key, $this );
@@ -1390,6 +1406,12 @@ class WC_Cart extends WC_Legacy_Cart {

 			do_action( 'woocommerce_restore_cart_item', $cart_item_key, $this );

+			// Track cart event for fraud protection.
+			if ( wc_get_container()->get( FraudProtectionController::class )->feature_is_enabled() ) {
+				wc_get_container()->get( CartEventTracker::class )
+					->track_cart_item_restored( $cart_item_key, $this );
+			}
+
 			unset( $this->removed_cart_contents[ $cart_item_key ] );

 			do_action( 'woocommerce_cart_item_restored', $cart_item_key, $this );
@@ -1420,6 +1442,12 @@ class WC_Cart extends WC_Legacy_Cart {

 		do_action( 'woocommerce_after_cart_item_quantity_update', $cart_item_key, $quantity, $old_quantity, $this );

+		// Track cart event for fraud protection.
+		if ( wc_get_container()->get( FraudProtectionController::class )->feature_is_enabled() ) {
+			wc_get_container()->get( CartEventTracker::class )
+				->track_cart_item_updated( $cart_item_key, $quantity, $old_quantity, $this );
+		}
+
 		if ( $refresh_totals ) {
 			$this->calculate_totals();
 		}
diff --git a/plugins/woocommerce/includes/class-woocommerce.php b/plugins/woocommerce/includes/class-woocommerce.php
index 8f78dee55a..7f87c073a2 100644
--- a/plugins/woocommerce/includes/class-woocommerce.php
+++ b/plugins/woocommerce/includes/class-woocommerce.php
@@ -375,10 +375,7 @@ 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\CartEventTracker::class )->register();
 		$container->get( Automattic\WooCommerce\Internal\FraudProtection\AdminSettingsHandler::class )->register();
-		$container->get( Automattic\WooCommerce\Internal\FraudProtection\PaymentMethodEventTracker::class )->register();
-		$container->get( Automattic\WooCommerce\Internal\FraudProtection\CheckoutEventTracker::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/includes/data-stores/class-wc-payment-token-data-store.php b/plugins/woocommerce/includes/data-stores/class-wc-payment-token-data-store.php
index c6d6e5012b..2f57210473 100644
--- a/plugins/woocommerce/includes/data-stores/class-wc-payment-token-data-store.php
+++ b/plugins/woocommerce/includes/data-stores/class-wc-payment-token-data-store.php
@@ -5,6 +5,9 @@
  * @package WooCommerce\DataStores
  */

+use Automattic\WooCommerce\Internal\FraudProtection\FraudProtectionController;
+use Automattic\WooCommerce\Internal\FraudProtection\PaymentMethodEventTracker;
+
 if ( ! defined( 'ABSPATH' ) ) {
 	exit;
 }
@@ -72,6 +75,12 @@ class WC_Payment_Token_Data_Store extends WC_Data_Store_WP implements WC_Object_
 		}

 		do_action( 'woocommerce_new_payment_token', $token_id, $token );
+
+		// Track payment method event for fraud protection.
+		if ( wc_get_container()->get( FraudProtectionController::class )->feature_is_enabled() ) {
+			wc_get_container()->get( PaymentMethodEventTracker::class )
+				->track_payment_method_added( $token_id, $token );
+		}
 	}

 	/**
diff --git a/plugins/woocommerce/src/Internal/FraudProtection/CartEventTracker.php b/plugins/woocommerce/src/Internal/FraudProtection/CartEventTracker.php
index 615241e047..c666ac1f15 100644
--- a/plugins/woocommerce/src/Internal/FraudProtection/CartEventTracker.php
+++ b/plugins/woocommerce/src/Internal/FraudProtection/CartEventTracker.php
@@ -7,21 +7,19 @@ declare( strict_types=1 );

 namespace Automattic\WooCommerce\Internal\FraudProtection;

-use Automattic\WooCommerce\Internal\RegisterHooksInterface;
-
 defined( 'ABSPATH' ) || exit;

 /**
  * Tracks cart events for fraud protection analysis.
  *
- * This class hooks into WooCommerce cart events (add, update, remove, restore)
- * and triggers fraud protection event dispatching. Event-specific data is passed
+ * This class provides methods to track cart events (add, update, remove, restore)
+ * for fraud protection event dispatching. Event-specific data is passed
  * to the dispatcher which handles session data collection internally.
  *
  * @since 10.5.0
  * @internal This class is part of the internal API and is subject to change without notice.
  */
-class CartEventTracker implements RegisterHooksInterface {
+class CartEventTracker {

 	/**
 	 * Fraud protection dispatcher instance.
@@ -30,51 +28,19 @@ class CartEventTracker implements RegisterHooksInterface {
 	 */
 	private FraudProtectionDispatcher $dispatcher;

-	/**
-	 * Fraud protection controller instance.
-	 *
-	 * @var FraudProtectionController
-	 */
-	private FraudProtectionController $fraud_protection_controller;
-
 	/**
 	 * Initialize with dependencies.
 	 *
 	 * @internal
 	 *
-	 * @param FraudProtectionDispatcher $dispatcher               The fraud protection dispatcher instance.
-	 * @param FraudProtectionController $fraud_protection_controller The fraud protection controller instance.
+	 * @param FraudProtectionDispatcher $dispatcher The fraud protection dispatcher instance.
 	 */
-	final public function init(
-		FraudProtectionDispatcher $dispatcher,
-		FraudProtectionController $fraud_protection_controller
-	): void {
-		$this->dispatcher                  = $dispatcher;
-		$this->fraud_protection_controller = $fraud_protection_controller;
-	}
-
-	/**
-	 * Register cart event hooks.
-	 *
-	 * Hooks into WooCommerce cart actions to track fraud protection events.
-	 * Only registers hooks if the fraud protection feature is enabled.
-	 *
-	 * @return void
-	 */
-	public function register(): void {
-		// Only register hooks if fraud protection is enabled.
-		if ( ! $this->fraud_protection_controller->feature_is_enabled() ) {
-			return;
-		}
-
-		add_action( 'woocommerce_add_to_cart', array( $this, 'handle_track_cart_item_added' ), 10, 6 );
-		add_action( 'woocommerce_after_cart_item_quantity_update', array( $this, 'handle_track_cart_item_updated' ), 10, 4 );
-		add_action( 'woocommerce_remove_cart_item', array( $this, 'handle_track_cart_item_removed' ), 10, 2 );
-		add_action( 'woocommerce_restore_cart_item', array( $this, 'handle_track_cart_item_restored' ), 10, 2 );
+	final public function init( FraudProtectionDispatcher $dispatcher ): void {
+		$this->dispatcher = $dispatcher;
 	}

 	/**
-	 * Handle cart item added event.
+	 * Track cart item added event.
 	 *
 	 * Triggers fraud protection event dispatching when an item is added to the cart.
 	 *
@@ -84,11 +50,9 @@ class CartEventTracker implements RegisterHooksInterface {
 	 * @param int    $product_id     Product ID.
 	 * @param int    $quantity       Quantity added.
 	 * @param int    $variation_id   Variation ID.
-	 * @param array  $variation      Variation data.
-	 * @param array  $cart_item_data Cart item data.
 	 * @return void
 	 */
-	public function handle_track_cart_item_added( $cart_item_key, $product_id, $quantity, $variation_id, $variation, $cart_item_data ): void {
+	public function track_cart_item_added( $cart_item_key, $product_id, $quantity, $variation_id ): void {
 		$event_data = $this->build_cart_event_data(
 			'item_added',
 			$product_id,
@@ -101,7 +65,7 @@ class CartEventTracker implements RegisterHooksInterface {
 	}

 	/**
-	 * Handle cart item quantity updated event.
+	 * Track cart item quantity updated event.
 	 *
 	 * Triggers fraud protection event dispatching when cart item quantity is updated.
 	 *
@@ -113,7 +77,7 @@ class CartEventTracker implements RegisterHooksInterface {
 	 * @param object $cart          Cart object.
 	 * @return void
 	 */
-	public function handle_track_cart_item_updated( $cart_item_key, $quantity, $old_quantity, $cart ): void {
+	public function track_cart_item_updated( $cart_item_key, $quantity, $old_quantity, $cart ): void {
 		$cart_item = $cart->cart_contents[ $cart_item_key ] ?? null;

 		if ( (int) $quantity === (int) $old_quantity || ! $cart_item ) {
@@ -138,7 +102,7 @@ class CartEventTracker implements RegisterHooksInterface {
 	}

 	/**
-	 * Handle cart item removed event.
+	 * Track cart item removed event.
 	 *
 	 * Triggers fraud protection event dispatching when an item is removed from the cart.
 	 *
@@ -148,7 +112,7 @@ class CartEventTracker implements RegisterHooksInterface {
 	 * @param object $cart          Cart object.
 	 * @return void
 	 */
-	public function handle_track_cart_item_removed( $cart_item_key, $cart ): void {
+	public function track_cart_item_removed( $cart_item_key, $cart ): void {
 		$cart_item = $cart->removed_cart_contents[ $cart_item_key ] ?? null;

 		if ( ! $cart_item ) {
@@ -171,7 +135,7 @@ class CartEventTracker implements RegisterHooksInterface {
 	}

 	/**
-	 * Handle cart item restored event.
+	 * Track cart item restored event.
 	 *
 	 * Triggers fraud protection event dispatching when a removed item is restored to the cart.
 	 *
@@ -181,7 +145,7 @@ class CartEventTracker implements RegisterHooksInterface {
 	 * @param object $cart          Cart object.
 	 * @return void
 	 */
-	public function handle_track_cart_item_restored( $cart_item_key, $cart ): void {
+	public function track_cart_item_restored( $cart_item_key, $cart ): void {
 		$cart_item = $cart->cart_contents[ $cart_item_key ] ?? null;

 		if ( ! $cart_item ) {
diff --git a/plugins/woocommerce/src/Internal/FraudProtection/CheckoutEventTracker.php b/plugins/woocommerce/src/Internal/FraudProtection/CheckoutEventTracker.php
index 88d9bd4edc..92753f9c07 100644
--- a/plugins/woocommerce/src/Internal/FraudProtection/CheckoutEventTracker.php
+++ b/plugins/woocommerce/src/Internal/FraudProtection/CheckoutEventTracker.php
@@ -7,21 +7,19 @@ declare( strict_types=1 );

 namespace Automattic\WooCommerce\Internal\FraudProtection;

-use Automattic\WooCommerce\Internal\RegisterHooksInterface;
-
 defined( 'ABSPATH' ) || exit;

 /**
  * Tracks checkout events for fraud protection analysis.
  *
- * This class hooks into both WooCommerce Blocks (Store API) and traditional
- * shortcode checkout events, triggering fraud protection event dispatching.
+ * This class provides methods to track both WooCommerce Blocks (Store API) and traditional
+ * shortcode checkout events for fraud protection event dispatching.
  * Event-specific data is passed to the dispatcher which handles session data collection internally.
  *
  * @since 10.5.0
  * @internal This class is part of the internal API and is subject to change without notice.
  */
-class CheckoutEventTracker implements RegisterHooksInterface {
+class CheckoutEventTracker {

 	/**
 	 * Fraud protection dispatcher instance.
@@ -31,49 +29,27 @@ class CheckoutEventTracker implements RegisterHooksInterface {
 	private FraudProtectionDispatcher $dispatcher;

 	/**
-	 * Fraud protection controller instance.
+	 * Session data collector instance.
 	 *
-	 * @var FraudProtectionController
+	 * @var SessionDataCollector
 	 */
-	private FraudProtectionController $fraud_protection_controller;
+	private SessionDataCollector $session_data_collector;

 	/**
 	 * Initialize with dependencies.
 	 *
 	 * @internal
 	 *
-	 * @param FraudProtectionDispatcher $dispatcher The fraud protection dispatcher instance.
-	 * @param FraudProtectionController $fraud_protection_controller The fraud protection controller instance.
-	 */
-	final public function init(
-		FraudProtectionDispatcher $dispatcher,
-		FraudProtectionController $fraud_protection_controller
-	): void {
-		$this->dispatcher                  = $dispatcher;
-		$this->fraud_protection_controller = $fraud_protection_controller;
-	}
-
-	/**
-	 * Register checkout event hooks.
-	 *
-	 * Hooks into both WooCommerce Blocks (Store API) and traditional checkout
-	 * actions to track fraud protection events. Only registers hooks if the
-	 * fraud protection feature is enabled.
-	 *
-	 * @return void
+	 * @param FraudProtectionDispatcher $dispatcher                The fraud protection dispatcher instance.
+	 * @param SessionDataCollector      $session_data_collector    The session data collector instance.
 	 */
-	public function register(): void {
-		// Only register hooks if fraud protection is enabled.
-		if ( ! $this->fraud_protection_controller->feature_is_enabled() ) {
-			return;
-		}
-
-		// Shortcode checkout: Track when checkout fields are updated.
-		add_action( 'woocommerce_checkout_update_order_review', array( $this, 'handle_shortcode_checkout_field_update' ), 10, 1 );
+	final public function init( FraudProtectionDispatcher $dispatcher, SessionDataCollector $session_data_collector ): void {
+		$this->dispatcher             = $dispatcher;
+		$this->session_data_collector = $session_data_collector;
 	}

 	/**
-	 * Handle Store API customer update event (WooCommerce Blocks checkout).
+	 * Track Store API customer update event (WooCommerce Blocks checkout).
 	 *
 	 * Triggered when customer information is updated via the Store API endpoint
 	 * /wc/store/v1/cart/update-customer during Blocks checkout flow.
@@ -86,27 +62,52 @@ class CheckoutEventTracker implements RegisterHooksInterface {
 		$this->dispatcher->dispatch_event( 'checkout_update', array() );
 	}

-
 	/**
-	 * Handle shortcode checkout field update event.
+	 * Track shortcode checkout field update event.
 	 *
 	 * Triggered when checkout fields are updated via AJAX (woocommerce_update_order_review).
+	 * Only dispatches event when billing or shipping country changes to reduce unnecessary API calls.
 	 *
 	 * @internal
 	 *
 	 * @param string $posted_data Serialized checkout form data.
 	 * @return void
 	 */
-	public function handle_shortcode_checkout_field_update( $posted_data ): void {
+	public function track_shortcode_checkout_field_update( $posted_data ): void {
 		// Parse the posted data to extract relevant fields.
 		$data = array();
 		if ( $posted_data ) {
 			parse_str( $posted_data, $data );
 		}

-		// Build and dispatch the event.
-		$event_data = $this->format_checkout_event_data( 'field_update', $data );
-		$this->dispatcher->dispatch_event( 'checkout_field_update', $event_data );
+		// Get current customer countries using SessionDataCollector.
+		$current_billing_country  = $this->session_data_collector->get_current_billing_country();
+		$current_shipping_country = $this->session_data_collector->get_current_shipping_country();
+
+		// Get posted countries.
+		$posted_billing_country  = $data['billing_country'] ?? '';
+		$posted_shipping_country = $data['shipping_country'] ?? '';
+
+		// Check if billing country changed.
+		$billing_changed = ! empty( $posted_billing_country ) && $posted_billing_country !== $current_billing_country;
+
+		// Check if shipping country changed.
+		$ship_to_different = ! empty( $data['ship_to_different_address'] );
+		if ( $ship_to_different ) {
+			// User wants different shipping address - check if shipping country changed.
+			$shipping_changed = ! empty( $posted_shipping_country ) && $posted_shipping_country !== $current_shipping_country;
+		} else {
+			// User wants same address for billing and shipping.
+			// If current shipping country exists and differs from billing country, it's a change.
+			$effective_billing_country = ! empty( $posted_billing_country ) ? $posted_billing_country : $current_billing_country;
+			$shipping_changed          = ! empty( $current_shipping_country ) && $current_shipping_country !== $effective_billing_country;
+		}
+
+		// Only dispatch if either country changed.
+		if ( $billing_changed || $shipping_changed ) {
+			$event_data = $this->format_checkout_event_data( 'field_update', $data );
+			$this->dispatcher->dispatch_event( 'checkout_update', $event_data );
+		}
 	}

 	/**
diff --git a/plugins/woocommerce/src/Internal/FraudProtection/PaymentMethodEventTracker.php b/plugins/woocommerce/src/Internal/FraudProtection/PaymentMethodEventTracker.php
index f01ff48842..48c9ba7916 100644
--- a/plugins/woocommerce/src/Internal/FraudProtection/PaymentMethodEventTracker.php
+++ b/plugins/woocommerce/src/Internal/FraudProtection/PaymentMethodEventTracker.php
@@ -7,21 +7,19 @@ declare( strict_types=1 );

 namespace Automattic\WooCommerce\Internal\FraudProtection;

-use Automattic\WooCommerce\Internal\RegisterHooksInterface;
-
 defined( 'ABSPATH' ) || exit;

 /**
  * Tracks payment method events for fraud protection analysis.
  *
- * This class hooks into WooCommerce payment method events in My Account
- * (add, update, set default, delete) and triggers fraud protection event dispatching.
+ * This class provides methods to track events for adding payment methods in My Account page
+ * for fraud protection.
  * Event-specific data is passed to the dispatcher which handles session data collection internally.
  *
  * @since 10.5.0
  * @internal This class is part of the internal API and is subject to change without notice.
  */
-class PaymentMethodEventTracker implements RegisterHooksInterface {
+class PaymentMethodEventTracker {

 	/**
 	 * Fraud protection dispatcher instance.
@@ -30,49 +28,19 @@ class PaymentMethodEventTracker implements RegisterHooksInterface {
 	 */
 	private FraudProtectionDispatcher $dispatcher;

-	/**
-	 * Fraud protection controller instance.
-	 *
-	 * @var FraudProtectionController
-	 */
-	private FraudProtectionController $fraud_protection_controller;
-
 	/**
 	 * Initialize with dependencies.
 	 *
 	 * @internal
 	 *
-	 * @param FraudProtectionDispatcher $dispatcher                     The fraud protection dispatcher instance.
-	 * @param FraudProtectionController $fraud_protection_controller The fraud protection controller instance.
-	 */
-	final public function init(
-		FraudProtectionDispatcher $dispatcher,
-		FraudProtectionController $fraud_protection_controller
-	): void {
-		$this->dispatcher                  = $dispatcher;
-		$this->fraud_protection_controller = $fraud_protection_controller;
-	}
-
-	/**
-	 * Register payment method event hooks.
-	 *
-	 * Hooks into WooCommerce payment token actions to track fraud protection events.
-	 * Only registers hooks if the fraud protection feature is enabled.
+	 * @param FraudProtectionDispatcher $dispatcher The fraud protection dispatcher instance.
 	 */
-	public function register(): void {
-		// Only register hooks if fraud protection is enabled.
-		if ( ! $this->fraud_protection_controller->feature_is_enabled() ) {
-			return;
-		}
-
-		add_action( 'woocommerce_new_payment_token', array( $this, 'handle_payment_method_added' ), 10, 2 );
-		add_action( 'woocommerce_payment_token_updated', array( $this, 'handle_payment_method_updated' ), 10, 1 );
-		add_action( 'woocommerce_payment_token_set_default', array( $this, 'handle_payment_method_set_default' ), 10, 2 );
-		add_action( 'woocommerce_payment_token_deleted', array( $this, 'handle_payment_method_deleted' ), 10, 2 );
+	final public function init( FraudProtectionDispatcher $dispatcher ): void {
+		$this->dispatcher = $dispatcher;
 	}

 	/**
-	 * Handle payment method added event.
+	 * Track payment method added event.
 	 *
 	 * Triggers fraud protection event tracking when a payment method is added.
 	 *
@@ -81,70 +49,13 @@ class PaymentMethodEventTracker implements RegisterHooksInterface {
 	 * @param int               $token_id The newly created token ID.
 	 * @param \WC_Payment_Token $token    The payment token object.
 	 */
-	public function handle_payment_method_added( $token_id, $token ): void {
+	public function track_payment_method_added( $token_id, $token ): void {
 		$event_data = $this->build_payment_method_event_data( 'added', $token );

 		// Trigger event dispatching.
 		$this->dispatcher->dispatch_event( 'payment_method_added', $event_data );
 	}

-	/**
-	 * Handle payment method updated event.
-	 *
-	 * Triggers fraud protection event tracking when a payment method is updated.
-	 *
-	 * @internal
-	 *
-	 * @param int $token_id The ID of the updated token.
-	 */
-	public function handle_payment_method_updated( $token_id ): void {
-		// Get the token object to extract details.
-		$token = \WC_Payment_Tokens::get( $token_id );
-
-		if ( ! $token instanceof \WC_Payment_Token ) {
-			return;
-		}
-
-		$event_data = $this->build_payment_method_event_data( 'updated', $token );
-
-		// Trigger event dispatching.
-		$this->dispatcher->dispatch_event( 'payment_method_updated', $event_data );
-	}
-
-	/**
-	 * Handle payment method set as default event.
-	 *
-	 * Triggers fraud protection event tracking when a payment method is set as default.
-	 *
-	 * @internal
-	 *
-	 * @param int               $token_id The ID of the token being set as default.
-	 * @param \WC_Payment_Token $token    The payment token object.
-	 */
-	public function handle_payment_method_set_default( $token_id, $token ): void {
-		$event_data = $this->build_payment_method_event_data( 'set_default', $token );
-
-		// Trigger event dispatching.
-		$this->dispatcher->dispatch_event( 'payment_method_set_default', $event_data );
-	}
-
-	/**
-	 * Handle payment method deleted event.
-	 *
-	 * Triggers fraud protection event tracking when a payment method is deleted.
-	 *
-	 * @internal
-	 *
-	 * @param int               $token_id The ID of the deleted token.
-	 * @param \WC_Payment_Token $token    The payment token object.
-	 */
-	public function handle_payment_method_deleted( $token_id, $token ): void {
-		$event_data = $this->build_payment_method_event_data( 'deleted', $token );
-
-		// Trigger event dispatching.
-		$this->dispatcher->dispatch_event( 'payment_method_deleted', $event_data );
-	}
-
 	/**
 	 * Build payment method event-specific data.
 	 *
diff --git a/plugins/woocommerce/src/Internal/FraudProtection/SessionDataCollector.php b/plugins/woocommerce/src/Internal/FraudProtection/SessionDataCollector.php
index 4cd6aa22a8..6cf284540f 100644
--- a/plugins/woocommerce/src/Internal/FraudProtection/SessionDataCollector.php
+++ b/plugins/woocommerce/src/Internal/FraudProtection/SessionDataCollector.php
@@ -75,6 +75,60 @@ class SessionDataCollector {
 		);
 	}

+	/**
+	 * Get current billing country from customer data.
+	 *
+	 * Reuses the same logic as get_billing_address() but returns only the country.
+	 * Tries WC_Customer first, falls back to session data, with graceful error handling.
+	 *
+	 * @since 10.5.0
+	 *
+	 * @return string|null Current billing country code or null if unavailable.
+	 */
+	public function get_current_billing_country(): ?string {
+		try {
+			if ( WC()->customer instanceof \WC_Customer ) {
+				$country = WC()->customer->get_billing_country();
+				return ! empty( $country ) ? \sanitize_text_field( $country ) : null;
+			} elseif ( WC()->session instanceof \WC_Session ) {
+				$customer_data = WC()->session->get( 'customer' );
+				if ( is_array( $customer_data ) && ! empty( $customer_data['country'] ) ) {
+					return \sanitize_text_field( $customer_data['country'] );
+				}
+			}
+		} catch ( \Exception $e ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
+			// Graceful degradation.
+		}
+		return null;
+	}
+
+	/**
+	 * Get current shipping country from customer data.
+	 *
+	 * Reuses the same logic as get_shipping_address() but returns only the country.
+	 * Tries WC_Customer first, falls back to session data, with graceful error handling.
+	 *
+	 * @since 10.5.0
+	 *
+	 * @return string|null Current shipping country code or null if unavailable.
+	 */
+	public function get_current_shipping_country(): ?string {
+		try {
+			if ( WC()->customer instanceof \WC_Customer ) {
+				$country = WC()->customer->get_shipping_country();
+				return ! empty( $country ) ? \sanitize_text_field( $country ) : null;
+			} elseif ( WC()->session instanceof \WC_Session ) {
+				$customer_data = WC()->session->get( 'customer' );
+				if ( is_array( $customer_data ) && ! empty( $customer_data['shipping_country'] ) ) {
+					return \sanitize_text_field( $customer_data['shipping_country'] );
+				}
+			}
+		} catch ( \Exception $e ) { // phpcs:ignore Generic.CodeAnalysis.EmptyStatement.DetectedCatch
+			// Graceful degradation.
+		}
+		return null;
+	}
+
 	/**
 	 * Get session data including session ID, IP address, email, and user agent.
 	 *
diff --git a/plugins/woocommerce/src/StoreApi/Utilities/CartController.php b/plugins/woocommerce/src/StoreApi/Utilities/CartController.php
index a2120758e8..a6e1aafe85 100644
--- a/plugins/woocommerce/src/StoreApi/Utilities/CartController.php
+++ b/plugins/woocommerce/src/StoreApi/Utilities/CartController.php
@@ -10,6 +10,8 @@ use Automattic\WooCommerce\StoreApi\Exceptions\OutOfStockException;
 use Automattic\WooCommerce\StoreApi\Exceptions\PartialOutOfStockException;
 use Automattic\WooCommerce\StoreApi\Exceptions\RouteException;
 use Automattic\WooCommerce\StoreApi\Exceptions\TooManyInCartException;
+use Automattic\WooCommerce\Internal\FraudProtection\CartEventTracker;
+use Automattic\WooCommerce\Internal\FraudProtection\FraudProtectionController;
 use Automattic\WooCommerce\StoreApi\Utilities\ArrayUtils;
 use Automattic\WooCommerce\StoreApi\Utilities\DraftOrderTrait;
 use Automattic\WooCommerce\StoreApi\Utilities\NoticeHandler;
@@ -223,6 +225,12 @@ class CartController {
 			$request['cart_item_data']
 		);

+		// Track cart event for fraud protection.
+		if ( $product instanceof \WC_Product && wc_get_container()->get( FraudProtectionController::class )->feature_is_enabled() ) {
+			wc_get_container()->get( CartEventTracker::class )
+				->track_cart_item_added( $cart_id, $this->get_product_id( $product ), (int) $request_quantity, $this->get_variation_id( $product ) );
+		}
+
 		return $cart_id;
 	}

diff --git a/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Utilities/CartControllerTests.php b/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Utilities/CartControllerTests.php
index 729d6af767..c7cc674876 100644
--- a/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Utilities/CartControllerTests.php
+++ b/plugins/woocommerce/tests/php/src/Blocks/StoreApi/Utilities/CartControllerTests.php
@@ -3,6 +3,8 @@ declare( strict_types = 1 );

 namespace Automattic\WooCommerce\Tests\Blocks\StoreApi\Utilities;

+use Automattic\WooCommerce\Internal\FraudProtection\CartEventTracker;
+use Automattic\WooCommerce\Internal\FraudProtection\FraudProtectionController;
 use Automattic\WooCommerce\StoreApi\Utilities\CartController;
 use Automattic\WooCommerce\Tests\Blocks\Helpers\FixtureData;
 use Yoast\PHPUnitPolyfills\TestCases\TestCase;
@@ -19,6 +21,11 @@ class CartControllerTests extends TestCase {
 		parent::tearDown();
 		WC()->cart->empty_cart();
 		remove_all_filters( 'woocommerce_cart_shipping_packages' );
+
+		// Reset DI container to clear any mocks.
+		$container = wc_get_container();
+		$container->reset_all_resolved();
+		$container->reset_all_replacements();
 	}

 	/**
@@ -283,4 +290,150 @@ class CartControllerTests extends TestCase {

 		remove_all_filters( 'woocommerce_cart_shipping_packages' );
 	}
+
+	/**
+	 * Test that fraud protection tracking is called when adding items via Store API.
+	 */
+	public function test_add_to_cart_triggers_fraud_protection_tracking(): void {
+		$fixtures = new FixtureData();
+		$product  = $fixtures->get_simple_product(
+			array(
+				'name'          => 'Fraud Protection Test Product',
+				'regular_price' => 10,
+			)
+		);
+
+		// Create mock for CartEventTracker.
+		$mock_cart_tracker = $this->createMock( CartEventTracker::class );
+
+		// Create mock for FraudProtectionController that returns enabled.
+		$mock_controller = $this->createMock( FraudProtectionController::class );
+		$mock_controller->method( 'feature_is_enabled' )->willReturn( true );
+
+		// Replace container instances with mocks.
+		$container = wc_get_container();
+		$container->replace( FraudProtectionController::class, $mock_controller );
+		$container->replace( CartEventTracker::class, $mock_cart_tracker );
+
+		// Expect track_cart_item_added to be called once with correct parameters.
+		$mock_cart_tracker
+			->expects( $this->once() )
+			->method( 'track_cart_item_added' )
+			->with(
+				$this->isType( 'string' ), // cart_id.
+				$this->equalTo( $product->get_id() ), // product_id.
+				$this->equalTo( 2 ), // quantity.
+				$this->equalTo( 0 ) // variation_id.
+			);
+
+		// Use Store API CartController to add item.
+		$cart_controller = new CartController();
+		$cart_controller->add_to_cart(
+			array(
+				'id'       => $product->get_id(),
+				'quantity' => 2,
+			)
+		);
+	}
+
+	/**
+	 * Test that fraud protection tracking is NOT called when feature is disabled.
+	 */
+	public function test_add_to_cart_skips_fraud_protection_tracking_when_disabled(): void {
+		$fixtures = new FixtureData();
+		$product  = $fixtures->get_simple_product(
+			array(
+				'name'          => 'Fraud Protection Disabled Test Product',
+				'regular_price' => 10,
+			)
+		);
+
+		// Create mock for CartEventTracker.
+		$mock_cart_tracker = $this->createMock( CartEventTracker::class );
+
+		// Create mock for FraudProtectionController that returns disabled.
+		$mock_controller = $this->createMock( FraudProtectionController::class );
+		$mock_controller->method( 'feature_is_enabled' )->willReturn( false );
+
+		// Replace container instances with mocks.
+		$container = wc_get_container();
+		$container->replace( FraudProtectionController::class, $mock_controller );
+		$container->replace( CartEventTracker::class, $mock_cart_tracker );
+
+		// Expect track_cart_item_added to NOT be called.
+		$mock_cart_tracker
+			->expects( $this->never() )
+			->method( 'track_cart_item_added' );
+
+		// Use Store API CartController to add item.
+		$cart_controller = new CartController();
+		$cart_controller->add_to_cart(
+			array(
+				'id'       => $product->get_id(),
+				'quantity' => 1,
+			)
+		);
+	}
+
+	/**
+	 * Test that fraud protection tracking includes variation_id for variable products.
+	 */
+	public function test_add_to_cart_tracks_variation_id_for_variable_products(): void {
+		// Create a variable product with variations using WC_Helper_Product.
+		$variable_product = \WC_Helper_Product::create_variation_product();
+
+		// Get the third variation which has all attributes defined (size, colour, number).
+		$children     = $variable_product->get_children();
+		$variation_id = $children[2]; // "DUMMY SKU VARIABLE HUGE RED 0" - has all attributes.
+
+		// Create mock for CartEventTracker.
+		$mock_cart_tracker = $this->createMock( CartEventTracker::class );
+
+		// Create mock for FraudProtectionController that returns enabled.
+		$mock_controller = $this->createMock( FraudProtectionController::class );
+		$mock_controller->method( 'feature_is_enabled' )->willReturn( true );
+
+		// Replace container instances with mocks.
+		$container = wc_get_container();
+		$container->replace( FraudProtectionController::class, $mock_controller );
+		$container->replace( CartEventTracker::class, $mock_cart_tracker );
+
+		// Expect track_cart_item_added to be called with the correct variation_id.
+		$mock_cart_tracker
+			->expects( $this->once() )
+			->method( 'track_cart_item_added' )
+			->with(
+				$this->isType( 'string' ), // cart_id.
+				$this->equalTo( $variable_product->get_id() ), // product_id.
+				$this->equalTo( 1 ), // quantity.
+				$this->equalTo( $variation_id ) // variation_id.
+			);
+
+		// Use Store API CartController to add variation with all required attributes.
+		// CartController::parse_variation_data() expects an array of {attribute, value} pairs.
+		$cart_controller = new CartController();
+		$cart_controller->add_to_cart(
+			array(
+				'id'        => $variation_id,
+				'quantity'  => 1,
+				'variation' => array(
+					array(
+						'attribute' => 'attribute_pa_size',
+						'value'     => 'huge',
+					),
+					array(
+						'attribute' => 'attribute_pa_colour',
+						'value'     => 'red',
+					),
+					array(
+						'attribute' => 'attribute_pa_number',
+						'value'     => '0',
+					),
+				),
+			)
+		);
+
+		// Clean up.
+		$variable_product->delete( true );
+	}
 }
diff --git a/plugins/woocommerce/tests/php/src/Internal/FraudProtection/CartEventTrackerTest.php b/plugins/woocommerce/tests/php/src/Internal/FraudProtection/CartEventTrackerTest.php
index da92b7d3de..c403b827be 100644
--- a/plugins/woocommerce/tests/php/src/Internal/FraudProtection/CartEventTrackerTest.php
+++ b/plugins/woocommerce/tests/php/src/Internal/FraudProtection/CartEventTrackerTest.php
@@ -63,10 +63,7 @@ class CartEventTrackerTest extends \WC_Unit_Test_Case {

 		// Create system under test.
 		$this->sut = new CartEventTracker();
-		$this->sut->init(
-			$this->mock_dispatcher,
-			$this->mock_controller
-		);
+		$this->sut->init( $this->mock_dispatcher );

 		// Create a test product.
 		$this->test_product = \WC_Helper_Product::create_simple_product();
@@ -76,43 +73,9 @@ class CartEventTrackerTest extends \WC_Unit_Test_Case {
 	}

 	/**
-	 * Test that register does not register hooks when feature is disabled.
-	 */
-	public function test_register_does_not_register_hooks_when_feature_disabled(): void {
-		// Mock feature as disabled.
-		$this->mock_controller->method( 'feature_is_enabled' )->willReturn( false );
-
-		// Call register.
-		$this->sut->register();
-
-		// Verify hooks were not registered.
-		$this->assertFalse( has_action( 'woocommerce_add_to_cart', array( $this->sut, 'handle_track_cart_item_added' ) ) );
-		$this->assertFalse( has_action( 'woocommerce_after_cart_item_quantity_update', array( $this->sut, 'handle_track_cart_item_updated' ) ) );
-		$this->assertFalse( has_action( 'woocommerce_remove_cart_item', array( $this->sut, 'handle_track_cart_item_removed' ) ) );
-		$this->assertFalse( has_action( 'woocommerce_restore_cart_item', array( $this->sut, 'handle_track_cart_item_restored' ) ) );
-	}
-
-	/**
-	 * Test that register registers hooks when feature is enabled.
+	 * Test track_cart_item_added tracks event.
 	 */
-	public function test_register_registers_hooks_when_feature_enabled(): void {
-		// Mock feature as enabled.
-		$this->mock_controller->method( 'feature_is_enabled' )->willReturn( true );
-
-		// Call register.
-		$this->sut->register();
-
-		// Verify hooks were registered with correct priority.
-		$this->assertEquals( 10, has_action( 'woocommerce_add_to_cart', array( $this->sut, 'handle_track_cart_item_added' ) ) );
-		$this->assertEquals( 10, has_action( 'woocommerce_after_cart_item_quantity_update', array( $this->sut, 'handle_track_cart_item_updated' ) ) );
-		$this->assertEquals( 10, has_action( 'woocommerce_remove_cart_item', array( $this->sut, 'handle_track_cart_item_removed' ) ) );
-		$this->assertEquals( 10, has_action( 'woocommerce_restore_cart_item', array( $this->sut, 'handle_track_cart_item_restored' ) ) );
-	}
-
-	/**
-	 * Test handle_track_cart_item_added tracks event.
-	 */
-	public function test_handle_track_cart_item_added_tracks_event(): void {
+	public function test_track_cart_item_added_tracks_event(): void {
 		// Mock the dispatcher to verify dispatch_event is called with event data.
 		$this->mock_dispatcher
 			->expects( $this->once() )
@@ -133,21 +96,19 @@ class CartEventTrackerTest extends \WC_Unit_Test_Case {
 				)
 			);

-		// Call the handler.
-		$this->sut->handle_track_cart_item_added(
+		// Call the method.
+		$this->sut->track_cart_item_added(
 			'test_cart_key',
 			$this->test_product->get_id(),
 			2,
-			0,
-			array(),
-			array()
+			0
 		);
 	}

 	/**
-	 * Test handle_track_cart_item_updated tracks event.
+	 * Test track_cart_item_updated tracks event.
 	 */
-	public function test_handle_track_cart_item_updated_tracks_event(): void {
+	public function test_track_cart_item_updated_tracks_event(): void {
 		// Add item to cart first.
 		$cart_item_key = WC()->cart->add_to_cart( $this->test_product->get_id(), 1 );

@@ -171,8 +132,8 @@ class CartEventTrackerTest extends \WC_Unit_Test_Case {
 				)
 			);

-		// Call the handler.
-		$this->sut->handle_track_cart_item_updated(
+		// Call the method.
+		$this->sut->track_cart_item_updated(
 			$cart_item_key,
 			5,
 			1,
@@ -181,9 +142,9 @@ class CartEventTrackerTest extends \WC_Unit_Test_Case {
 	}

 	/**
-	 * Test handle_track_cart_item_removed tracks event.
+	 * Test track_cart_item_removed tracks event.
 	 */
-	public function test_handle_track_cart_item_removed_tracks_event(): void {
+	public function test_track_cart_item_removed_tracks_event(): void {
 		// Add item to cart.
 		$cart_item_key = WC()->cart->add_to_cart( $this->test_product->get_id(), 1 );

@@ -206,14 +167,14 @@ class CartEventTrackerTest extends \WC_Unit_Test_Case {
 		// Remove the item from cart.
 		WC()->cart->remove_cart_item( $cart_item_key );

-		// Call the handler directly (since hooks aren't registered in test context).
-		$this->sut->handle_track_cart_item_removed( $cart_item_key, WC()->cart );
+		// Call the method directly.
+		$this->sut->track_cart_item_removed( $cart_item_key, WC()->cart );
 	}

 	/**
-	 * Test handle_track_cart_item_restored tracks event.
+	 * Test track_cart_item_restored tracks event.
 	 */
-	public function test_handle_track_cart_item_restored_tracks_event(): void {
+	public function test_track_cart_item_restored_tracks_event(): void {
 		// Add item to cart.
 		$cart_item_key = WC()->cart->add_to_cart( $this->test_product->get_id(), 1 );

@@ -233,8 +194,8 @@ class CartEventTrackerTest extends \WC_Unit_Test_Case {
 				)
 			);

-		// Call the handler directly (simulating restore action).
-		$this->sut->handle_track_cart_item_restored(
+		// Call the method directly (simulating restore action).
+		$this->sut->track_cart_item_restored(
 			$cart_item_key,
 			WC()->cart
 		);
@@ -267,14 +228,12 @@ class CartEventTrackerTest extends \WC_Unit_Test_Case {
 				)
 			);

-		// Call the handler with variation ID.
-		$this->sut->handle_track_cart_item_added(
+		// Call the method with variation ID.
+		$this->sut->track_cart_item_added(
 			'test_cart_key',
 			$variable_product->get_id(),
 			1,
-			$variation_id,
-			array(),
-			array()
+			$variation_id
 		);

 		// Clean up.
@@ -294,11 +253,5 @@ class CartEventTrackerTest extends \WC_Unit_Test_Case {

 		// Empty cart.
 		WC()->cart->empty_cart();
-
-		// Remove all registered hooks.
-		remove_all_actions( 'woocommerce_add_to_cart' );
-		remove_all_actions( 'woocommerce_after_cart_item_quantity_update' );
-		remove_all_actions( 'woocommerce_remove_cart_item' );
-		remove_all_actions( 'woocommerce_restore_cart_item' );
 	}
 }
diff --git a/plugins/woocommerce/tests/php/src/Internal/FraudProtection/CheckoutEventTrackerTest.php b/plugins/woocommerce/tests/php/src/Internal/FraudProtection/CheckoutEventTrackerTest.php
index 5406b8aa92..2a1a0a498e 100644
--- a/plugins/woocommerce/tests/php/src/Internal/FraudProtection/CheckoutEventTrackerTest.php
+++ b/plugins/woocommerce/tests/php/src/Internal/FraudProtection/CheckoutEventTrackerTest.php
@@ -35,6 +35,13 @@ class CheckoutEventTrackerTest extends \WC_Unit_Test_Case {
 	 */
 	private $mock_dispatcher;

+	/**
+	 * Mock session data collector.
+	 *
+	 * @var SessionDataCollector|\PHPUnit\Framework\MockObject\MockObject
+	 */
+	private $mock_session_data_collector;
+
 	/**
 	 * Mock fraud protection controller.
 	 *
@@ -54,43 +61,13 @@ class CheckoutEventTrackerTest extends \WC_Unit_Test_Case {
 		}

 		// Create mocks.
-		$this->mock_dispatcher = $this->createMock( FraudProtectionDispatcher::class );
-		$this->mock_controller = $this->createMock( FraudProtectionController::class );
+		$this->mock_dispatcher             = $this->createMock( FraudProtectionDispatcher::class );
+		$this->mock_session_data_collector = $this->createMock( SessionDataCollector::class );
+		$this->mock_controller             = $this->createMock( FraudProtectionController::class );

 		// Create system under test.
 		$this->sut = new CheckoutEventTracker();
-		$this->sut->init(
-			$this->mock_dispatcher,
-			$this->mock_controller
-		);
-	}
-
-	/**
-	 * Test that register does not register hooks when feature is disabled.
-	 */
-	public function test_register_does_not_register_hooks_when_feature_disabled(): void {
-		// Mock feature as disabled.
-		$this->mock_controller->method( 'feature_is_enabled' )->willReturn( false );
-
-		// Call register.
-		$this->sut->register();
-
-		// Verify hooks were not registered.
-		$this->assertFalse( has_action( 'woocommerce_checkout_update_order_review', array( $this->sut, 'handle_shortcode_checkout_field_update' ) ) );
-	}
-
-	/**
-	 * Test that register registers shortcode checkout hooks when feature is enabled.
-	 */
-	public function test_register_registers_hooks_when_feature_enabled(): void {
-		// Mock feature as enabled.
-		$this->mock_controller->method( 'feature_is_enabled' )->willReturn( true );
-
-		// Call register.
-		$this->sut->register();
-
-		// Verify hook was registered.
-		$this->assertNotFalse( has_action( 'woocommerce_checkout_update_order_review', array( $this->sut, 'handle_shortcode_checkout_field_update' ) ) );
+		$this->sut->init( $this->mock_dispatcher, $this->mock_session_data_collector );
 	}

 	// ========================================
@@ -122,18 +99,27 @@ class CheckoutEventTrackerTest extends \WC_Unit_Test_Case {
 	// ========================================

 	/**
-	 * Test handle_shortcode_checkout_field_update schedules event with billing email.
+	 * Test track_shortcode_checkout_field_update schedules event with billing email when billing country changes.
 	 */
-	public function test_handle_shortcode_checkout_field_update_schedules_event_with_billing_email(): void {
+	public function test_track_shortcode_checkout_field_update_schedules_event_with_billing_email(): void {
 		// Mock feature as enabled.
 		$this->mock_controller->method( 'feature_is_enabled' )->willReturn( true );

+		// Mock SessionDataCollector to return different billing country.
+		$this->mock_session_data_collector
+			->method( 'get_current_billing_country' )
+			->willReturn( 'CA' ); // Current country is CA.
+
+		$this->mock_session_data_collector
+			->method( 'get_current_shipping_country' )
+			->willReturn( null );
+
 		// Mock scheduler to verify dispatch_event is called.
 		$this->mock_dispatcher
 			->expects( $this->once() )
 			->method( 'dispatch_event' )
 			->with(
-				$this->equalTo( 'checkout_field_update' ),
+				$this->equalTo( 'checkout_update' ),
 				$this->callback(
 					function ( $event_data ) {
 						return isset( $event_data['action'] )
@@ -144,21 +130,27 @@ class CheckoutEventTrackerTest extends \WC_Unit_Test_Case {
 				)
 			);

-		// Register hooks.
-		$this->sut->register();
-
-		// Simulate checkout field update with billing email.
-		$posted_data = 'billing_email=test@example.com&billing_first_name=John&billing_last_name=Doe';
-		$this->sut->handle_shortcode_checkout_field_update( $posted_data );
+		// Simulate checkout field update with billing email and country change (CA -> US).
+		$posted_data = 'billing_email=test@example.com&billing_first_name=John&billing_last_name=Doe&billing_country=US';
+		$this->sut->track_shortcode_checkout_field_update( $posted_data );
 	}

 	/**
-	 * Test handle_shortcode_checkout_field_update extracts billing fields correctly.
+	 * Test track_shortcode_checkout_field_update extracts billing fields correctly when country changes.
 	 */
-	public function test_handle_shortcode_checkout_field_update_extracts_billing_fields(): void {
+	public function test_track_shortcode_checkout_field_update_extracts_billing_fields(): void {
 		// Mock feature as enabled.
 		$this->mock_controller->method( 'feature_is_enabled' )->willReturn( true );

+		// Mock SessionDataCollector to return different billing country.
+		$this->mock_session_data_collector
+			->method( 'get_current_billing_country' )
+			->willReturn( 'CA' ); // Current country is CA.
+
+		$this->mock_session_data_collector
+			->method( 'get_current_shipping_country' )
+			->willReturn( null );
+
 		// Mock scheduler to capture event data.
 		$captured_event_data = null;
 		$this->mock_dispatcher
@@ -170,12 +162,9 @@ class CheckoutEventTrackerTest extends \WC_Unit_Test_Case {
 				}
 			);

-		// Register hooks.
-		$this->sut->register();
-
-		// Simulate checkout field update with multiple billing fields.
+		// Simulate checkout field update with multiple billing fields and country change.
 		$posted_data = 'billing_email=test@example.com&billing_first_name=John&billing_last_name=Doe&billing_country=US&billing_city=New+York';
-		$this->sut->handle_shortcode_checkout_field_update( $posted_data );
+		$this->sut->track_shortcode_checkout_field_update( $posted_data );

 		// Verify extracted fields.
 		$this->assertNotNull( $captured_event_data );
@@ -188,12 +177,21 @@ class CheckoutEventTrackerTest extends \WC_Unit_Test_Case {
 	}

 	/**
-	 * Test handle_shortcode_checkout_field_update extracts shipping fields when ship_to_different_address is set.
+	 * Test track_shortcode_checkout_field_update extracts shipping fields when ship_to_different_address is set and shipping country changes.
 	 */
-	public function test_handle_shortcode_checkout_field_update_extracts_shipping_fields(): void {
+	public function test_track_shortcode_checkout_field_update_extracts_shipping_fields(): void {
 		// Mock feature as enabled.
 		$this->mock_controller->method( 'feature_is_enabled' )->willReturn( true );

+		// Mock SessionDataCollector to return different shipping country.
+		$this->mock_session_data_collector
+			->method( 'get_current_billing_country' )
+			->willReturn( null );
+
+		$this->mock_session_data_collector
+			->method( 'get_current_shipping_country' )
+			->willReturn( 'CA' ); // Current shipping country is CA.
+
 		// Mock scheduler to capture event data.
 		$captured_event_data = null;
 		$this->mock_dispatcher
@@ -205,12 +203,9 @@ class CheckoutEventTrackerTest extends \WC_Unit_Test_Case {
 				}
 			);

-		// Register hooks.
-		$this->sut->register();
-
-		// Simulate checkout field update with shipping fields.
-		$posted_data = 'billing_email=test@example.com&ship_to_different_address=1&shipping_first_name=Jane&shipping_last_name=Smith&shipping_city=Los+Angeles';
-		$this->sut->handle_shortcode_checkout_field_update( $posted_data );
+		// Simulate checkout field update with shipping fields and country change.
+		$posted_data = 'billing_email=test@example.com&ship_to_different_address=1&shipping_first_name=Jane&shipping_last_name=Smith&shipping_city=Los+Angeles&shipping_country=US';
+		$this->sut->track_shortcode_checkout_field_update( $posted_data );

 		// Verify extracted fields.
 		$this->assertNotNull( $captured_event_data );
@@ -220,12 +215,21 @@ class CheckoutEventTrackerTest extends \WC_Unit_Test_Case {
 	}

 	/**
-	 * Test handle_shortcode_checkout_field_update does not extract shipping fields when ship_to_different_address is not set.
+	 * Test track_shortcode_checkout_field_update does not extract shipping fields when ship_to_different_address is not set.
 	 */
-	public function test_handle_shortcode_checkout_field_update_skips_shipping_fields_when_not_different_address(): void {
+	public function test_track_shortcode_checkout_field_update_skips_shipping_fields_when_not_different_address(): void {
 		// Mock feature as enabled.
 		$this->mock_controller->method( 'feature_is_enabled' )->willReturn( true );

+		// Mock SessionDataCollector to return different billing country.
+		$this->mock_session_data_collector
+			->method( 'get_current_billing_country' )
+			->willReturn( 'CA' ); // Current billing country is CA.
+
+		$this->mock_session_data_collector
+			->method( 'get_current_shipping_country' )
+			->willReturn( null );
+
 		// Mock scheduler to capture event data.
 		$captured_event_data = null;
 		$this->mock_dispatcher
@@ -237,16 +241,224 @@ class CheckoutEventTrackerTest extends \WC_Unit_Test_Case {
 				}
 			);

-		// Register hooks.
-		$this->sut->register();
-
-		// Simulate checkout field update without ship_to_different_address.
-		$posted_data = 'billing_email=test@example.com&shipping_first_name=Jane&shipping_last_name=Smith';
-		$this->sut->handle_shortcode_checkout_field_update( $posted_data );
+		// Simulate checkout field update without ship_to_different_address but with billing country change.
+		$posted_data = 'billing_email=test@example.com&billing_country=US&shipping_first_name=Jane&shipping_last_name=Smith';
+		$this->sut->track_shortcode_checkout_field_update( $posted_data );

 		// Verify shipping fields are not extracted.
 		$this->assertNotNull( $captured_event_data );
 		$this->assertArrayNotHasKey( 'shipping_first_name', $captured_event_data );
 		$this->assertArrayNotHasKey( 'shipping_last_name', $captured_event_data );
 	}
+
+	// ========================================
+	// Country Change Detection Tests
+	// ========================================
+
+	/**
+	 * Test event is dispatched when billing country changes.
+	 */
+	public function test_event_dispatched_when_billing_country_changes(): void {
+		// Mock SessionDataCollector to return different billing country.
+		$this->mock_session_data_collector
+			->method( 'get_current_billing_country' )
+			->willReturn( 'CA' ); // Current country is CA.
+
+		$this->mock_session_data_collector
+			->method( 'get_current_shipping_country' )
+			->willReturn( null );
+
+		// Expect event to be dispatched once.
+		$this->mock_dispatcher
+			->expects( $this->once() )
+			->method( 'dispatch_event' )
+			->with(
+				$this->equalTo( 'checkout_update' ),
+				$this->callback(
+					function ( $event_data ) {
+						return isset( $event_data['billing_country'] ) && 'US' === $event_data['billing_country'];
+					}
+				)
+			);
+
+		// Posted data with billing country changing from CA to US.
+		$posted_data = 'billing_email=test@example.com&billing_country=US';
+		$this->sut->track_shortcode_checkout_field_update( $posted_data );
+	}
+
+	/**
+	 * Test event is dispatched when shipping country changes.
+	 */
+	public function test_event_dispatched_when_shipping_country_changes(): void {
+		// Mock SessionDataCollector to return current countries.
+		$this->mock_session_data_collector
+			->method( 'get_current_billing_country' )
+			->willReturn( 'US' ); // Current billing country matches posted.
+
+		$this->mock_session_data_collector
+			->method( 'get_current_shipping_country' )
+			->willReturn( 'CA' ); // Current shipping country is CA.
+
+		// Expect event to be dispatched once.
+		$this->mock_dispatcher
+			->expects( $this->once() )
+			->method( 'dispatch_event' )
+			->with(
+				$this->equalTo( 'checkout_update' ),
+				$this->callback(
+					function ( $event_data ) {
+						return isset( $event_data['shipping_country'] ) && 'US' === $event_data['shipping_country'];
+					}
+				)
+			);
+
+		// Posted data with shipping country changing from CA to US.
+		$posted_data = 'billing_country=US&ship_to_different_address=1&shipping_country=US';
+		$this->sut->track_shortcode_checkout_field_update( $posted_data );
+	}
+
+	/**
+	 * Test event is NOT dispatched when neither country changes.
+	 */
+	public function test_event_not_dispatched_when_no_country_changes(): void {
+		// Mock SessionDataCollector to return same countries as posted.
+		$this->mock_session_data_collector
+			->method( 'get_current_billing_country' )
+			->willReturn( 'US' ); // Same as posted.
+
+		$this->mock_session_data_collector
+			->method( 'get_current_shipping_country' )
+			->willReturn( 'US' ); // Same as posted.
+
+		// Expect event to NOT be dispatched.
+		$this->mock_dispatcher
+			->expects( $this->never() )
+			->method( 'dispatch_event' );
+
+		// Posted data with no country changes.
+		$posted_data = 'billing_email=test@example.com&billing_country=US&shipping_country=US';
+		$this->sut->track_shortcode_checkout_field_update( $posted_data );
+	}
+
+	/**
+	 * Test event is NOT dispatched when only non-country fields change.
+	 */
+	public function test_event_not_dispatched_when_only_non_country_fields_change(): void {
+		// Mock SessionDataCollector to return countries.
+		$this->mock_session_data_collector
+			->method( 'get_current_billing_country' )
+			->willReturn( 'US' );
+
+		$this->mock_session_data_collector
+			->method( 'get_current_shipping_country' )
+			->willReturn( null );
+
+		// Expect event to NOT be dispatched.
+		$this->mock_dispatcher
+			->expects( $this->never() )
+			->method( 'dispatch_event' );
+
+		// Posted data with only non-country fields (email, name, phone).
+		$posted_data = 'billing_email=test@example.com&billing_first_name=John&billing_phone=1234567890';
+		$this->sut->track_shortcode_checkout_field_update( $posted_data );
+	}
+
+	/**
+	 * Test event is NOT dispatched when ship_to_different_address is not set and current shipping matches billing.
+	 */
+	public function test_event_not_dispatched_when_shipping_already_matches_billing(): void {
+		// Mock SessionDataCollector: shipping already matches billing.
+		$this->mock_session_data_collector
+			->method( 'get_current_billing_country' )
+			->willReturn( 'US' );
+
+		$this->mock_session_data_collector
+			->method( 'get_current_shipping_country' )
+			->willReturn( 'US' ); // Already matches billing - no change.
+
+		// Expect event to NOT be dispatched (no effective change).
+		$this->mock_dispatcher
+			->expects( $this->never() )
+			->method( 'dispatch_event' );
+
+		// Posted data with NO ship_to_different_address flag, billing stays US.
+		$posted_data = 'billing_country=US&billing_email=test@example.com';
+		$this->sut->track_shortcode_checkout_field_update( $posted_data );
+	}
+
+	/**
+	 * Test event is dispatched when billing country changes from null.
+	 */
+	public function test_event_dispatched_when_billing_country_changes_from_null(): void {
+		// Mock SessionDataCollector to return null for current billing country.
+		$this->mock_session_data_collector
+			->method( 'get_current_billing_country' )
+			->willReturn( null ); // No current billing country.
+
+		$this->mock_session_data_collector
+			->method( 'get_current_shipping_country' )
+			->willReturn( null );
+
+		// Expect event to be dispatched.
+		$this->mock_dispatcher
+			->expects( $this->once() )
+			->method( 'dispatch_event' );
+
+		// Posted data with billing country (first time setting).
+		$posted_data = 'billing_email=test@example.com&billing_country=US';
+		$this->sut->track_shortcode_checkout_field_update( $posted_data );
+	}
+
+	/**
+	 * Test event is dispatched when user unchecks ship_to_different_address and current shipping country differs from billing.
+	 *
+	 * Scenario: User had different shipping address with different country (e.g., shipping=CA, billing=US),
+	 * then unchecks "ship to different address". The effective shipping country changes from CA to US.
+	 */
+	public function test_event_dispatched_when_ship_to_different_address_unchecked_with_different_countries(): void {
+		// Mock SessionDataCollector: billing=US, shipping=CA (previously different).
+		$this->mock_session_data_collector
+			->method( 'get_current_billing_country' )
+			->willReturn( 'US' );
+
+		$this->mock_session_data_collector
+			->method( 'get_current_shipping_country' )
+			->willReturn( 'CA' ); // Was different.
+
+		// Expect event to be dispatched (shipping effectively changed from CA to US).
+		$this->mock_dispatcher
+			->expects( $this->once() )
+			->method( 'dispatch_event' )
+			->with(
+				$this->equalTo( 'checkout_update' ),
+				$this->anything()
+			);
+
+		// Posted data: ship_to_different_address NOT set (unchecked), billing country is US.
+		$posted_data = 'billing_country=US&billing_email=test@example.com';
+		$this->sut->track_shortcode_checkout_field_update( $posted_data );
+	}
+
+	/**
+	 * Test event is NOT dispatched when user unchecks ship_to_different_address but countries are already the same.
+	 */
+	public function test_event_not_dispatched_when_ship_to_different_address_unchecked_with_same_countries(): void {
+		// Mock SessionDataCollector: billing=US, shipping=US (already same).
+		$this->mock_session_data_collector
+			->method( 'get_current_billing_country' )
+			->willReturn( 'US' );
+
+		$this->mock_session_data_collector
+			->method( 'get_current_shipping_country' )
+			->willReturn( 'US' ); // Same as billing.
+
+		// Expect event to NOT be dispatched (no effective change).
+		$this->mock_dispatcher
+			->expects( $this->never() )
+			->method( 'dispatch_event' );
+
+		// Posted data: ship_to_different_address NOT set, billing country is US.
+		$posted_data = 'billing_country=US&billing_email=test@example.com';
+		$this->sut->track_shortcode_checkout_field_update( $posted_data );
+	}
 }
diff --git a/plugins/woocommerce/tests/php/src/Internal/FraudProtection/PaymentMethodEventTrackerTest.php b/plugins/woocommerce/tests/php/src/Internal/FraudProtection/PaymentMethodEventTrackerTest.php
index c1636572b1..5c4cc88c7b 100644
--- a/plugins/woocommerce/tests/php/src/Internal/FraudProtection/PaymentMethodEventTrackerTest.php
+++ b/plugins/woocommerce/tests/php/src/Internal/FraudProtection/PaymentMethodEventTrackerTest.php
@@ -41,47 +41,12 @@ class PaymentMethodEventTrackerTest extends \WC_Unit_Test_Case {
 		$this->sut = $container->get( PaymentMethodEventTracker::class );
 	}

-	/**
-	 * Test that hooks are registered when feature is enabled.
-	 *
-	 * @testdox Should register hooks when feature is enabled.
-	 */
-	public function test_hooks_registered_when_feature_enabled(): void {
-		$this->sut->register();
-
-		$this->assertNotFalse( has_action( 'woocommerce_new_payment_token', array( $this->sut, 'handle_payment_method_added' ) ) );
-		$this->assertNotFalse( has_action( 'woocommerce_payment_token_updated', array( $this->sut, 'handle_payment_method_updated' ) ) );
-		$this->assertNotFalse( has_action( 'woocommerce_payment_token_set_default', array( $this->sut, 'handle_payment_method_set_default' ) ) );
-		$this->assertNotFalse( has_action( 'woocommerce_payment_token_deleted', array( $this->sut, 'handle_payment_method_deleted' ) ) );
-	}
-
-	/**
-	 * Test that hooks are not registered when feature is disabled.
-	 *
-	 * @testdox Should not register hooks when feature is disabled.
-	 */
-	public function test_hooks_not_registered_when_feature_disabled(): void {
-		update_option( 'woocommerce_feature_fraud_protection_enabled', 'no' );
-
-		$container = wc_get_container();
-		$container->reset_all_resolved();
-		$this->sut = $container->get( PaymentMethodEventTracker::class );
-
-		$this->sut->register();
-
-		$this->assertFalse( has_action( 'woocommerce_new_payment_token', array( $this->sut, 'handle_payment_method_added' ) ) );
-		$this->assertFalse( has_action( 'woocommerce_payment_token_updated', array( $this->sut, 'handle_payment_method_updated' ) ) );
-		$this->assertFalse( has_action( 'woocommerce_payment_token_set_default', array( $this->sut, 'handle_payment_method_set_default' ) ) );
-		$this->assertFalse( has_action( 'woocommerce_payment_token_deleted', array( $this->sut, 'handle_payment_method_deleted' ) ) );
-	}
-
 	/**
 	 * Test payment method added event tracking.
 	 *
 	 * @testdox Should track payment method added event.
 	 */
 	public function test_handle_payment_method_added(): void {
-		$this->sut->register();

 		$user_id = $this->factory->user->create();

@@ -115,159 +80,12 @@ class PaymentMethodEventTrackerTest extends \WC_Unit_Test_Case {
 		);
 	}

-	/**
-	 * Test payment method updated event tracking.
-	 *
-	 * @testdox Should track payment method updated event.
-	 */
-	public function test_handle_payment_method_updated(): void {
-		$this->sut->register();
-
-		$user_id = $this->factory->user->create();
-
-		$token = new \WC_Payment_Token_CC();
-		$token->set_token( 'test_token_456' );
-		$token->set_gateway_id( 'stripe' );
-		$token->set_card_type( 'mastercard' );
-		$token->set_last4( '5555' );
-		$token->set_expiry_month( '06' );
-		$token->set_expiry_year( '2026' );
-		$token->set_user_id( $user_id );
-		$token->save();
-
-		// Update the token to trigger the 'updated' event.
-		$token->set_expiry_year( '2027' );
-		$token->save();
-
-		// Verify that the event was sent to the API with correct payload.
-		$this->assertLogged(
-			'info',
-			'Sending fraud protection event: payment_method_updated',
-			array(
-				'source'  => 'woo-fraud-protection',
-				'payload' => array(
-					'event_type' => 'payment_method_updated',
-					'event_data' => array(
-						'action'     => 'updated',
-						'token_id'   => $token->get_id(),
-						'gateway_id' => 'stripe',
-						'card_type'  => 'mastercard',
-					),
-				),
-			)
-		);
-	}
-
-	/**
-	 * Test payment method set as default event tracking.
-	 *
-	 * @testdox Should track payment method set as default event.
-	 */
-	public function test_handle_payment_method_set_default(): void {
-		$this->sut->register();
-
-		$user_id = $this->factory->user->create();
-
-		// Create first token (will be automatically set as default since it's the user's first token).
-		$token1 = new \WC_Payment_Token_CC();
-		$token1->set_token( 'test_token_first' );
-		$token1->set_gateway_id( 'stripe' );
-		$token1->set_card_type( 'visa' );
-		$token1->set_last4( '1111' );
-		$token1->set_expiry_month( '01' );
-		$token1->set_expiry_year( '2026' );
-		$token1->set_user_id( $user_id );
-		$token1->save();
-
-		// Create second token (won't be default).
-		$token2 = new \WC_Payment_Token_CC();
-		$token2->set_token( 'test_token_789' );
-		$token2->set_gateway_id( 'stripe' );
-		$token2->set_card_type( 'amex' );
-		$token2->set_last4( '0005' );
-		$token2->set_expiry_month( '03' );
-		$token2->set_expiry_year( '2027' );
-		$token2->set_user_id( $user_id );
-		$token2->save();
-
-		// Note: We use do_action() here because WC_Payment_Tokens::set_users_default()
-		// relies on get_customer_tokens() which doesn't retrieve tokens properly in the test environment.
-		// In production, the hook is triggered by WC_Payment_Tokens::set_users_default().
-		// phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingHookComment, WooCommerce.Commenting.CommentHooks.MissingSinceComment
-		do_action( 'woocommerce_payment_token_set_default', $token2->get_id(), $token2 );
-
-		// Verify that the event was sent to the API with correct payload.
-		$this->assertLogged(
-			'info',
-			'Sending fraud protection event: payment_method_set_default',
-			array(
-				'source'  => 'woo-fraud-protection',
-				'payload' => array(
-					'event_type' => 'payment_method_set_default',
-					'event_data' => array(
-						'action'     => 'set_default',
-						'token_id'   => $token2->get_id(),
-						'gateway_id' => 'stripe',
-						'is_default' => true,
-					),
-				),
-			)
-		);
-	}
-
-	/**
-	 * Test payment method deleted event tracking.
-	 *
-	 * @testdox Should track payment method deleted event.
-	 */
-	public function test_handle_payment_method_deleted(): void {
-		$this->sut->register();
-
-		$user_id = $this->factory->user->create();
-
-		$token = new \WC_Payment_Token_CC();
-		$token->set_token( 'test_token_delete' );
-		$token->set_gateway_id( 'stripe' );
-		$token->set_card_type( 'visa' );
-		$token->set_last4( '1111' );
-		$token->set_expiry_month( '09' );
-		$token->set_expiry_year( '2028' );
-		$token->set_user_id( $user_id );
-		$token->save();
-
-		// Delete the token to trigger the 'deleted' event.
-		\WC_Payment_Tokens::delete( $token->get_id() );
-
-		// Verify that the event was sent to the API with correct payload.
-		$this->assertLogged(
-			'info',
-			'Sending fraud protection event: payment_method_deleted',
-			array(
-				'source'  => 'woo-fraud-protection',
-				'payload' => array(
-					'event_type' => 'payment_method_deleted',
-					'event_data' => array(
-						'action'     => 'deleted',
-						'token_id'   => $token->get_id(),
-						'gateway_id' => 'stripe',
-					),
-				),
-			)
-		);
-	}
-
 	/**
 	 * Cleanup after test.
 	 */
 	public function tearDown(): void {
 		parent::tearDown();

-		// Remove all hooks.
-		remove_all_actions( 'woocommerce_new_payment_token' );
-		remove_all_actions( 'woocommerce_payment_token_updated' );
-		remove_all_actions( 'woocommerce_payment_token_set_default' );
-		remove_all_actions( 'woocommerce_payment_token_deleted' );
-
 		// Clean up options.
 		delete_option( 'woocommerce_feature_fraud_protection_enabled' );