PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 16.2-a.1
Jetpack – WP Security, Backup, Speed, & Growth v16.2-a.1
16.1.2 16.2-a.1 16.1.1 16.1 16.1-beta 16.1-beta.2 16.1-beta.3 16.1-a.5 16.1-a.3 16.0.1 16.1-a.1 16.0 16.0-beta 16.0-a.7 16.0-a.5 15.9.1 16.0-a.3 16.0-a.1 15.9 15.9-beta 15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / jetpack_vendor / automattic / woocommerce-analytics / src / class-universal.php
jetpack / jetpack_vendor / automattic / woocommerce-analytics / src Last commit date
API 8 months ago mu-plugin 5 months ago class-consent-manager.php 8 months ago class-features.php 5 months ago class-my-account.php 8 months ago class-pixel-builder.php 4 months ago class-universal.php 3 weeks ago class-wc-analytics-tracking.php 3 weeks ago class-woo-analytics-trait.php 3 weeks ago class-woocommerce-analytics.php 3 weeks ago
class-universal.php
725 lines
1 <?php
2 /**
3 * General store tracking actions.
4 *
5 * @package automattic/woocommerce-analytics
6 */
7
8 namespace Automattic\Woocommerce_Analytics;
9
10 use Automattic\Jetpack\Constants;
11 use WC_Order;
12 use WC_Product;
13
14 /**
15 * Filters and Actions added to Store pages to perform analytics.
16 */
17 class Universal {
18 /**
19 * Trait to handle common analytics functions.
20 */
21 use Woo_Analytics_Trait;
22
23 /**
24 * Constructor.
25 */
26 public function init_hooks() {
27 $this->find_cart_checkout_content_sources();
28 $this->additional_blocks_on_cart_page = $this->get_additional_blocks_on_page( 'cart' );
29 $this->additional_blocks_on_checkout_page = $this->get_additional_blocks_on_page( 'checkout' );
30
31 // Capture search
32 add_action( 'template_redirect', array( $this, 'capture_search_query' ), 11 );
33
34 // Capture cart events.
35 add_action( 'woocommerce_add_to_cart', array( $this, 'capture_add_to_cart' ), 10, 6 );
36 add_action( 'woocommerce_after_cart_item_quantity_update', array( $this, 'capture_cart_quantity_update' ), 10, 4 );
37 add_action( 'woocommerce_cart_item_removed', array( $this, 'capture_remove_from_cart' ), 10, 2 );
38 add_filter( 'woocommerce_cart_item_remove_link', array( $this, 'remove_from_cart_attributes' ), 10, 2 );
39
40 // Checkout.
41 // Send events after checkout template (shortcode).
42 add_action( 'woocommerce_after_checkout_form', array( $this, 'checkout_process' ) );
43 // Send events after checkout block.
44 add_action( 'woocommerce_blocks_enqueue_checkout_block_scripts_after', array( $this, 'checkout_process' ) );
45
46 // order processed.
47 add_action( 'woocommerce_checkout_order_processed', array( $this, 'order_process' ), 10, 1 );
48 add_action( 'woocommerce_store_api_checkout_order_processed', array( $this, 'order_process' ), 10, 1 );
49
50 add_filter( 'woocommerce_checkout_posted_data', array( $this, 'save_checkout_post_data' ), 10, 1 );
51
52 add_action( 'woocommerce_created_customer', array( $this, 'capture_created_customer' ), 10, 2 );
53
54 add_action( 'woocommerce_created_customer', array( $this, 'capture_post_checkout_created_customer' ), 10, 2 );
55
56 // single product page view.
57 add_action( 'woocommerce_after_single_product', array( $this, 'capture_product_view' ) );
58
59 // order confirmed page view
60 add_action( 'woocommerce_thankyou', array( $this, 'capture_order_confirmation_view' ), 10, 1 );
61
62 // checkout page view
63 add_action( 'wp_footer', array( $this, 'capture_checkout_view' ), 11 );
64
65 // cart page view
66 add_action( 'wp_footer', array( $this, 'capture_cart_view' ), 11 );
67
68 // Enqueue events to track.
69 add_action( 'wp_footer', array( $this, 'inject_analytics_data' ), 999 );
70 }
71
72 /**
73 * Inject analytics data into the window object
74 */
75 public function inject_analytics_data() {
76 $is_clickhouse_enabled = Features::is_clickhouse_enabled();
77 $is_proxy_tracking_enabled = Features::is_proxy_tracking_enabled();
78 // When proxy tracking is enabled, we don't need to send the common properties to the client.
79 // Otherwise send only the page-safe properties: this markup is cacheable, so nothing
80 // derived from the current request may go into it.
81 $common_properties = $is_proxy_tracking_enabled ? array() : $this->get_page_common_properties();
82 ?>
83 <script type="text/javascript">
84 (function() {
85 window.wcAnalytics = window.wcAnalytics || {};
86 const wcAnalytics = window.wcAnalytics;
87
88 // Set the assets URL for webpack to find the split assets.
89 wcAnalytics.assets_url = '<?php echo esc_url( plugins_url( '../build/', __DIR__ . '/class-woocommerce-analytics.php' ) ); ?>';
90
91 // Set the REST API tracking endpoint URL.
92 <?php
93 $track_endpoint = rest_url( 'woocommerce-analytics/v1/track' );
94 // Include the WP REST nonce for logged-in users so WordPress can identify
95 // the current user via cookie auth. This is needed for the store_admin
96 // property detection, not for endpoint authorization.
97 if ( is_user_logged_in() ) {
98 $track_endpoint = add_query_arg( '_wpnonce', wp_create_nonce( 'wp_rest' ), $track_endpoint );
99 }
100 ?>
101 wcAnalytics.trackEndpoint = <?php echo wp_json_encode( esc_url_raw( $track_endpoint ), JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ); ?>;
102
103 // Set common properties for all events.
104 wcAnalytics.commonProps = <?php echo wp_json_encode( $common_properties, JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ); ?>;
105
106 // Set the event queue.
107 wcAnalytics.eventQueue = <?php echo wp_json_encode( WC_Analytics_Tracking::get_event_queue(), JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ); ?>;
108
109 // Features.
110 wcAnalytics.features = {
111 ch: <?php echo $is_clickhouse_enabled ? 'true' : 'false'; ?>,
112 sessionTracking: <?php echo $is_clickhouse_enabled ? 'true' : 'false'; ?>,
113 proxy: <?php echo $is_proxy_tracking_enabled ? 'true' : 'false'; ?>,
114 };
115
116 wcAnalytics.breadcrumbs = <?php echo wp_json_encode( $this->get_breadcrumb_titles(), JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ); ?>;
117
118 // Page context flags.
119 wcAnalytics.pages = {
120 isAccountPage: <?php echo is_account_page() ? 'true' : 'false'; ?>,
121 isCart: <?php echo is_cart() ? 'true' : 'false'; ?>,
122 };
123 })();
124 </script>
125 <?php
126 }
127
128 /**
129 * Capture remove from cart events in mini-cart and cart blocks
130 *
131 * @param string $cart_item_key The cart item removed.
132 * @param \WC_Cart $cart The cart.
133 *
134 * @return void
135 */
136 public function capture_remove_from_cart( $cart_item_key, $cart ) {
137 $item = $cart->removed_cart_contents[ $cart_item_key ] ?? null;
138
139 if ( ! is_array( $item ) || ! isset( $item['product_id'], $item['quantity'] ) ) {
140 return;
141 }
142
143 WC_Analytics_Tracking::record_event(
144 'remove_from_cart',
145 $this->get_cart_checkout_event_properties(
146 array(
147 'pi' => (int) $item['product_id'],
148 'pq' => (int) $item['quantity'],
149 )
150 )
151 );
152 }
153
154 /**
155 * Capture remove/add from cart events using the Cart Controller
156 *
157 * @param string $cart_item_key The cart item updated.
158 * @param int $quantity Contains the new quantity of the item.
159 * @param int $old_quantity Contains the old quantity of the item.
160 * @param \WC_Cart $cart The cart.
161 *
162 * @return void
163 */
164 public function capture_cart_quantity_update( $cart_item_key, $quantity, $old_quantity, $cart ) {
165 if ( ! isset( $cart->cart_contents[ $cart_item_key ]['product_id'] ) ) {
166 return;
167 }
168
169 $product_id = $cart->cart_contents[ $cart_item_key ]['product_id'];
170 if ( $quantity > $old_quantity ) {
171 WC_Analytics_Tracking::record_event(
172 'add_to_cart',
173 $this->get_cart_checkout_event_properties(
174 array(
175 'pi' => $product_id,
176 'pq' => $quantity,
177 )
178 )
179 );
180 $this->lock_add_to_cart_events = true;
181 return;
182 }
183
184 if ( $quantity < $old_quantity ) {
185 WC_Analytics_Tracking::record_event(
186 'remove_from_cart',
187 $this->get_cart_checkout_event_properties(
188 array(
189 'pi' => $product_id,
190 'pq' => $quantity,
191 )
192 )
193 );
194 return;
195 }
196 }
197
198 /**
199 * Adds the product ID to the remove product link (for use by remove_from_cart above) if not present
200 *
201 * @param string $url Full HTML a tag of the link to remove an item from the cart.
202 * @param string $key Unique Key ID for a cart item.
203 *
204 * @return string
205 */
206 public function remove_from_cart_attributes( $url, $key ) {
207 if ( str_contains( $url, 'data-product_id' ) ) {
208 return $url;
209 }
210
211 $item = WC()->cart->get_cart_item( $key );
212 $product = $item['data'];
213
214 $new_attributes = sprintf(
215 '" data-product_id="%s">',
216 esc_attr( $product->get_id() )
217 );
218
219 $url = str_replace( '">', $new_attributes, $url );
220 return $url;
221 }
222
223 /**
224 * Get the selected shipping option for a cart item. If the name cannot be found in the options table, the method's
225 * ID will be used.
226 *
227 * @param string $cart_item_key the cart item key.
228 *
229 * @return mixed|bool
230 */
231 public function get_shipping_option_for_item( $cart_item_key ) {
232 $packages = wc()->shipping()->get_packages();
233 $selected_options = wc()->session->get( 'chosen_shipping_methods' );
234
235 if ( ! is_array( $packages ) || ! is_array( $selected_options ) ) {
236 return false;
237 }
238
239 foreach ( $packages as $package_id => $package ) {
240
241 if ( ! isset( $package['contents'] ) || ! is_array( $package['contents'] ) ) {
242 return false;
243 }
244
245 foreach ( $package['contents'] as $package_item ) {
246 if ( ! isset( $package_item['key'] ) || $package_item['key'] !== $cart_item_key || ! isset( $selected_options[ $package_id ] ) ) {
247 continue;
248 }
249 $selected_rate_id = $selected_options[ $package_id ];
250 $method_key_id = sanitize_text_field( str_replace( ':', '_', $selected_rate_id ) );
251 $option_name = 'woocommerce_' . $method_key_id . '_settings';
252 $option_value = get_option( $option_name );
253 $title = '';
254 if ( is_array( $option_value ) && isset( $option_value['title'] ) ) {
255 $title = $option_value['title'];
256 }
257 if ( ! $title ) {
258 return $selected_rate_id;
259 }
260 return $title;
261 }
262 }
263
264 return false;
265 }
266
267 /**
268 * On the Checkout page, trigger an event for each product in the cart
269 */
270 public function checkout_process() {
271 global $post;
272 $checkout_page_id = wc_get_page_id( 'checkout' );
273 $cart = WC()->cart->get_cart();
274 $is_in_checkout_page = isset( $post->ID ) && $checkout_page_id === $post->ID ? 'Yes' : 'No';
275 $session = WC()->session;
276 if ( is_object( $session ) ) {
277 $session->set( 'checkout_page_used', 'Yes' === $is_in_checkout_page );
278 $session->save_data();
279 }
280
281 foreach ( $cart as $cart_item_key => $cart_item ) {
282 /**
283 * This filter is already documented in woocommerce/templates/cart/cart.php
284 */
285 $product = apply_filters( 'woocommerce_cart_item_product', $cart_item['data'], $cart_item, $cart_item_key );
286
287 if ( ! $product || ! $product instanceof WC_Product ) {
288 continue;
289 }
290
291 $data = $this->get_cart_checkout_shared_data();
292
293 $data['from_checkout'] = $is_in_checkout_page;
294
295 if ( ! empty( $data['products'] ) ) {
296 unset( $data['products'] );
297 }
298
299 if ( ! empty( $data['shipping_options_count'] ) ) {
300 unset( $data['shipping_options_count'] );
301 }
302
303 $data['pq'] = $cart_item['quantity'];
304 $this->enqueue_event( 'product_checkout', $this->get_cart_checkout_event_properties( $data ), $product->get_id() );
305 }
306 }
307
308 /**
309 * After the order processed, fire an event for each item in the order
310 *
311 * @param int|string|WC_Order $order_id_or_order Order Id or Order object.
312 */
313 public function order_process( $order_id_or_order ) {
314 $order = wc_get_order( $order_id_or_order );
315
316 if ( ! $order ) {
317 return;
318 }
319
320 $payment_option = $order->get_payment_method();
321
322 if ( is_object( WC()->session ) ) {
323 $create_account = true === WC()->session->get( 'wc_checkout_createaccount_used' ) ? 'Yes' : 'No';
324 $checkout_page_used = true === WC()->session->get( 'checkout_page_used' ) ? 'Yes' : 'No';
325
326 } else {
327 $create_account = 'No';
328 $checkout_page_used = 'No';
329 }
330
331 $delayed_account_creation = ucfirst( get_option( 'woocommerce_enable_delayed_account_creation', 'Yes' ) );
332
333 $guest_checkout = $order->get_user() ? 'No' : 'Yes';
334
335 $express_checkout = 'null';
336 // When the payment option is woocommerce_payment
337 // See if Google Pay or Apple Pay was used.
338 if ( 'woocommerce_payments' === $payment_option ) {
339 $payment_option_title = $order->get_payment_method_title();
340 if ( 'Google Pay (WooCommerce Payments)' === $payment_option_title ) {
341 $express_checkout = array( 'google_pay' );
342 } elseif ( 'Apple Pay (WooCommerce Payments)' === $payment_option_title ) {
343 $express_checkout = array( 'apple_pay' );
344 }
345 }
346
347 $checkout_page_contains_checkout_block = '0';
348 $checkout_page_contains_checkout_shortcode = '0';
349
350 $order_source = $order->get_created_via();
351 if ( 'store-api' === $order_source ) {
352 $checkout_page_contains_checkout_block = '1';
353 } elseif ( 'checkout' === $order_source ) {
354 $checkout_page_contains_checkout_shortcode = '1';
355 }
356
357 // loop through products in the order and queue a purchase event.
358 foreach ( $order->get_items() as $order_item ) {
359 // @phan-suppress-next-line PhanUndeclaredMethod -- Checked before being called. See also https://github.com/phan/phan/issues/1204.
360 $product_id = is_callable( array( $order_item, 'get_product_id' ) ) ? $order_item->get_product_id() : -1;
361
362 $order_items = $order->get_items();
363 $order_items_count = 0;
364 if ( is_array( $order_items ) ) {
365 $order_items_count = count( $order_items );
366 }
367 $order_coupons = $order->get_coupons();
368 $order_coupons_count = 0;
369 if ( is_array( $order_coupons ) ) {
370 $order_coupons_count = count( $order_coupons );
371 }
372
373 WC_Analytics_Tracking::record_event(
374 'product_purchase',
375 $this->get_cart_checkout_event_properties(
376 array(
377 'oi' => $order->get_order_number(),
378 'pi' => $product_id,
379 'pq' => $order_item->get_quantity(),
380 'payment_option' => $payment_option,
381 'create_account' => $create_account,
382 'guest_checkout' => $guest_checkout,
383 'delayed_account_creation' => $delayed_account_creation,
384 'express_checkout' => $express_checkout,
385 'coupon_used' => $order_coupons_count,
386 'products_count' => $order_items_count,
387 'order_value' => $order->get_subtotal(),
388 'order_total' => $order->get_total(),
389 'total_discount' => $order->get_discount_total(),
390 'total_taxes' => $order->get_total_tax(),
391 'total_shipping' => $order->get_shipping_total(),
392 'from_checkout' => $checkout_page_used,
393 'checkout_page_contains_checkout_block' => $checkout_page_contains_checkout_block,
394 'checkout_page_contains_checkout_shortcode' => $checkout_page_contains_checkout_shortcode,
395 )
396 )
397 );
398 }
399 }
400 /**
401 * Gets the inner blocks of a block.
402 *
403 * @param array $inner_blocks The inner blocks.
404 *
405 * @return array
406 */
407 private function get_inner_blocks( $inner_blocks ) {
408 $block_names = array();
409 if ( ! empty( $inner_blocks['blockName'] ) ) {
410 $block_names[] = $inner_blocks['blockName'];
411 }
412 if ( isset( $inner_blocks['innerBlocks'] ) && is_array( $inner_blocks['innerBlocks'] ) ) {
413 $block_names = array_merge( $block_names, $this->get_inner_blocks( $inner_blocks['innerBlocks'] ) );
414 }
415 return $block_names;
416 }
417
418 /**
419 * Track adding items to the cart.
420 *
421 * @param string $cart_item_key Cart item key.
422 * @param int $product_id Product added to cart.
423 * @param int $quantity Quantity added to cart.
424 * @param int $variation_id Product variation.
425 * @param array $variation Variation attributes..
426 * @param array $cart_item_data Other cart data.
427 */
428 public function capture_add_to_cart( $cart_item_key, $product_id, $quantity, $variation_id, $variation, $cart_item_data ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
429 if ( $this->lock_add_to_cart_events ) {
430 return;
431 }
432 WC_Analytics_Tracking::record_event(
433 'add_to_cart',
434 $this->get_cart_checkout_event_properties(
435 array(
436 'pi' => $product_id,
437 'pq' => $quantity,
438 )
439 )
440 );
441 }
442
443 /**
444 * Get the event properties for the cart and checkout events.
445 *
446 * @param array $event_properties Event properties.
447 */
448 public function get_cart_checkout_event_properties( $event_properties = array() ) {
449 if ( isset( $event_properties['pq'] ) ) {
450 $event_properties['pq'] = 0 === $event_properties['pq'] ? 1 : $event_properties['pq'];
451 $event_properties['pq'] = (string) $event_properties['pq'];
452 }
453 $product = isset( $event_properties['pi'] ) ? wc_get_product( $event_properties['pi'] ) : null;
454 $product_details = $product instanceof WC_Product ? $this->get_product_details( $product ) : array();
455
456 $checkout_cart_details = array(
457 'template_used' => $this->cart_checkout_templates_in_use ? '1' : '0',
458 'additional_blocks_on_cart_page' => $this->additional_blocks_on_cart_page,
459 'additional_blocks_on_checkout_page' => $this->additional_blocks_on_checkout_page,
460 'order_value' => $this->get_cart_subtotal(),
461 'order_total' => $this->get_cart_total(),
462 'total_tax' => $this->get_cart_taxes(),
463 'total_discount' => $this->get_total_discounts(),
464 'total_shipping' => $this->get_cart_shipping_total(),
465 'products_count' => $this->get_cart_items_count(),
466 );
467 $cart_checkout_info = $this->get_cart_checkout_info();
468
469 $event_properties = array_merge( $product_details, $checkout_cart_details, $cart_checkout_info, $event_properties ); // event properties should be last to allow for overrides
470
471 return $event_properties;
472 }
473
474 /**
475 * Save create account post data to be used in $this->order_process.
476 *
477 * @param array|null $data Post data from the checkout page.
478 *
479 * @return array|null
480 */
481 public function save_checkout_post_data( ?array $data ) {
482 if ( is_object( WC()->session ) && ! empty( $data['createaccount'] ) ) {
483 WC()->session->set( 'wc_checkout_createaccount_used', true );
484 WC()->session->save_data();
485 }
486 return $data;
487 }
488
489 /**
490 * Capture the create account event. Similar to save_checkout_post_data but works with Store API.
491 *
492 * @param int $customer_id Customer ID.
493 * @param array $new_customer_data New customer data.
494 */
495 public function capture_created_customer( $customer_id, $new_customer_data ) {
496 $session = WC()->session;
497 if (
498 is_object( $session )
499 && is_array( $new_customer_data )
500 && ! empty( $new_customer_data['source'] )
501 ) {
502 if ( str_contains( $new_customer_data['source'], 'store-api' ) ) {
503 $session->set( 'wc_checkout_createaccount_used', true );
504 $session->save_data();
505 }
506 }
507 }
508
509 /**
510 * Capture the post checkout create account event.
511 *
512 * @param int $customer_id Customer ID.
513 * @param array $new_customer_data New customer data.
514 */
515 public function capture_post_checkout_created_customer( $customer_id, $new_customer_data ) {
516 if (
517 is_array( $new_customer_data )
518 && ! empty( $new_customer_data['source'] )
519 && str_contains( $new_customer_data['source'], 'delayed-account-creation' )
520 ) {
521
522 $checkout_page_used = true === WC()->session->get( 'checkout_page_used' ) ? 'Yes' : 'No';
523 $checkout_page_contains_checkout_block = '1';
524 $checkout_page_contains_checkout_shortcode = '0';
525
526 $this->enqueue_event(
527 'post_account_creation',
528 $this->get_cart_checkout_event_properties(
529 array(
530 'from_checkout' => $checkout_page_used,
531 'checkout_page_contains_checkout_block' => $checkout_page_contains_checkout_block,
532 'checkout_page_contains_checkout_shortcode' => $checkout_page_contains_checkout_shortcode,
533 )
534 )
535 );
536 }
537 }
538
539 /**
540 * Capture a search event.
541 *
542 * The term is capped because this event is assembled here but fired by the
543 * client, so it travels through the page markup and then reaches the pixel
544 * URL. See `cap_page_string()`.
545 */
546 public function capture_search_query() {
547 if ( is_search() ) {
548 global $wp_query;
549 $this->enqueue_event(
550 'search',
551 array(
552 'search_query' => $this->cap_page_string( $wp_query->get( 's' ) ),
553 'qty' => $wp_query->found_posts,
554 )
555 );
556 }
557 }
558
559 /**
560 * Track the cart page view
561 */
562 public function capture_cart_view() {
563 global $post;
564 $cart_page_id = wc_get_page_id( 'cart' );
565
566 $is_cart = $cart_page_id && is_page( $cart_page_id )
567 || wc_post_content_has_shortcode( 'woocommerce_cart' )
568 || has_block( 'woocommerce/cart', $post )
569 || apply_filters( 'woocommerce_is_cart', false )
570 || Constants::is_defined( 'WOOCOMMERCE_CART' )
571 || is_cart();
572
573 if ( ! $is_cart ) {
574 return;
575 }
576
577 $this->enqueue_event(
578 'cart_view',
579 $this->get_cart_checkout_event_properties(
580 $this->get_cart_checkout_shared_data()
581 )
582 );
583 }
584
585 /**
586 * Track a product page view
587 */
588 public function capture_product_view() {
589 global $product;
590 if ( ! $product instanceof WC_Product ) {
591 return;
592 }
593
594 $this->enqueue_event(
595 'product_view',
596 array(),
597 $product->get_id()
598 );
599 }
600
601 /**
602 * Track the order confirmation page view
603 */
604 public function capture_order_confirmation_view() {
605 $order_id = absint( get_query_var( 'order-received' ) );
606 if ( ! $order_id ) {
607 return;
608 }
609
610 if ( ! is_order_received_page() ) {
611 return;
612 }
613
614 $order = wc_get_order( $order_id );
615
616 $order_source = $order->get_created_via();
617 $checkout_page_contains_checkout_block = '0';
618 $checkout_page_contains_checkout_shortcode = '0';
619
620 if ( 'store-api' === $order_source ) {
621 $checkout_page_contains_checkout_block = '1';
622 } elseif ( 'checkout' === $order_source ) {
623 $checkout_page_contains_checkout_shortcode = '1';
624 }
625
626 $coupons = $order->get_coupons();
627 $coupon_used = 0;
628 if ( is_countable( $coupons ) ) {
629 $coupon_used = count( $coupons ) ? 1 : 0;
630 }
631
632 if ( is_object( WC()->session ) ) {
633 $create_account = true === WC()->session->get( 'wc_checkout_createaccount_used' ) ? 'Yes' : 'No';
634 $checkout_page_used = true === WC()->session->get( 'checkout_page_used' ) ? 'Yes' : 'No';
635 } else {
636 $create_account = 'No';
637 $checkout_page_used = 'No';
638 }
639
640 $delayed_account_creation = ucfirst( get_option( 'woocommerce_enable_delayed_account_creation', 'Yes' ) );
641 $this->enqueue_event(
642 'order_confirmation_view',
643 $this->get_cart_checkout_event_properties(
644 array(
645 'coupon_used' => $coupon_used,
646 'create_account' => $create_account,
647 'express_checkout' => 'null', // TODO: not solved yet.
648 'guest_checkout' => $order->get_customer_id() ? 'No' : 'Yes',
649 'delayed_account_creation' => $delayed_account_creation,
650 'oi' => $order->get_id(),
651 'order_value' => $order->get_subtotal(),
652 'order_total' => $order->get_total(),
653 'products_count' => $order->get_item_count(),
654 'total_discount' => $order->get_discount_total(),
655 'total_shipping' => $order->get_shipping_total(),
656 'total_tax' => $order->get_total_tax(),
657 'payment_option' => $order->get_payment_method(),
658 'products' => $this->format_items_to_json( $order->get_items() ),
659 'order_note' => $order->get_customer_note(),
660 'shipping_option' => $order->get_shipping_method(),
661 'from_checkout' => $checkout_page_used,
662 'checkout_page_contains_checkout_block' => $checkout_page_contains_checkout_block,
663 'checkout_page_contains_checkout_shortcode' => $checkout_page_contains_checkout_shortcode,
664 )
665 )
666 );
667 }
668
669 /**
670 * Track the checkout page view
671 */
672 public function capture_checkout_view() {
673 global $post;
674 if ( ! $post instanceof \WP_Post ) {
675 return;
676 }
677
678 $checkout_page_id = wc_get_page_id( 'checkout' );
679
680 $is_checkout = $checkout_page_id && is_page( $checkout_page_id )
681 || wc_post_content_has_shortcode( 'woocommerce_checkout' )
682 || has_block( 'woocommerce/checkout', $post )
683 || apply_filters( 'woocommerce_is_checkout', false )
684 || Constants::is_defined( 'WOOCOMMERCE_CHECKOUT' );
685
686 if ( ! $is_checkout ) {
687 return;
688 }
689
690 $is_in_checkout_page = isset( $post->ID ) && $checkout_page_id === $post->ID ? 'Yes' : 'No';
691 $checkout_page_contains_checkout_block = '0';
692 $checkout_page_contains_checkout_shortcode = '1';
693
694 $session = WC()->session;
695 if ( is_object( $session ) ) {
696 $session->set( 'checkout_page_used', true );
697 $session->save_data();
698 $draft_order_id = $session->get( 'store_api_draft_order', 0 );
699 if ( $draft_order_id ) {
700 $checkout_page_contains_checkout_block = '1';
701 $checkout_page_contains_checkout_shortcode = '0';
702 }
703 }
704
705 // Order received page is also a checkout page, so we need to bail out if we are on that page.
706 if ( is_order_received_page() ) {
707 return;
708 }
709
710 $this->enqueue_event(
711 'checkout_view',
712 $this->get_cart_checkout_event_properties(
713 array_merge(
714 $this->get_cart_checkout_shared_data(),
715 array(
716 'from_checkout' => $is_in_checkout_page,
717 'checkout_page_contains_checkout_block' => $checkout_page_contains_checkout_block,
718 'checkout_page_contains_checkout_shortcode' => $checkout_page_contains_checkout_shortcode,
719 )
720 )
721 )
722 );
723 }
724 }
725