Enhanced ECommerce Duplicate Transactions in Tag Manager

Posted on June 9, 2016 in

I’ve had real difficulty for months now with Enhanced ECommerce in Google Analytics recording a transaction more than once. I have tried different solutions found across the web, but still didn’t solve my problem.

The Cause

After watching almost 100 different purchases that recorded duplicates transactions I found one constant. This was always on a mobile phone and the second session that recorded the duplicate transaction was a single page view on the order receipt/thank you page. What I interpret this to mean is people place their order on their phone, and after getting their receipt, leave that tab open and continue on with their regular activity.

So if they go and reopen that tab again the tag had all the event data cached and would just re-push it again. I am making this assumption because my first attempts were to not return the purchase event data from the server on page load. In WordPress’s WooCommerce it would like like this:

add_action('woocommerce_thankyou', 'purchaseMade');

/**
 * Rendering thank you page after purchase.
 */
public function purchaseMade( $orderId )
{
    //Check to see if we've tracked this order before
    $orderTracked = get_post_meta($orderId, 'gtmOrderTracked', true);

    //Don't do any of this conversion stuff if something returned. We've done this before
    if ($orderTracked) {
      return;
    }

    // Lets grab the order
    $order = new WC_Order( $orderId );

    //Add the order ID as a custom field so we don't push conversion data that's already been logged
    update_post_meta($orderId, 'gtmOrderTracked', $orderId);

    //execute all code down here you want to happen once
    //for instance, this is where I used to echo the purchase event

The action above is only fired when loading the thank you page when a transaction is complete. This method uses a very simple step to check to see if we’ve loaded this page before for this order. If we have, just exit the method so we don’t execute code that was done the first time. After a lot of debugging, I found this was working as expected, but I was still getting duplicate orders.

My only explanation at this point was mobile browsers were aggressively caching pages to improve the user’s experience on a site. That would cause all the event data embedded in the page to get cached as well, so Tag Manager picked up on it again and push the event as expected. It feels like I made countless attempts to try and resolve this problem from trying to instruct the browser to never cache this page, using Blocking Triggers on the purchase event tag in Tag Manager, or even trying to use eventCallbacks after the push to remove the purchase event from the dataLayer object. This is the only thing that has worked to date for me.

The Solution

add_action('woocommerce_thankyou', 'purchaseMade');

/**
 * Rendering thank you page after purchase.
 */
public function purchaseMade( $orderId )
{
    //Check to see if we've tracked this order before
    $orderTracked = get_post_meta($orderId, 'gtmOrderTracked', true);

    //transaction ID needed on front end to prevent purchase event multiple times
    wp_localize_script('theme_js', 'transactionData', array('oid' => $orderId));

    //Don't do any of this conversion stuff if something returned. We've done this before
    if ($orderTracked) {
      return;
    }

The wp_localize_script line will push the order ID to the front end and make it available from JavaScript.

In the article I linked earlier from LunaMetrics, his code is creating a cookie using the transaction ID as the cookie name. While I couldn’t get his to ever work consistently on mobile devices, that’s a key piece of this solution. So the first time a customer places an order, a cookie is created with the order ID as part of the name. The following code is executed every time the order receipt page is loaded.

function writeOrderCookie(transactionId) {
	
  var currentTime = Math.round(new Date().getTime() / 1000);

  //Set expiration time for new cookie
  var d = new Date();
  d.setTime(d.getTime()+(365*24*60*60*1000));
  var expires = "expires="+d.toGMTString();

  document.cookie = "TID_" + transactionId +"=" + currentTime + "; " + expires;

}

function checkOrderCookie(transactionId) {
    var cookievalue = "test";
    var cname = "";
    cname =  "TID_" + transactionId + "=";
    var myCookies = document.cookie.split(';');
    
    //Checks for existing Cookie   
   for(var x = 0; x < myCookies.length; x++) {  

      var ck = myCookies[x].trim().toString();

      if (ck.indexOf(cname) === 0) {

          return ck.substring(cname.length).toString();
      }
    }
}

jQuery(document).ready(function() {

    //order received object available
    if (typeof ee_order !== 'undefined' ) {

      if (transactionData) {

        var orderId = transactionData.oid;
        var hasCookie = checkOrderCookie(orderId);

        if (typeof hasCookie === 'undefined') {
          dataLayer.push(ee_order); 
          writeOrderCookie(orderId);
        }
      }
    }
});

This code checks to see if the purchase event for Enhanced ECommerce is available. Then it checks to see if we have the transaction ID returned from the server. It takes that ID to check and see if there is a cookie with the same order ID. If that cookie doesn’t exist, push the purchase event. I have been using this for about a month and duplicate transactions have stopped. If that changes, I will post updates here.

A Bug?

I am not really sure what is at fault here, or if there is anything really at fault. Maybe just an oversight. But I was following Google’s instructions on using Enhanced ECommerce, but still came across this issue. It just seems to be a combination of user, tag, and mobile browser behavior. If anyone else is experience this issue on mobile devices please let me know. If this is enough of an issue hopefully we can get built in mechanisms in Tag Manager to account for this.

By Nathan Byloff

Nathan is the CTO for RankHammer. His area of expertise is technical SEO and everything to do with data - collection, analysis, etc. He is driven by automating any reporting task that has to be done more than once.