Blog

Why your purchase events are double-counting

Five causes, in order of frequency, plus the diagnostic question that narrows it down in under a minute.

A purchase event firing twice is the most common ecommerce reporting bug, and it almost always traces back to one of five causes. Walk through them in this order; the first one to match is usually the answer.

1. Page reload after order completion

The user lands on your order confirmation page, the GA4 tag fires, the user refreshes, the tag fires again. Symptoms: identical purchase events with the same transaction_id within seconds.

Fix: store the transaction_id in sessionStorage when the tag fires, and check it before firing. If it matches, skip. The check is two lines of code in a Custom Template.

2. Both client-side and server-side firing without dedupe

You migrated to server-side tagging but did not turn off the client-side tag. Both fire, neither has a dedup key, GA4 records both. Symptoms: every purchase appears twice in reports.

Fix: remove the client-side GA4 tag once the server-side one is verified. Or set up event_id deduplication, but for GA4 the cleaner answer is to just turn off the duplicate tag.

3. Multiple GA4 properties receiving the same event

You have a development property and a production property, and your tag config sends to both. Symptoms: purchase counts are roughly double on the production property.

Fix: check the GA4 measurement ID in the tag. It should be a single value matching the production property. If you genuinely need two destinations, use two separate tags rather than one tag with multiple destinations.

4. Browser back-button replay

User reaches confirmation page, navigates away, hits back. The browser caches the page and the tag re-executes. Symptoms: scattered duplicates with the same transaction_id appearing minutes or hours apart.

Fix: same as cause 1, but with a longer storage window. Use localStorage instead of sessionStorage to catch longer back-button gaps.

5. Webhook from your backend firing in addition to client-side

Your Shopify or Stripe webhook posts to your tagging server on order completion, and the client-side checkout page also fires. Symptoms: every purchase appears twice with slightly different timestamps.

Fix: pick one source of truth. The webhook is more reliable (no ad blocker can stop it) but lacks the user-agent and IP that client-side captures. The client-side has more matching keys but loses some events to ad blockers. For most ecommerce sites, webhook-only is the cleaner answer.

Diagnostic question

If you want to narrow this down in 30 seconds, look at any double-counted purchase in BigQuery and check whether the two records have the same user_pseudo_id and the same ga_session_id. Same both: it is cause 1, 2, or 3. Different session: cause 4. No matching session at all: cause 5.