Commit d2b5b74eab for woocommerce

commit d2b5b74eabfd3806062605f3b4a7e027d4c7909d
Author: Leonardo Lopes de Albuquerque <leonardo.albuquerque@automattic.com>
Date:   Thu Jan 8 18:36:09 2026 -0300

    [Fraud Protection] Added class to hook into the shortcode checkout (#62639)

    * Add SessionClearanceManager for fraud protection

    Implements session status management for the WooCommerce fraud protection
    feature. This class tracks three session states (pending, allowed, blocked)
    and provides the foundation for fraud detection decision enforcement.

    Key features:
    - Session status management (pending, allowed, blocked)
    - Cart emptying on blocked sessions
    - Logging via FraudProtectionController helper
    - DEFAULT_STATUS constant for consistent default behavior
    - Comprehensive unit tests (9 tests, 20 assertions)

    Closes WOOSUBS-1246

    🤖 Generated with [Claude Code](https://claude.com/claude-code)

    Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>

    * Address coderabbit comments

    * fix lint error

    * Address phpstan error

    * Add SessionDataCollector foundation class and tests

    Create SessionDataCollector class with basic structure to collect session data for fraud protection. This initial implementation establishes the class foundation with proper nested data structure, dependency injection, and comprehensive unit tests.

    - Add SessionDataCollector class with collect() method
    - Implement nested data structure with 9 top-level sections
    - Add dependency injection for SessionClearanceManager
    - Add UTC timestamp generation
    - Create 6 focused unit tests covering foundation requirements
    - Follow WooCommerce coding standards and patterns

    Part of Task Group 1 for WOOSUBS-1248 (Session Data Collection Framework).

    * Improve SessionDataCollector dependency injection and data collection

    - Update to use init() pattern instead of constructor for dependency injection (follows WooCommerce standards)
    - Add session data fallback for customer data collection (first_name, last_name, billing_email)
    - Reorder email fallback chain to check WC_Customer before session data
    - Add test for customer data session fallback
    - Fix PHPCS issues: replace short ternaries, fix array/variable alignment, remove trailing whitespace

    All tests passing (15 tests, 64 assertions).

    * Implement order, cart, and address data collection (Task Group 3)

    Add comprehensive order, cart item, and address data collection methods to SessionDataCollector with multiple improvements based on WooCommerce best practices.

    Implementation:
    - Add get_order_data() method collecting 11 order fields including totals, currency, and cart hash
    - Add get_cart_items() method extracting 12 detailed fields per cart item
    - Add get_billing_address() and get_shipping_address() methods with 6 fields each
    - Add get_product_category_names() helper for cleaner category extraction
    - Update collect() method to populate order and address sections

    Key improvements:
    - Use WC()->customer->get_id() for customer_id instead of user login check (proper WooCommerce abstraction)
    - Use get_subtotal() for items_total (before discounts) instead of get_cart_contents_total() (after discounts)
    - Use get_total('edit') for total instead of manual calculation (more reliable)
    - Use wc_get_product_terms() for categories with caching (WooCommerce helper vs WordPress get_term)
    - Return all product categories as comma-separated list (better fraud detection data)
    - Extract category logic to reusable helper method (cleaner code)

    Testing:
    - Add 9 new focused tests for order, cart, and address data (24 total tests, 137 assertions)
    - Fix cart state isolation between tests (add empty_cart() calls)
    - Add customer reinitialization in logged-in user test
    - All tests passing

    Part of Task Group 3 for WOOSUBS-1248 (Session Data Collection Framework).

    * Add payment data structure and complete SessionDataCollector (Task Group 4)

    Complete SessionDataCollector implementation with payment data structure and comprehensive integration testing. All data collection methods now implemented with graceful degradation throughout.

    Implementation:
    - Add get_payment_data() method with 11 payment fields (gateway name/type populated, card details null for future gateway extensions)
    - Add get_product_category_names() helper for cleaner category extraction
    - Update collect() to populate payment section and extract order_id from event_data
    - Update get_order_data() to accept order_id parameter from event_data

    Integration testing:
    - Add 6 comprehensive integration tests (35 total tests, 202 assertions)
    - Test payment data structure with all 11 required fields
    - Test complete collect() output with all 9 sections
    - Test end-to-end data collection with full cart scenario
    - Test graceful degradation across all sections (empty cart, no user, minimal data)
    - Test manual triggering verification
    - Fix graceful degradation test to properly initialize guest customer

    Data collection improvements:
    - Order ID now extracted from event_data when provided by calling code
    - Category extraction uses wc_get_product_terms() with caching (WooCommerce helper)
    - Returns all product categories as comma-separated list (better fraud detection data)
    - Comprehensive PHPDoc comments throughout

    All acceptance criteria met:
    - Payment structure complete with gateway extension placeholders
    - All 9 sections implemented and tested
    - Graceful degradation verified end-to-end
    - Manual triggering only (no automatic hooks in SessionDataCollector)
    - All tests passing (35 tests, 202 assertions)

    Part of Task Group 4 for WOOSUBS-1248 (Session Data Collection Framework).

    * Added Cart Event Tracker class to capture cart events

    * Added test for the CartEventTracker class

    * Add SessionDataCollector foundation class and tests

    Create SessionDataCollector class with basic structure to collect session data for fraud protection. This initial implementation establishes the class foundation with proper nested data structure, dependency injection, and comprehensive unit tests.

    - Add SessionDataCollector class with collect() method
    - Implement nested data structure with 9 top-level sections
    - Add dependency injection for SessionClearanceManager
    - Add UTC timestamp generation
    - Create 6 focused unit tests covering foundation requirements
    - Follow WooCommerce coding standards and patterns

    Part of Task Group 1 for WOOSUBS-1248 (Session Data Collection Framework).

    * Improve SessionDataCollector dependency injection and data collection

    - Update to use init() pattern instead of constructor for dependency injection (follows WooCommerce standards)
    - Add session data fallback for customer data collection (first_name, last_name, billing_email)
    - Reorder email fallback chain to check WC_Customer before session data
    - Add test for customer data session fallback
    - Fix PHPCS issues: replace short ternaries, fix array/variable alignment, remove trailing whitespace

    All tests passing (15 tests, 64 assertions).

    * Implement order, cart, and address data collection (Task Group 3)

    Add comprehensive order, cart item, and address data collection methods to SessionDataCollector with multiple improvements based on WooCommerce best practices.

    Implementation:
    - Add get_order_data() method collecting 11 order fields including totals, currency, and cart hash
    - Add get_cart_items() method extracting 12 detailed fields per cart item
    - Add get_billing_address() and get_shipping_address() methods with 6 fields each
    - Add get_product_category_names() helper for cleaner category extraction
    - Update collect() method to populate order and address sections

    Key improvements:
    - Use WC()->customer->get_id() for customer_id instead of user login check (proper WooCommerce abstraction)
    - Use get_subtotal() for items_total (before discounts) instead of get_cart_contents_total() (after discounts)
    - Use get_total('edit') for total instead of manual calculation (more reliable)
    - Use wc_get_product_terms() for categories with caching (WooCommerce helper vs WordPress get_term)
    - Return all product categories as comma-separated list (better fraud detection data)
    - Extract category logic to reusable helper method (cleaner code)

    Testing:
    - Add 9 new focused tests for order, cart, and address data (24 total tests, 137 assertions)
    - Fix cart state isolation between tests (add empty_cart() calls)
    - Add customer reinitialization in logged-in user test
    - All tests passing

    Part of Task Group 3 for WOOSUBS-1248 (Session Data Collection Framework).

    * Add payment data structure and complete SessionDataCollector (Task Group 4)

    Complete SessionDataCollector implementation with payment data structure and comprehensive integration testing. All data collection methods now implemented with graceful degradation throughout.

    Implementation:
    - Add get_payment_data() method with 11 payment fields (gateway name/type populated, card details null for future gateway extensions)
    - Add get_product_category_names() helper for cleaner category extraction
    - Update collect() to populate payment section and extract order_id from event_data
    - Update get_order_data() to accept order_id parameter from event_data

    Integration testing:
    - Add 6 comprehensive integration tests (35 total tests, 202 assertions)
    - Test payment data structure with all 11 required fields
    - Test complete collect() output with all 9 sections
    - Test end-to-end data collection with full cart scenario
    - Test graceful degradation across all sections (empty cart, no user, minimal data)
    - Test manual triggering verification
    - Fix graceful degradation test to properly initialize guest customer

    Data collection improvements:
    - Order ID now extracted from event_data when provided by calling code
    - Category extraction uses wc_get_product_terms() with caching (WooCommerce helper)
    - Returns all product categories as comma-separated list (better fraud detection data)
    - Comprehensive PHPDoc comments throughout

    All acceptance criteria met:
    - Payment structure complete with gateway extension placeholders
    - All 9 sections implemented and tested
    - Graceful degradation verified end-to-end
    - Manual triggering only (no automatic hooks in SessionDataCollector)
    - All tests passing (35 tests, 202 assertions)

    Part of Task Group 4 for WOOSUBS-1248 (Session Data Collection Framework).

    * Remove todo comment in order to calm down the linter

    * Use get_order_count on customer for optimized order count retrieval with caching support

    * Add ApiClient for Fraud Protection API communication

    Implements WOOSUBS-1247: Creates an API client that sends session data
    to the WPCOM fraud protection endpoint via Jetpack Connection.

    Key features:
    - Fail-open pattern: returns "allow" on all error conditions
    - Comprehensive error logging for debugging
    - 30s timeout for API requests
    - Validates verdict responses (allow/block)

    Also adds LoggerSpyTrait for cleaner log assertions in tests.

    * Fix lint and PHPStan errors

    * Fixed empty customer $lifetime_order_count

    * Added comment about why we're reloading the customer

    * Now we always create a unique session id

    * Fixed session hash name

    * Removed unused imports

    * Removed unecessary dependency

    * Updated isset check to the null coallese operator

    * Casting $old_quantity to int

    * Added check to prevent sending cart update events when the number of items didn't change

    * Disabled taxes to prevent fraud tests from failing when checking the cart totals

    * Lint fixes

    * Added extra checks for when the decoded json is not an array or is null

    * Added microtime to the sku so each product under test is unique

    * Fixed lint errors

    * Added class to hook into payment method actions to track fraud data

    * Changed the log method so it's possible to override it during test running

    * Added test to check if the PaymentMethodEventTracker works as expected

    * Revert "Changed the log method so it's possible to override it during test running"

    This reverts commit b9fd1f501777cbe0f5aa1ad93c36029830a0f9c0.

    * Updated test to use the LoggerSpyTrait

    * Removed unecessary dependency

    * phpcs fixes

    * Fixed lint error about todo task

    * Added hooks for payment methods to add their customer data when a payment method is selected.

    * Added tests

    * Added hooks documentation

    * trigger tests

    * Fixed lint errors

    * phpstan fix

    * Added class to hook into the shortcode checkout

    * Added tracking to the shortcode payment method select event

    * Added tests for PaymentMethodHelper class

    * Added tests for the CheckoutEventTracker class

    * Created a centralized tracker for all classes

    * Fixed tests after refactoring the track_event methods

    * Fixed shipping methods event tracking key

    * Removed unecessary test

    * Removed unecessary batching code

    * Removed batching and fixed the scheduled action to contain valid customer data

    * Added a custom query to remove the correct scheduled actions for a session

    * Created method with a custom query to remove previous unexecuted scheduled actions

    * Linting and phpstan fixes

    * Linting and phpstan fixes

    * phpstan fixes

    * more phpstan fixes

    * Fixed test missing session setup

    * Now the event listener that tracks payment method changes is only going to be added when fraud protection is enabled

    * Test fixes

    * Test fix

    * Removed unecessary hook

    * Removed woocommerce_new_payment_token manual trigger to rely on the core calling it

    * Fixed sut and testdox

    * Removed unecessary tests for woocommerce_payment_token_add_failed

    * Fixed hook comment

    * Added explanation of when the woocommerce_fraud_protection_payment_data hooks are called

    * Fixed tests to use the woocommerce hook system instead of manually calling the hooks

    * Removed unecessary space

    * Updated test to use assertLogged instead of manually checking each log key

    * Fix array alignment in PaymentMethodEventTrackerTest

    * Removed unecessary hook and related tests and documentation

    * Removed whitespace

    * Fixed PaymentMethodEventTracker expected data format

    * Lint fixes

    * Removed unecessary scheduled action to track the checkout form data

    * Removed whitespace

    * Removed whitespace

    * Removed payment method change tracking

    * Merged default payment data with data that has been previously collected

    * Removed left over data

    * Fixed function call

    * Reverted function call

    * Reverted headers_sent check

    * Moved method that gets payment gateway name by it's id to the WC_Payment_Gateways class

    * Fixed linting and phpstan errors

    * Fixed call function

    ---------

    Co-authored-by: Vasily Belolapotkov <vasily.belolapotkov@automattic.com>
    Co-authored-by: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
    Co-authored-by: Luiz Reis <luiz.reis@automattic.com>

diff --git a/plugins/woocommerce/includes/class-wc-payment-gateways.php b/plugins/woocommerce/includes/class-wc-payment-gateways.php
index 0288f75848..7322df7d6a 100644
--- a/plugins/woocommerce/includes/class-wc-payment-gateways.php
+++ b/plugins/woocommerce/includes/class-wc-payment-gateways.php
@@ -343,6 +343,33 @@ All at %6$s
 		return $_available_gateways;
 	}

+	/**
+	 * Get readable payment method name from payment method ID.
+	 *
+	 * Retrieves the payment gateway title from the payment method ID by loading
+	 * the payment gateway instance.
+	 *
+	 * @param string $payment_gateway_id Payment method ID (e.g., "stripe", "paypal", "bacs").
+	 * @return string Payment method name or ID if name not found.
+	 */
+	public function get_payment_gateway_name_by_id( string $payment_gateway_id ): string {
+		// Get available payment gateways.
+		$payment_gateways = $this->payment_gateways();
+
+		// Check if the payment method exists and has a title.
+		if ( isset( $payment_gateways[ $payment_gateway_id ] ) ) {
+			$gateway = $payment_gateways[ $payment_gateway_id ];
+			if ( is_object( $gateway ) && method_exists( $gateway, 'get_title' ) ) {
+				return $gateway->get_title();
+			} elseif ( is_object( $gateway ) && isset( $gateway->title ) ) {
+				return $gateway->title;
+			}
+		}
+
+		// Return the ID as fallback if no title found.
+		return $payment_gateway_id;
+	}
+
 	/**
 	 * Get array of registered gateway ids
 	 *
diff --git a/plugins/woocommerce/includes/class-woocommerce.php b/plugins/woocommerce/includes/class-woocommerce.php
index 6492bde084..8f78dee55a 100644
--- a/plugins/woocommerce/includes/class-woocommerce.php
+++ b/plugins/woocommerce/includes/class-woocommerce.php
@@ -378,6 +378,7 @@ final class WooCommerce {
 		$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/src/Internal/FraudProtection/CartEventTracker.php b/plugins/woocommerce/src/Internal/FraudProtection/CartEventTracker.php
index 0c6d2ca286..a854843ffb 100644
--- a/plugins/woocommerce/src/Internal/FraudProtection/CartEventTracker.php
+++ b/plugins/woocommerce/src/Internal/FraudProtection/CartEventTracker.php
@@ -24,6 +24,13 @@ defined( 'ABSPATH' ) || exit;
  */
 class CartEventTracker implements RegisterHooksInterface {

+	/**
+	 * Fraud protection tracker instance.
+	 *
+	 * @var FraudProtectionTracker
+	 */
+	private FraudProtectionTracker $tracker;
+
 	/**
 	 * Session data collector instance.
 	 *
@@ -43,13 +50,16 @@ class CartEventTracker implements RegisterHooksInterface {
 	 *
 	 * @internal
 	 *
+	 * @param FraudProtectionTracker    $tracker                     The fraud protection tracker instance.
 	 * @param SessionDataCollector      $data_collector              The session data collector instance.
 	 * @param FraudProtectionController $fraud_protection_controller The fraud protection controller instance.
 	 */
 	final public function init(
+		FraudProtectionTracker $tracker,
 		SessionDataCollector $data_collector,
 		FraudProtectionController $fraud_protection_controller
 	): void {
+		$this->tracker                     = $tracker;
 		$this->data_collector              = $data_collector;
 		$this->fraud_protection_controller = $fraud_protection_controller;
 	}
@@ -97,7 +107,25 @@ class CartEventTracker implements RegisterHooksInterface {
 			$variation_id
 		);

-		$this->track_event( 'cart_item_added', $event_data );
+		// Collect comprehensive session data.
+		try {
+			$collected_data = $this->data_collector->collect( 'cart_item_added', $event_data );
+			$this->tracker->track_event( 'cart_item_added', $collected_data );
+		} catch ( \Exception $e ) {
+			// Log error but don't break functionality.
+			FraudProtectionController::log(
+				'error',
+				sprintf(
+					'Failed to collect session data for cart event: %s | Error: %s',
+					'cart_item_added',
+					$e->getMessage()
+				),
+				array(
+					'event_type' => 'cart_item_added',
+					'exception'  => $e,
+				)
+			);
+		}
 	}

 	/**
@@ -133,7 +161,25 @@ class CartEventTracker implements RegisterHooksInterface {
 		// Add old quantity for context.
 		$event_data['old_quantity'] = (int) $old_quantity;

-		$this->track_event( 'cart_item_updated', $event_data );
+		// Collect comprehensive session data.
+		try {
+			$collected_data = $this->data_collector->collect( 'cart_item_updated', $event_data );
+			$this->tracker->track_event( 'cart_item_updated', $collected_data );
+		} catch ( \Exception $e ) {
+			// Log error but don't break functionality.
+			FraudProtectionController::log(
+				'error',
+				sprintf(
+					'Failed to collect session data for cart event: %s | Error: %s',
+					'cart_item_updated',
+					$e->getMessage()
+				),
+				array(
+					'event_type' => 'cart_item_updated',
+					'exception'  => $e,
+				)
+			);
+		}
 	}

 	/**
@@ -165,7 +211,25 @@ class CartEventTracker implements RegisterHooksInterface {
 			$variation_id
 		);

-		$this->track_event( 'cart_item_removed', $event_data );
+		// Collect comprehensive session data.
+		try {
+			$collected_data = $this->data_collector->collect( 'cart_item_removed', $event_data );
+			$this->tracker->track_event( 'cart_item_removed', $collected_data );
+		} catch ( \Exception $e ) {
+			// Log error but don't break functionality.
+			FraudProtectionController::log(
+				'error',
+				sprintf(
+					'Failed to collect session data for cart event: %s | Error: %s',
+					'cart_item_removed',
+					$e->getMessage()
+				),
+				array(
+					'event_type' => 'cart_item_removed',
+					'exception'  => $e,
+				)
+			);
+		}
 	}

 	/**
@@ -197,7 +261,25 @@ class CartEventTracker implements RegisterHooksInterface {
 			$variation_id
 		);

-		$this->track_event( 'cart_item_restored', $event_data );
+		// Collect comprehensive session data.
+		try {
+			$collected_data = $this->data_collector->collect( 'cart_item_restored', $event_data );
+			$this->tracker->track_event( 'cart_item_restored', $collected_data );
+		} catch ( \Exception $e ) {
+			// Log error but don't break functionality.
+			FraudProtectionController::log(
+				'error',
+				sprintf(
+					'Failed to collect session data for cart event: %s | Error: %s',
+					'cart_item_restored',
+					$e->getMessage()
+				),
+				array(
+					'event_type' => 'cart_item_restored',
+					'exception'  => $e,
+				)
+			);
+		}
 	}

 	/**
@@ -229,61 +311,4 @@ class CartEventTracker implements RegisterHooksInterface {
 			'cart_item_count' => $cart_item_count,
 		);
 	}
-
-	/**
-	 * Track fraud protection event with comprehensive session context.
-	 *
-	 * This method orchestrates the event tracking by:
-	 * 1. Collecting comprehensive session data via SessionDataCollector
-	 * 2. Merging with event-specific data
-	 * 3. Logging the event (will call EventTracker/API client once available)
-	 *
-	 * The method implements graceful degradation - any errors during tracking
-	 * will be logged but will not break the cart functionality.
-	 *
-	 * @param string $event_type          Event type identifier (e.g., 'cart_item_added').
-	 * @param array  $event_specific_data Event-specific data to merge with session context.
-	 * @return void
-	 */
-	private function track_event( string $event_type, array $event_specific_data ): void {
-		try {
-			// Collect comprehensive session data.
-			$session_data = $this->data_collector->collect( $event_type, $event_specific_data );
-
-			// Once EventTracker/API client is implemented (WOOSUBS-1249), call it here:
-			// $event_tracker = wc_get_container()->get( EventTracker::class );
-			// $event_tracker->track( $event_type, $session_data );
-			//
-			// For now, log the event for debugging and verification.
-			FraudProtectionController::log(
-				'info',
-				sprintf(
-					'Fraud protection event tracked: %s | Product ID: %s | Quantity: %s | Session ID: %s',
-					$event_type,
-					$event_specific_data['product_id'] ?? 'N/A',
-					$event_specific_data['quantity'] ?? 'N/A',
-					$session_data['session']['session_id'] ?? 'N/A'
-				),
-				array(
-					'event_type'   => $event_type,
-					'event_data'   => $event_specific_data,
-					'session_data' => $session_data,
-				)
-			);
-		} catch ( \Exception $e ) {
-			// Gracefully handle errors - fraud protection should never break the cart.
-			FraudProtectionController::log(
-				'error',
-				sprintf(
-					'Failed to track fraud protection event: %s | Error: %s',
-					$event_type,
-					$e->getMessage()
-				),
-				array(
-					'event_type' => $event_type,
-					'exception'  => $e,
-				)
-			);
-		}
-	}
 }
diff --git a/plugins/woocommerce/src/Internal/FraudProtection/CheckoutEventTracker.php b/plugins/woocommerce/src/Internal/FraudProtection/CheckoutEventTracker.php
new file mode 100644
index 0000000000..a6842837f3
--- /dev/null
+++ b/plugins/woocommerce/src/Internal/FraudProtection/CheckoutEventTracker.php
@@ -0,0 +1,374 @@
+<?php
+/**
+ * CheckoutEventTracker class file.
+ */
+
+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 WooCommerce checkout events (billing/email changes,
+ * payment selection) and triggers comprehensive event tracking with full session
+ * context.
+ *
+ * @since 10.5.0
+ * @internal This class is part of the internal API and is subject to change without notice.
+ */
+class CheckoutEventTracker implements RegisterHooksInterface {
+
+	/**
+	 * Fraud protection tracker instance.
+	 *
+	 * @var FraudProtectionTracker
+	 */
+	private FraudProtectionTracker $tracker;
+
+	/**
+	 * Session data collector instance.
+	 *
+	 * @var SessionDataCollector
+	 */
+	private SessionDataCollector $data_collector;
+
+	/**
+	 * Fraud protection controller instance.
+	 *
+	 * @var FraudProtectionController
+	 */
+	private FraudProtectionController $fraud_protection_controller;
+
+	/**
+	 * Initialize with dependencies.
+	 *
+	 * @internal
+	 *
+	 * @param FraudProtectionTracker    $tracker                     The fraud protection tracker instance.
+	 * @param SessionDataCollector      $data_collector              The session data collector instance.
+	 * @param FraudProtectionController $fraud_protection_controller The fraud protection controller instance.
+	 */
+	final public function init(
+		FraudProtectionTracker $tracker,
+		SessionDataCollector $data_collector,
+		FraudProtectionController $fraud_protection_controller
+	): void {
+		$this->tracker                     = $tracker;
+		$this->data_collector              = $data_collector;
+		$this->fraud_protection_controller = $fraud_protection_controller;
+	}
+
+	/**
+	 * Register checkout event hooks.
+	 *
+	 * Hooks into WooCommerce checkout 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;
+		}
+
+		// Traditional checkout: Track when checkout fields are updated.
+		add_action( 'woocommerce_checkout_update_order_review', array( $this, 'handle_checkout_field_update' ), 10, 1 );
+	}
+
+	/**
+	 * Handle traditional checkout field update event.
+	 *
+	 * Triggered when checkout fields are updated via AJAX (woocommerce_update_order_review).
+	 *
+	 * @internal
+	 *
+	 * @param string $posted_data Serialized checkout form data.
+	 * @return void
+	 */
+	public function handle_checkout_field_update( $posted_data ): void {
+		// Parse the posted data to extract relevant fields.
+		$data = array();
+		if ( $posted_data ) {
+			parse_str( $posted_data, $data );
+		}
+
+		$event_data = $this->build_checkout_event_data( 'field_update', $data );
+		$this->track_checkout_event( 'checkout_field_update', $event_data );
+	}
+
+	/**
+	 * Build checkout event-specific data.
+	 *
+	 * Prepares the checkout event data including action type and any changed fields.
+	 * This data will be merged with comprehensive session data during event tracking.
+	 *
+	 * @param string $action      Action type (field_update, payment_method_selected, store_api_update).
+	 * @param array  $posted_data Posted form data or event context.
+	 * @return array Checkout event data.
+	 */
+	private function build_checkout_event_data( string $action, array $posted_data ): array {
+		$event_data = array( 'action' => $action );
+
+		// Extract and merge all checkout field groups.
+		$event_data = array_merge(
+			$event_data,
+			$this->extract_billing_fields( $posted_data ),
+			$this->extract_shipping_fields( $posted_data ),
+			$this->extract_payment_method( $posted_data ),
+			$this->extract_shipping_methods( $posted_data )
+		);
+
+		return $event_data;
+	}
+
+	/**
+	 * Extract payment method data from posted data.
+	 *
+	 * Extracts payment method ID and retrieves the readable gateway name.
+	 *
+	 * @param array $posted_data Posted form data.
+	 * @return array Payment method data with ID and name, or empty array if not found.
+	 */
+	private static function extract_payment_method( array $posted_data ): array {
+		$payment_data = array();
+
+		if ( ! empty( $posted_data['payment']['payment_method_type'] ) ) {
+			$payment_gateway_id   = sanitize_text_field( wp_unslash( $posted_data['payment']['payment_method_type'] ) );
+			$payment_gateway_name = WC()->payment_gateways()->get_payment_gateway_name_by_id( $payment_gateway_id );
+
+			$payment_data['payment'] = array(
+				'payment_gateway_type' => $payment_gateway_id,
+				'payment_gateway_name' => $payment_gateway_name,
+			);
+		}
+
+		return $payment_data;
+	}
+
+	/**
+	 * Extract billing fields from posted data.
+	 *
+	 * @param array $posted_data Posted form data.
+	 * @return array Billing fields.
+	 */
+	private function extract_billing_fields( array $posted_data ): array {
+		$field_map = array(
+			'billing_email'      => 'sanitize_email',
+			'billing_first_name' => 'sanitize_text_field',
+			'billing_last_name'  => 'sanitize_text_field',
+			'billing_country'    => 'sanitize_text_field',
+			'billing_address_1'  => 'sanitize_text_field',
+			'billing_address_2'  => 'sanitize_text_field',
+			'billing_city'       => 'sanitize_text_field',
+			'billing_state'      => 'sanitize_text_field',
+			'billing_postcode'   => 'sanitize_text_field',
+			'billing_phone'      => 'sanitize_text_field',
+		);
+
+		$extracted_fields = $this->extract_fields_by_map( $field_map, $posted_data );
+
+		// Store API uses 'email' instead of 'billing_email'.
+		if ( empty( $extracted_fields['billing_email'] ) && ! empty( $posted_data['email'] ) ) {
+			$extracted_fields['email'] = sanitize_email( $posted_data['email'] );
+		}
+
+		return $extracted_fields;
+	}
+
+	/**
+	 * Extract shipping fields from posted data.
+	 *
+	 * @param array $posted_data Posted form data.
+	 * @return array Shipping fields.
+	 */
+	private function extract_shipping_fields( array $posted_data ): array {
+		if ( ! isset( $posted_data['ship_to_different_address'] ) || ! $posted_data['ship_to_different_address'] ) {
+			return array();
+		}
+
+		$field_map = array(
+			'shipping_first_name' => 'sanitize_text_field',
+			'shipping_last_name'  => 'sanitize_text_field',
+			'shipping_country'    => 'sanitize_text_field',
+			'shipping_address_1'  => 'sanitize_text_field',
+			'shipping_address_2'  => 'sanitize_text_field',
+			'shipping_city'       => 'sanitize_text_field',
+			'shipping_state'      => 'sanitize_text_field',
+			'shipping_postcode'   => 'sanitize_text_field',
+		);
+
+		return $this->extract_fields_by_map( $field_map, $posted_data );
+	}
+
+	/**
+	 * Extract and sanitize fields from posted data using a field map.
+	 *
+	 * Generic extraction method that iterates through a field map and extracts
+	 * non-empty fields from posted data, applying the appropriate sanitization
+	 * function to each field.
+	 *
+	 * @param array $field_map    Map of field names to sanitization functions.
+	 * @param array $posted_data  Posted form data.
+	 * @return array Extracted and sanitized fields.
+	 */
+	private function extract_fields_by_map( array $field_map, array $posted_data ): array {
+		$extracted_fields = array();
+
+		foreach ( $field_map as $field_name => $sanitize_function ) {
+			if ( ! empty( $posted_data[ $field_name ] ) ) {
+				$extracted_fields[ $field_name ] = $sanitize_function( wp_unslash( $posted_data[ $field_name ] ) );
+			}
+		}
+
+		return $extracted_fields;
+	}
+
+	/**
+	 * Extract and convert shipping method IDs to readable names.
+	 *
+	 * @param array $posted_data Posted form data.
+	 * @return array Shipping method data wrapped in 'shipping_methods' key.
+	 */
+	private function extract_shipping_methods( array $posted_data ): array {
+		$shipping_method_data = array();
+
+		if ( ! empty( $posted_data['shipping_method'] ) ) {
+			$shipping_method_ids = $posted_data['shipping_method'];
+
+			$shipping_methods = $this->get_shipping_method_names( $shipping_method_ids );
+			if ( ! empty( $shipping_methods ) ) {
+				$shipping_method_data['shipping_methods'] = $shipping_methods;
+			}
+		}
+
+		return $shipping_method_data;
+	}
+
+	/**
+	 * Track a checkout event immediately.
+	 *
+	 * Collects comprehensive session data and tracks the event immediately.
+	 *
+	 * @param string $event_type          Event type identifier.
+	 * @param array  $event_specific_data Event-specific data to merge with session context.
+	 * @return void
+	 */
+	private function track_checkout_event( string $event_type, array $event_specific_data ): void {
+		// Collect comprehensive session data.
+		try {
+			$collected_data = $this->data_collector->collect( $event_type, $event_specific_data );
+		} catch ( \Exception $e ) {
+			// If collection fails, log and abort tracking.
+			FraudProtectionController::log(
+				'error',
+				sprintf(
+					'Failed to collect session data for checkout event: %s | Error: %s',
+					$event_type,
+					$e->getMessage()
+				),
+				array(
+					'event_type' => $event_type,
+					'exception'  => $e,
+				)
+			);
+			return;
+		}
+
+		// Track the event immediately.
+		$this->tracker->track_event( $event_type, $collected_data );
+	}
+
+	/**
+	 * Get readable shipping method names from shipping method IDs.
+	 *
+	 * Converts shipping method IDs (e.g., "flat_rate:1", "free_shipping:2")
+	 * to their human-readable labels by loading the shipping method instances.
+	 *
+	 * @param array $shipping_method_ids Array of shipping method IDs.
+	 * @return array Associative array mapping shipping method IDs to their names.
+	 */
+	private function get_shipping_method_names( array $shipping_method_ids ): array {
+		$shipping_method_map = array();
+
+		try {
+			// Get WooCommerce shipping instance.
+			$shipping = WC()->shipping();
+			if ( ! $shipping ) {
+				return $shipping_method_map;
+			}
+
+			// Get all available shipping methods.
+			$shipping_methods = $shipping->get_shipping_methods();
+
+			foreach ( $shipping_method_ids as $method_id ) {
+				if ( ! is_string( $method_id ) ) {
+					continue;
+				}
+
+				// Sanitize the method ID.
+				$method_id = sanitize_text_field( $method_id );
+
+				// Shipping method IDs can be in format "method_id:instance_id".
+				// Extract the base method ID.
+				$method_parts   = explode( ':', $method_id );
+				$base_method_id = $method_parts[0];
+				$instance_id    = isset( $method_parts[1] ) ? $method_parts[1] : null;
+
+				// Try to get the method label.
+				$method_label = null;
+
+				// If we have an instance ID, try to get the specific instance label.
+				if ( $instance_id && WC()->session instanceof \WC_Session ) {
+					// Get chosen shipping methods from session or packages.
+					$packages = WC()->shipping()->get_packages();
+
+					foreach ( $packages as $package ) {
+						if ( isset( $package['rates'][ $method_id ] ) ) {
+							$rate         = $package['rates'][ $method_id ];
+							$method_label = $rate->get_label();
+							break;
+						}
+					}
+				}
+
+				// Fallback to base method title if no instance label found.
+				if ( ! $method_label && isset( $shipping_methods[ $base_method_id ] ) ) {
+					$method = $shipping_methods[ $base_method_id ];
+					if ( method_exists( $method, 'get_method_title' ) ) {
+						$method_label = $method->get_method_title();
+					} elseif ( property_exists( $method, 'method_title' ) ) {
+						$method_label = $method->method_title;
+					}
+				}
+
+				// Use the method ID as fallback if no label found.
+				if ( ! $method_label ) {
+					$method_label = $method_id;
+				}
+
+				$shipping_method_map[ $method_id ] = $method_label;
+			}
+		} catch ( \Exception $e ) {
+			// Gracefully handle errors - return what we have so far.
+			FraudProtectionController::log(
+				'warning',
+				sprintf(
+					'Failed to get shipping method names: %s',
+					$e->getMessage()
+				),
+				array(
+					'shipping_method_ids' => $shipping_method_ids,
+					'exception'           => $e,
+				)
+			);
+		}
+
+		return $shipping_method_map;
+	}
+}
diff --git a/plugins/woocommerce/src/Internal/FraudProtection/FraudProtectionTracker.php b/plugins/woocommerce/src/Internal/FraudProtection/FraudProtectionTracker.php
new file mode 100644
index 0000000000..ed93dcd3e9
--- /dev/null
+++ b/plugins/woocommerce/src/Internal/FraudProtection/FraudProtectionTracker.php
@@ -0,0 +1,72 @@
+<?php
+/**
+ * FraudProtectionTracker class file.
+ */
+
+declare( strict_types=1 );
+
+namespace Automattic\WooCommerce\Internal\FraudProtection;
+
+defined( 'ABSPATH' ) || exit;
+
+/**
+ * Centralized fraud protection event tracker.
+ *
+ * This class provides a unified interface for tracking fraud protection events.
+ * It logs events for the fraud protection service using already-collected data.
+ *
+ * @since 10.5.0
+ * @internal This class is part of the internal API and is subject to change without notice.
+ */
+class FraudProtectionTracker {
+
+	/**
+	 * Track fraud protection event with already-collected data.
+	 *
+	 * This method accepts fully-collected event data (including session context)
+	 * and logs it for the fraud protection service.
+	 *
+	 * The method implements graceful degradation - any errors during tracking
+	 * will be logged but will not break the functionality.
+	 *
+	 * @param string $event_type     Event type identifier (e.g., 'cart_item_added').
+	 * @param array  $collected_data Fully-collected event data including session context.
+	 * @return void
+	 */
+	public function track_event( string $event_type, array $collected_data ): void {
+		try {
+			// phpcs:ignore Generic.Commenting.Todo.TaskFound
+			// TODO: Once EventTracker/API client is implemented (WOOSUBS-1249), call it here:
+			// $event_tracker = wc_get_container()->get( EventTracker::class );
+			// $event_tracker->track( $event_type, $collected_data );
+			//
+			// For now, log the event for debugging and verification.
+			FraudProtectionController::log(
+				'info',
+				sprintf(
+					'Fraud protection event tracked: %s | Session ID: %s',
+					$event_type,
+					$collected_data['session']['session_id'] ?? 'N/A'
+				),
+				array(
+					'event_type'     => $event_type,
+					'collected_data' => $collected_data,
+				)
+			);
+		} catch ( \Exception $e ) {
+			// Gracefully handle errors - fraud protection should never break functionality.
+			FraudProtectionController::log(
+				'error',
+				sprintf(
+					'Failed to track fraud protection event: %s | Error: %s',
+					$event_type,
+					$e->getMessage()
+				),
+				array(
+					'event_type' => $event_type,
+					'exception'  => $e,
+				)
+			);
+		}
+	}
+}
diff --git a/plugins/woocommerce/src/Internal/FraudProtection/PaymentMethodEventTracker.php b/plugins/woocommerce/src/Internal/FraudProtection/PaymentMethodEventTracker.php
index 1061e08035..d52af3f2a6 100644
--- a/plugins/woocommerce/src/Internal/FraudProtection/PaymentMethodEventTracker.php
+++ b/plugins/woocommerce/src/Internal/FraudProtection/PaymentMethodEventTracker.php
@@ -26,6 +26,13 @@ defined( 'ABSPATH' ) || exit;
  */
 class PaymentMethodEventTracker implements RegisterHooksInterface {

+	/**
+	 * Fraud protection tracker instance.
+	 *
+	 * @var FraudProtectionTracker
+	 */
+	private FraudProtectionTracker $tracker;
+
 	/**
 	 * Session data collector instance.
 	 *
@@ -45,13 +52,16 @@ class PaymentMethodEventTracker implements RegisterHooksInterface {
 	 *
 	 * @internal
 	 *
+	 * @param FraudProtectionTracker    $tracker                     The fraud protection tracker instance.
 	 * @param SessionDataCollector      $data_collector              The session data collector instance.
 	 * @param FraudProtectionController $fraud_protection_controller The fraud protection controller instance.
 	 */
 	final public function init(
+		FraudProtectionTracker $tracker,
 		SessionDataCollector $data_collector,
 		FraudProtectionController $fraud_protection_controller
 	): void {
+		$this->tracker                     = $tracker;
 		$this->data_collector              = $data_collector;
 		$this->fraud_protection_controller = $fraud_protection_controller;
 	}
@@ -86,7 +96,26 @@ class PaymentMethodEventTracker implements RegisterHooksInterface {
 	 */
 	public function handle_payment_method_added( $token_id, $token ): void {
 		$event_data = $this->build_payment_method_event_data( 'added', $token );
-		$this->track_event( 'payment_method_added', $event_data );
+
+		// Collect comprehensive session data.
+		try {
+			$collected_data = $this->data_collector->collect( 'payment_method_added', $event_data );
+			$this->tracker->track_event( 'payment_method_added', $collected_data );
+		} catch ( \Exception $e ) {
+			// Log error but don't break functionality.
+			FraudProtectionController::log(
+				'error',
+				sprintf(
+					'Failed to collect session data for payment method event: %s | Error: %s',
+					'payment_method_added',
+					$e->getMessage()
+				),
+				array(
+					'event_type' => 'payment_method_added',
+					'exception'  => $e,
+				)
+			);
+		}
 	}

 	/**
@@ -107,7 +136,26 @@ class PaymentMethodEventTracker implements RegisterHooksInterface {
 		}

 		$event_data = $this->build_payment_method_event_data( 'updated', $token );
-		$this->track_event( 'payment_method_updated', $event_data );
+
+		// Collect comprehensive session data.
+		try {
+			$collected_data = $this->data_collector->collect( 'payment_method_updated', $event_data );
+			$this->tracker->track_event( 'payment_method_updated', $collected_data );
+		} catch ( \Exception $e ) {
+			// Log error but don't break functionality.
+			FraudProtectionController::log(
+				'error',
+				sprintf(
+					'Failed to collect session data for payment method event: %s | Error: %s',
+					'payment_method_updated',
+					$e->getMessage()
+				),
+				array(
+					'event_type' => 'payment_method_updated',
+					'exception'  => $e,
+				)
+			);
+		}
 	}

 	/**
@@ -122,7 +170,26 @@ class PaymentMethodEventTracker implements RegisterHooksInterface {
 	 */
 	public function handle_payment_method_set_default( $token_id, $token ): void {
 		$event_data = $this->build_payment_method_event_data( 'set_default', $token );
-		$this->track_event( 'payment_method_set_default', $event_data );
+
+		// Collect comprehensive session data.
+		try {
+			$collected_data = $this->data_collector->collect( 'payment_method_set_default', $event_data );
+			$this->tracker->track_event( 'payment_method_set_default', $collected_data );
+		} catch ( \Exception $e ) {
+			// Log error but don't break functionality.
+			FraudProtectionController::log(
+				'error',
+				sprintf(
+					'Failed to collect session data for payment method event: %s | Error: %s',
+					'payment_method_set_default',
+					$e->getMessage()
+				),
+				array(
+					'event_type' => 'payment_method_set_default',
+					'exception'  => $e,
+				)
+			);
+		}
 	}

 	/**
@@ -137,7 +204,26 @@ class PaymentMethodEventTracker implements RegisterHooksInterface {
 	 */
 	public function handle_payment_method_deleted( $token_id, $token ): void {
 		$event_data = $this->build_payment_method_event_data( 'deleted', $token );
-		$this->track_event( 'payment_method_deleted', $event_data );
+
+		// Collect comprehensive session data.
+		try {
+			$collected_data = $this->data_collector->collect( 'payment_method_deleted', $event_data );
+			$this->tracker->track_event( 'payment_method_deleted', $collected_data );
+		} catch ( \Exception $e ) {
+			// Log error but don't break functionality.
+			FraudProtectionController::log(
+				'error',
+				sprintf(
+					'Failed to collect session data for payment method event: %s | Error: %s',
+					'payment_method_deleted',
+					$e->getMessage()
+				),
+				array(
+					'event_type' => 'payment_method_deleted',
+					'exception'  => $e,
+				)
+			);
+		}
 	}

 	/**
@@ -171,62 +257,4 @@ class PaymentMethodEventTracker implements RegisterHooksInterface {

 		return $event_data;
 	}
-
-	/**
-	 * Track fraud protection event with comprehensive session context.
-	 *
-	 * This method orchestrates the event tracking by:
-	 * 1. Collecting comprehensive session data via SessionDataCollector
-	 * 2. Merging with event-specific data
-	 * 3. Logging the event (will call EventTracker/API client once available)
-	 *
-	 * The method implements graceful degradation - any errors during tracking
-	 * will be logged but will not break the payment method functionality.
-	 *
-	 * @param string $event_type          Event type identifier (e.g., 'payment_method_added').
-	 * @param array  $event_specific_data Event-specific data to merge with session context.
-	 */
-	private function track_event( string $event_type, array $event_specific_data ): void {
-		try {
-			// Collect comprehensive session data.
-			$session_data = $this->data_collector->collect( $event_type, $event_specific_data );
-
-			// phpcs:ignore Generic.Commenting.Todo.TaskFound
-			// TODO: Once EventTracker/API client is implemented (WOOSUBS-1249), call it here:
-			// $event_tracker = wc_get_container()->get( EventTracker::class );
-			// $event_tracker->track( $event_type, $session_data );
-			//
-			// For now, log the event for debugging and verification.
-			FraudProtectionController::log(
-				'info',
-				sprintf(
-					'Fraud protection event tracked: %s | Token ID: %s | Gateway: %s | User ID: %s | Session ID: %s',
-					$event_type,
-					$event_specific_data['token_id'] ?? 'N/A',
-					$event_specific_data['gateway_id'] ?? 'N/A',
-					$event_specific_data['user_id'] ?? 'N/A',
-					$session_data['session']['session_id'] ?? 'N/A'
-				),
-				array(
-					'event_type'   => $event_type,
-					'event_data'   => $event_specific_data,
-					'session_data' => $session_data,
-				)
-			);
-		} catch ( \Exception $e ) {
-			// Gracefully handle errors - fraud protection should never break payment method management.
-			FraudProtectionController::log(
-				'error',
-				sprintf(
-					'Failed to track fraud protection event: %s | Error: %s',
-					$event_type,
-					$e->getMessage()
-				),
-				array(
-					'event_type' => $event_type,
-					'exception'  => $e,
-				)
-			);
-		}
-	}
 }
diff --git a/plugins/woocommerce/src/Internal/FraudProtection/SessionClearanceManager.php b/plugins/woocommerce/src/Internal/FraudProtection/SessionClearanceManager.php
index c25dad1e0f..8ac90768cb 100644
--- a/plugins/woocommerce/src/Internal/FraudProtection/SessionClearanceManager.php
+++ b/plugins/woocommerce/src/Internal/FraudProtection/SessionClearanceManager.php
@@ -134,6 +134,7 @@ class SessionClearanceManager {
 		// Ensure session cookie is set so the session persists across page loads.
 		// This is important because fraud protection may set session status before
 		// any cart action triggers the cookie to be set.
+		// Skip cookie setting if headers have already been sent (e.g., in test environment).
 		if ( WC()->session instanceof \WC_Session_Handler ) {
 			WC()->session->set_customer_session_cookie( true );
 		}
diff --git a/plugins/woocommerce/src/Internal/FraudProtection/SessionDataCollector.php b/plugins/woocommerce/src/Internal/FraudProtection/SessionDataCollector.php
index 791571cf9d..99a19fb35b 100644
--- a/plugins/woocommerce/src/Internal/FraudProtection/SessionDataCollector.php
+++ b/plugins/woocommerce/src/Internal/FraudProtection/SessionDataCollector.php
@@ -70,7 +70,7 @@ class SessionDataCollector {
 			'order'            => $this->get_order_data( $order_id_from_event ),
 			'shipping_address' => $this->get_shipping_address(),
 			'billing_address'  => $this->get_billing_address(),
-			'payment'          => $this->get_payment_data(),
+			'payment'          => $this->get_payment_data( $event_data ),
 			'event_data'       => $event_data,
 		);
 	}
@@ -481,51 +481,40 @@ class SessionDataCollector {
 	 *
 	 * @since 10.5.0
 	 *
+	 * @param array $event_data Event-specific data that may contain payment information.
 	 * @return array Payment data array with 11 keys.
 	 */
-	private function get_payment_data(): array {
+	private function get_payment_data( array $event_data = array() ): array {
+		$payment_data = array(
+			'payment_gateway_name'      => null,
+			'payment_method_type'       => null,
+			'card_bin'                  => null,
+			'card_last4'                => null,
+			'card_brand'                => null,
+			'payer_id'                  => null,
+			'outcome'                   => null,
+			'decline_reason'            => null,
+			'avs_result'                => null,
+			'cvc_result'                => null,
+			'tokenized_card_identifier' => null,
+		);
+
 		try {
-			$payment_gateway_name = null;
-			$payment_method_type  = null;
+			if ( ! empty( $event_data['payment'] ) ) {
+				return array_merge( $payment_data, $event_data['payment'] );
+			}

 			// Try to get chosen payment method from session.
 			$chosen_payment_method = $this->get_chosen_payment_method();
 			if ( $chosen_payment_method ) {
-				$payment_gateway_name = \sanitize_text_field( $chosen_payment_method );
-				$payment_method_type  = \sanitize_text_field( $chosen_payment_method );
+				$payment_data['payment_gateway_name'] = \sanitize_text_field( $chosen_payment_method );
+				$payment_data['payment_method_type']  = \sanitize_text_field( $chosen_payment_method );
 			}

-			// Initialize payment data with default null values.
-			$payment_data = array(
-				'payment_gateway_name'      => $payment_gateway_name,
-				'payment_method_type'       => $payment_method_type,
-				'card_bin'                  => null,
-				'card_last4'                => null,
-				'card_brand'                => null,
-				'payer_id'                  => null,
-				'outcome'                   => null,
-				'decline_reason'            => null,
-				'avs_result'                => null,
-				'cvc_result'                => null,
-				'tokenized_card_identifier' => null,
-			);
-
 			return $payment_data;
 		} catch ( \Exception $e ) {
-			// Graceful degradation - return structure with null values.
-			return array(
-				'payment_gateway_name'      => null,
-				'payment_method_type'       => null,
-				'card_bin'                  => null,
-				'card_last4'                => null,
-				'card_brand'                => null,
-				'payer_id'                  => null,
-				'outcome'                   => null,
-				'decline_reason'            => null,
-				'avs_result'                => null,
-				'cvc_result'                => null,
-				'tokenized_card_identifier' => null,
-			);
+			// Graceful degradation.
+			return $payment_data;
 		}
 	}

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 8c3b9de07e..610b5a771a 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
@@ -84,4 +84,29 @@ class WC_Payment_Gateways_Test extends WC_Unit_Test_Case {
 		}
 		remove_filter( 'wp_mail', $watcher );
 	}
+
+	/**
+	 * Test get_payment_gateway_name_by_id returns gateway title for known gateway.
+	 *
+	 * @return void
+	 */
+	public function test_get_payment_gateway_name_by_id_returns_gateway_title_for_known_gateway(): void {
+		// Test with a known gateway (bacs is available by default in WooCommerce).
+		$result = $this->sut->get_payment_gateway_name_by_id( 'bacs' );
+
+		// Should return a readable name, not just the ID.
+		$this->assertNotEmpty( $result );
+		$this->assertEquals( 'Direct bank transfer', $result );
+	}
+
+	/**
+	 * Test get_payment_gateway_name_by_id returns ID when gateway not found.
+	 *
+	 * @return void
+	 */
+	public function test_get_payment_gateway_name_by_id_returns_id_when_gateway_not_found(): void {
+		// Test that get_payment_gateway_name_by_id returns the ID as fallback.
+		$result = $this->sut->get_payment_gateway_name_by_id( 'nonexistent_gateway' );
+		$this->assertEquals( 'nonexistent_gateway', $result );
+	}
 }
diff --git a/plugins/woocommerce/tests/php/src/Internal/FraudProtection/CartEventTrackerTest.php b/plugins/woocommerce/tests/php/src/Internal/FraudProtection/CartEventTrackerTest.php
index 4b6f66d534..d1f5fd87d5 100644
--- a/plugins/woocommerce/tests/php/src/Internal/FraudProtection/CartEventTrackerTest.php
+++ b/plugins/woocommerce/tests/php/src/Internal/FraudProtection/CartEventTrackerTest.php
@@ -8,6 +8,7 @@ declare( strict_types = 1 );
 namespace Automattic\WooCommerce\Tests\Internal\FraudProtection;

 use Automattic\WooCommerce\Internal\FraudProtection\CartEventTracker;
+use Automattic\WooCommerce\Internal\FraudProtection\FraudProtectionTracker;
 use Automattic\WooCommerce\Internal\FraudProtection\SessionDataCollector;
 use Automattic\WooCommerce\Internal\FraudProtection\FraudProtectionController;

@@ -25,6 +26,13 @@ class CartEventTrackerTest extends \WC_Unit_Test_Case {
 	 */
 	private $sut;

+	/**
+	 * Mock fraud protection tracker.
+	 *
+	 * @var FraudProtectionTracker|\PHPUnit\Framework\MockObject\MockObject
+	 */
+	private $mock_tracker;
+
 	/**
 	 * Mock session data collector.
 	 *
@@ -58,12 +66,14 @@ class CartEventTrackerTest extends \WC_Unit_Test_Case {
 		}

 		// Create mocks.
+		$this->mock_tracker        = $this->createMock( FraudProtectionTracker::class );
 		$this->mock_data_collector = $this->createMock( SessionDataCollector::class );
 		$this->mock_controller     = $this->createMock( FraudProtectionController::class );

 		// Create system under test.
 		$this->sut = new CartEventTracker();
 		$this->sut->init(
+			$this->mock_tracker,
 			$this->mock_data_collector,
 			$this->mock_controller
 		);
@@ -110,31 +120,30 @@ class CartEventTrackerTest extends \WC_Unit_Test_Case {
 	}

 	/**
-	 * Test handle_track_cart_item_added collects and logs event data.
+	 * Test handle_track_cart_item_added tracks event.
 	 */
 	public function test_handle_track_cart_item_added_tracks_event(): void {
-		// Mock the data collector to return test data.
+		$collected_data = array(
+			'action'          => 'item_added',
+			'product_id'      => $this->test_product->get_id(),
+			'quantity'        => 2,
+			'cart_item_count' => 1,
+			'session'         => array( 'session_id' => 'test-session' ),
+		);
+
+		// Mock data collector to return collected data.
 		$this->mock_data_collector
 			->expects( $this->once() )
 			->method( 'collect' )
+			->willReturn( $collected_data );
+
+		// Mock the tracker to verify track_event is called with collected data.
+		$this->mock_tracker
+			->expects( $this->once() )
+			->method( 'track_event' )
 			->with(
 				$this->equalTo( 'cart_item_added' ),
-				$this->callback(
-					function ( $event_data ) {
-						return isset( $event_data['action'] )
-							&& 'item_added' === $event_data['action']
-							&& isset( $event_data['product_id'] )
-							&& isset( $event_data['quantity'] )
-							&& isset( $event_data['cart_item_count'] )
-							&& is_numeric( $event_data['cart_item_count'] );
-					}
-				)
-			)
-			->willReturn(
-				array(
-					'event_type' => 'cart_item_added',
-					'session'    => array( 'session_id' => 'test-session' ),
-				)
+				$this->equalTo( $collected_data )
 			);

 		// Call the handler.
@@ -149,31 +158,32 @@ class CartEventTrackerTest extends \WC_Unit_Test_Case {
 	}

 	/**
-	 * Test handle_track_cart_item_updated collects event data.
+	 * Test handle_track_cart_item_updated tracks event.
 	 */
 	public function test_handle_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 );

-		// Mock the data collector.
+		$collected_data = array(
+			'action'       => 'item_updated',
+			'old_quantity' => 1,
+			'quantity'     => 5,
+			'session'      => array( 'session_id' => 'test-session' ),
+		);
+
+		// Mock data collector.
 		$this->mock_data_collector
 			->expects( $this->once() )
 			->method( 'collect' )
+			->willReturn( $collected_data );
+
+		// Mock the tracker.
+		$this->mock_tracker
+			->expects( $this->once() )
+			->method( 'track_event' )
 			->with(
 				$this->equalTo( 'cart_item_updated' ),
-				$this->callback(
-					function ( $event_data ) {
-						return isset( $event_data['action'] )
-							&& 'item_updated' === $event_data['action']
-							&& isset( $event_data['old_quantity'] );
-					}
-				)
-			)
-			->willReturn(
-				array(
-					'event_type' => 'cart_item_updated',
-					'session'    => array( 'session_id' => 'test-session' ),
-				)
+				$this->equalTo( $collected_data )
 			);

 		// Call the handler.
@@ -186,30 +196,30 @@ class CartEventTrackerTest extends \WC_Unit_Test_Case {
 	}

 	/**
-	 * Test handle_track_cart_item_removed collects event data.
+	 * Test handle_track_cart_item_removed tracks event.
 	 */
 	public function test_handle_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 );

-		// Mock the data collector.
+		$collected_data = array(
+			'action'  => 'item_removed',
+			'session' => array( 'session_id' => 'test-session' ),
+		);
+
+		// Mock data collector.
 		$this->mock_data_collector
 			->expects( $this->once() )
 			->method( 'collect' )
+			->willReturn( $collected_data );
+
+		// Mock the tracker.
+		$this->mock_tracker
+			->expects( $this->once() )
+			->method( 'track_event' )
 			->with(
 				$this->equalTo( 'cart_item_removed' ),
-				$this->callback(
-					function ( $event_data ) {
-						return isset( $event_data['action'] )
-							&& 'item_removed' === $event_data['action'];
-					}
-				)
-			)
-			->willReturn(
-				array(
-					'event_type' => 'cart_item_removed',
-					'session'    => array( 'session_id' => 'test-session' ),
-				)
+				$this->equalTo( $collected_data )
 			);

 		// Remove the item from cart.
@@ -220,30 +230,30 @@ class CartEventTrackerTest extends \WC_Unit_Test_Case {
 	}

 	/**
-	 * Test handle_track_cart_item_restored collects event data.
+	 * Test handle_track_cart_item_restored tracks event.
 	 */
 	public function test_handle_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 );

-		// Mock the data collector.
+		$collected_data = array(
+			'action'  => 'item_restored',
+			'session' => array( 'session_id' => 'test-session' ),
+		);
+
+		// Mock data collector.
 		$this->mock_data_collector
 			->expects( $this->once() )
 			->method( 'collect' )
+			->willReturn( $collected_data );
+
+		// Mock the tracker.
+		$this->mock_tracker
+			->expects( $this->once() )
+			->method( 'track_event' )
 			->with(
 				$this->equalTo( 'cart_item_restored' ),
-				$this->callback(
-					function ( $event_data ) {
-						return isset( $event_data['action'] )
-							&& 'item_restored' === $event_data['action'];
-					}
-				)
-			)
-			->willReturn(
-				array(
-					'event_type' => 'cart_item_restored',
-					'session'    => array( 'session_id' => 'test-session' ),
-				)
+				$this->equalTo( $collected_data )
 			);

 		// Call the handler directly (simulating restore action).
@@ -262,20 +272,26 @@ class CartEventTrackerTest extends \WC_Unit_Test_Case {
 		$variations       = $variable_product->get_available_variations();
 		$variation_id     = $variations[0]['variation_id'];

-		// Mock the data collector to capture event data.
+		$collected_data = array(
+			'action'       => 'item_added',
+			'variation_id' => $variation_id,
+			'session'      => array( 'session_id' => 'test-session' ),
+		);
+
+		// Mock data collector.
 		$this->mock_data_collector
 			->expects( $this->once() )
 			->method( 'collect' )
+			->willReturn( $collected_data );
+
+		// Mock the tracker to capture event data.
+		$this->mock_tracker
+			->expects( $this->once() )
+			->method( 'track_event' )
 			->with(
 				$this->equalTo( 'cart_item_added' ),
-				$this->callback(
-					function ( $event_data ) use ( $variation_id ) {
-						return isset( $event_data['variation_id'] )
-							&& $variation_id === $event_data['variation_id'];
-					}
-				)
-			)
-			->willReturn( array() );
+				$this->equalTo( $collected_data )
+			);

 		// Call the handler with variation ID.
 		$this->sut->handle_track_cart_item_added(
diff --git a/plugins/woocommerce/tests/php/src/Internal/FraudProtection/CheckoutEventTrackerTest.php b/plugins/woocommerce/tests/php/src/Internal/FraudProtection/CheckoutEventTrackerTest.php
new file mode 100644
index 0000000000..9e66c02c33
--- /dev/null
+++ b/plugins/woocommerce/tests/php/src/Internal/FraudProtection/CheckoutEventTrackerTest.php
@@ -0,0 +1,176 @@
+<?php
+/**
+ * CheckoutEventTrackerTest class file.
+ */
+
+declare( strict_types = 1 );
+
+namespace Automattic\WooCommerce\Tests\Internal\FraudProtection;
+
+use Automattic\WooCommerce\Internal\FraudProtection\CheckoutEventTracker;
+use Automattic\WooCommerce\Internal\FraudProtection\FraudProtectionTracker;
+use Automattic\WooCommerce\Internal\FraudProtection\SessionDataCollector;
+use Automattic\WooCommerce\Internal\FraudProtection\FraudProtectionController;
+
+/**
+ * Tests for CheckoutEventTracker.
+ *
+ * @covers \Automattic\WooCommerce\Internal\FraudProtection\CheckoutEventTracker
+ */
+class CheckoutEventTrackerTest extends \WC_Unit_Test_Case {
+
+	/**
+	 * The system under test.
+	 *
+	 * @var CheckoutEventTracker
+	 */
+	private $sut;
+
+	/**
+	 * Mock fraud protection tracker.
+	 *
+	 * @var FraudProtectionTracker|\PHPUnit\Framework\MockObject\MockObject
+	 */
+	private $mock_tracker;
+
+	/**
+	 * Mock session data collector.
+	 *
+	 * @var SessionDataCollector|\PHPUnit\Framework\MockObject\MockObject
+	 */
+	private $mock_data_collector;
+
+	/**
+	 * Mock fraud protection controller.
+	 *
+	 * @var FraudProtectionController|\PHPUnit\Framework\MockObject\MockObject
+	 */
+	private $mock_controller;
+
+	/**
+	 * Runs before each test.
+	 */
+	public function setUp(): void {
+		parent::setUp();
+
+		// Ensure WooCommerce cart and session are available.
+		if ( ! did_action( 'woocommerce_load_cart_from_session' ) && function_exists( 'wc_load_cart' ) ) {
+			wc_load_cart();
+		}
+
+		// Create mocks.
+		$this->mock_tracker        = $this->createMock( FraudProtectionTracker::class );
+		$this->mock_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_tracker,
+			$this->mock_data_collector,
+			$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_checkout_field_update' ) ) );
+	}
+
+	/**
+	 * Test that register registers 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 hooks were registered.
+		$this->assertNotFalse( has_action( 'woocommerce_checkout_update_order_review', array( $this->sut, 'handle_checkout_field_update' ) ) );
+	}
+
+	/**
+	 * Test handle_checkout_field_update tracks event with billing email immediately.
+	 */
+	public function test_handle_checkout_field_update_tracks_event_with_billing_email(): void {
+		// Mock feature as enabled.
+		$this->mock_controller->method( 'feature_is_enabled' )->willReturn( true );
+
+		$collected_data = array(
+			'action'        => 'field_update',
+			'billing_email' => 'test@example.com',
+			'session'       => array( 'session_id' => 'test-session' ),
+		);
+
+		// Mock data collector to return collected data.
+		$this->mock_data_collector
+			->expects( $this->once() )
+			->method( 'collect' )
+			->willReturn( $collected_data );
+
+		// Mock tracker to verify track_event is called immediately with collected data.
+		$this->mock_tracker
+			->expects( $this->once() )
+			->method( 'track_event' )
+			->with(
+				$this->equalTo( 'checkout_field_update' ),
+				$this->equalTo( $collected_data )
+			);
+
+		// 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_checkout_field_update( $posted_data );
+	}
+
+	/**
+	 * Test handle_checkout_field_update tracks shipping method immediately.
+	 */
+	public function test_handle_checkout_field_update_tracks_shipping_method(): void {
+		// Mock feature as enabled.
+		$this->mock_controller->method( 'feature_is_enabled' )->willReturn( true );
+
+		$collected_data = array(
+			'action'           => 'field_update',
+			'billing_email'    => 'test@example.com',
+			'shipping_methods' => array( 'flat_rate:1' => 'Flat rate' ),
+			'session'          => array( 'session_id' => 'test-session' ),
+		);
+
+		// Mock data collector to return collected data.
+		$this->mock_data_collector
+			->expects( $this->once() )
+			->method( 'collect' )
+			->willReturn( $collected_data );
+
+		// Mock tracker to verify track_event is called with collected data.
+		$this->mock_tracker
+			->expects( $this->once() )
+			->method( 'track_event' )
+			->with(
+				$this->equalTo( 'checkout_field_update' ),
+				$this->equalTo( $collected_data )
+			);
+
+		// Register hooks.
+		$this->sut->register();
+
+		// Simulate checkout field update with shipping method.
+		// shipping_method is passed as an array in the posted data.
+		$posted_data = 'billing_email=test@example.com&shipping_method[0]=flat_rate:1';
+		$this->sut->handle_checkout_field_update( $posted_data );
+	}
+}
diff --git a/plugins/woocommerce/tests/php/src/Internal/FraudProtection/FraudProtectionTrackerTest.php b/plugins/woocommerce/tests/php/src/Internal/FraudProtection/FraudProtectionTrackerTest.php
new file mode 100644
index 0000000000..9c8394d2cb
--- /dev/null
+++ b/plugins/woocommerce/tests/php/src/Internal/FraudProtection/FraudProtectionTrackerTest.php
@@ -0,0 +1,125 @@
+<?php
+/**
+ * FraudProtectionTrackerTest class file.
+ */
+
+declare( strict_types = 1 );
+
+namespace Automattic\WooCommerce\Tests\Internal\FraudProtection;
+
+use Automattic\WooCommerce\Internal\FraudProtection\FraudProtectionTracker;
+use Automattic\WooCommerce\RestApi\UnitTests\LoggerSpyTrait;
+
+/**
+ * Tests for FraudProtectionTracker.
+ *
+ * @covers \Automattic\WooCommerce\Internal\FraudProtection\FraudProtectionTracker
+ */
+class FraudProtectionTrackerTest extends \WC_Unit_Test_Case {
+
+	use LoggerSpyTrait;
+
+	/**
+	 * The system under test.
+	 *
+	 * @var FraudProtectionTracker
+	 */
+	private $sut;
+
+	/**
+	 * Runs before each test.
+	 */
+	public function setUp(): void {
+		parent::setUp();
+
+		// Create system under test.
+		$this->sut = new FraudProtectionTracker();
+	}
+
+	/**
+	 * Test track_event logs collected data successfully.
+	 */
+	public function test_track_event_logs_collected_data_successfully(): void {
+		$event_type     = 'test_event';
+		$collected_data = array(
+			'session'    => array( 'session_id' => 'test-session-123' ),
+			'action'     => 'test_action',
+			'product_id' => 123,
+		);
+
+		// Call track_event.
+		$this->sut->track_event( $event_type, $collected_data );
+
+		// Verify the log was captured.
+		$this->assertCount( 1, $this->captured_logs );
+		$this->assertEquals( 'info', $this->captured_logs[0]['level'] );
+		$this->assertStringContainsString( 'test_event', $this->captured_logs[0]['message'] );
+		$this->assertStringContainsString( 'test-session-123', $this->captured_logs[0]['message'] );
+		$this->assertEquals( 'test_event', $this->captured_logs[0]['context']['event_type'] );
+		$this->assertEquals( $collected_data, $this->captured_logs[0]['context']['collected_data'] );
+	}
+
+	/**
+	 * Test track_event handles data without session gracefully.
+	 */
+	public function test_track_event_handles_data_without_session_gracefully(): void {
+		$event_type     = 'test_event';
+		$collected_data = array( 'invalid' => 'data_without_session' );
+
+		// Call track_event with data without session - should handle gracefully and log with N/A.
+		$this->sut->track_event( $event_type, $collected_data );
+
+		// Verify the log was captured with N/A for session ID.
+		$this->assertCount( 1, $this->captured_logs );
+		$this->assertEquals( 'info', $this->captured_logs[0]['level'] );
+		$this->assertStringContainsString( 'test_event', $this->captured_logs[0]['message'] );
+		$this->assertStringContainsString( 'N/A', $this->captured_logs[0]['message'] );
+		$this->assertEquals( 'test_event', $this->captured_logs[0]['context']['event_type'] );
+		$this->assertEquals( $collected_data, $this->captured_logs[0]['context']['collected_data'] );
+	}
+
+	/**
+	 * Test track_event accepts various data structures.
+	 */
+	public function test_track_event_accepts_various_data_structures(): void {
+		$event_type     = 'cart_item_added';
+		$collected_data = array(
+			'session'    => array( 'session_id' => 'test' ),
+			'action'     => 'item_added',
+			'product_id' => 456,
+			'quantity'   => 2,
+		);
+
+		// Call track_event.
+		$this->sut->track_event( $event_type, $collected_data );
+
+		// Verify the log was captured with all data.
+		$this->assertCount( 1, $this->captured_logs );
+		$this->assertEquals( 'info', $this->captured_logs[0]['level'] );
+		$this->assertStringContainsString( 'cart_item_added', $this->captured_logs[0]['message'] );
+		$this->assertEquals( 'cart_item_added', $this->captured_logs[0]['context']['event_type'] );
+		$this->assertEquals( $collected_data, $this->captured_logs[0]['context']['collected_data'] );
+		$this->assertEquals( 'item_added', $this->captured_logs[0]['context']['collected_data']['action'] );
+		$this->assertEquals( 456, $this->captured_logs[0]['context']['collected_data']['product_id'] );
+		$this->assertEquals( 2, $this->captured_logs[0]['context']['collected_data']['quantity'] );
+	}
+
+	/**
+	 * Test track_event works with empty collected data.
+	 */
+	public function test_track_event_works_with_empty_collected_data(): void {
+		$event_type     = 'test_event';
+		$collected_data = array();
+
+		// Call track_event with empty data.
+		$this->sut->track_event( $event_type, $collected_data );
+
+		// Verify the log was captured even with empty data.
+		$this->assertCount( 1, $this->captured_logs );
+		$this->assertEquals( 'info', $this->captured_logs[0]['level'] );
+		$this->assertStringContainsString( 'test_event', $this->captured_logs[0]['message'] );
+		$this->assertStringContainsString( 'N/A', $this->captured_logs[0]['message'] );
+		$this->assertEquals( 'test_event', $this->captured_logs[0]['context']['event_type'] );
+		$this->assertEquals( $collected_data, $this->captured_logs[0]['context']['collected_data'] );
+	}
+}
diff --git a/plugins/woocommerce/tests/php/src/Internal/FraudProtection/PaymentMethodEventTrackerTest.php b/plugins/woocommerce/tests/php/src/Internal/FraudProtection/PaymentMethodEventTrackerTest.php
index 038294eda8..c5a267d802 100644
--- a/plugins/woocommerce/tests/php/src/Internal/FraudProtection/PaymentMethodEventTrackerTest.php
+++ b/plugins/woocommerce/tests/php/src/Internal/FraudProtection/PaymentMethodEventTrackerTest.php
@@ -93,12 +93,14 @@ class PaymentMethodEventTrackerTest extends \WC_Unit_Test_Case {
 			'info',
 			'payment_method_added',
 			array(
-				'source'     => 'woo-fraud-protection',
-				'event_type' => 'payment_method_added',
-				'event_data' => array(
-					'action'     => 'added',
-					'token_id'   => $token->get_id(),
-					'gateway_id' => 'stripe',
+				'source'         => 'woo-fraud-protection',
+				'event_type'     => 'payment_method_added',
+				'collected_data' => array(
+					'event_data' => array(
+						'action'     => 'added',
+						'token_id'   => $token->get_id(),
+						'gateway_id' => 'stripe',
+					),
 				),
 			)
 		);
@@ -130,12 +132,14 @@ class PaymentMethodEventTrackerTest extends \WC_Unit_Test_Case {
 			'info',
 			'payment_method_updated',
 			array(
-				'source'     => 'woo-fraud-protection',
-				'event_type' => 'payment_method_updated',
-				'event_data' => array(
-					'action'     => 'updated',
-					'token_id'   => $token->get_id(),
-					'gateway_id' => 'stripe',
+				'source'         => 'woo-fraud-protection',
+				'event_type'     => 'payment_method_updated',
+				'collected_data' => array(
+					'event_data' => array(
+						'action'     => 'updated',
+						'token_id'   => $token->get_id(),
+						'gateway_id' => 'stripe',
+					),
 				),
 			)
 		);
@@ -181,13 +185,15 @@ class PaymentMethodEventTrackerTest extends \WC_Unit_Test_Case {
 			'info',
 			'payment_method_set_default',
 			array(
-				'source'     => 'woo-fraud-protection',
-				'event_type' => 'payment_method_set_default',
-				'event_data' => array(
-					'action'     => 'set_default',
-					'token_id'   => $token2->get_id(),
-					'gateway_id' => 'stripe',
-					'is_default' => true,
+				'source'         => 'woo-fraud-protection',
+				'event_type'     => 'payment_method_set_default',
+				'collected_data' => array(
+					'event_data' => array(
+						'action'     => 'set_default',
+						'token_id'   => $token2->get_id(),
+						'gateway_id' => 'stripe',
+						'is_default' => true,
+					),
 				),
 			)
 		);
@@ -218,12 +224,14 @@ class PaymentMethodEventTrackerTest extends \WC_Unit_Test_Case {
 			'info',
 			'payment_method_deleted',
 			array(
-				'source'     => 'woo-fraud-protection',
-				'event_type' => 'payment_method_deleted',
-				'event_data' => array(
-					'action'     => 'deleted',
-					'token_id'   => $token->get_id(),
-					'gateway_id' => 'stripe',
+				'source'         => 'woo-fraud-protection',
+				'event_type'     => 'payment_method_deleted',
+				'collected_data' => array(
+					'event_data' => array(
+						'action'     => 'deleted',
+						'token_id'   => $token->get_id(),
+						'gateway_id' => 'stripe',
+					),
 				),
 			)
 		);
diff --git a/plugins/woocommerce/tests/php/src/Internal/FraudProtection/SessionClearanceManagerTest.php b/plugins/woocommerce/tests/php/src/Internal/FraudProtection/SessionClearanceManagerTest.php
index 57aebb2ef3..a3646ab1d7 100644
--- a/plugins/woocommerce/tests/php/src/Internal/FraudProtection/SessionClearanceManagerTest.php
+++ b/plugins/woocommerce/tests/php/src/Internal/FraudProtection/SessionClearanceManagerTest.php
@@ -28,12 +28,11 @@ class SessionClearanceManagerTest extends \WC_Unit_Test_Case {
 	 */
 	public function setUp(): void {
 		parent::setUp();
-		$this->sut = new SessionClearanceManager();

-		// Ensure WooCommerce cart and session are available.
-		if ( ! did_action( 'woocommerce_load_cart_from_session' ) && function_exists( 'wc_load_cart' ) ) {
-			wc_load_cart();
-		}
+		WC()->session = new \WC_Session_Handler();
+		WC()->session->init();
+
+		$this->sut = new SessionClearanceManager();
 	}

 	/**