Short answer: your backend's PAID orders are the truth. GA4 is a browser-side observer with known blind spots. The real question is whether your gap is a normal one or a broken one - here's how to tell.
Most "GA4 is wrong" panics die here. Compare the same thing: GA4 transactions for a full month vs backend orders that were actually PAID in that month. Backend "all orders" usually includes cancellations, failed payments and abandoned financing applications that no analytics tool should count.
GA4 will always see fewer purchases than your backend. Consent rejections (in the EU, everyone who clicks "decline"), ad blockers, Safari's tracking prevention, and users who close the tab before the thank-you page loads. With a strict opt-in consent banner, a 15-30% gap can be fully legitimate - your baseline is roughly your consent rejection rate plus 5-10%.
Corollary: if GA4 shows MORE than your paid backend orders, that's never normal - hunt for duplicate events or test traffic.
PayPal, financing, bank redirects: buyers pay off-site and a share never return to the page where the purchase event fires. The loss concentrates in specific payment methods, which is exactly how you catch it.
A surprisingly common one: a server GTM container exists (Stape or self-hosted), but the web container's GA4 tag has no server_container_url - so every event still goes client-side only and inherits every browser blind spot. The server container sits at zero requests and nobody notices.
Two GA4 configs on one page, a purchase event firing on page refresh, or a tag on a too-broad trigger. This produces the scarier version: GA4 ABOVE backend.
Separate checkout domain, rebranded domain, or a landing domain that hands off to the store - without cross-domain configuration, GA4 restarts the session mid-journey and purchases detach from their sources (everything collapses into Direct or referral spam).
With Consent Mode, Google models conversions for visitors who declined. Modeled conversions appear in some reports and not others, so two GA4 screens can legitimately disagree with each other - before you even compare to the backend.
1. backend: paid orders only, one full month 2. ga4: transactions, same month 3. join on transaction_id -> list the missing orders 4. segment missing by payment method + device 5. the cluster tells you the mechanism; fix THAT, not "tracking" in general
The named list of missing orders is the deliverable that turns a vague "our numbers are off" into a scoped, fixable engineering task.
Want this done properly? I run exactly this reconciliation as a fixed-scope audit - every number verified against live APIs, every finding with severity, effort and a fix direction. Then I build the server-side pipeline that closes the gap.
work@rytisbalys.com