Duplicate Transactions in Google Analytics – The Check and the Fix
By far the most common issue I’ve come across with ecommerce sites; duplicate transactions can inflate revenue and ecommerce metrics, altering your attribution reports and making you question your data integrity.
When talking about where to put the ecommerce tracking code, Google suggests the following for Universal Analytics:
… If successful, the server redirects the user to a “Thank You” or receipt page with transaction details and a receipt of the purchase. You can use the analytics.js library to send the ecommerce data from the “Thank You” page to Google Analytics.”
The missing step here is to ensure that either A) the user cannot access the page more than once or B) you have logic in place to make sure the transaction is only sent once. The biggest issues I’ve seen are when this receipt page is automatically emailed to the customer, with the ability for them to return as frequently as they please, each time sending a duplicate transaction.
Many people incorrectly assume this is something that is handled through Google Analytics processing, or that if it does occur, it is a bug. In reality, it is simply an implementation issue and one that is often overlooked.
Within a session, Google Analytics will filter out duplicate transactions provided they have the same information. But if a visitor comes back later that day, or two weeks later, and another transaction is sent, then these will show up in your reports.
Check Your Data
How do we check if this issue exists in your ecommerce data? There’s a fairly simple Custom Report you can create to check for this. I’ve created a template which, if you have the appropriate permissions, you can attempt to import via this link or you search the Solutions Gallery for “Duplicate Transactions.” If you cannot import the Custom Report for some reason, simply create the report yourself with this setup (jpg).
Adjust your date range to at least a month. If you have Transaction IDs that have multiple transactions, then you’re either A) sending in duplicate transactions or B) reusing transaction IDs, both of which should be corrected.
When Do Duplicate Transactions Occur?
The following scenarios are the most likely culprits for sending in the duplicate information:
- Returning to the page via emailed link or bookmark
- Refreshing the page
- Navigating to a different page, and returning via back button
- Page restoring from a closed browser session or on a smartphone
As you implement your solution, try checking each of these scenarios to make sure you’re completely covered!
Server-Side Is Better
There are several schools of thought around fixing duplicate transactions. My background is more on client-side implementations, specifically with Google Tag Manager. However, in general, if you have the resources and time to spend, I would recommend handling this issue server-side.
Without going into specifics, I would add in some sort of server-side logic to ensure that the ecommerce analytics code is only delivered once to the page. This could be using a database to record and check to see if the ecommerce info has already been sent.
It could also be some sort of server-side variable that is similarly checked. Another option I’ve seen is to redirect the user away from the receipt page after the ecommerce info has been sent to Google Analytics, then preventing the user from returning to that page.
Sometimes a page refresh doesn’t require fully reloading the page from the server, however, so make sure to test all of the above scenarios.
A Two-Pronged Approach
Not all of us have access to the server though, and sometimes we just need a solution. My tactic for dealing with duplicate transactions uses two different methods to attempt to determine if the transaction has already been sent.
- A browser cookie records the transaction ID
- A timestamp on the transaction serves as a backup
Cookies by themselves can filter out most duplicate transactions, but can be less than 100% effective due to privacy settings and user preferences. Someone can clear their cookies, browse in incognito mode, or pull up the same receipt on two different devices.
For that purpose, I also use a timestamp to help determine how old the transaction is. This timestamp should come from the page immediately before the receipt page, so very little time should pass. We can set this to be 15 or 30 minutes to be safe, just in case there’s some kind of validation check or third party system before they hit the receipt.
Here is the general user flow that we’ll follow. We’ll check to see if a cookie with this transaction ID exists. If it does, then we know it’s a repeat transaction, and we won’t send the ecommerce information to Google Analytics.
If there’s no cookie, we’ll check the timestamp. If there’s no timestamp, then we know it’s days or weeks old, from before the date we put our new process went into place, so we’ll label this as missing.
If there is a timestamp, how old is it? If it’s more than 30 minutes old, then we’ll assume it’s an old transaction and we’ll label this as expired.
Lastly, if there’s no cookie and it’s been less than 30 minutes, we’ll call this a new transaction. We’ll set a new cookie on this computer and then proceed with the checkout as normal.
Stopping Duplicate Transactions via Google Tag Manager
To get the full functionality of this solution, you will need access to update the site or have a developer you can call to help you get the timestamp into place.
We will need to create the following inside of Tag Manager:
- MACRO – Data Layer Variable – “transactionId”
- MACRO – Data Layer Variable – “timeStamp”
- RULE – “Receipt Page – Transaction Present”
- TAG – Custom HTML – “Duplicate Transaction Checking”
- MACRO – Data Layer Variable – “transactionType”
- RULE – “New Transactions Only”
- TAG – Google Analytics – “GA Ecommerce Transaction”
MACRO – Data Layer Variable – “transactionId”
This macro will simply return the transaction ID from the correctly formatted data layer on the receipt page.
MACRO – Data Layer Variable – “timeStamp”
This will be something we need to add to the site itself. We need to get the timestamp of the transaction, either from some server-side code, or by passing this value through the submit form. Either way, this piece does require you to update the site itself.
Then we can use a simple macro to pull out this value.
RULE – “Receipt Page – Transaction Present”
This rule checks to see if we’re on the receipt page and if there is a transaction present.
TAG – Custom HTML – “Duplicate Transaction Checking”
Here is where the magic happens. This Custom HTML will take care of all of the work, checking for cookies, setting cookies, and checking the timestamp. The result is then pushed to the data layer with a custom event.
MACRO – Data Layer Variable – “transactionType”
Now that the Tag has checked if the transaction is a duplicate, we’ll use this macro to pull out the result.
RULE – “New Transactions Only”
We’ll create a rule that uses the event and transaction type that gets pushed from the Duplicate Transaction Tag.
TAG – Google Analytics – “GA Ecommerce Transaction”
Finally, put it all together with a Google Analytics transaction Tag, with the Firing Rule set to “New Transactions Only.”
That’s all there is to it! It’s a little complicated, but when you break it down step by step, it should make sense logically. I would recommend setting up a test property to send these ecommerce transactions to until you’re sure that this is working properly, then, make the switch at a time when there are few people using the site.
Questions/comments? Did you have duplicate transactions on your site?
About Jon Meck
Jon Meck is our Technical Marketing Manager, promoting our services and trainings to the world. He has a jack-of-all-trades background, working for companies large and small in Social Media, Website Design and Maintenance, and Analytics. He is an Excel enthusiast, he loves efficiency, and he is strong proponent of the "Work Smarter, Not Harder" mantra. Outside of work, Jon enjoys late nights working on pet projects, running races, and spending time with his family.