In this tip, we’ll take a look at how to leverage a custom first-party cookie to prevent repeat hits of any kind. This is most useful for transactions, since a common problem with Google Analytics (traditional) eCommerce tracking is that a transaction hit is sent again upon subsequent entries to the receipt page, for example using the Back button of the browser. In some cases, and this is not a good practice, a receipt e-mail is sent to the user with a link back to the receipt page, where the transaction is sent over and over again upon entry.

Here is the use case:

  1. User lands on the receipt page

  2. The transaction tag checks if the transaction ID is in the transaction cookie

  3. If the ID is in the cookie, the transaction tag does not fire

  4. If the ID is not in the cookie, the transaction tag fires

  5. The transaction ID is added to the cookie in the hitCallback of the tag

So it’s very similar to what I wrote some time ago about firing a tag just once per session.

For this to work, you’ll need four variables:

  1. Data Layer Variable for the key transactionId in the transaction payload on the receipt page

  2. 1st Party Cookie Variable, which retrieves the value of the cookie where all the user’s transaction IDs are stored

  3. Custom JavaScript Variable, which returns true if the transaction ID in the receipt page is found in the cookie

  4. Custom JavaScript Variable, which returns the callback function, where the current transaction ID is added to the cookie after the transaction tag has fired

Data Layer Variable: {{dlv transaction id}}

This one is easy. Just create a new Data Layer Variable called {{dlv transaction id}}, and have it point to the variable where the transaction ID is stored on the receipt page (should be transactionId).

Create a new 1st Party Cookie variable called {{cookie user transactions}}, and have it refer to whatever you want your cookie to be called. In this example, I’ll use user_transaction_ids.

The purpose of this variable is to return true if the transaction ID on the thank you page is found in the cookie. This means that one of the conditions of the Transaction Tag firing trigger is a check for if the cookie value is not true. This would mean that the transaction ID has not been fired yet by this user. The code looks like this:

function() {
  return /(,|^){{dlv transaction id}}/.test({{cookie user transactions}});

In this Custom JavaScript variable, we’ll add the value returned by {{transactionId}} to the cookie that’s referenced in the variable we just created above. Since I’m assuming the visitor can have multiple transactions, the cookie will be appended with the value, not overwritten. The expiration date depends on the lifetime of a single transaction ID. In my case, I’m assuming that they’re always unique, so I have an expiration time of two years (completely arbitrary figure). If you know that transaction IDs are not unique, you should make the expiration shorter to reflect this.

Here’s what the {{js hitcallback set transaction cookie}} should have within:

function() {
  return function() {
    var d, expires;
    var cvalue = '';
    // Run the code only if a transaction is found in the data layer
    if ({{dlv transaction id}}) {
      d = new Date();
      d.setTime(d.getTime() + (2*365*24*60*60*1000));
      expires = 'expires='+d.toUTCString();
      // If the cookie already exists, append not overwrite
      if ({{cookie user transactions}}) {
        cvalue = {{cookie user transactions}} + ',';
      document.cookie = 'user_transaction_ids=' + 
            cvalue + {{dlv transaction id}} + '; ' + 
            expires + '; path=/';

Set up the tag

Finally, you need to set up the tag. The only modifications you need to make are to the firing trigger and the hitCallback field. For the firing trigger, you need to add the following condition:

{{js is transaction id in cookie}} does not equal true

This means that the tag will only fire if the transaction ID on the page template is not found in the cookie. Makes sense, right?

Next, you need to set up the hitCallback field. So scroll down to More Settings -> Fields To Set, and fill in the values like this:

And that should do it.


Here’s what should take place when a user lands on the receipt page:

  1. The Transaction Tag checks if the transaction ID on the page template (stored in {{dlv transaction id}}) can be found in the value returned by the cookie variable {{cookie user transactions}}.

  2. If the ID exists in the cookie, it means that a transaction has already been fired with this ID, so nothing is done.

  3. If the ID doesn’t exist, the transaction tag fires.

  4. After the tag has completed execution, the hitCallback function, {{js hitcallback set transaction cookie}} is invoked.

  5. This function checks if the cookie already exists. If it does, it appends the current transaction ID to the end of the cookie (using a comma as the separator). If the cookie doesn’t exist, a new cookie is created with the transaction ID as its only value.

  6. This way any repeat visits to the page with the same transaction ID in the template will not cause the tag to fire again.

It looks like a complicated way to do things, and on some level it probably is. However, GTM and GA are stateless in the user’s browser. The only things they persist are the tracking cookies. Everything else has to be done by the user, which is why we need to manually create the cookies and the logic behind them.

UPDATE: David Vallejo has written a great guide on how to achieve the same in Enhanced Ecommerce. Check it out!