get_price() === '' ) { return null; } return (float) wc_get_price_to_display( $product ); } function x_pixel_contents_from_product( $product, $quantity, $price = null ) { if ( ! $product ) { return array(); } if ( $price === null ) { $price = x_pixel_wc_display_price( $product ); } $parent_id = $product->get_parent_id(); return array( x_pixel_content_item( $product->get_id(), $product->get_name(), $price, $quantity, $parent_id ? $parent_id : $product->get_id() ), ); } function x_pixel_wc_calling_code( $country ) { if ( $country === '' || ! WC()->countries ) { return ''; } $code = WC()->countries->get_country_calling_code( $country ); return is_array( $code ) ? (string) reset( $code ) : (string) $code; } function x_pixel_wc_customer_params() { $customer = WC()->customer; if ( ! $customer ) { return array(); } $email = $customer->get_billing_email(); if ( $email === '' ) { $email = $customer->get_email(); } return x_pixel_matching_params( $email, $customer->get_billing_phone(), $customer->get_billing_first_name(), $customer->get_billing_last_name(), x_pixel_wc_calling_code( (string) $customer->get_billing_country() ) ); } function x_pixel_wc_order_params( $order ) { return x_pixel_matching_params( $order->get_billing_email(), $order->get_billing_phone(), $order->get_billing_first_name(), $order->get_billing_last_name(), x_pixel_wc_calling_code( (string) $order->get_billing_country() ) ); } function x_pixel_clean_twclid( $value ) { $value = is_string( $value ) ? trim( $value ) : ''; if ( $value === '' || strlen( $value ) > 128 || preg_match( '/^[A-Za-z0-9_-]+$/', $value ) !== 1 ) { return ''; } return $value; } function x_pixel_twclid_from_cookie( $raw ) { $raw = (string) $raw; if ( $raw === '' || strlen( $raw ) > 2048 ) { return ''; } foreach ( array( $raw, rawurldecode( $raw ) ) as $candidate ) { $decoded = json_decode( $candidate, true ); if ( is_array( $decoded ) && isset( $decoded['twclid'] ) && is_string( $decoded['twclid'] ) ) { return x_pixel_clean_twclid( $decoded['twclid'] ); } } return ''; } function x_pixel_request_twclid() { if ( isset( $_GET['twclid'] ) && ! is_array( $_GET['twclid'] ) ) { $twclid = x_pixel_clean_twclid( sanitize_text_field( wp_unslash( $_GET['twclid'] ) ) ); if ( $twclid !== '' ) { return $twclid; } } if ( isset( $_COOKIE['_twclid'] ) && ! is_array( $_COOKIE['_twclid'] ) ) { $twclid = x_pixel_twclid_from_cookie( filter_var( wp_unslash( $_COOKIE['_twclid'] ), FILTER_SANITIZE_URL ) ); if ( $twclid !== '' ) { return $twclid; } } if ( isset( $_COOKIE['twclid'] ) && ! is_array( $_COOKIE['twclid'] ) ) { return x_pixel_clean_twclid( sanitize_text_field( wp_unslash( $_COOKIE['twclid'] ) ) ); } return ''; } function x_pixel_client_ip() { $ip = isset( $_SERVER['REMOTE_ADDR'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) ) : ''; return filter_var( $ip, FILTER_VALIDATE_IP ) === false ? '' : $ip; } function x_pixel_request_identifiers() { $ids = array( 'ip_address' => x_pixel_client_ip(), 'user_agent' => isset( $_SERVER['HTTP_USER_AGENT'] ) ? sanitize_text_field( wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) ) : '', ); $twclid = x_pixel_request_twclid(); if ( $twclid !== '' ) { $ids['twclid'] = $twclid; } if ( is_user_logged_in() ) { $user = wp_get_current_user(); $hash = x_pixel_hash_value( $user->user_email ); if ( $hash !== '' ) { $ids['hashed_email'] = $hash; } } return $ids; } function x_pixel_on_add_to_cart( $cart_item_key, $product_id, $quantity, $variation_id ) { $event_id = get_option( X_PIXEL_OPTION_EVENT_ATC, '' ); if ( $event_id === '' ) { return; } if ( x_pixel_is_untrusted_request( filter_input( INPUT_GET, 'add-to-cart' ) !== null ) ) { return; } $id = $variation_id ? $variation_id : $product_id; $product = wc_get_product( $id ); if ( ! $product ) { return; } $qty = max( 1, (int) $quantity ); $price = x_pixel_wc_display_price( $product ); $conversion_id = 'atc-' . wp_generate_uuid4(); $params = array( 'value' => (float) $price * $qty, 'currency' => get_woocommerce_currency(), 'num_items' => $qty, 'contents' => x_pixel_contents_from_product( $product, $qty, $price ), 'conversion_id' => $conversion_id, ) + x_pixel_wc_customer_params(); x_pixel_queue_browser_event( $event_id, $params ); if ( wp_doing_ajax() ) { add_filter( 'woocommerce_add_to_cart_fragments', 'x_pixel_add_to_cart_fragments', 40 ); } x_pixel_send_capi( $event_id, $params, x_pixel_matching_identifiers( x_pixel_request_identifiers(), $params ), $conversion_id, x_pixel_event_source_url() ); } function x_pixel_add_to_cart_fragments( $fragments ) { if ( ! is_array( $fragments ) ) { $fragments = array(); } $events = x_pixel_take_queued_events(); if ( $events !== array() ) { $fragments['x_pixel_events'] = $events; } return $fragments; } function x_pixel_on_product_view() { if ( ! is_product() ) { return; } $event_id = get_option( X_PIXEL_OPTION_EVENT_VIEW, '' ); if ( $event_id === '' ) { return; } $product = wc_get_product( get_queried_object_id() ); if ( ! $product ) { return; } $params = array( 'value' => (float) x_pixel_wc_display_price( $product ), 'currency' => get_woocommerce_currency(), 'contents' => x_pixel_contents_from_product( $product, 1 ), ); $code = x_pixel_event_code( x_pixel_get_id(), $event_id ); if ( $code === '' ) { return; } x_pixel_print_event_script( $code, $params ); } function x_pixel_on_checkout() { if ( ! is_checkout() || is_wc_endpoint_url( 'order-received' ) ) { return; } $event_id = get_option( X_PIXEL_OPTION_EVENT_CHECKOUT, '' ); if ( $event_id === '' || ! WC()->cart || WC()->cart->is_empty() ) { return; } if ( x_pixel_is_untrusted_request() ) { return; } $cart = WC()->cart; $session = WC()->session; $cart_hash = $cart->get_cart_hash(); $conversion_id = x_pixel_conversion_id( 'checkout-' . $cart_hash ); $params = array( 'value' => (float) $cart->get_total( 'edit' ), 'currency' => get_woocommerce_currency(), 'num_items' => (int) $cart->get_cart_contents_count(), 'contents' => array(), 'conversion_id' => $conversion_id, ) + x_pixel_wc_customer_params(); foreach ( $cart->get_cart() as $item ) { $product = isset( $item['data'] ) ? $item['data'] : null; $qty = isset( $item['quantity'] ) ? (int) $item['quantity'] : 1; $params['contents'] = array_merge( $params['contents'], x_pixel_contents_from_product( $product, $qty ) ); } $code = x_pixel_event_code( x_pixel_get_id(), $event_id ); if ( $code === '' ) { return; } x_pixel_print_event_script( $code, $params ); if ( ! $session || $session->get( 'x_pixel_checkout_capi_hash' ) === $cart_hash ) { return; } $sent = x_pixel_send_capi( $event_id, $params, x_pixel_matching_identifiers( x_pixel_request_identifiers(), $params ), $conversion_id, x_pixel_current_url() ); if ( $sent ) { $session->set( 'x_pixel_checkout_capi_hash', $cart_hash ); } } function x_pixel_track_purchase( $order_id, $send_browser ) { $event_id = get_option( X_PIXEL_OPTION_EVENT_PURCHASE, '' ); if ( $event_id === '' ) { return; } $order = wc_get_order( $order_id ); if ( ! $order ) { return; } if ( ! $order->is_paid() && $order->needs_payment() ) { return; } if ( in_array( $order->get_status(), array( 'cancelled', 'failed', 'refunded', 'trash' ), true ) ) { return; } $contents = array(); $qty = 0; $inc_tax = get_option( 'woocommerce_tax_display_shop' ) === 'incl'; foreach ( $order->get_items() as $item ) { $product = $item->get_product(); $item_qty = (int) $item->get_quantity(); $qty += $item_qty; $contents = array_merge( $contents, x_pixel_contents_from_product( $product, $item_qty, (float) $order->get_item_total( $item, $inc_tax ) ) ); } $conversion_id = x_pixel_conversion_id( 'wc-' . $order->get_id() ); $params = array( 'value' => (float) $order->get_total(), 'currency' => $order->get_currency(), 'num_items' => $qty, 'contents' => $contents, 'conversion_id' => $conversion_id, 'description' => sprintf( 'WooCommerce order %s (%s)', $order->get_order_number(), $order->get_status() ), ) + x_pixel_wc_order_params( $order ); $dirty = false; if ( $send_browser && $order->get_meta( '_x_pixel_purchase_browser_sent' ) !== '1' ) { if ( x_pixel_queue_browser_event( $event_id, $params ) ) { $order->update_meta_data( '_x_pixel_purchase_browser_sent', '1' ); $dirty = true; } } if ( $order->get_meta( '_x_pixel_purchase_sent' ) !== '1' ) { $sent = x_pixel_send_capi( $event_id, $params, x_pixel_matching_identifiers( x_pixel_order_identifiers( $order ), $params ), $conversion_id, $order->get_checkout_order_received_url() ); if ( $sent ) { $order->update_meta_data( '_x_pixel_purchase_sent', '1' ); $dirty = true; } } if ( $dirty ) { $order->save(); } } function x_pixel_order_identifiers( $order ) { $ids = array( 'ip_address' => sanitize_text_field( (string) $order->get_customer_ip_address() ), 'user_agent' => sanitize_text_field( (string) $order->get_customer_user_agent() ), ); $twclid = (string) $order->get_meta( '_x_pixel_twclid' ); if ( $twclid !== '' ) { $ids['twclid'] = $twclid; } return $ids; } function x_pixel_remember_twclid_on_order( $order ) { if ( ! $order instanceof WC_Order ) { return; } $twclid = x_pixel_request_twclid(); if ( $twclid !== '' && $order->get_meta( '_x_pixel_twclid' ) === '' ) { $order->update_meta_data( '_x_pixel_twclid', $twclid ); } } function x_pixel_on_purchase( $order_id ) { x_pixel_track_purchase( $order_id, true ); } function x_pixel_on_payment_complete( $order_id ) { x_pixel_track_purchase( $order_id, false ); } function x_pixel_register_woocommerce_hooks() { if ( ! class_exists( 'WooCommerce' ) ) { return; } add_action( 'woocommerce_add_to_cart', 'x_pixel_on_add_to_cart', 40, 4 ); add_action( 'wp_footer', 'x_pixel_on_product_view', 15 ); add_action( 'wp_footer', 'x_pixel_on_checkout', 16 ); add_action( 'wp_footer', 'x_pixel_flush_queued_events', 20 ); add_action( 'wp_footer', 'x_pixel_print_drain_script', 21 ); add_action( 'woocommerce_checkout_create_order', 'x_pixel_remember_twclid_on_order', 10, 1 ); add_action( 'woocommerce_store_api_checkout_update_order_from_request', 'x_pixel_remember_twclid_on_order', 10, 1 ); add_action( 'woocommerce_thankyou', 'x_pixel_on_purchase', 40 ); add_action( 'woocommerce_payment_complete', 'x_pixel_on_payment_complete', 40 ); add_action( 'woocommerce_order_status_processing', 'x_pixel_on_payment_complete', 40 ); } add_action( 'plugins_loaded', 'x_pixel_register_woocommerce_hooks' );