isset( $config['ga_measurement_id'] ) ? trim( (string) $config['ga_measurement_id'] ) : '', 'api_secret' => isset( $config['ga_api_secret'] ) ? trim( (string) $config['ga_api_secret'] ) : '', ); } 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( $order, $data = array() ) { unset( $data ); $this->do_snapshot( $order ); } public function snapshot_block( $order ) { $this->do_snapshot( $order ); } private function do_snapshot( $order ) { if ( ! is_a( $order, 'WC_Order' ) ) { return; } $cid = $this->client_id_from_cookie(); if ( '' !== $cid ) { $order->update_meta_data( self::CID_META, $cid ); } } private function client_id_from_cookie() { if ( empty( $_COOKIE['_ga'] ) ) { return ''; } $raw = sanitize_text_field( wp_unslash( $_COOKIE['_ga'] ) ); $parts = explode( '.', $raw ); if ( count( $parts ) >= 4 ) { return $parts[ count( $parts ) - 2 ] . '.' . $parts[ count( $parts ) - 1 ]; } return ''; } 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 ) || self::consent_blocks() ) { return; } $creds = self::resolve_credentials( $config ); if ( '' === $creds['measurement_id'] || '' === $creds['api_secret'] ) { return; } $order = wc_get_order( $order_id ); if ( ! $order || $order->get_meta( self::SENT_META ) ) { return; } if ( isset( self::$enqueued[ $order_id ] ) ) { return; } self::$enqueued[ $order_id ] = true; 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 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['measurement_id'] || '' === $creds['api_secret'] ) { return; } $order = wc_get_order( $order_id ); if ( ! $order ) { return; } if ( $this->already_sent( (string) $order_id ) ) { return; } $body = $this->build_mp_payload( $order ); $url = add_query_arg( array( 'measurement_id' => rawurlencode( $creds['measurement_id'] ), 'api_secret' => rawurlencode( $creds['api_secret'] ), ), self::ENDPOINT ); $blocking = (bool) apply_filters( 'aieo_server_event_blocking', true, 'ga4' ); $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, (string) $order_id, $body, $resp ); if ( $sent ) { $order->update_meta_data( self::SENT_META, 1 ); $order->save(); } } elseif ( function_exists( 'aieo_server_event_log_dispatched' ) ) { aieo_server_event_log_dispatched( 'ga4', 'purchase', $order, (string) $order_id ); } } public function build_mp_payload( $order ) { $cid = (string) $order->get_meta( self::CID_META ); if ( '' === $cid ) { $cid = sprintf( '%d.%d', crc32( 'aieo' . $order->get_id() ) & 0x7fffffff, (int) ( $order->get_date_created() ? $order->get_date_created()->getTimestamp() : time() ) ); } $items = array(); 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(); $prod = $item->get_product(); $items[] = array( 'item_id' => (string) $pid, 'item_name' => (string) $item->get_name(), 'quantity' => $qty, 'price' => $qty > 0 ? round( (float) $item->get_total() / $qty, 2 ) : (float) $item->get_total(), ); } $event = array( 'name' => 'purchase', 'params' => array( 'currency' => $order->get_currency(), 'value' => (float) $order->get_total(), 'transaction_id' => (string) $order->get_id(), 'items' => $items, ), ); $body = array( 'client_id' => $cid, 'events' => array( $event ), ); if ( class_exists( 'AIEO_Consent' ) ) { $cs = AIEO_Consent::ads_consent_for_order( $order ); if ( 'granted' === $cs || 'denied' === $cs ) { $g = ( 'granted' === $cs ) ? 'GRANTED' : 'DENIED'; $body['consent'] = array( 'ad_user_data' => $g, 'ad_personalization' => $g, ); } } if ( class_exists( 'AIEO_PII_Hasher' ) ) { $ud = array(); $email = AIEO_PII_Hasher::normalize_and_hash( 'em', (string) $order->get_billing_email() ); $phone = AIEO_PII_Hasher::normalize_and_hash( 'ph', (string) $order->get_billing_phone() ); if ( '' !== $email ) { $ud['sha256_email_address'] = $email; } if ( '' !== $phone ) { $ud['sha256_phone_number'] = $phone; } $addr = array(); $fn = AIEO_PII_Hasher::normalize_and_hash( 'fn', (string) $order->get_billing_first_name() ); $ln = AIEO_PII_Hasher::normalize_and_hash( 'ln', (string) $order->get_billing_last_name() ); $street = AIEO_PII_Hasher::normalize_and_hash( 'street', trim( $order->get_billing_address_1() . ' ' . $order->get_billing_address_2() ) ); if ( '' !== $fn ) { $addr['sha256_first_name'] = $fn; } if ( '' !== $ln ) { $addr['sha256_last_name'] = $ln; } if ( '' !== $street ) { $addr['sha256_street'] = $street; } $city = AIEO_PII_Hasher::lc( $order->get_billing_city() ); $region = AIEO_PII_Hasher::lc( $order->get_billing_state() ); $postal = AIEO_PII_Hasher::lc( $order->get_billing_postcode() ); $country = AIEO_PII_Hasher::lc( $order->get_billing_country() ); if ( '' !== $city ) { $addr['city'] = $city; } if ( '' !== $region ) { $addr['region'] = $region; } if ( '' !== $postal ) { $addr['postal_code'] = $postal; } if ( '' !== $country ) { $addr['country'] = $country; } if ( ! empty( $addr ) ) { $ud['address'] = array( $addr ); } if ( ! empty( $ud ) ) { $body['user_data'] = $ud; } } $cust = (int) $order->get_customer_id(); if ( $cust > 0 ) { $body['user_id'] = (string) $cust; } return $body; } 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 ) { 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; $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 ); $status = ( $http_code >= 200 && $http_code < 300 ) ? 'sent' : 'failed'; } $now = current_time( 'mysql', true ); $params = isset( $payload['events'][0]['params'] ) ? $payload['events'][0]['params'] : 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, 0, %s, %s) ON DUPLICATE KEY UPDATE status = VALUES(status), http_code = VALUES(http_code), 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, '', wp_json_encode( $payload ), substr( $resp_body, 0, 4000 ), null === $err ? '' : $err, isset( $params['value'] ) ? (float) $params['value'] : 0, isset( $params['currency'] ) ? (string) $params['currency'] : '', $now, 'sent' === $status ? $now : null ) ); return 'sent' === $status; } }