| |
| <?php
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| function vg_meta_capi_log($message, $context = []) {
|
| $uploads = wp_upload_dir();
|
| $file = trailingslashit($uploads['basedir']) . 'vg-meta-capi.log';
|
|
|
| $line = '[' . gmdate('Y-m-d H:i:s') . ' UTC] ' . $message;
|
| if (!empty($context)) {
|
| $line .= ' | ' . wp_json_encode($context);
|
| }
|
| $line .= PHP_EOL;
|
|
|
|
|
| @file_put_contents($file, $line, FILE_APPEND);
|
| }
|
|
|
| function vg_meta_capi_get_ip() {
|
| if (class_exists('WC_Geolocation') && method_exists('WC_Geolocation', 'get_ip_address')) {
|
| return WC_Geolocation::get_ip_address();
|
| }
|
| return isset($_SERVER['REMOTE_ADDR']) ? sanitize_text_field($_SERVER['REMOTE_ADDR']) : '';
|
| }
|
|
|
| function vg_meta_capi_get_ua() {
|
| return isset($_SERVER['HTTP_USER_AGENT']) ? sanitize_text_field($_SERVER['HTTP_USER_AGENT']) : '';
|
| }
|
|
|
| function vg_meta_capi_hash($value, $type = 'generic') {
|
| if ($value === null) return '';
|
|
|
| $v = (string) $value;
|
| $v = trim($v);
|
| $v = strtolower($v);
|
|
|
| if ($type === 'phone') {
|
| $v = preg_replace('/\D+/', '', $v);
|
| } elseif ($type === 'zip') {
|
| $v = preg_replace('/\s+/', '', $v);
|
| } elseif ($type === 'country') {
|
| $v = preg_replace('/\s+/', '', $v);
|
| }
|
|
|
| if ($v === '') return '';
|
| return hash('sha256', $v);
|
| }
|
|
|
|
|
|
|
|
|
| add_action('woocommerce_checkout_create_order', 'vg_meta_capi_capture_checkout_context', 10, 2);
|
| function vg_meta_capi_capture_checkout_context($order, $data) {
|
|
|
| if (!is_a($order, 'WC_Order')) return;
|
|
|
|
|
| $fbp = isset($_COOKIE['_fbp']) ? sanitize_text_field($_COOKIE['_fbp']) : '';
|
| $fbc = isset($_COOKIE['_fbc']) ? sanitize_text_field($_COOKIE['_fbc']) : '';
|
|
|
|
|
| $ip = vg_meta_capi_get_ip();
|
| $ua = vg_meta_capi_get_ua();
|
|
|
|
|
| $url = '';
|
| if (!empty($_SERVER['HTTP_HOST']) && !empty($_SERVER['REQUEST_URI'])) {
|
| $scheme = is_ssl() ? 'https://' : 'http://';
|
| $url = $scheme . sanitize_text_field($_SERVER['HTTP_HOST']) . sanitize_text_field($_SERVER['REQUEST_URI']);
|
| }
|
|
|
| if ($fbp) $order->update_meta_data('_vg_fbp', $fbp);
|
| if ($fbc) $order->update_meta_data('_vg_fbc', $fbc);
|
| if ($ip) $order->update_meta_data('_vg_client_ip', $ip);
|
| if ($ua) $order->update_meta_data('_vg_client_ua', $ua);
|
| if ($url) $order->update_meta_data('_vg_event_source_url', $url);
|
| }
|
|
|
|
|
|
|
|
|
| add_action('woocommerce_payment_complete', 'vg_meta_capi_on_payment_complete', 10, 1);
|
| function vg_meta_capi_on_payment_complete($order_id) {
|
| vg_meta_capi_maybe_send_purchase((int)$order_id, 'payment_complete');
|
| }
|
|
|
| add_action('woocommerce_order_status_changed', 'vg_meta_capi_on_status_changed', 10, 4);
|
| function vg_meta_capi_on_status_changed($order_id, $old_status, $new_status, $order) {
|
|
|
|
|
| if (!in_array($new_status, ['processing', 'completed'], true)) {
|
| return;
|
| }
|
|
|
| vg_meta_capi_maybe_send_purchase((int)$order_id, 'status_changed_' . $old_status . '_to_' . $new_status);
|
| }
|
|
|
|
|
|
|
|
|
| function vg_meta_capi_maybe_send_purchase($order_id, $trigger) {
|
|
|
| if (!$order_id || is_admin() || wp_doing_ajax()) return;
|
|
|
| if (empty(VG_META_PIXEL_ID) || empty(VG_META_CAPI_TOKEN) || VG_META_CAPI_TOKEN === 'PASTE_YOUR_CAPI_ACCESS_TOKEN_HERE') {
|
| vg_meta_capi_log('CAPI not configured (missing token).', ['order_id' => $order_id]);
|
| return;
|
| }
|
|
|
| $order = wc_get_order($order_id);
|
| if (!$order) return;
|
|
|
|
|
| $status = $order->get_status();
|
| if (!in_array($status, ['processing', 'completed'], true)) {
|
| return;
|
| }
|
|
|
|
|
| if ($order->get_meta('_vg_capi_purchase_sent')) {
|
| return;
|
| }
|
|
|
|
|
| $can_track = apply_filters('vg_meta_capi_can_track', true, $order_id, $trigger);
|
| if (!$can_track) return;
|
|
|
| $event_id = 'order_' . (string) $order_id;
|
|
|
|
|
| $content_ids = [];
|
| $contents = [];
|
|
|
| foreach ($order->get_items() as $item) {
|
| if (!is_a($item, 'WC_Order_Item_Product')) continue;
|
|
|
| $product_id = (int) $item->get_product_id();
|
| $variation_id = (int) $item->get_variation_id();
|
| $id = $variation_id ? $variation_id : $product_id;
|
|
|
| $qty = (int) $item->get_quantity();
|
| if ($id <= 0 || $qty <= 0) continue;
|
|
|
| $line_total = (float) $item->get_total();
|
| $unit_price = $qty > 0 ? ($line_total / $qty) : 0.0;
|
|
|
| $content_ids[] = (string) $id;
|
| $contents[] = [
|
| 'id' => (string) $id,
|
| 'quantity' => $qty,
|
| 'item_price' => round((float) $unit_price, 2),
|
| ];
|
| }
|
|
|
| $content_ids = array_values(array_unique($content_ids));
|
|
|
|
|
| $billing_email = $order->get_billing_email();
|
| $billing_phone = $order->get_billing_phone();
|
| $billing_fn = $order->get_billing_first_name();
|
| $billing_ln = $order->get_billing_last_name();
|
| $billing_city = $order->get_billing_city();
|
| $billing_state = $order->get_billing_state();
|
| $billing_zip = $order->get_billing_postcode();
|
| $billing_ctry = $order->get_billing_country();
|
|
|
| $user_id = $order->get_user_id();
|
|
|
|
|
| $fbp = (string) $order->get_meta('_vg_fbp');
|
| $fbc = (string) $order->get_meta('_vg_fbc');
|
| $ip = (string) $order->get_meta('_vg_client_ip');
|
| $ua = (string) $order->get_meta('_vg_client_ua');
|
| $src_url = (string) $order->get_meta('_vg_event_source_url');
|
|
|
| if (!$ip) $ip = vg_meta_capi_get_ip();
|
| if (!$ua) $ua = vg_meta_capi_get_ua();
|
| if (!$src_url) $src_url = home_url('/');
|
|
|
| $user_data = [];
|
|
|
| $em = vg_meta_capi_hash($billing_email, 'generic');
|
| if ($em) $user_data['em'] = [$em];
|
|
|
| $ph = vg_meta_capi_hash($billing_phone, 'phone');
|
| if ($ph) $user_data['ph'] = [$ph];
|
|
|
| $fn = vg_meta_capi_hash($billing_fn, 'generic');
|
| if ($fn) $user_data['fn'] = [$fn];
|
|
|
| $ln = vg_meta_capi_hash($billing_ln, 'generic');
|
| if ($ln) $user_data['ln'] = [$ln];
|
|
|
| $ct = vg_meta_capi_hash($billing_city, 'generic');
|
| if ($ct) $user_data['ct'] = [$ct];
|
|
|
| $st = vg_meta_capi_hash($billing_state, 'generic');
|
| if ($st) $user_data['st'] = [$st];
|
|
|
| $zp = vg_meta_capi_hash($billing_zip, 'zip');
|
| if ($zp) $user_data['zp'] = [$zp];
|
|
|
| $country = vg_meta_capi_hash($billing_ctry, 'country');
|
| if ($country) $user_data['country'] = [$country];
|
|
|
| if ($user_id) {
|
|
|
| $user_data['external_id'] = [vg_meta_capi_hash((string)$user_id, 'generic')];
|
| }
|
|
|
|
|
| if ($ip) $user_data['client_ip_address'] = $ip;
|
| if ($ua) $user_data['client_user_agent'] = $ua;
|
|
|
|
|
| if ($fbp) $user_data['fbp'] = $fbp;
|
| if ($fbc) $user_data['fbc'] = $fbc;
|
|
|
| $event = [
|
| 'event_name' => 'Purchase',
|
| 'event_time' => time(),
|
| 'event_id' => $event_id,
|
| 'action_source' => 'website',
|
| 'event_source_url' => $src_url,
|
| 'user_data' => $user_data,
|
| 'custom_data' => [
|
| 'currency' => $order->get_currency() ? $order->get_currency() : 'ZAR',
|
| 'value' => (float) $order->get_total(),
|
| 'content_type' => 'product',
|
| 'content_ids' => $content_ids,
|
| 'contents' => $contents,
|
| 'order_id' => (string) $order_id,
|
| ],
|
| ];
|
|
|
| $payload = [
|
| 'data' => [$event],
|
| ];
|
|
|
| if (!empty(VG_META_CAPI_TEST_EVENT_CODE)) {
|
| $payload['test_event_code'] = VG_META_CAPI_TEST_EVENT_CODE;
|
| }
|
|
|
| $url = 'https://graph.facebook.com/' . rawurlencode(VG_META_CAPI_API_VERSION) . '/'
|
| . rawurlencode(VG_META_PIXEL_ID) . '/events?access_token=' . rawurlencode(VG_META_CAPI_TOKEN);
|
|
|
| $res = wp_remote_post($url, [
|
| 'headers' => [
|
| 'Content-Type' => 'application/json',
|
| ],
|
| 'timeout' => 12,
|
| 'body' => wp_json_encode($payload),
|
| ]);
|
|
|
| if (is_wp_error($res)) {
|
| vg_meta_capi_log('CAPI request failed (WP_Error).', [
|
| 'order_id' => $order_id,
|
| 'trigger' => $trigger,
|
| 'error' => $res->get_error_message(),
|
| ]);
|
| return;
|
| }
|
|
|
| $code = (int) wp_remote_retrieve_response_code($res);
|
| $body = (string) wp_remote_retrieve_body($res);
|
|
|
| if ($code >= 200 && $code < 300) {
|
|
|
| $order->update_meta_data('_vg_capi_purchase_sent', 1);
|
| $order->save();
|
|
|
| vg_meta_capi_log('CAPI Purchase sent OK.', [
|
| 'order_id' => $order_id,
|
| 'trigger' => $trigger,
|
| 'http' => $code,
|
| ]);
|
| return;
|
| }
|
|
|
| vg_meta_capi_log('CAPI Purchase failed (non-2xx).', [
|
| 'order_id' => $order_id,
|
| 'trigger' => $trigger,
|
| 'http' => $code,
|
| 'body' => $body,
|
| ]);
|
| }
|
|
|
| |
| |
Comments