This is a technical guide for advanced users that explains how you can get 100% accurate ecommerce reports in Matomo.

To achieve this, we will measure all of the ecommerce orders in Matomo to ensure that all revenue metrics are consistent between your ecommerce platform reports and Matomo reports. This will include all your online orders as well as any offline sales.

Why this guide?

While you would normally simply track Ecommerce orders in JavaScript as documented in the Ecommerce Matomo guide, sometimes you require 100% eCommerce tracking accuracy. If you require 100% of your orders to be tracked, we do not recommend using JavaScript because tracking requests in JS can be blocked by ad-blockers and for other reasons. As a result, Matomo wouldn’t track all orders and it would lead to lack of trust in the ecommerce reports (as they wouldn’t match with your Ecommerce platform reports).

In this guide, we’ll describe the overall steps in implementing a server-side module in your Ecommerce platform, that will accurately track all ecommerce interactions in your Matomo instance.

This guide also applies when you want to track offline sales & offline conversions in Matomo – for example any purchases that occurred in your physical store.

Assumptions

The solution described here assumes that your website and Ecommerce sites are already tagged with the default Matomo JavaScript Tracking code, but that you haven’t setup Ecommerce Tracking yet.

Therefore it is a hybrid approach where:

  • All standard user interactions (pageviews, events, heatmaps, forms, etc.) are tracked using the JavaScript tag

  • All ecommerce interactions (cart updates and orders, including possibly offline interactions) are tracked from the server-side within your Ecommerce platform.

Requirements

1. Latest version of Matomo

2. Special permission to run code on your Ecommerce platform so you can:

  • run custom code when a cart is changed, or a new order recorded

  • and also record new metadata in your ecommerce order record so we keep an audit log in each order for troubleshooting and auditing

3. The ability to send HTTPS requests from your Ecommerce platform to your Matomo instance.

4. A secret API authentication token (token_auth) which will be used to connect to your Matomo Tracking API.

  • How to get a token_auth: In Matomo, create a new user which will be used specifically for Tracking API requests. For example a user called « Tracking API (Ecommerce) ». Then give this new user « Admin » access to the website. To get the token_auth for this user see this faq.

5. You have tagged your ecommerce websites with the standard, non-ecommerce, Matomo JavaScript Tracking code.

6. Ecommerce is enabled in the websites management for the site you want to track ecommerce data.

Note: if you are using PHP language server side, use our PHP SDK to track ecommerce interactions. Or check out our other SDKs. There are also other un-official SDKs available.

Server side implementation in your Ecommerce platform

A. When there is a new ecommerce order

When a new order is processed, then your Ecommerce platform will need to do the following:

1. Check that this order has not yet been recorded in Matomo

  • this is done by checking a flag eg. matomoTracked when processing a new order, to ensure there is no double counting of orders in Matomo
    (see below for when to set this flag)

2. Store some metadata about the customer along with the order

  • ipAddress: IP Address of customer

  • userAgent: User agent of customer

  • matomoVisitorId: Matomo ID Visitor

  • attributionInfo: The referrer attribution info (acquisition channels) for this order

  • matomoTracked: Whether this order has already been tracked in Matomo

When using our PHP SDK, the code will look like this:

// it is assumed that $order->setMetadata() will record a new metadata 
// for this ecommerce order in the ecommerce DB

$order->setMetadata(`ipAddress`, $tracker->getIp());
$order->setMetadata(`userAgent`, $tracker->getUserAgent());
$order->setMetadata(`matomoVisitorId`, $tracker->getVisitorId());
$order->setMetadata(`attributionInfo`, $tracker->getAttributionInfo());

3. Record the ecommerce order and all products to Matomo by sending an HTTPS Tracking API request

Note: Because of the type of data being sent, these requests must be sent using HTTPS

The Tracking API request will contain:

  1. the visitor metadata that was stored in the order (ip address, user agent, visitor id, referrer attribution information)

  2. the order details and revenue

  3. the list of products in the order (and for each: SKU, name, a list of categories, price, quantity)

  4. your secret token_auth

When using our PHP SDK, the code will look like this:

// it is assumed that $order is an array/object which contains
// this ecommerce order's metadata as well as product details

$tracker = new Tracker($matomoIdSite, $matomoUrl);

// Specify an API token_auth with at least Write permission, so the Visitor IP address can be recorded
$tracker->setTokenAuth('my_token_auth_value_here');

$tracker->setVisitorId($order['matomoVisitorId']);
if (!empty($order['userAgent'])) {
   $tracker->setUserAgent($order['userAgent']);
}

if (!empty($order['ipAddress'])) {
   $tracker->setIp($order['ipAddress']);
}

if (!empty($order['attributionInfo'])) {
   $tracker->setAttributionInfo($order['attributionInfo']);
}

// Only confirmed orders should be tracked in Matomo (recommended)
if( in_array( $order['orderStatus'], array('cancelled', 'failed', 'pending', 'refunded')) ) {
   $this->log('Ignoring ecommerce order ' . $orderId . ' becauses of status: ' . $orderStatus);
   return;
}

foreach ($order->getProducts() as $product) {
   $sku = $product['sku'];
   $price = $product['total'];
   $title = $product['title'];
   $categories = $product['categories']; // an array of category names
   $quantity = $product['qty'];

   try {
       $tracker->addEcommerceItem($sku, $title, $categories, $price, $quantity);
   } catch (\Exception $e) {
       $this->log(sprintf('Failed to add ecommerce item: %s', $title));
   }

}

$tracker->setRequestTimeout($PURCHASE_REQUEST_TIMEOUT_IN_SECONDS = 7);

$tracker->doTrackEcommerceOrder(
   $order['orderId'],
   $order['revenueTotal'],
   $order['revenueSubtotal'],
   $order['revenueTax'],
   $order['revenueShipping'],
   $order['revenueDiscount']
);

// Important: before logging the tracking URL we make sure to remove the secret token_auth:
$debug_TrackingUrlWithoutToken = str_replace('token_auth=' . $TOKEN_AUTH, 'token_auth=XYZANONYMIZED', PiwikTracker::$DEBUG_LAST_REQUESTED_URL);

$this->log(sprintf('Tracked ecommerce order %s: URL was %s', $order['orderId'], $debug_TrackingUrlWithoutToken));

// we set the flag that this order has been tracked in Matomo
$order->setMetadata(`matomoTracked`, 1);

// we also record in the order a flag with the Tracking URL which was used to track the request
$order->setMetadata(`matomoTrackingUrl`, $debug_TrackingUrlWithoutToken);

B. When there is a new cart update

When an item is added or removed from the cart (or a quantity of any item changes), or when a product or service is selected for purchase (when there may not be an actual cart), or when a special coupon is applied or removed, then your Ecommerce platform will need to do the following:

1. Record the ecommerce cart update and all products to Matomo by sending an HTTPS Tracking API request

The Tracking API request will contain:

  1. the list of products in the order (and for each: SKU, name, a list of categories, price, quantity)

  2. your secret token_auth

When using our PHP SDK, the code will look like this:

// it is assumed that $cart is an array/object which contains
// this cart's product details

$tracker = new Tracker($matomoIdSite, $matomoUrl);

// Specify an API token_auth with at least Write permission, so the Visitor IP address can be recorded
$tracker->setTokenAuth('my_token_auth_value_here');

foreach ($cart->getProducts() as $product) {
   $sku = $product['sku'];
   $price = $product['total'];
   $title = $product['title'];
   $categories = $product['categories']; // an array of category names
   $quantity = $product['qty'];

   try {
       $tracker->addEcommerceItem($sku, $title, $categories, $price, $quantity);
   } catch (\Exception $e) {
       $this->log(sprintf('Failed to add ecommerce item: %s', $title));
   }
}

$tracker->setRequestTimeout($CART_UPDATE_REQUEST_TIMEOUT_IN_SECONDS = 2);

$tracker->doTrackEcommerceCartUpdate($cart['revenueTotal']);

// Important: before logging the tracking URL we make sure to remove the secret token_auth:
$debug_TrackingUrlWithoutToken = str_replace('token_auth=' . $TOKEN_AUTH, 'token_auth=XYZANONYMIZED', PiwikTracker::$DEBUG_LAST_REQUESTED_URL);

$this->log(sprintf('Tracked ecommerce cart update: URL was %s', $debug_TrackingUrlWithoutToken));

Congratulations, if you followed all the steps listed above then you should have successfully setup server-side Ecommerce tracking.

Reminder: If you don’t need 100% data accuracy for eCommerce tracking, and you don’t need to track offline sales & conversions, then you can simply follow our JavaScript Ecommerce Tracking Guide.

Limitations

  • Tracking ecommerce cart updates on the server-side may incur a small delay which may slightly slow down the subsequent page view. The Matomo (Piwik) Tracking API should respond in less than 500ms so this should not impact users experience much. In our code example above we set a maximum delay of up to 2 seconds for tracking cart updates, and up to 7 seconds for tracking ecommerce orders. It would be possible to workaround this by sending an asynchronous HTTP request instead of a synchronous one.

  • Refunds and partial refunds are not fully supported in Matomo yet.

  • Known issue: this solution may create (once) a new visitor as soon as a cart is updated. If this is the case, all following pageviews and actions will be tracked into the newly created visitor. This happens for example when a user visited your shop in the past, deletes all the cookies and then visits your shop again. It may also happen when opening the website in an « Incognito window » in your browser.

Previous FAQ: Anonymise Ecommerce order IDs