diff --git a/assets/js/event-providers/easy-digital-downloads.js b/assets/js/event-providers/easy-digital-downloads.js index 89c371e7e80..c5928076353 100644 --- a/assets/js/event-providers/easy-digital-downloads.js +++ b/assets/js/event-providers/easy-digital-downloads.js @@ -37,6 +37,15 @@ ], } ); } ); + + if ( + global._googlesitekit?.gtagUserData && + global._googlesitekit?.edddata?.purchase?.user_data + ) { + global._googlesitekit?.gtagEvent?.( 'purchase', { + user_data: global._googlesitekit.edddata.purchase.user_data, + } ); + } } )( global.jQuery ); /** diff --git a/includes/Core/Conversion_Tracking/Conversion_Event_Providers/Easy_Digital_Downloads.php b/includes/Core/Conversion_Tracking/Conversion_Event_Providers/Easy_Digital_Downloads.php index 78cf4efd8b0..b8e0c697120 100644 --- a/includes/Core/Conversion_Tracking/Conversion_Event_Providers/Easy_Digital_Downloads.php +++ b/includes/Core/Conversion_Tracking/Conversion_Event_Providers/Easy_Digital_Downloads.php @@ -12,6 +12,9 @@ use Google\Site_Kit\Core\Assets\Script; use Google\Site_Kit\Core\Conversion_Tracking\Conversion_Events_Provider; +use Google\Site_Kit\Core\Util\Feature_Flags; +use Google\Site_Kit\Core\Util\Method_Proxy_Trait; +use Google\Site_Kit\Modules\Ads\Enhanced_Conversions; /** * Class for handling Easy Digital Downloads conversion events. @@ -22,6 +25,8 @@ */ class Easy_Digital_Downloads extends Conversion_Events_Provider { + use Method_Proxy_Trait; + const CONVERSION_EVENT_PROVIDER_SLUG = 'easy-digital-downloads'; /** @@ -43,7 +48,13 @@ public function is_active() { * @return array List of event names. */ public function get_event_names() { - return array( 'add_to_cart' ); + $event_names = array( 'add_to_cart' ); + + if ( Feature_Flags::enabled( 'gtagUserData' ) ) { + $event_names[] = 'purchase'; + } + + return $event_names; } /** @@ -67,4 +78,141 @@ public function register_script() { return $script; } + + /** + * Registers hooks for the Easy Digital Downloads provider. + * + * @since n.e.x.t + */ + public function register_hooks() { + if ( Feature_Flags::enabled( 'gtagUserData' ) ) { + add_action( + 'wp_footer', + $this->get_method_proxy( 'maybe_add_purchase_data_from_session' ) + ); + } + } + + /** + * Prints the purchase data. + * + * @since n.e.x.t + */ + protected function maybe_add_purchase_data_from_session() { + if ( ! function_exists( 'edd_get_purchase_session' ) || ! function_exists( 'edd_is_success_page' ) || ! edd_is_success_page() ) { + return; + } + + $purchase_session = edd_get_purchase_session(); + $purchase_data = $this->get_enhanced_conversions_data_from_session( $purchase_session ); + + wp_add_inline_script( + 'googlesitekit-events-provider-' . self::CONVERSION_EVENT_PROVIDER_SLUG, + join( + "\n", + array( + 'window._googlesitekit.edddata = window._googlesitekit.edddata || {};', + sprintf( 'window._googlesitekit.edddata.purchase = %s;', wp_json_encode( $purchase_data ) ), + ) + ), + 'before' + ); + } + + + /** + * Extracts Enhanced Conversions data from an EDD session. + * + * @since n.e.x.t + * + * @param mixed|array|null $session_data An array containing EDD purchase session data. + * + * @return array + */ + protected function get_enhanced_conversions_data_from_session( $session_data ) { + if ( ! is_array( $session_data ) ) { + return array(); + } + + $user_data = $this->extract_user_data_from_session( $session_data ); + + if ( empty( $user_data ) ) { + return array(); + } + + return array( + 'user_data' => $user_data, + ); + } + + + /** + * Extracts user data from an EDD session. + * + * @since n.e.x.t + * + * @param array $session_data An array containing EDD purchase session data. + * + * @return array + */ + protected function extract_user_data_from_session( $session_data ) { + $user_data = array(); + $address_data = array(); + + if ( isset( $session_data['user_info'] ) ) { + $email = $session_data['user_info']['email'] ?? $session_data['user_email'] ?? ''; + + if ( ! empty( $email ) ) { + $user_data['email'] = Enhanced_Conversions::get_normalized_email( $email ); + } + + if ( ! empty( $session_data['user_info']['first_name'] ) ) { + $address_data['first_name'] = Enhanced_Conversions::get_normalized_value( $session_data['user_info']['first_name'] ); + } + + if ( ! empty( $session_data['user_info']['last_name'] ) ) { + $address_data['last_name'] = Enhanced_Conversions::get_normalized_value( $session_data['user_info']['last_name'] ); + } + + if ( isset( $session_data['user_info']['address'] ) ) { + + if ( ! empty( $session_data['user_info']['address']['phone'] ) ) { + $user_data['phone_number'] = Enhanced_Conversions::get_normalized_value( $session_data['user_info']['address']['phone'] ); + } + + if ( ! empty( $session_data['user_info']['address']['line1'] ) ) { + $address_data['street'] = Enhanced_Conversions::get_normalized_value( $session_data['user_info']['address']['line1'] ); + } + + if ( ! empty( $session_data['user_info']['address']['city'] ) ) { + $address_data['city'] = Enhanced_Conversions::get_normalized_value( $session_data['user_info']['address']['city'] ); + } + + if ( ! empty( $session_data['user_info']['address']['state'] ) ) { + $region = $session_data['user_info']['address']['state']; + + // Attempt to get full region name. + if ( function_exists( 'edd_get_state_name' ) && ! empty( $session_data['user_info']['address']['country'] ) ) { + $region = edd_get_state_name( $session_data['user_info']['address']['country'], $region ); + } + + $address_data['region'] = Enhanced_Conversions::get_normalized_value( $region ); + } + + if ( ! empty( $session_data['user_info']['address']['zip'] ) ) { + $address_data['postal_code'] = Enhanced_Conversions::get_normalized_value( $session_data['user_info']['address']['zip'] ); + } + + if ( ! empty( $session_data['user_info']['address']['country'] ) ) { + $address_data['country'] = $session_data['user_info']['address']['country']; + } + } + } + + if ( ! empty( $address_data ) ) { + $user_data['address'] = $address_data; + } + + return $user_data; + } } diff --git a/tests/phpunit/integration/Core/Conversion_Tracking/Conversion_Event_Providers/Easy_Digital_DownloadsTest.php b/tests/phpunit/integration/Core/Conversion_Tracking/Conversion_Event_Providers/Easy_Digital_DownloadsTest.php index dcd4efeb57f..1dda8a83616 100644 --- a/tests/phpunit/integration/Core/Conversion_Tracking/Conversion_Event_Providers/Easy_Digital_DownloadsTest.php +++ b/tests/phpunit/integration/Core/Conversion_Tracking/Conversion_Event_Providers/Easy_Digital_DownloadsTest.php @@ -22,34 +22,241 @@ class Easy_Digital_DownloadsTest extends TestCase { * * @var Easy_Digital_Downloads */ - private $contactform; + private $edd; public function set_up() { parent::set_up(); - $this->contactform = new Easy_Digital_Downloads( new Context( GOOGLESITEKIT_PLUGIN_MAIN_FILE ) ); + $this->edd = new Easy_Digital_Downloads( new Context( GOOGLESITEKIT_PLUGIN_MAIN_FILE ) ); } /** * @runInSeparateProcess */ public function test_is_active() { - $this->assertFalse( $this->contactform->is_active() ); + $this->assertFalse( $this->edd->is_active() ); define( 'EDD_VERSION', 1 ); - $this->assertTrue( $this->contactform->is_active() ); + $this->assertTrue( $this->edd->is_active() ); } - public function test_get_event_names() { - $events = $this->contactform->get_event_names(); + public function test_get_event_names_with_gtag_disabled() { + $events = $this->edd->get_event_names(); $this->assertCount( 1, $events ); $this->assertEquals( 'add_to_cart', $events[0] ); } + public function test_get_event_names_with_gtag_enabled() { + $this->enable_feature( 'gtagUserData' ); + $events = $this->edd->get_event_names(); + $this->assertCount( 2, $events ); + $this->assertEquals( array( 'add_to_cart', 'purchase' ), $events ); + } + + public function test_register_script() { $handle = 'googlesitekit-events-provider-' . Easy_Digital_Downloads::CONVERSION_EVENT_PROVIDER_SLUG; $this->assertFalse( wp_script_is( $handle, 'registered' ) ); - $script = $this->contactform->register_script(); + $script = $this->edd->register_script(); $this->assertInstanceOf( Script::class, $script ); $this->assertTrue( wp_script_is( $handle, 'registered' ) ); } + + public function test_register_hooks_without_feature_flag() { + remove_all_actions( 'wp_footer' ); + + $this->edd->register_hooks(); + $this->assertFalse( has_action( 'wp_footer' ), 'Expected wp_footer action to not be registered.' ); + } + + public function test_register_hooks_with_feature_flag() { + $this->enable_feature( 'gtagUserData' ); + remove_all_actions( 'wp_footer' ); + + $this->edd->register_hooks(); + $this->assertTrue( has_action( 'wp_footer' ), 'Expected wp_footer action to be registered.' ); + } + + /** + * @dataProvider enhanced_conversion_session_provider + */ + public function test_get_enhanced_conversions_data_from_session_returns_expected_data( $session_data, $expected ) { + $reflection = new \ReflectionClass( $this->edd ); + $method = $reflection->getMethod( 'get_enhanced_conversions_data_from_session' ); + $method->setAccessible( true ); + + $result = $method->invoke( $this->edd, $session_data ); + $this->assertSame( $expected, $result ); + } + + /** + * @dataProvider session_user_data_provider + */ + public function test_extract_user_data_from_session_returns_expected_data( $session_data, $expected ) { + $reflection = new \ReflectionClass( $this->edd ); + $method = $reflection->getMethod( 'extract_user_data_from_session' ); + $method->setAccessible( true ); + + $result = $method->invoke( $this->edd, $session_data ); + $this->assertSame( $expected, $result ); + } + + public function enhanced_conversion_session_provider() { + return array( + 'non-array session' => array( + 'invalid-session', + array(), + ), + 'missing user data' => array( + array( 'user_info' => array() ), + array(), + ), + 'complete user data array' => array( + array( + 'user_info' => array( + 'email' => ' John+Doe@gmail.com ', + 'first_name' => ' John ', + 'last_name' => ' DOE ', + 'address' => array( + 'phone' => ' 123-456-7890 ', + 'line1' => ' 123 Main St ', + 'city' => ' New York ', + 'state' => ' NY ', + 'zip' => ' 12345 ', + 'country' => 'US', + ), + ), + ), + array( + 'user_data' => array( + 'email' => 'john+doe@gmail.com', + 'phone_number' => '123-456-7890', + 'address' => array( + 'first_name' => 'john', + 'last_name' => 'doe', + 'street' => '123 main st', + 'city' => 'new york', + 'region' => 'ny', + 'postal_code' => '12345', + 'country' => 'US', + ), + ), + ), + ), + ); + } + + public function session_user_data_provider() { + return array( + 'empty session' => array( + array(), + array(), + ), + 'full user profile' => array( + array( + 'user_info' => array( + 'email' => ' John.Doe@gmail.com ', + 'first_name' => ' John ', + 'last_name' => ' DOE ', + 'address' => array( + 'phone' => ' 123-456-7890 ', + 'line1' => ' 123 Main St ', + 'city' => ' New York ', + 'state' => ' NY ', + 'zip' => ' 12345 ', + 'country' => 'US', + ), + ), + ), + array( + 'email' => 'johndoe@gmail.com', + 'phone_number' => '123-456-7890', + 'address' => array( + 'first_name' => 'john', + 'last_name' => 'doe', + 'street' => '123 main st', + 'city' => 'new york', + 'region' => 'ny', + 'postal_code' => '12345', + 'country' => 'US', + ), + ), + ), + 'email only' => array( + array( + 'user_info' => array( + 'email' => ' john@example.com ', + ), + ), + array( + 'email' => 'john@example.com', + ), + ), + 'no address keys' => array( + array( + 'user_info' => array( + 'email' => ' John.Doe@gmail.com ', + 'first_name' => ' John ', + 'last_name' => ' Doe ', + ), + ), + array( + 'email' => 'johndoe@gmail.com', + 'address' => array( + 'first_name' => 'john', + 'last_name' => 'doe', + ), + ), + ), + 'partial address fields' => array( + array( + 'user_info' => array( + 'email' => 'john@example.com', + 'address' => array( + 'line1' => ' 123 Main St ', + 'country' => 'US', + ), + ), + ), + array( + 'email' => 'john@example.com', + 'address' => array( + 'street' => '123 main st', + 'country' => 'US', + ), + ), + ), + 'state without country' => array( + array( + 'user_info' => array( + 'email' => 'john@example.com', + 'address' => array( + 'line1' => ' 123 Main St ', + 'state' => ' NY ', + ), + ), + ), + array( + 'email' => 'john@example.com', + 'address' => array( + 'street' => '123 main st', + 'region' => 'ny', + ), + ), + ), + 'phone number only' => array( + array( + 'user_info' => array( + 'email' => 'john@example.com', + 'address' => array( + 'phone' => ' (123) 456-7890 ', + ), + ), + ), + array( + 'email' => 'john@example.com', + 'phone_number' => '(123) 456-7890', + ), + ), + ); + } }