getPixelIDs() : array(); $tokens = method_exists( $fb, 'getApiTokens' ) ? (array) $fb->getApiTokens() : array(); if ( '' === $pixel_id && ! empty( $ids ) ) { $pixel_id = (string) reset( $ids ); } if ( '' === $token ) { if ( '' !== $pixel_id && isset( $tokens[ $pixel_id ] ) ) { $token = (string) $tokens[ $pixel_id ]; } elseif ( ! empty( $tokens ) ) { $token = (string) reset( $tokens ); } } } catch ( \Throwable $e ) { } } return array( 'pixel_id' => $pixel_id, 'token' => $token ); } private static function server_enabled( array $config ) { $mode = isset( $config['fb_mode'] ) ? $config['fb_mode'] : 'off'; return in_array( $mode, array( 'capi', 'both' ), true ); } private static function consent_blocks() { if ( ! class_exists( 'AIEO_Session_Tracker' ) ) { return false; } if ( method_exists( 'AIEO_Session_Tracker', 'is_marketing_consent_blocking' ) ) { return AIEO_Session_Tracker::is_marketing_consent_blocking(); } if ( method_exists( 'AIEO_Session_Tracker', 'is_consent_blocking_writes' ) ) { return AIEO_Session_Tracker::is_consent_blocking_writes(); } return false; } public function snapshot_identifiers( $order, $data = array() ) { unset( $data ); $this->do_snapshot( $order ); } public function snapshot_identifiers_block( $order ) { $this->do_snapshot( $order ); } private function do_snapshot( $order ) { if ( ! is_a( $order, 'WC_Order' ) ) { return; } $fbp = isset( $_COOKIE['_fbp'] ) ? sanitize_text_field( wp_unslash( $_COOKIE['_fbp'] ) ) : ''; $fbc = isset( $_COOKIE['_fbc'] ) ? sanitize_text_field( wp_unslash( $_COOKIE['_fbc'] ) ) : ''; $sid = ''; foreach ( array( 'aieo_attr_sid', 'aieo_session_id' ) as $ck ) { if ( ! empty( $_COOKIE[ $ck ] ) ) { $sid = sanitize_text_field( wp_unslash( $_COOKIE[ $ck ] ) ); break; } } if ( '' !== $fbp ) { $order->update_meta_data( '_aieo_fbp', $fbp ); } if ( '' !== $fbc ) { $order->update_meta_data( '_aieo_fbc', $fbc ); } if ( '' !== $sid ) { $order->update_meta_data( '_aieo_attr_sid', $sid ); } } public function maybe_send( $order_id ) { $order_id = (int) $order_id; if ( $order_id <= 0 || ! function_exists( 'wc_get_order' ) ) { return; } $config = self::config(); if ( ! self::server_enabled( $config ) ) { return; } if ( self::consent_blocks() ) { return; } $creds = self::resolve_credentials( $config ); if ( '' === $creds['pixel_id'] || '' === $creds['token'] ) { return; } $order = wc_get_order( $order_id ); if ( ! $order ) { return; } if ( $order->get_meta( self::SENT_META ) ) { return; } if ( isset( self::$enqueued[ $order_id ] ) ) { return; } self::$enqueued[ $order_id ] = true; self::get_or_mint_event_id( $order ); if ( function_exists( 'aieo_server_event_dispatch' ) ) { aieo_server_event_dispatch( self::ASYNC_HOOK, $order_id ); } elseif ( function_exists( 'as_enqueue_async_action' ) ) { as_enqueue_async_action( self::ASYNC_HOOK, array( $order_id ), 'aieo-server-events' ); } else { wp_schedule_single_event( time() + 1, self::ASYNC_HOOK, array( $order_id ) ); } } public static function get_or_mint_event_id( $order ) { if ( ! is_a( $order, 'WC_Order' ) ) { return ''; } $eid = (string) $order->get_meta( self::EVENT_ID_META ); if ( '' === $eid ) { $eid = function_exists( 'wp_generate_uuid4' ) ? wp_generate_uuid4() : sprintf( '%04x%04x-%04x-%04x-%04x-%04x%04x%04x', wp_rand( 0, 0xffff ), wp_rand( 0, 0xffff ), wp_rand( 0, 0xffff ), wp_rand( 0, 0x0fff ) | 0x4000, wp_rand( 0, 0x3fff ) | 0x8000, wp_rand( 0, 0xffff ), wp_rand( 0, 0xffff ), wp_rand( 0, 0xffff ) ); $order->update_meta_data( self::EVENT_ID_META, $eid ); $order->save(); } return $eid; } public function send_purchase( $order_id ) { $order_id = (int) $order_id; if ( $order_id <= 0 || ! function_exists( 'wc_get_order' ) ) { return; } $config = self::config(); if ( ! self::server_enabled( $config ) || self::consent_blocks() ) { return; } $creds = self::resolve_credentials( $config ); if ( '' === $creds['pixel_id'] || '' === $creds['token'] ) { return; } $order = wc_get_order( $order_id ); if ( ! $order ) { return; } $eid = (string) $order->get_meta( self::EVENT_ID_META ); if ( '' === $eid ) { return; } if ( $this->already_sent( $eid ) ) { return; } $payload = $this->build_purchase_payload( $order, $config ); $test_code = isset( $config['fb_test_event_code'] ) ? trim( (string) $config['fb_test_event_code'] ) : ''; $body = array( 'data' => array( $payload ) ); if ( '' !== $test_code ) { $body['test_event_code'] = $test_code; } $dpo = apply_filters( 'aieo_capi_data_processing_options', array(), $order ); $body['data_processing_options'] = is_array( $dpo ) ? array_values( $dpo ) : array(); if ( ! empty( $body['data_processing_options'] ) ) { $body['data_processing_options_country'] = (int) apply_filters( 'aieo_capi_data_processing_country', 0, $order ); $body['data_processing_options_state'] = (int) apply_filters( 'aieo_capi_data_processing_state', 0, $order ); } $url = sprintf( 'https://graph.facebook.com/%s/%s/events?access_token=%s', self::GRAPH_VERSION, rawurlencode( $creds['pixel_id'] ), rawurlencode( $creds['token'] ) ); $blocking = ( '' !== $test_code ) || (bool) apply_filters( 'aieo_server_event_blocking', true, self::PLATFORM ); $resp = wp_remote_post( $url, array( 'timeout' => 6, 'blocking' => $blocking, 'headers' => array( 'Content-Type' => 'application/json' ), 'body' => wp_json_encode( $body ), ) ); if ( $blocking ) { $sent = $this->log_result( $order, $eid, $payload, $resp, ( '' !== $test_code ) ); if ( $sent && '' === $test_code ) { $order->update_meta_data( self::SENT_META, 1 ); $order->save(); } } elseif ( function_exists( 'aieo_server_event_log_dispatched' ) ) { aieo_server_event_log_dispatched( self::PLATFORM, 'Purchase', $order, $eid ); } } public function build_purchase_payload( $order, array $config = array() ) { $ids = $this->resolve_identifiers( $order ); $content_ids = array(); $contents = array(); $num_items = 0; foreach ( $order->get_items() as $item ) { if ( ! is_a( $item, 'WC_Order_Item_Product' ) ) { continue; } $pid = $item->get_variation_id() ? (int) $item->get_variation_id() : (int) $item->get_product_id(); $qty = (int) $item->get_quantity(); $num_items += $qty; $content_ids[] = (string) $pid; $contents[] = array( 'id' => (string) $pid, 'quantity' => $qty, 'item_price' => $qty > 0 ? round( (float) $item->get_total() / $qty, 2 ) : (float) $item->get_total(), ); } $created = $order->get_date_created(); $event_time = $created ? $created->getTimestamp() : time(); $source_url = method_exists( $order, 'get_checkout_order_received_url' ) ? $order->get_checkout_order_received_url() : home_url( '/' ); $payload = array( 'event_name' => 'Purchase', 'event_time' => (int) $event_time, 'event_id' => (string) $order->get_meta( self::EVENT_ID_META ), 'action_source' => 'website', 'event_source_url' => $source_url, 'user_data' => AIEO_PII_Hasher::build_user_data( $order, $ids ), 'custom_data' => array( 'currency' => $order->get_currency(), 'value' => (float) $order->get_total(), 'num_items' => $num_items, 'content_type' => 'product', 'content_ids' => $content_ids, 'contents' => $contents, ), ); return $payload; } private function resolve_identifiers( $order ) { $fbp = (string) $order->get_meta( '_aieo_fbp' ); $fbc = (string) $order->get_meta( '_aieo_fbc' ); $sid = (string) $order->get_meta( '_aieo_attr_sid' ); if ( '' === $fbc && '' !== $sid ) { global $wpdb; $statics = $wpdb->prefix . 'aieo_sessions_statics'; $row = $wpdb->get_row( $wpdb->prepare( "SELECT entry_click_id, entry_channel, first_timestamp FROM `{$statics}` WHERE session_id = %s LIMIT 1", $sid ), ARRAY_A ); if ( $row && ! empty( $row['entry_click_id'] ) && 'paid_facebook' === ( $row['entry_channel'] ?? '' ) ) { $ts = ! empty( $row['first_timestamp'] ) ? (int) $row['first_timestamp'] : (int) ( time() * 1000 ); $fbc = 'fb.1.' . $ts . '.' . $row['entry_click_id']; } } return array( 'fbp' => $fbp, 'fbc' => $fbc, 'external_id' => $sid, ); } private function table() { global $wpdb; return $wpdb->prefix . 'aieo_server_events_log'; } private function table_ok() { global $wpdb; static $ok = null; if ( null === $ok ) { $t = $this->table(); $ok = ( (string) $wpdb->get_var( $wpdb->prepare( 'SHOW TABLES LIKE %s', $t ) ) === $t ); } return $ok; } private function already_sent( $event_id ) { if ( ! $this->table_ok() ) { return false; } global $wpdb; $t = $this->table(); return (bool) $wpdb->get_var( $wpdb->prepare( "SELECT 1 FROM `{$t}` WHERE event_id = %s AND platform = %s AND status = 'sent' LIMIT 1", $event_id, self::PLATFORM ) ); } private function log_result( $order, $event_id, $payload, $resp, $test_mode ) { if ( ! $this->table_ok() ) { $code = is_wp_error( $resp ) ? 0 : (int) wp_remote_retrieve_response_code( $resp ); return ( $code >= 200 && $code < 300 ); } global $wpdb; $t = $this->table(); $http_code = null; $fbtrace = null; $resp_body = ''; $status = 'failed'; $err = null; if ( is_wp_error( $resp ) ) { $err = substr( $resp->get_error_message(), 0, 255 ); } else { $http_code = (int) wp_remote_retrieve_response_code( $resp ); $resp_body = (string) wp_remote_retrieve_body( $resp ); $decoded = json_decode( $resp_body, true ); if ( is_array( $decoded ) && isset( $decoded['fbtrace_id'] ) ) { $fbtrace = substr( (string) $decoded['fbtrace_id'], 0, 64 ); } $status = ( $http_code >= 200 && $http_code < 300 ) ? 'sent' : 'failed'; if ( 'failed' === $status && is_array( $decoded ) && isset( $decoded['error']['message'] ) ) { $err = substr( (string) $decoded['error']['message'], 0, 255 ); } } $now = current_time( 'mysql', true ); $cd = isset( $payload['custom_data'] ) ? $payload['custom_data'] : array(); $wpdb->query( $wpdb->prepare( "INSERT INTO `{$t}` (event_id, platform, event_name, order_id, session_id, user_id, status, http_code, fbtrace_id, attempts, request_payload, response_body, error_message, value, currency, test_mode, created_at, sent_at) VALUES (%s, %s, %s, %d, %s, %d, %s, %d, %s, 1, %s, %s, %s, %f, %s, %d, %s, %s) ON DUPLICATE KEY UPDATE status = VALUES(status), http_code = VALUES(http_code), fbtrace_id = VALUES(fbtrace_id), attempts = attempts + 1, request_payload = VALUES(request_payload), response_body = VALUES(response_body), error_message = VALUES(error_message), sent_at = VALUES(sent_at)", $event_id, self::PLATFORM, 'Purchase', (int) $order->get_id(), (string) $order->get_meta( '_aieo_attr_sid' ), (int) $order->get_customer_id(), $status, null === $http_code ? 0 : $http_code, null === $fbtrace ? '' : $fbtrace, wp_json_encode( $payload ), substr( $resp_body, 0, 4000 ), null === $err ? '' : $err, isset( $cd['value'] ) ? (float) $cd['value'] : 0, isset( $cd['currency'] ) ? (string) $cd['currency'] : '', $test_mode ? 1 : 0, $now, 'sent' === $status ? $now : null ) ); return 'sent' === $status; } }