Track customer actions and conversions using Meta Pixel standard and custom events on your NopCommerce store to optimize Facebook and Instagram ad campaigns.
Before You Begin
Prerequisites:
- Meta Pixel installed (see Meta Pixel Setup)
- Pixel ID configured and verified
- Understanding of Meta's event structure
Meta Pixel Event Structure:
fbq('track', 'EventName', {
parameter1: 'value1',
parameter2: 'value2'
});
Standard Events Overview
Meta provides standard events optimized for ad delivery and conversion tracking:
- ViewContent - Product page view
- Search - Product search performed
- AddToCart - Product added to cart
- AddToWishlist - Product added to wishlist
- InitiateCheckout - Checkout process started
- AddPaymentInfo - Payment information added
- Purchase - Order completed
- Lead - Newsletter signup, contact form
- CompleteRegistration - Customer account created
ViewContent Event (Product Pages)
Razor Implementation
Add to /Themes/YourTheme/Views/Product/ProductTemplate.Simple.cshtml:
@model ProductDetailsModel
@section scripts {
<script>
fbq('track', 'ViewContent', {
content_name: '@Html.Raw(System.Text.Json.JsonSerializer.Serialize(Model.Name))',
content_ids: ['@Model.Id'],
content_type: 'product',
value: @Model.ProductPrice.PriceValue,
currency: '@Model.ProductPrice.CurrencyCode'
});
</script>
}
C# Plugin Implementation
// Event consumer for product views
public class ProductViewedEventConsumer : IConsumer<ProductViewedEvent>
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IPriceCalculationService _priceCalculationService;
private readonly IWorkContext _workContext;
public async Task HandleEventAsync(ProductViewedEvent eventMessage)
{
var product = eventMessage.Product;
var priceModel = await _priceCalculationService.GetFinalPriceAsync(product);
var currency = await _workContext.GetWorkingCurrencyAsync();
var eventData = new
{
event_name = "ViewContent",
content_name = product.Name,
content_ids = new[] { product.Id.ToString() },
content_type = "product",
value = priceModel.finalPrice,
currency = currency.CurrencyCode
};
var httpContext = _httpContextAccessor.HttpContext;
if (httpContext?.Items != null)
{
if (!httpContext.Items.ContainsKey("MetaPixelEvents"))
httpContext.Items["MetaPixelEvents"] = new List<object>();
((List<object>)httpContext.Items["MetaPixelEvents"]).Add(eventData);
}
}
}
Search Event
Search Results Page
@model SearchModel
@section scripts {
@if (!string.IsNullOrEmpty(Model.q))
{
<script>
fbq('track', 'Search', {
search_string: '@Html.Raw(Model.q)',
content_category: 'product_search',
content_ids: [@string.Join(",", Model.Products.Select(p => $"'{p.Id}'"))],
value: @Model.Products.Sum(p => p.ProductPrice.PriceValue),
currency: '@Model.Products.FirstOrDefault()?.ProductPrice.CurrencyCode'
});
</script>
}
}
JavaScript Implementation
// Track search when form submitted
document.querySelector('.search-box-form')?.addEventListener('submit', function(e) {
var searchTerm = this.querySelector('input[name="q"]').value;
fbq('track', 'Search', {
search_string: searchTerm,
content_category: 'product_search'
});
});
AddToCart Event
AJAX Add to Cart Implementation
@model ProductDetailsModel
<script>
// Override NopCommerce add to cart function
function addProductToCart_details(url) {
var form = $('#product-details-form');
var quantityInput = form.find('input[name="addtocart_@Model.Id.EnteredQuantity"]');
var quantity = parseFloat(quantityInput.val()) || 1;
$.ajax({
cache: false,
url: url,
type: 'POST',
data: form.serialize(),
success: function(data) {
if (data.success) {
// Track Meta Pixel AddToCart event
fbq('track', 'AddToCart', {
content_name: '@Html.Raw(System.Text.Json.JsonSerializer.Serialize(Model.Name))',
content_ids: ['@Model.Id'],
content_type: 'product',
value: @Model.ProductPrice.PriceValue * quantity,
currency: '@Model.ProductPrice.CurrencyCode',
content_category: '@(Model.Breadcrumb.CategoryBreadcrumb.LastOrDefault()?.Name ?? "")'
});
}
// Original NopCommerce logic
displayAjaxLoading(false);
if (data.updatetopcartsectionhtml) {
$('.header-links').html(data.updatetopcartsectionhtml);
}
if (data.message) {
displayBarNotification(data.message, data.success ? 'success' : 'error', 3500);
}
}
});
return false;
}
</script>
C# Event Consumer Approach
public class ShoppingCartEventConsumer : IConsumer<EntityInsertedEvent<ShoppingCartItem>>
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IProductService _productService;
private readonly IPriceCalculationService _priceCalculationService;
private readonly IWorkContext _workContext;
public async Task HandleEventAsync(EntityInsertedEvent<ShoppingCartItem> eventMessage)
{
var cartItem = eventMessage.Entity;
if (cartItem.ShoppingCartType != ShoppingCartType.ShoppingCart)
return;
var product = await _productService.GetProductByIdAsync(cartItem.ProductId);
var priceModel = await _priceCalculationService.GetFinalPriceAsync(product, cartItem.Quantity);
var currency = await _workContext.GetWorkingCurrencyAsync();
var eventData = new
{
event_name = "AddToCart",
content_name = product.Name,
content_ids = new[] { product.Id.ToString() },
content_type = "product",
value = priceModel.finalPrice * cartItem.Quantity,
currency = currency.CurrencyCode
};
QueuePixelEvent(eventData);
}
private void QueuePixelEvent(object eventData)
{
var httpContext = _httpContextAccessor.HttpContext;
if (httpContext?.Items != null)
{
if (!httpContext.Items.ContainsKey("MetaPixelEvents"))
httpContext.Items["MetaPixelEvents"] = new List<object>();
((List<object>)httpContext.Items["MetaPixelEvents"]).Add(eventData);
}
}
}
AddToWishlist Event
@model ProductDetailsModel
<script>
function trackWishlistAdd() {
fbq('track', 'AddToWishlist', {
content_name: '@Html.Raw(System.Text.Json.JsonSerializer.Serialize(Model.Name))',
content_ids: ['@Model.Id'],
content_category: '@(Model.Breadcrumb.CategoryBreadcrumb.LastOrDefault()?.Name ?? "")',
value: @Model.ProductPrice.PriceValue,
currency: '@Model.ProductPrice.CurrencyCode'
});
}
document.querySelector('.add-to-wishlist-button')?.addEventListener('click', function() {
trackWishlistAdd();
});
</script>
InitiateCheckout Event
Checkout Page Load
@model CheckoutModel
@section scripts {
<script>
fbq('track', 'InitiateCheckout', {
content_ids: [@string.Join(",", Model.Cart.Items.Select(i => $"'{i.ProductId}'"))],
contents: [
@for (int i = 0; i < Model.Cart.Items.Count; i++)
{
var item = Model.Cart.Items[i];
<text>
{
id: '@item.ProductId',
quantity: @item.Quantity,
item_price: @item.UnitPrice
}@(i < Model.Cart.Items.Count - 1 ? "," : "")
</text>
}
],
num_items: @Model.Cart.Items.Sum(i => i.Quantity),
value: @Model.OrderTotals.SubTotalValue,
currency: '@Model.OrderTotals.Currency'
});
</script>
}
AddPaymentInfo Event
@* Checkout payment method selection *@
<script>
function trackPaymentInfo(paymentMethod) {
fbq('track', 'AddPaymentInfo', {
content_category: paymentMethod,
value: @Model.OrderTotals.OrderTotalValue,
currency: '@Model.OrderTotals.Currency'
});
}
document.querySelectorAll('input[name="paymentmethod"]').forEach(function(radio) {
radio.addEventListener('change', function() {
if (this.checked) {
var method = this.closest('.payment-method').dataset.methodName;
trackPaymentInfo(method);
}
});
});
</script>
Purchase Event
Order Confirmation Page
@model CheckoutCompletedModel
@section scripts {
<script>
@{
var order = await orderService.GetOrderByIdAsync(Model.OrderId);
var orderItems = await orderService.GetOrderItemsAsync(order.Id);
}
fbq('track', 'Purchase', {
value: @order.OrderTotal,
currency: '@order.CustomerCurrencyCode',
content_ids: [@string.Join(",", orderItems.Select(i => $"'{i.ProductId}'"))],
contents: [
@for (int i = 0; i < orderItems.Count; i++)
{
var item = orderItems[i];
<text>
{
id: '@item.ProductId',
quantity: @item.Quantity,
item_price: @item.UnitPriceInclTax
}@(i < orderItems.Count - 1 ? "," : "")
</text>
}
],
num_items: @orderItems.Sum(i => i.Quantity),
content_type: 'product'
});
</script>
}
Event Consumer Implementation
public class OrderPlacedEventConsumer : IConsumer<OrderPlacedEvent>
{
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly IOrderService _orderService;
private readonly IProductService _productService;
public async Task HandleEventAsync(OrderPlacedEvent eventMessage)
{
var order = eventMessage.Order;
var orderItems = await _orderService.GetOrderItemsAsync(order.Id);
var contents = new List<object>();
var contentIds = new List<string>();
foreach (var item in orderItems)
{
var product = await _productService.GetProductByIdAsync(item.ProductId);
contentIds.Add(product.Id.ToString());
contents.Add(new
{
id = product.Id.ToString(),
quantity = item.Quantity,
item_price = item.UnitPriceInclTax
});
}
var eventData = new
{
event_name = "Purchase",
value = order.OrderTotal,
currency = order.CustomerCurrencyCode,
content_ids = contentIds.ToArray(),
contents = contents.ToArray(),
num_items = orderItems.Sum(i => i.Quantity),
content_type = "product"
};
var httpContext = _httpContextAccessor.HttpContext;
if (httpContext?.Items != null)
{
httpContext.Items["MetaPixelPurchase"] = System.Text.Json.JsonSerializer.Serialize(eventData);
}
}
}
Lead Event (Newsletter Subscription)
@model NewsletterBoxModel
<script>
document.querySelector('.newsletter-subscribe-button')?.addEventListener('click', function(e) {
e.preventDefault();
var email = document.querySelector('input[name="newsletter-email"]').value;
// Submit newsletter subscription
$.ajax({
url: '/subscribenewsletter',
type: 'POST',
data: { email: email },
success: function(response) {
if (response.Success) {
// Track Lead event
fbq('track', 'Lead', {
content_category: 'newsletter_subscription',
content_name: 'Newsletter Signup',
value: 0,
currency: 'USD'
});
displayBarNotification(response.Message, 'success');
}
}
});
});
</script>
CompleteRegistration Event
@model RegisterModel
<script>
document.querySelector('.register-button')?.addEventListener('click', function(e) {
var form = document.querySelector('#register-form');
// Attach to form submit for successful registration
form.addEventListener('submit', function(submitEvent) {
// Track CompleteRegistration on successful validation
if (form.checkValidity()) {
fbq('track', 'CompleteRegistration', {
content_name: 'Customer Registration',
status: true
});
}
});
});
</script>
Post-Registration Confirmation
@* After successful registration *@
@if (Model.RegisteredSuccessfully)
{
<script>
fbq('track', 'CompleteRegistration', {
content_name: 'Customer Account Created',
status: true,
value: 0,
currency: 'USD'
});
</script>
}
Custom Events
Track Custom Actions
// Track product comparison
function trackProductCompare(productId, productName) {
fbq('trackCustom', 'CompareProducts', {
content_ids: [productId],
content_name: productName,
content_type: 'product'
});
}
// Track coupon application
function trackCouponApplied(couponCode, discountAmount) {
fbq('trackCustom', 'CouponApplied', {
coupon_code: couponCode,
discount_amount: discountAmount,
currency: 'USD'
});
}
// Track product review submission
function trackReviewSubmit(productId, productName, rating) {
fbq('trackCustom', 'SubmitReview', {
content_ids: [productId],
content_name: productName,
rating: rating
});
}
// Track gift card purchase
function trackGiftCardPurchase(amount) {
fbq('trackCustom', 'GiftCardPurchase', {
value: amount,
currency: 'USD'
});
}
Event Deduplication
Prevent duplicate events when using both browser and server-side tracking:
// Generate unique event ID
function generateEventId() {
return 'evt_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
}
// Track with event ID
var eventId = generateEventId();
fbq('track', 'Purchase', {
value: 99.99,
currency: 'USD',
content_ids: ['123']
}, {
eventID: eventId
});
C# Server-Side Event ID
// Generate consistent event ID for deduplication
public string GenerateEventId(int orderId)
{
return $"order_{orderId}_{DateTime.UtcNow.Ticks}";
}
// Use in both browser and Conversions API
var eventId = GenerateEventId(order.Id);
ViewBag.MetaPixelEventId = eventId;
Advanced Matching Parameters
Enhanced user matching for better attribution:
fbq('init', '123456789012345', {
em: 'customer@example.com', // Email (hashed automatically)
fn: 'john', // First name
ln: 'doe', // Last name
ph: '15551234567', // Phone number
ge: 'm', // Gender
db: '19900101', // Date of birth (YYYYMMDD)
ct: 'new york', // City
st: 'ny', // State
zp: '10001', // Zip code
country: 'us' // Country code
});
C# Implementation
// Build advanced matching parameters
var advancedMatching = new
{
em = customer.Email?.ToLowerInvariant(),
fn = firstName?.ToLowerInvariant(),
ln = lastName?.ToLowerInvariant(),
ph = phone?.Replace("-", "").Replace(" ", ""),
ct = city?.ToLowerInvariant(),
st = state?.ToLowerInvariant(),
zp = zipCode,
country = country?.ToLowerInvariant()
};
ViewBag.MetaPixelAdvancedMatching = System.Text.Json.JsonSerializer.Serialize(advancedMatching);
Testing and Debugging
Using Meta Pixel Helper
- Install Meta Pixel Helper Chrome extension
- Navigate to page with event
- Trigger event (add to cart, purchase, etc.)
- Click extension icon
- Verify event appears with correct parameters
Test Events in Events Manager
Meta Events Manager > Test Events
- Add test event code to configuration
- Browse store and trigger events
- View events in real-time
- Verify all parameters passed correctly
Console Debugging
// Log all pixel events
var originalFbq = window.fbq;
window.fbq = function() {
console.log('Meta Pixel Event:', arguments);
originalFbq.apply(window, arguments);
};
// Check pixel version
console.log(fbq.version); // Should output '2.0'
// View queued events
console.log(fbq.queue);
Event Parameter Best Practices
Required Parameters
Always include these for standard events:
{
content_ids: ['123'], // Product/content IDs
content_type: 'product', // Type of content
value: 99.99, // Numeric value
currency: 'USD' // ISO currency code
}
Recommended Parameters
Add for better optimization:
{
content_name: 'Product Name',
content_category: 'Category',
num_items: 2,
predicted_ltv: 500.00
}
Next Steps
- Meta Pixel Setup - Initial pixel configuration
- GTM Data Layer - Manage pixels via GTM
- Events Not Firing - Debug event issues