Meta Pixel Event Tracking on PrestaShop | Blue Frog Docs

Meta Pixel Event Tracking on PrestaShop

Implement Meta Pixel conversion events on PrestaShop for Facebook and Instagram ads optimization including ViewContent, AddToCart, Purchase, and custom events.

Meta Pixel Event Tracking on PrestaShop

Track customer actions on your PrestaShop store with Meta Pixel events to optimize Facebook and Instagram advertising campaigns, build custom audiences, and measure ROI.

Meta Pixel Standard Events

Event Overview

Event Name When to Fire PrestaShop Context Business Impact
PageView Every page load All pages Audience building, retargeting
ViewContent Product viewed Product page Product retargeting, dynamic ads
AddToCart Item added to cart Add to cart action Cart abandonment campaigns
InitiateCheckout Checkout started Checkout page Checkout abandonment
AddPaymentInfo Payment info added Payment step Lower funnel optimization
Purchase Order completed Order confirmation Conversion tracking, ROAS
Search Site search performed Search page Search-based audiences
AddToWishlist Item wishlisted Wishlist action Interest-based targeting

Event Parameters

Standard Parameters:

{
  content_name: "Product Name",      // Product/page name
  content_category: "Category",      // Product category
  content_ids: ["12345"],            // Array of product IDs
  content_type: "product",           // product or product_group
  value: 29.99,                      // Monetary value
  currency: "USD",                   // ISO currency code
  num_items: 1                       // Quantity
}

Implementing Events with PrestaShop Hooks

ViewContent Event (Product Page)

Using Hook: displayFooterProduct

<?php
// In your Meta Pixel module

public function hookDisplayFooterProduct($params)
{
    $product = $params['product'];
    $product_obj = new Product($product->id, true, $this->context->language->id);

    // Get category
    $category = new Category($product_obj->id_category_default, $this->context->language->id);

    // Get price
    $price = $product_obj->getPrice(true);

    // Get selected combination if any
    $id_product_attribute = Tools::getValue('id_product_attribute', 0);

    $event_data = array(
        'content_name' => $product_obj->name,
        'content_category' => $category->name,
        'content_ids' => array((string)$product->id),
        'content_type' => 'product',
        'value' => (float)$price,
        'currency' => $this->context->currency->iso_code
    );

    $this->context->smarty->assign(array(
        'meta_event' => 'ViewContent',
        'meta_event_data' => $event_data
    ));

    return $this->display(__FILE__, 'views/templates/hook/meta-viewcontent.tpl');
}

Template:

{* views/templates/hook/meta-viewcontent.tpl *}
<script>
  fbq('track', 'ViewContent', {
    content_name: '{$meta_event_data.content_name|escape:'javascript':'UTF-8'}',
    content_category: '{$meta_event_data.content_category|escape:'javascript':'UTF-8'}',
    content_ids: ['{$meta_event_data.content_ids.0|escape:'javascript':'UTF-8'}'],
    content_type: 'product',
    value: {$meta_event_data.value|floatval},
    currency: '{$meta_event_data.currency|escape:'javascript':'UTF-8'}'
  });
</script>

AddToCart Event

Method 1: Server-Side Hook (actionCartSave)

<?php
// Hook: actionCartSave

public function hookActionCartSave($params)
{
    if (!isset($params['cart'])) {
        return;
    }

    $cart = $params['cart'];
    $last_product = $cart->getLastProduct();

    if (!$last_product) {
        return;
    }

    // Get category
    $category = new Category($last_product['id_category_default'], $this->context->language->id);

    $event_data = array(
        'event' => 'AddToCart',
        'content_name' => $last_product['name'],
        'content_category' => $category->name,
        'content_ids' => array((string)$last_product['id_product']),
        'content_type' => 'product',
        'value' => (float)$last_product['price'],
        'currency' => $this->context->currency->iso_code
    );

    // Store in cookie for JavaScript to read
    $this->context->cookie->__set('meta_cart_event', json_encode($event_data));
}

JavaScript to Read and Fire:

// modules/custommeta/views/js/cart-tracking.js

document.addEventListener('DOMContentLoaded', function() {
  // Check for cart event in cookie
  var metaEvent = getCookie('meta_cart_event');

  if (metaEvent && typeof fbq !== 'undefined') {
    try {
      var eventData = JSON.parse(metaEvent);

      fbq('track', 'AddToCart', {
        content_name: eventData.content_name,
        content_category: eventData.content_category,
        content_ids: eventData.content_ids,
        content_type: eventData.content_type,
        value: eventData.value,
        currency: eventData.currency
      });

      // Clear cookie
      document.cookie = 'meta_cart_event=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
    } catch (e) {
      console.error('Meta cart event error:', e);
    }
  }
});

function getCookie(name) {
  var value = "; " + document.cookie;
  var parts = value.split("; " + name + "=");
  if (parts.length == 2) return parts.pop().split(";").shift();
}

Method 2: Client-Side JavaScript (AJAX Cart)

// Track add to cart button clicks
document.addEventListener('DOMContentLoaded', function() {

  // Listen for PrestaShop cart updates
  prestashop.on('updateCart', function(event) {
    if (event && event.reason && event.reason.linkAction === 'add-to-cart') {

      var productData = getProductDataFromPage();

      if (typeof fbq !== 'undefined') {
        fbq('track', 'AddToCart', {
          content_name: productData.name,
          content_category: productData.category,
          content_ids: [productData.id],
          content_type: 'product',
          value: parseFloat(productData.price),
          currency: prestashop.currency.iso_code
        });
      }
    }
  });

  // Direct button click tracking (backup)
  var addToCartButtons = document.querySelectorAll('[data-button-action="add-to-cart"]');

  addToCartButtons.forEach(function(button) {
    button.addEventListener('click', function(e) {
      var productData = getProductDataFromPage();

      if (typeof fbq !== 'undefined') {
        fbq('track', 'AddToCart', {
          content_name: productData.name,
          content_ids: [productData.id],
          content_type: 'product',
          value: parseFloat(productData.price),
          currency: prestashop.currency.iso_code
        });
      }
    });
  });
});

function getProductDataFromPage() {
  return {
    id: prestashop.page.id_product || document.querySelector('[data-product-id]')?.dataset.productId,
    name: document.querySelector('h1.product-title')?.textContent.trim(),
    category: document.querySelector('.breadcrumb .category')?.textContent.trim() || '',
    price: document.querySelector('[data-product-price]')?.dataset.productPrice ||
           document.querySelector('.product-price .current-price-value')?.textContent.replace(/[^0-9.]/g, '')
  };
}

InitiateCheckout Event

Hook: displayBeforeCheckout or displayShoppingCart

<?php
// Hook: displayBeforeCheckout

public function hookDisplayBeforeCheckout($params)
{
    $cart = $this->context->cart;
    $products = $cart->getProducts(true);

    $content_ids = array();
    $total_value = 0;
    $num_items = 0;

    foreach ($products as $product) {
        $content_ids[] = (string)$product['id_product'];
        $total_value += $product['price'] * $product['quantity'];
        $num_items += $product['quantity'];
    }

    $event_data = array(
        'content_ids' => $content_ids,
        'content_type' => 'product',
        'value' => (float)$cart->getOrderTotal(true),
        'currency' => $this->context->currency->iso_code,
        'num_items' => $num_items
    );

    $this->context->smarty->assign(array(
        'meta_event' => 'InitiateCheckout',
        'meta_event_data' => $event_data
    ));

    return $this->display(__FILE__, 'views/templates/hook/meta-checkout.tpl');
}

Template:

{* views/templates/hook/meta-checkout.tpl *}
<script>
  fbq('track', 'InitiateCheckout', {
    content_ids: {$meta_event_data.content_ids|@json_encode nofilter},
    content_type: 'product',
    value: {$meta_event_data.value|floatval},
    currency: '{$meta_event_data.currency|escape:'javascript':'UTF-8'}',
    num_items: {$meta_event_data.num_items|intval}
  });
</script>

AddPaymentInfo Event

JavaScript Implementation:

// Track payment method selection
document.addEventListener('change', function(e) {
  if (e.target.matches('input[name="payment-option"], .payment-option')) {

    var paymentMethod = e.target.dataset.moduleName || e.target.value;
    var cartTotal = document.querySelector('.cart-total .value')?.textContent.replace(/[^0-9.]/g, '') || '0';

    if (typeof fbq !== 'undefined') {
      fbq('track', 'AddPaymentInfo', {
        content_category: 'checkout',
        value: parseFloat(cartTotal),
        currency: prestashop.currency.iso_code
      });
    }
  }
});

Purchase Event (Conversion)

Most critical event for campaign optimization:

<?php
// Hook: displayOrderConfirmation

public function hookDisplayOrderConfirmation($params)
{
    $order = $params['order'];

    // Get order products
    $order_detail = $order->getOrderDetailList();
    $content_ids = array();
    $contents = array();

    foreach ($order_detail as $product) {
        $content_ids[] = (string)$product['product_id'];

        $contents[] = array(
            'id' => (string)$product['product_id'],
            'quantity' => (int)$product['product_quantity'],
            'item_price' => (float)$product['unit_price_tax_incl']
        );
    }

    $event_data = array(
        'content_ids' => $content_ids,
        'contents' => $contents,
        'content_type' => 'product',
        'value' => (float)$order->total_paid_tax_incl,
        'currency' => $this->context->currency->iso_code,
        'num_items' => count($order_detail)
    );

    // Prevent duplicate on refresh
    $this->context->smarty->assign(array(
        'meta_event' => 'Purchase',
        'meta_event_data' => $event_data,
        'order_reference' => $order->reference
    ));

    return $this->display(__FILE__, 'views/templates/hook/meta-purchase.tpl');
}

Template with Deduplication:

{* views/templates/hook/meta-purchase.tpl *}
<script>
  // Prevent duplicate purchase tracking on page refresh
  var orderRef = '{$order_reference|escape:'javascript':'UTF-8'}';
  var cookieName = 'meta_purchase_' + orderRef;

  function getCookie(name) {
    var value = "; " + document.cookie;
    var parts = value.split("; " + name + "=");
    if (parts.length == 2) return parts.pop().split(";").shift();
  }

  if (!getCookie(cookieName) && typeof fbq !== 'undefined') {
    fbq('track', 'Purchase', {
      content_ids: {$meta_event_data.content_ids|@json_encode nofilter},
      contents: {$meta_event_data.contents|@json_encode nofilter},
      content_type: 'product',
      value: {$meta_event_data.value|floatval},
      currency: '{$meta_event_data.currency|escape:'javascript':'UTF-8'}',
      num_items: {$meta_event_data.num_items|intval}
    });

    // Set cookie to prevent duplicate (expires in 24 hours)
    document.cookie = cookieName + '=1; path=/; max-age=86400';
  }
</script>

Search Event

Hook: actionSearch

<?php
// Hook: actionSearch

public function hookActionSearch($params)
{
    $search_query = isset($params['search_query']) ? $params['search_query'] : Tools::getValue('s');

    $event_data = array(
        'search_string' => $search_query
    );

    $this->context->smarty->assign(array(
        'meta_event' => 'Search',
        'meta_event_data' => $event_data
    ));

    return $this->display(__FILE__, 'views/templates/hook/meta-search.tpl');
}

Template:

<script>
  fbq('track', 'Search', {
    search_string: '{$meta_event_data.search_string|escape:'javascript':'UTF-8'}'
  });
</script>

AddToWishlist Event

JavaScript Implementation:

// Track wishlist additions
document.addEventListener('click', function(e) {
  if (e.target.matches('.wishlist-button-add, [data-action="add-to-wishlist"]')) {

    var productId = e.target.dataset.productId ||
                    e.target.closest('[data-id-product]').dataset.idProduct;
    var productName = e.target.dataset.productName ||
                      document.querySelector('.product-title').textContent.trim();
    var productPrice = e.target.dataset.productPrice ||
                       document.querySelector('.product-price .price')?.textContent.replace(/[^0-9.]/g, '');

    if (typeof fbq !== 'undefined') {
      fbq('track', 'AddToWishlist', {
        content_name: productName,
        content_ids: [productId],
        content_type: 'product',
        value: parseFloat(productPrice) || 0,
        currency: prestashop.currency.iso_code
      });
    }
  }
});

Custom Events

CompleteRegistration Event

Hook: actionCustomerAccountAdd

<?php
// Hook: actionCustomerAccountAdd

public function hookActionCustomerAccountAdd($params)
{
    $customer = $params['newCustomer'];

    $event_data = array(
        'content_name' => 'Account Registration',
        'status' => 'completed'
    );

    // Store in cookie for template to read
    $this->context->cookie->__set('meta_registration_event', json_encode($event_data));
}

JavaScript:

// Fire registration event
var regEvent = getCookie('meta_registration_event');

if (regEvent && typeof fbq !== 'undefined') {
  try {
    var eventData = JSON.parse(regEvent);

    fbq('track', 'CompleteRegistration', {
      content_name: eventData.content_name,
      status: eventData.status
    });

    // Clear cookie
    document.cookie = 'meta_registration_event=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
  } catch (e) {
    console.error('Meta registration event error:', e);
  }
}

Lead Event (Newsletter Signup)

// Track newsletter subscriptions
var newsletterForm = document.querySelector('.block_newsletter form, #newsletter-form');

if (newsletterForm) {
  newsletterForm.addEventListener('submit', function(e) {
    if (typeof fbq !== 'undefined') {
      fbq('track', 'Lead', {
        content_name: 'Newsletter Subscription',
        content_category: 'newsletter'
      });
    }
  });
}

Contact Event (Contact Form)

// Track contact form submissions
var contactForm = document.querySelector('.contact-form, #contact-form');

if (contactForm) {
  contactForm.addEventListener('submit', function(e) {
    if (typeof fbq !== 'undefined') {
      fbq('track', 'Contact', {
        content_name: 'Contact Form Submission'
      });
    }
  });
}

Using GTM for Meta Pixel Events

Recommended approach for flexibility:

Create Event Tags in GTM

Example: ViewContent Tag

Tag Type: Custom HTML
Tag Name: Meta Pixel - ViewContent

HTML:
<script>
  fbq('track', 'ViewContent', {
    content_name: {{Product Name}},
    content_ids: [{{Product ID}}],
    content_type: 'product',
    value: {{Product Price}},
    currency: {{Currency}}
  });
</script>

Triggering: Page Type equals "product"

Use Data Layer Variables:

Create GTM variables that read from PrestaShop data layer:

  • Product Name: \{\{ecommerce.items.0.item_name\}\}
  • Product ID: \{\{ecommerce.items.0.item_id\}\}
  • Product Price: \{\{ecommerce.items.0.price\}\}
  • Currency: \{\{ecommerce.currency\}\}

See GTM Data Layer guide for implementation.

Meta Conversions API (Server-Side Tracking)

Why Use Conversions API?

  • Overcome iOS 14+ tracking limitations
  • Improve event match quality
  • Reduce impact of ad blockers
  • Server-side redundancy for critical events

Implementation Overview

Requirements:

  • Access Token from Facebook Business Settings
  • Server-side capability (PHP)
  • PrestaShop hooks for server-side events

Basic CAPI Implementation:

<?php
// Send purchase event to Conversions API

public function sendPurchaseToConversionsAPI($order)
{
    $access_token = Configuration::get('META_CAPI_ACCESS_TOKEN');
    $pixel_id = Configuration::get('CUSTOMMETA_PIXEL_ID');

    if (empty($access_token) || empty($pixel_id)) {
        return;
    }

    $customer = new Customer($order->id_customer);
    $address = new Address($order->id_address_delivery);

    // Get order products
    $order_detail = $order->getOrderDetailList();
    $content_ids = array();
    $contents = array();

    foreach ($order_detail as $product) {
        $content_ids[] = (string)$product['product_id'];
        $contents[] = array(
            'id' => (string)$product['product_id'],
            'quantity' => (int)$product['product_quantity'],
            'item_price' => (float)$product['unit_price_tax_incl']
        );
    }

    // Build event data
    $event_data = array(
        'event_name' => 'Purchase',
        'event_time' => time(),
        'event_source_url' => $this->context->link->getPageLink('order-confirmation', true),
        'action_source' => 'website',
        'user_data' => array(
            'em' => array(hash('sha256', strtolower(trim($customer->email)))),
            'fn' => array(hash('sha256', strtolower(trim($customer->firstname)))),
            'ln' => array(hash('sha256', strtolower(trim($customer->lastname)))),
            'ph' => array(hash('sha256', preg_replace('/[^0-9]/', '', $address->phone))),
            'ct' => array(hash('sha256', strtolower(trim($address->city)))),
            'zp' => array(hash('sha256', trim($address->postcode))),
            'country' => array(hash('sha256', strtolower(trim($this->context->country->iso_code)))),
            'client_ip_address' => $_SERVER['REMOTE_ADDR'],
            'client_user_agent' => $_SERVER['HTTP_USER_AGENT']
        ),
        'custom_data' => array(
            'content_ids' => $content_ids,
            'contents' => $contents,
            'content_type' => 'product',
            'value' => (float)$order->total_paid_tax_incl,
            'currency' => $this->context->currency->iso_code,
            'num_items' => count($order_detail)
        )
    );

    $data = array(
        'data' => array($event_data)
    );

    // Send to Conversions API
    $url = "https://graph.facebook.com/v18.0/{$pixel_id}/events?access_token={$access_token}";

    $ch = curl_init($url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
    curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json'));

    $response = curl_exec($ch);
    $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    // Log response for debugging
    if ($http_code !== 200) {
        PrestaShopLogger::addLog(
            'Meta CAPI Error: ' . $response,
            3,
            null,
            'Order',
            $order->id
        );
    }

    return $response;
}

Call in Order Confirmation Hook:

public function hookDisplayOrderConfirmation($params)
{
    $order = $params['order'];

    // Send to Conversions API
    $this->sendPurchaseToConversionsAPI($order);

    // Also fire browser pixel (for deduplication)
    // ... browser pixel code ...
}

Event Deduplication

Prevent counting same event twice (browser + server):

// Browser-side: Include event_id
var eventId = 'order_' + orderReference + '_' + Date.now();

fbq('track', 'Purchase', {
  // ... event data ...
}, {
  eventID: eventId
});
// Server-side: Use same event_id
$event_data = array(
    'event_name' => 'Purchase',
    'event_id' => 'order_' . $order->reference . '_' . time(),
    // ... rest of data ...
);

Testing Meta Pixel Events

Test Events Tool

In Facebook Events Manager:

  1. Navigate to Events Manager > Test Events
  2. Enter your store URL or use browser extension
  3. Browse store and perform actions
  4. Verify events appear in real-time
  5. Check event parameters are correct

Verify:

  • ✅ Event name correct
  • ✅ Parameters populated
  • ✅ Values are numeric (not strings)
  • ✅ Currency is 3-letter code
  • ✅ content_ids are strings

Meta Pixel Helper

Chrome Extension checks:

  • Events firing at correct times
  • No pixel errors
  • Multiple pixels detected if configured
  • Parameter warnings

Browser Console Debugging

// Enable debug mode
fbq('set', 'autoConfig', false, 'YOUR_PIXEL_ID');

// Log all pixel events
var originalTrack = fbq.track;
fbq.track = function() {
  console.log('Meta Pixel Track:', arguments);
  return originalTrack.apply(this, arguments);
};

// Check event queue
fbq.queue

Common Issues and Solutions

Events Firing Multiple Times

Cause: Multiple implementations active

Fix:

  • Check for duplicate pixel base code
  • Verify only one module/implementation active
  • Remove manual code if using module

Incorrect Event Values

Cause: Price formatting issues

Fix:

// Ensure numeric values
value: parseFloat(productPrice.replace(/[^0-9.]/g, ''))

// Not:
value: productPrice  // May be string "$29.99"

Purchase Event Not Firing

Cause: Page caching, JavaScript errors, missing pixel

Fix:

  • Clear PrestaShop cache
  • Check browser console for errors
  • Verify pixel base code loads before event
  • Test order confirmation page directly

Low Event Match Quality

Cause: Missing customer data, not using advanced matching

Fix:

  • Enable advanced matching with hashed customer data
  • Implement Conversions API
  • Include more user_data parameters

Performance Optimization

Batch Related Events:

// Instead of multiple fbq calls
fbq('track', 'AddToCart', data1);
fbq('track', 'CustomEvent', data2);

// Use single call with custom event
fbq('trackSingle', 'PIXEL_ID', 'AddToCart', data1);

Lazy Load Non-Critical Events:

// Load pixel after user interaction
var pixelLoaded = false;

function ensurePixelLoaded() {
  if (!pixelLoaded && typeof fbq === 'undefined') {
    loadMetaPixel();
    pixelLoaded = true;
  }
}

// Call before firing events
ensurePixelLoaded();
fbq('track', 'ViewContent', data);

Next Steps

// SYS.FOOTER