GA4 revenue and your order table will never match exactly, and chasing perfect agreement is how a reconciliation exercise eats a week. The useful version of this work isn't "make the numbers equal" — it's "know exactly why they differ, and confirm none of the reasons are a bug."
1. Decide what "reconciled" means before you start
Pick a tolerance up front — most ecommerce accounts settle somewhere between 2–5% depending on traffic mix. Without a target, every session spent chasing the last half a percent feels equally urgent, and none of it is.
Write the tolerance down and share it before the reconciliation starts, not after someone has already spent a day explaining a 1.5% gap that was inside the agreed range the whole time.
2. Match the window exactly, including timezone
GA4 buckets sessions by the property's configured timezone; your order system almost certainly timestamps in UTC or the store's local time. A one-hour offset shuffles a full day's worth of late-night orders across the date boundary in one export and not the other, and it looks like a much bigger gap than it is.
3. Check what each side counts as "purchase"
GA4's purchase event fires on the confirmation page load. Your order table might record the order at payment authorization, at fulfilment, or at some other point in the pipeline. A few hours of lag between those two moments, multiplied across a day's orders, is a normal and boring source of disagreement.
4. Refunds and cancellations move one side and not the other
GA4's purchase event is fire-and-forget: once sent, it's never retracted. Your order table almost certainly reflects the current state, refunds included. If a meaningful share of orders get refunded, GA4 revenue will run structurally higher than your order table — not because either system is wrong, but because they're answering different questions.
5. Bot and QA traffic inflates GA4 with no order to match
Internal QA runs, uptime monitors, and unfiltered bot traffic can generate GA4 sessions and even events with no corresponding order. This shows up as GA4 having more of everything — sessions, page views, sometimes events — without a proportional gap in revenue, which is the tell that it's traffic quality, not a tracking bug.
Where to start
- Set a tolerance before comparing a single number
- Export both sides in the same timezone, for the same window
- Confirm what event/state each system calls "purchase"
- Check whether refunds are reflected in GA4, your order table, or both
- Filter known bot and internal traffic before comparing totals
Once the gap is inside your tolerance and every remaining point of difference has a named cause, the reconciliation is done — whether or not the two numbers are identical.