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');
}
{* 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:
- Navigate to Events Manager > Test Events
- Enter your store URL or use browser extension
- Browse store and perform actions
- Verify events appear in real-time
- 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
- Meta Pixel Setup - Initial pixel installation
- GTM Data Layer - Use data layer for events
- Events Not Firing - Debug tracking issues