JezK
Edit File: tiktok-server.php
<?php namespace PixelYourSite; require_once PYS_PATH . '/modules/tiktok/tiktok-server-async-task.php'; use DateTimeInterface; use PixelYourSite; use PYS_PRO_GLOBAL\GuzzleHttp\Client; if ( !defined( 'ABSPATH' ) ) { exit; // Exit if accessed directly. } /** * TikTok Server Events Handler * * Extends AbstractServerEvents to handle TikTok Conversions API */ class TikTokServer extends AbstractServerEvents { private static $_instance; private $isEnabled; private $testCode; public static function instance() { if ( is_null( self::$_instance ) ) { self::$_instance = new self(); } return self::$_instance; } public function __construct() { parent::__construct(); add_action('init', array($this, 'init')); } public function init() { $this->isEnabled = Tiktok()->isServerApiEnabled(); if ( $this->isEnabled ) { // AJAX handlers for fallback (from parent class) $this->registerAjaxHandlers(); // Initialize tiktok event async task (only if queue is disabled) if (!PYS()->getOption('queue_enabled', true)) { new TikTokAsyncTask(); } } } /** * Get the pixel instance * * @return Tiktok */ protected function getPixelInstance() { return Tiktok(); } /** * Get the platform name for queue system * * @return string */ protected function getQueuePlatformName() { return 'tiktok'; } /** * Get the async task action name * * @return string */ protected function getServerEventAction() { return 'pys_send_tiktok_server_event'; } /** * Get the AJAX action name for catching events * * @return string */ protected function getAjaxActionName() { return 'pys_tiktok_api_event'; } /** * Track Woo Add to cart events * * @param $cart_item_key * @param $product_id * @param $quantity * @param $variation_id * @return void */ function trackAddToCartEvent( $cart_item_key, $product_id, $quantity, $variation_id ) { if ( EventsWoo()->isReadyForFire( "woo_add_to_cart_on_button_click" ) && PYS()->getOption( 'woo_add_to_cart_catch_method' ) == "add_cart_js" ) { // it ok. We send server method after js, and take event id from cookies Tiktok()->getLog()->debug( ' trackAddToCartEvent send TikTok server without browser event' ); if ( !empty( $variation_id ) && $variation_id > 0 && ( !Tiktok()->getOption( 'woo_variable_as_simple' ) ) ) { $_product_id = $variation_id; } else { $_product_id = $product_id; } $event = new SingleEvent( "woo_add_to_cart_on_button_click", EventTypes::$DYNAMIC, 'woo' ); $event->args = [ 'productId' => $_product_id, 'quantity' => $quantity ]; $event->params[ 'uri' ] = self::getRequestUri( PYS()->getOption( 'enable_remove_source_url_params' ) ); add_filter( 'pys_conditional_post_id', function ( $id ) use ( $product_id ) { return $product_id; } ); $events = Tiktok()->generateEvents( $event ); remove_all_filters( 'pys_conditional_post_id' ); do_action( 'pys_send_tiktok_server_event', $events ); } } /** * Send event for each pixel id * @param array $pixel_Ids //array of TikTok pixel ids * @param $event //One TikTok event object */ function sendEvent( $pixel_Ids, $event ) { if ( !$event ) { return; } if ( !$this->access_token ) { $this->access_token = Tiktok()->getApiTokens(); $this->testCode = Tiktok()->getApiTestCode(); } foreach ( $pixel_Ids as $pixel_Id ) { if ( !Tiktok()->enabled() || empty( $this->access_token[ $pixel_Id ] ) ) continue; $event->pixel_code = $pixel_Id; if ( $this->testCode[ $pixel_Id ] ) { $event->test_event_code = $this->testCode[ $pixel_Id ]; } $url = "https://business-api.tiktok.com/open_api/v1.3/pixel/track/"; $headers = array( 'Content-Type' => 'application/json', 'Access-Token' => $this->access_token[ $pixel_Id ] ); Tiktok()->getLog()->debug( ' Send TikTok server event', $event ); try { $client = new Client(); $response = $client->request( 'POST', $url, [ 'headers' => $headers, 'body' => json_encode( $event ) ] ); Tiktok()->getLog()->debug( ' Response from Tiktok server', $response ); // Track fired pixel for meta storage ServerEventsManager::addFiredPixel( 'tiktok', $pixel_Id ); } catch ( \Exception $e ) { Tiktok()->getLog()->error( 'Error send TikTok server event ' . $e->getMessage() ); } } } /** * Map SingleEvent to TikTok server event format * * @param SingleEvent $event The event to map * @return \stdClass TikTok event object */ protected function mapEventToServerEvent( $event ) { $eventData = $event->getData(); $eventData = EventsManager::filterEventParams( $eventData, $event->getCategory(), [ 'event_id' => $event->getId(), 'pixel' => Tiktok()->getSlug() ] ); $eventName = $eventData[ 'name' ]; $wooOrder = isset( $event->payload[ 'woo_order' ] ) ? $event->payload[ 'woo_order' ] : null; $eddOrder = isset( $event->payload[ 'edd_order' ] ) ? $event->payload[ 'edd_order' ] : null; $user_data = $this->getUserData( $wooOrder, $eddOrder ); $custom_data = $this->paramsToCustomData( $eventData[ 'params' ] ); $uri = $this->getEventUri( $event ); $user_data->page = new \stdClass; $user_data->page->url = $uri; $user_data->page->referrer = get_home_url(); $serverEvent = new \stdClass; $serverEvent->event = $eventName; $datetime = new \DateTime( 'now' ); $serverEvent->timestamp = $datetime->format( DateTimeInterface::ATOM ); $serverEvent->context = $user_data; if ( count( get_object_vars( $custom_data ) ) > 0 ) { $serverEvent->properties = $custom_data; } if ( is_array( $event->params ) ) { foreach ( $event->params as $key => $param ) { $serverEvent->$key = $param; } } $serverEvent->event_id = $event->payload[ 'eventID' ] ?? $event->payload[ 'event_id' ] ?? ''; return $serverEvent; } /** * Get user data for TikTok event * * @param int|null $wooOrder WooCommerce order ID * @param int|null $eddOrder EDD order ID * @return \stdClass User data object */ protected function getUserData( $wooOrder = null, $eddOrder = null ) { $userData = new \stdClass; $userData->user = new \stdClass; $user = wp_get_current_user(); /** * Add purchase WooCommerce Advanced Matching params */ if ( PixelYourSite\isWooCommerceActive() && isEventEnabled( 'woo_purchase_enabled' ) && ( $wooOrder || ( PYS()->woo_is_order_received_page() && wooIsRequestContainOrderId() ) ) ) { if ( wooIsRequestContainOrderId() ) { $order_id = wooGetOrderIdFromRequest(); } else { $order_id = $wooOrder; } $order = wc_get_order( $order_id ); if ( $order ) { $this->woo_order = $order_id; if ( PixelYourSite\isWooCommerceVersionGte( '3.0.0' ) ) { $user_email = $order->get_billing_email(); $user_phone = $order->get_billing_phone(); } else { $user_email = $order->billing_email; $user_phone = $order->billing_phone; } $user_persistence_data = get_persistence_user_data( $user_email, '', '', $user_phone ); if ( !empty( $user_persistence_data[ 'em' ] ) ) $userData->user->email = hash( 'sha256', mb_strtolower( $user_persistence_data[ 'em' ] ), false ); if ( !empty( $user_persistence_data[ 'tel' ] ) ) $userData->user->phone_number = hash( 'sha256', preg_replace( '/[^0-9]/', '', $user_persistence_data[ 'tel' ] ), false ); // Get external_id from WooCommerce order meta if ( PixelYourSite\EventsManager::isTrackExternalId() ) { $external_id = null; if ( PixelYourSite\isWooCommerceVersionGte( '3.0.0' ) ) { $external_id = $order->get_meta( 'external_id' ); } else { $external_id = get_post_meta( $order_id, 'external_id', true ); } if ( !empty( $external_id ) ) { $userData->user->external_id = $external_id; } } if ( isset( $_COOKIE[ '_ttp' ] ) && !empty( $_COOKIE[ '_ttp' ] ) ) { $userData->user->ttp = sanitize_text_field($_COOKIE[ '_ttp' ]); } } else { $userData = $this->getRegularUserData(); } } else { if ( PixelYourSite\isEddActive() && isEventEnabled( 'edd_purchase_enabled' ) && ( $eddOrder || edd_is_success_page() ) ) { $this->edd_order = $eddOrder; if ( $eddOrder ) $payment_id = $eddOrder; else { $payment_key = getEddPaymentKey(); $payment_id = (int) edd_get_purchase_id_by_key( $payment_key ); } $user_persistence_data = get_persistence_user_data( edd_get_payment_user_email( $payment_id ), '', '', '' ); if ( !empty( $user_persistence_data[ 'em' ] ) ) $userData->user->email = hash( 'sha256', mb_strtolower( $user_persistence_data[ 'em' ] ), false ); if ( !empty( $user_persistence_data[ 'tel' ] ) ) $userData->user->phone_number = hash( 'sha256', preg_replace( '/[^0-9]/', '', $user_persistence_data[ 'tel' ] ), false ); // Get external_id from EDD payment meta if ( PixelYourSite\EventsManager::isTrackExternalId() && $payment_id > 0 ) { $external_id = edd_get_payment_meta( $payment_id, 'external_id', true ); if ( !empty( $external_id ) ) { $userData->user->external_id = $external_id; } } if ( isset( $_COOKIE[ '_ttp' ] ) && !empty( $_COOKIE[ '_ttp' ] ) ) { $userData->user->ttp = sanitize_text_field($_COOKIE[ '_ttp' ]); } } else { $userData = $this->getRegularUserData(); } } // Set external_id from user meta or pbid if not already set from order if(PixelYourSite\EventsManager::isTrackExternalId() && !isset($userData->user->external_id)){ if($user && $user->get( 'external_id' )){ $userData->user->external_id = $user->get( 'external_id' ); } elseif (PixelYourSite\PYS()->get_pbid()) { $userData->user->external_id = PixelYourSite\PYS()->get_pbid(); } } $userData->ip = self::getIpAddress(); $userData->user_agent = self::getHttpUserAgent(); return apply_filters( "pys_tiktok_server_user_data", $userData ); } private function getRegularUserData() { $user = wp_get_current_user(); $userData = new \stdClass; $userData->user = new \stdClass; $user_email = $user_phone = ''; if ( $user->ID ) { // get user regular data $user_email = $user->get( 'user_email' ); /** * Add common WooCommerce Advanced Matching params */ if ( PixelYourSite\isWooCommerceActive() ) { $user_phone = $user->get( 'billing_phone' ); } } $user_persistence_data = get_persistence_user_data( $user_email, '', '', $user_phone ); if ( !empty( $user_persistence_data[ 'em' ] ) ) $userData->user->email = hash( 'sha256', mb_strtolower( $user_persistence_data[ 'em' ] ), false ); if ( !empty( $user_persistence_data[ 'tel' ] ) ) $userData->user->phone_number = hash( 'sha256', preg_replace( '/[^0-9]/', '', $user_persistence_data[ 'tel' ] ), false ); if ( !empty( $_COOKIE[ '_ttp' ] ) ) { $userData->user->ttp = sanitize_text_field($_COOKIE[ '_ttp' ]); } return $userData; } private function paramsToCustomData( $data ) { $custom_data = new \stdClass; if ( isset( $data[ 'quantity' ] ) ) { $data_line_items = array(); $data_line_items[ 'quantity' ] = $data[ 'quantity' ]; if ( isset( $data[ 'value' ] ) ) { $data_line_items[ 'price' ] = $data[ 'value' ]; } if ( isset( $data[ 'content_id' ] ) ) { $data_line_items[ 'content_id' ] = $data[ 'content_id' ]; } if ( isset( $data[ 'content_category' ] ) ) { $data_line_items[ 'content_category' ] = $data[ 'content_category' ]; } if ( isset( $data[ 'content_name' ] ) ) { $data_line_items[ 'content_name' ] = $data[ 'content_name' ]; } if ( isset( $data[ 'content_type' ] ) ) { $data_line_items[ 'content_type' ] = $data[ 'content_type' ]; } else { $data_line_items[ 'content_type' ] = 'product'; } $data[ 'contents' ][] = $data_line_items; if ( isset( $data[ 'currency' ] ) ) { $custom_data->currency = $data[ 'currency' ]; } } if ( isset( $data[ 'contents' ] ) && is_array( $data[ 'contents' ] ) ) { $contents = array(); $cost = 0; $custom_data->content_type = $data[ 'content_type' ]; foreach ( $data[ 'contents' ] as $c ) { if ( isset( $c[ 'quantity' ] ) ) { $contents[] = array( 'quantity' => (int) $c[ 'quantity' ], 'price' => isset( $c[ 'price' ] ) ? strval( $c[ 'price' ] ) : '', 'content_id' => $c[ 'content_id' ], 'content_category' => $c[ 'content_category' ] ?? '', 'content_name' => $c[ 'content_name' ] ?? '', ); $cost += $c[ 'quantity' ] * ( $c[ 'price' ] ?? 0 ); } } $custom_data->contents = $contents; $custom_data->value = $cost; if ( isset( $data[ 'currency' ] ) ) { $custom_data->currency = $data[ 'currency' ]; } } if ( !empty( $_GET[ 's' ] ) ) { $custom_data->query = sanitize_text_field( wp_unslash( $_GET['s'] ) ); } return apply_filters( "pys_tiktok_server_custom_data", $custom_data ); } } /** * @return TikTokServer */ function TikTokServer() { return TikTokServer::instance(); } TikTokServer();