Meta Pixel Event Tracking on NopCommerce | OpsBlu Docs

Meta Pixel Event Tracking on NopCommerce

Implement Meta Pixel standard and custom events on NopCommerce for conversion tracking, audience building, and ad optimization with C# and JavaScript...

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 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

  1. Install Meta Pixel Helper Chrome extension
  2. Navigate to page with event
  3. Trigger event (add to cart, purchase, etc.)
  4. Click extension icon
  5. Verify event appears with correct parameters

Test Events in Events Manager

Meta Events Manager > Test Events
  1. Add test event code to configuration
  2. Browse store and trigger events
  3. View events in real-time
  4. 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
}

Add for better optimization:

{
    content_name: 'Product Name',
    content_category: 'Category',
    num_items: 2,
    predicted_ltv: 500.00
}

Next Steps