File manager - Edit - /home/verseaumee/colorkeysstudio/wp-content/plugins/reddit-for-woocommerce/includes/Tracking/ConversionTrackingService.php
Back
<?php /** * Registers and manages WooCommerce hooks for conversion tracking. * * This class integrates with core WooCommerce events (such as purchases and add-to-cart actions) * and delegates conversion tracking responsibilities to an underlying tracker implementation. * * It acts as a coordinator between WooCommerce events and the Ad Partner's Conversions API. * * @package RedditForWooCommerce\Tracking */ namespace RedditForWooCommerce\Tracking; use RedditForWooCommerce\Utils\Storage\OptionDefaults; use RedditForWooCommerce\Utils\Storage\Options; use RedditForWooCommerce\Utils\Helper; /** * Service class for registering WooCommerce conversion tracking hooks. * * This class listens to WooCommerce events and triggers corresponding * server-side conversion tracking via the provided {@see ConversionTrackerInterface}. * * - `woocommerce_thankyou` - triggers purchase event tracking * - `woocommerce_add_to_cart` - triggers add-to-cart tracking * - Custom async action (e.g., Action Scheduler) can be used via `send_conversion_event` * * @since 0.1.0 */ class ConversionTrackingService implements ServiceStatusInterface { /** * Instance of a class implementing conversion tracking logic. * * @var ConversionTrackerInterface */ protected ConversionTrackerInterface $tracker; /** * Constructor. * * Accepts a concrete implementation of the {@see ConversionTrackerInterface}, * which is responsible for sending the actual tracking payloads to the Ad Partner API. * * @since 0.1.0 * * @param ConversionTrackerInterface $tracker Tracker implementation instance. */ public function __construct( ConversionTrackerInterface $tracker ) { $this->tracker = $tracker; } /** * Registers WooCommerce hooks for conversion event tracking. * * It attaches conversion tracking to: * - Purchase events (`woocommerce_thankyou`) * - Add to Cart events (`woocommerce_add_to_cart`) * - Asynchronous tracking hook via Action Scheduler * * @since 0.1.0 * * @return void */ public function register_hooks(): void { if ( ! self::is_enabled() ) { return; } add_action( 'woocommerce_thankyou', array( $this, 'handle_purchase' ) ); add_action( 'woocommerce_add_to_cart', array( $this, 'handle_single_product_add_to_cart' ), 10, 4 ); Helper::register_ajax_action( 'AddToCart', array( $this, 'handle_async_add_to_cart' ) ); Helper::register_ajax_action( 'ViewContent', array( $this, 'handle_async_view_content' ) ); Helper::register_ajax_action( 'PageVisit', array( $this, 'handle_async_page_view' ) ); add_action( Helper::with_prefix( 'send_conversion_event' ), array( $this->tracker, 'send' ), 10, 2 ); add_action( Helper::with_prefix( 'conversion_sent' ), array( $this, 'mark_as_tracked' ), 10, 2 ); add_action( Helper::with_prefix( 'ad_account_connected' ), array( $this->tracker, 'track_dummy_purchase' ) ); } /** * Determines whether Reddit Conversion tracking is currently enabled. * * This checks the persisted plugin option configured via the admin interface or defaults. * * @since 0.1.0 * * @return bool True if pixel tracking is enabled; false otherwise. */ public static function is_enabled(): bool { return 'yes' === Options::get( OptionDefaults::CONVERSIONS_ENABLED ); } /** * Callback for WooCommerce purchase completion events. * * Invoked after a customer completes checkout. Delegates to the tracker * to send the order data to the Ad Partner's Conversions API. * * @since 0.1.0 * * @param int $order_id WooCommerce order ID. * @return void */ public function handle_purchase( int $order_id ): void { $this->tracker->track_purchase( $order_id ); } /** * Callback for WooCommerce add-to-cart actions. * * Triggered when a product is added to the cart. * Passes product ID and quantity to the tracker for building the tracking payload. * * @since 0.1.0 * * @param string $cart_item_key Unique key for the cart item. * @param int $product_id WooCommerce product ID added to the cart. * @param int $quantity Quantity of product added. * @param int $variation_id WooCommerce product variation ID added to the cart. * @return void */ public function handle_single_product_add_to_cart( string $cart_item_key, int $product_id, int $quantity, int $variation_id ): void { /** * We only track synchronous Add to Cart events in this handler. * * WooCommerce provides three ways for adding products to the cart: * 1. Form submission on single product pages – Standard POST to `?add-to-cart=ID` * 2. AJAX-based add to cart – Typically used in archive pages or custom JS * 3. REST API-based add to cart – Used by modern frontends or headless implementations * * All of these methods trigger the `woocommerce_add_to_cart` hook. * However, we must be careful *not* to process every add-to-cart action here blindly, * because this hook is also triggered for asynchronous flows (AJAX/REST) that are handled elsewhere. * * Reddit's deduplication logic requires the Pixel (client-side) and CAPI (server-side) events * to share a *common* unique `event_id`. Without this shared ID, Reddit will treat them * as duplicate events and discard one of them. * * For synchronous form submissions (on single product pages), we inject an `event_id` hidden field * via `render_event_id_field()`, and also add inline JavaScript that assigns a freshly generated * `crypto.randomUUID()` into the field. This ensures every click on the Add to Cart button * generates a new unique ID, even though the page reloads afterward. * * For asynchronous flows (AJAX or REST), this logic is different. There is *no* page reload, * so the same field-based injection strategy doesn't apply. In these cases, the frontend JS * is responsible for generating a new UUID and submitting it via an async request * to the `handle_async_add_to_cart()` method. * * To avoid double-tracking, we skip processing async add-to-cart actions here by checking * `Helper::is_request_async()`. These are already handled in their dedicated async flow. */ if ( Helper::is_request_async() ) { return; } //phpcs:ignore WordPress.Security.NonceVerification.Missing $event_id = sanitize_text_field( wp_unslash( $_POST[ Helper::with_prefix( 'event_id' ) ] ?? '' ) ); $this->tracker->track_add_to_cart( $variation_id ? $variation_id : $product_id, $quantity, $event_id ); } /** * Handles AJAX-based Add to Cart requests for server-side tracking. * * This endpoint is called when a product is added to the cart synchronously * via JavaScript. * * @since 0.1.0 * * @return void */ public function handle_async_add_to_cart(): void { check_ajax_referer( 'capi_nonce', 'security' ); $payload = wp_unslash( $_POST['payload'] ?? '{}' ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $data = json_decode( $payload, true ); $product_id = absint( $data['productId'] ?? 0 ); $quantity = absint( $data['quantity'] ?? 0 ); $event_id = sanitize_text_field( $data['conversionId'] ?? '' ); $this->tracker->track_add_to_cart( $product_id, $quantity, $event_id ); } /** * Handles asynchronous View Content tracking requests. * * This method is called via AJAX when a single product page is viewed. * It extracts the product ID and event ID from the request and triggers * the tracker. * * @since 0.1.0 * * @return void */ public function handle_async_view_content(): void { check_ajax_referer( 'capi_nonce', 'security' ); $payload = wp_unslash( $_POST['payload'] ?? '{}' ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $data = json_decode( $payload, true ); $products = $data['products'] ?? array(); $product_id = absint( $products['id'] ?? 0 ); $event_id = sanitize_text_field( $data['conversionId'] ?? '' ); $this->tracker->track_view_content( $product_id, $event_id ); } /** * Handles asynchronous Page View tracking requests. * * This method is called via AJAX when any page view event is triggered by frontend JavaScript. * It extracts the `event_id` from the request and delegates tracking to the configured * {@see ConversionTrackerInterface} implementation. * * This handler is used in low-priority tracking scenarios (e.g., audience seeding, general analytics) * where page views are recorded. It is intended for broad usage across all site pages, * including product and content pages, in an MVP setup. * * @since 0.1.0 * * @return void */ public function handle_async_page_view(): void { check_ajax_referer( 'capi_nonce', 'security' ); $payload = wp_unslash( $_POST['payload'] ?? '{}' ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $data = json_decode( $payload, true ); $event_id = sanitize_text_field( $data['conversionId'] ?? '' ); $this->tracker->track_page_view( $event_id ); } /** * Marks the given order as tracked to prevent duplicate conversion reporting. * * This should be called after a successful conversion event has been sent * to the Ad Partner. It updates the order meta with a flag to indicate * that the conversion has already been tracked. * * @since 0.1.0 * * @param array $event_payload The payload that was sent to the Ad Partner. * @param array $args { * Additional arguments passed to the tracking action. * * @type int $order_id The ID of the WooCommerce order to mark as tracked. * } */ public function mark_as_tracked( $event_payload, $args ) { if ( empty( $args['order_id'] ) ) { return; } $order = wc_get_order( $args['order_id'] ); // Mark the order as tracked, to avoid double-reporting if the confirmation page is reloaded. $order->update_meta_data( RemoteConversionTracker::ORDER_CONVERSION_TRACKED_META_KEY, 'tracked' ); $order->save_meta_data(); } }
| ver. 1.4 |
Github
|
.
| PHP 8.5.7 | Generation time: 0 |
proxy
|
phpinfo
|
Settings