$event_id)) ); } return sprintf( "\noaiq('measure', %s, %s);", ridoway_pixel_json_encode($event_name), ridoway_pixel_json_encode($data) ); } /** * One contents[] item. Amount and currency are attached when the unit price is * known, because OpenAI requires them to travel together. * * @param string $id * @param string $name * @param string $content_type * @param int $quantity * @param mixed $unit_amount * @param string $currency * @return array */ function ridoway_pixel_content_item($id, $name, $content_type, $quantity, $unit_amount = null, $currency = '') { $item = array( 'id' => (string) $id, 'name' => (string) $name, 'content_type' => (string) $content_type, 'quantity' => (int) $quantity, ); $minor_units = ridoway_pixel_to_minor_units($unit_amount, $currency); if ($minor_units !== null && $currency !== '') { $item['amount'] = $minor_units; $item['currency'] = $currency; } return $item; } /** * Attach the event-level amount + currency, in integer minor units, when a * usable decimal amount is known. * * @param array $data * @param mixed $amount * @param string $currency * @return array */ function ridoway_pixel_with_amount($data, $amount, $currency) { $minor_units = ridoway_pixel_to_minor_units($amount, $currency); if ($minor_units === null || $currency === '') { return $data; } $data['amount'] = $minor_units; $data['currency'] = $currency; return $data; } function ridoway_pixel_get_page_id() { if (is_singular()) { $post = get_queried_object(); if ($post && isset($post->ID)) { return (string) $post->ID; } } $id = get_queried_object_id(); return $id ? (string) $id : '0'; } function ridoway_pixel_get_page_title() { if (is_singular()) { $post = get_queried_object(); if ($post && isset($post->post_title)) { return $post->post_title; } } return wp_get_document_title(); } function ridoway_pixel_store_event($event_name, $data, $event_id = '') { $events = get_transient('ridoway_pixel_pending_events'); if (!is_array($events)) { $events = array(); } $events[] = array( 'event' => $event_name, 'event_id' => $event_id, 'data' => $data, ); set_transient('ridoway_pixel_pending_events', $events, 10 * MINUTE_IN_SECONDS); } /** * Per-event toggle lookup. Every pending event must pass through here so an * event option covers both the browser copy and the server copy. * * @param string $event_name * @return bool */ function ridoway_pixel_event_enabled($event_name) { $map = array( 'items_added' => 'track_items_added', 'checkout_started' => 'track_checkout_started', 'order_created' => 'track_order_created', 'lead_created' => 'track_lead_created', 'page_viewed' => 'track_page_viewed', 'contents_viewed' => 'track_contents_viewed', ); $option = isset($map[$event_name]) ? $map[$event_name] : ''; if ($option === '') { return true; } return ridoway_pixel_option_enabled($option, '1'); } add_action('wp_enqueue_scripts', function () { if (is_admin() || wp_doing_ajax() || wp_doing_cron()) { return; } $pixel_id = ridoway_pixel_option('pixel_id', ''); if (empty($pixel_id)) { return; } wp_register_script('ridoway-pixel', false, array(), RIDOWAY_PIXEL_VERSION, false); wp_enqueue_script('ridoway-pixel'); $debug = ridoway_pixel_option_enabled('debug_mode') ? 'true' : 'false'; $pixel_id_json = ridoway_pixel_json_encode($pixel_id); $respect_consent = ridoway_pixel_option_enabled('respect_consent'); $consent_ok = ridoway_pixel_consent_rule(); $defer_consent = $respect_consent && !$consent_ok; $script = '(function (w, d, s, u) {' . "\n"; $script .= ' if (w.oaiq) return;' . "\n"; $script .= ' var q = function () { q.q.push(arguments); };' . "\n"; $script .= ' q.q = [];' . "\n"; $script .= ' w.oaiq = q;' . "\n"; $script .= ' var js = d.createElement(s);' . "\n"; $script .= ' js.async = true;' . "\n"; $script .= ' js.src = u;' . "\n"; $script .= ' var f = d.getElementsByTagName(s)[0];' . "\n"; $script .= ' f.parentNode.insertBefore(js, f);' . "\n"; $script .= '})(window, document, "script", "https://bzrcdn.openai.com/sdk/oaiq.min.js");' . "\n"; $script .= "\n"; // Persist the ad-click id from the landing URL so later events - browser // and server alike - can still attribute to that ChatGPT ad click. $script .= "(function(){var p=new URLSearchParams(window.location.search).get('oppref');if(p){document.cookie='__oppref='+encodeURIComponent(p)+'; path=/; max-age=2592000';}})();\n"; // Consent must be declared before the pixel is initialised. if ($defer_consent) { $script .= "oaiq('consent', false);\n"; } $script .= "oaiq(\"init\", {\n pixelId: {$pixel_id_json},\n debug: {$debug}\n});\n"; // Hook for consent managers and GDPR plugins. $script .= "if (typeof window.ridowayPixelConsent === 'undefined') {\n"; $script .= " window.ridowayPixelConsent = {\n"; $script .= " grant: function () { if (window.oaiq) { oaiq('consent', true); } },\n"; $script .= " deny: function () { if (window.oaiq) { oaiq('consent', false); } }\n"; $script .= " };\n"; $script .= "}\n"; if (!$defer_consent) { $script .= "oaiq('consent', true);\n"; } if (!$defer_consent) { if (ridoway_pixel_event_enabled('page_viewed')) { $script .= ridoway_pixel_build_measure_script('page_viewed', array( 'type' => 'contents', )); } if (ridoway_pixel_event_enabled('contents_viewed') && function_exists('is_product') && is_product() ) { $product = wc_get_product(get_queried_object_id()); if ($product) { $currency = function_exists('get_woocommerce_currency') ? get_woocommerce_currency() : ''; $unit_price = (float) $product->get_price(); $contents = array( ridoway_pixel_content_item( $product->get_sku() ? $product->get_sku() : (string) $product->get_id(), $product->get_name(), 'product', 1, $unit_price, $currency ), ); $data = ridoway_pixel_with_amount(array( 'type' => 'contents', 'contents' => $contents, ), $unit_price, $currency); $script .= ridoway_pixel_build_measure_script('contents_viewed', $data); } } $track_wc = ridoway_pixel_option_enabled('woocommerce_tracking'); if ($track_wc && ridoway_pixel_is_woocommerce_active() && ridoway_pixel_event_enabled('checkout_started') ) { if (function_exists('is_checkout') && is_checkout() && !is_wc_endpoint_url('order-received') && !is_wc_endpoint_url('order-pay') ) { $cart = WC()->cart; if ($cart && !$cart->is_empty()) { $contents = array(); $total = 0; $currency = function_exists('get_woocommerce_currency') ? get_woocommerce_currency() : ''; foreach ($cart->get_cart() as $cart_item) { $product = $cart_item['data']; $qty = (int) $cart_item['quantity']; $price = (float) $product->get_price(); $total += $price * $qty; $contents[] = ridoway_pixel_content_item( $product->get_sku() ? $product->get_sku() : (string) $product->get_id(), $product->get_name(), 'product', $qty ); } // Only fire once per cart composition, not on every reload. $dedup_key = 'ridoway_pixel_checkout_fired_' . md5(wp_json_encode($contents) . '|' . $total . '|' . $currency); $data = ridoway_pixel_with_amount(array( 'type' => 'contents', 'contents' => $contents, ), $total, $currency); if (false === get_transient($dedup_key)) { $script .= ridoway_pixel_build_measure_script('checkout_started', $data); set_transient($dedup_key, 1, 15 * MINUTE_IN_SECONDS); } } } } $events = get_transient('ridoway_pixel_pending_events'); if (!empty($events) && is_array($events)) { delete_transient('ridoway_pixel_pending_events'); foreach ($events as $event) { if (!isset($event['event'], $event['data'])) { continue; } if (!ridoway_pixel_event_enabled($event['event'])) { continue; } $event_id = isset($event['event_id']) ? $event['event_id'] : ''; $script .= ridoway_pixel_build_measure_script($event['event'], $event['data'], $event_id); } } } wp_add_inline_script('ridoway-pixel', $script); }); add_action('woocommerce_add_to_cart', function ($cart_item_key, $product_id, $quantity, $variation_id, $variation, $cart_item_data) { if (!ridoway_pixel_option_enabled('woocommerce_tracking') || !ridoway_pixel_event_enabled('items_added')) { return; } if (!ridoway_pixel_is_woocommerce_active()) { return; } $product = wc_get_product($variation_id ? $variation_id : $product_id); if (!$product) { $product = wc_get_product($product_id); } if (!$product) { return; } $price = (float) $product->get_price(); $currency = function_exists('get_woocommerce_currency') ? get_woocommerce_currency() : ''; $event_id = ridoway_pixel_generate_event_id(); $contents = array( ridoway_pixel_content_item( $product->get_sku() ? $product->get_sku() : (string) $product_id, $product->get_name(), 'product', $quantity, $price, $currency ), ); $data = ridoway_pixel_with_amount(array( 'type' => 'contents', 'contents' => $contents, ), $price, $currency); // The contents carry the unit price; the event carries the line total. $line_total = ridoway_pixel_to_minor_units($price * $quantity, $currency); if ($line_total !== null) { $data['amount'] = $line_total; } ridoway_pixel_store_event('items_added', $data, $event_id); ridoway_pixel_capi_send_event($event_id, 'items_added', $data); }, 10, 6); add_action('woocommerce_thankyou', function ($order_id) { if (!ridoway_pixel_option_enabled('woocommerce_tracking') || !ridoway_pixel_event_enabled('order_created')) { return; } if (!ridoway_pixel_is_woocommerce_active()) { return; } $order = wc_get_order($order_id); if (!$order) { return; } // Only report orders in a tracked status (paid workflows by default), so a // failed or cancelled order never counts as a conversion. $status = 'wc-' . $order->get_status('edit'); $tracked_statuses = ridoway_pixel_option('order_statuses', array('wc-processing', 'wc-completed', 'wc-on-hold')); if (!is_array($tracked_statuses) || !in_array($status, $tracked_statuses, true)) { return; } // Skip orders already reported: the thank-you page can be reloaded, and // each reload must not mint a fresh conversion. if ($order->get_meta('_ridoway_order_created_fired', true) === '1') { return; } $order->update_meta_data('_ridoway_order_created_fired', '1'); $order->save(); $contents = array(); foreach ($order->get_items() as $item) { $product = $item->get_product(); if ($product) { $contents[] = ridoway_pixel_content_item( $product->get_sku() ? $product->get_sku() : (string) $product->get_id(), $product->get_name(), 'product', $item->get_quantity() ); } } $currency = $order->get_currency(); $data = ridoway_pixel_with_amount(array( 'type' => 'contents', 'contents' => $contents, ), $order->get_total(), $currency); $event_id = ridoway_pixel_generate_event_id(); ridoway_pixel_store_event('order_created', $data, $event_id); ridoway_pixel_capi_send_event($event_id, 'order_created', $data); }, 10, 1); /** * Pull the submitted email out of a Contact Form 7 submission. * * @param \WPCF7_ContactForm $contact_form * @return string */ function ridoway_pixel_cf7_email($contact_form) { if (!class_exists('WPCF7_Submission') || !method_exists($contact_form, 'scan_form_tags')) { return ''; } $submission = WPCF7_Submission::get_instance(); if (!$submission) { return ''; } $posted = $submission->get_posted_data(); if (!is_array($posted)) { return ''; } foreach ($contact_form->scan_form_tags() as $tag) { if (empty($tag['name'])) { continue; } $type = isset($tag['type']) ? (string) $tag['type'] : ''; if (strpos($type, 'email') === false && stripos($tag['name'], 'email') === false) { continue; } $value = isset($posted[$tag['name']]) ? $posted[$tag['name']] : ''; if (is_array($value)) { $value = reset($value); } $value = sanitize_email((string) $value); if ($value) { return $value; } } return ''; } add_action('wpcf7_mailsent', function ($contact_form) { if (!ridoway_pixel_option_enabled('lead_form_tracking') || !ridoway_pixel_event_enabled('lead_created')) { return; } $event_id = ridoway_pixel_generate_event_id(); $data = array('type' => 'customer_action'); $user = array(); $email = ridoway_pixel_cf7_email($contact_form); if ($email) { $user['email_sha256'] = hash('sha256', strtolower(trim($email))); } ridoway_pixel_store_event('lead_created', $data, $event_id); ridoway_pixel_capi_send_event($event_id, 'lead_created', $data, $user); }); /** * Pull the submitted email out of a WPForms submission. * * @param array $fields * @return string */ function ridoway_pixel_wpforms_email($fields) { if (!is_array($fields)) { return ''; } foreach ($fields as $field) { if (!is_array($field)) { continue; } $type = isset($field['type']) ? (string) $field['type'] : ''; if (strpos($type, 'email') === false) { continue; } $value = isset($field['value']) ? sanitize_email((string) $field['value']) : ''; if ($value) { return $value; } } return ''; } add_action('wpforms_process_complete', function ($fields, $entry, $form_data, $entry_id) { if (!ridoway_pixel_option_enabled('lead_form_tracking') || !ridoway_pixel_event_enabled('lead_created')) { return; } $event_id = ridoway_pixel_generate_event_id(); $data = array('type' => 'customer_action'); $user = array(); $email = ridoway_pixel_wpforms_email($fields); if ($email) { $user['email_sha256'] = hash('sha256', strtolower(trim($email))); } ridoway_pixel_store_event('lead_created', $data, $event_id); ridoway_pixel_capi_send_event($event_id, 'lead_created', $data, $user); }, 10, 4);