'', 'store_key' => '', 'api_key' => '']); } public static function admin_menu() { add_submenu_page('woocommerce', 'ROASaurus', 'ROASaurus', 'manage_woocommerce', 'roasaurus', [__CLASS__, 'settings_page']); } /** @param string[] $links */ public static function action_links($links) { array_unshift($links, sprintf('%s', esc_url(admin_url('admin.php?page=roasaurus')), esc_html__('Settings', 'roasaurus'))); return $links; } public static function privacy_policy_content() { wp_add_privacy_policy_content('ROASaurus', wp_kses_post(sprintf( /* translators: %s: URL of the ROASaurus privacy policy */ __('This store uses ROASaurus for ad tracking. For paid orders, ROASaurus receives the order details and the customer\'s name, email address, phone number, billing address, IP address and browser. With marketing consent it also receives the visitor\'s tracking cookies. See the ROASaurus privacy policy.', 'roasaurus'), 'https://roasaurus.com/privacy' ))); } public static function register_settings() { register_setting('roasaurus', self::OPTION, [ 'sanitize_callback' => function ($in) { $api_key = sanitize_text_field($in['api_key'] ?? ''); return [ 'endpoint' => esc_url_raw(rtrim(trim($in['endpoint'] ?? ''), '/')), 'store_key' => sanitize_text_field($in['store_key'] ?? ''), // The form never shows the saved key, so an empty field keeps it. 'api_key' => $api_key !== '' ? $api_key : self::settings()['api_key'], ]; }, ]); } public static function settings_page() { $s = self::settings(); ?>

ROASaurus

Settings › Orders API.', 'roasaurus'), ['strong' => []]); ?>

'async', 'in_footer' => false]); wp_add_inline_script('roasaurus', 'window.tlq=window.tlq||function(){(window.tlq.q=window.tlq.q||[]).push(arguments)};', 'before'); } /** @param WC_Order|int $order */ public static function capture_cookies($order) { $order = is_numeric($order) ? wc_get_order($order) : $order; if (!$order) { return; } if (!self::marketing_allowed()) { return; } foreach (self::COOKIES as $name) { if (!empty($_COOKIE[$name])) { $order->update_meta_data('_roasaurus' . $name, sanitize_text_field(wp_unslash($_COOKIE[$name]))); } } $order->save(); } /** * Whether the shopper allows marketing cookies. With the WP Consent API (used by Complianz, * CookieYes, Cookiebot and others) that is its answer. Without it, the ROASaurus script only sets * the _tl cookie when consent is given or not needed, so a missing _tl means no ad cookies are kept. */ private static function marketing_allowed() { if (function_exists('wp_has_consent')) { return wp_has_consent('marketing'); } return !empty($_COOKIE['_tl']); } public static function send_order($order_id) { $s = self::settings(); $order = wc_get_order($order_id); if (!$order || !$s['endpoint'] || !$s['store_key'] || !$s['api_key'] || $order->get_meta('_roasaurus_sent')) { return; } // Unpaid orders are only sent once they reach processing/completed (COD, bank transfer). if (!$order->is_paid() && !in_array($order->get_status(), ['processing', 'completed'], true)) { return; } $items = []; foreach ($order->get_items() as $item) { /** @var WC_Order_Item_Product $item */ $qty = max(1, (int) $item->get_quantity()); $items[] = [ 'variant_id' => (string) ($item->get_variation_id() ?: $item->get_product_id()), 'product_id' => (string) $item->get_product_id(), 'sku' => $item->get_product() ? $item->get_product()->get_sku() : null, 'name' => $item->get_name(), 'quantity' => $qty, 'price' => round(((float) $item->get_total()) / $qty, 2), ]; } $customer_id = $order->get_customer_id(); $is_first = $customer_id ? wc_get_customer_order_count($customer_id) <= 1 : null; $meta = function ($name) use ($order) { $v = $order->get_meta('_roasaurus' . $name); return $v !== '' ? $v : null; }; $body = [ 'order_id' => (string) $order->get_id(), 'name' => $order->get_order_number(), 'total' => (float) $order->get_total(), 'tax' => (float) $order->get_total_tax(), 'shipping' => (float) $order->get_shipping_total(), 'currency' => $order->get_currency(), 'created_at' => $order->get_date_created() ? $order->get_date_created()->format(DATE_ATOM) : null, 'email' => $order->get_billing_email(), 'phone' => $order->get_billing_phone(), 'first_name' => $order->get_billing_first_name(), 'last_name' => $order->get_billing_last_name(), 'city' => $order->get_billing_city(), 'state' => $order->get_billing_state(), 'zip' => $order->get_billing_postcode(), 'country' => $order->get_billing_country(), 'customer_id' => $customer_id ? (string) $customer_id : null, 'is_first_order' => $is_first, 'visitor_id' => $meta('_tl'), 'click_ids' => array_filter([ 'fbc' => $meta('_fbc'), 'fbp' => $meta('_fbp'), 'ttp' => $meta('_ttp'), 'scid' => $meta('_scid'), 'epik' => $meta('_epik'), ]), 'client_ip' => $order->get_customer_ip_address(), 'user_agent' => $order->get_customer_user_agent(), 'items' => $items, ]; $res = wp_remote_post($s['endpoint'] . '/api/v1/orders', [ 'timeout' => 8, 'headers' => [ 'content-type' => 'application/json', 'X-Roasaurus-Key' => $s['store_key'], 'Authorization' => 'Bearer ' . $s['api_key'], ], 'body' => wp_json_encode($body), ]); $code = is_wp_error($res) ? 0 : wp_remote_retrieve_response_code($res); if ($code >= 200 && $code < 300) { $order->update_meta_data('_roasaurus_sent', time()); $order->save(); } else { $msg = is_wp_error($res) ? $res->get_error_message() : wp_remote_retrieve_body($res); $order->add_order_note(sprintf( /* translators: 1: HTTP status code, 2: error message */ __('ROASaurus: order not sent (%1$d) %2$s', 'roasaurus'), $code, substr(wp_strip_all_tags((string) $msg), 0, 200) )); } } public static function thankyou_pixel($order_id) { $s = self::settings(); $order = wc_get_order($order_id); if (!$order || !$s['endpoint'] || !$s['store_key']) { return; } $ids = []; foreach ($order->get_items() as $item) { $ids[] = (string) ($item->get_variation_id() ?: $item->get_product_id()); } $data = [ 'event_id' => 'order-' . $order->get_id(), 'order_id' => (string) $order->get_id(), 'value' => (float) $order->get_total(), 'currency' => $order->get_currency(), 'content_ids' => $ids, ]; // Browser pixels only; the server event comes from send_order with the same event id. wp_print_inline_script_tag('tlq("track","Purchase",' . wp_json_encode($data) . ');'); } } // Tell the WP Consent API that this plugin follows the visitor's consent choice. add_filter('wp_consent_api_registered_' . plugin_basename(__FILE__), '__return_true'); add_action('plugins_loaded', function () { if (class_exists('WooCommerce')) { Roasaurus_WC::init(); } }); // Declare compatibility with WooCommerce High-Performance Order Storage. add_action('before_woocommerce_init', function () { if (class_exists(\Automattic\WooCommerce\Utilities\FeaturesUtil::class)) { \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility('custom_order_tables', __FILE__, true); \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility('cart_checkout_blocks', __FILE__, true); } });