The Problem: GA4 Purchase Events Disappear Silently
Your GA4 purchase event fires perfectly in GTM Preview mode. You see it in the debug panel with all parameters populated. You publish the container. Two weeks later, someone checks the Monetization → E-commerce purchases report and finds 40% fewer transactions than Shopify or your payment processor reports.
GA4 purchase events fail silently because GA4 does not validate the data you send. It accepts malformed events without error. It drops events that hit rate limits without warning. It records events with missing required parameters but excludes them from commerce reports. The event exists in the raw data stream but never appears in the reports you actually look at.
Seven Silent Purchase Event Failures
1. Missing or Malformed Currency Code
GA4 requires the currency parameter on every purchase event. It must be a valid ISO 4217 code: USD, INR, EUR. If you send $, Rs, usd (lowercase), or an empty string, GA4 accepts the event but excludes it from all monetary reports. The event appears in DebugView. It never appears in Revenue metrics.
// WRONG - will be excluded from revenue reports
dataLayer.push({
event: 'purchase',
ecommerce: {
currency: 'Rs', // Not a valid ISO 4217 code
value: 4999
}
});
// CORRECT
dataLayer.push({
event: 'purchase',
ecommerce: {
currency: 'INR',
value: 4999
}
});
2. Empty Items Array
The items array is required for purchase events to appear in e-commerce reports. If the array is empty, null, or missing, the purchase event fires successfully but does not populate product-level reports. Common cause: the data layer push happens before the cart data is available, so items resolves to an empty array.
3. Duplicate Transaction IDs
GA4 deduplicates purchase events by transaction_id. If a user refreshes the thank-you page, the purchase event fires again with the same transaction ID. GA4 drops the duplicate. This is correct behaviour. But if your transaction ID generation is broken — returning the same ID for different transactions, or returning undefined (which GA4 treats as a single shared ID) — GA4 silently deduplicates real, distinct transactions.
4. Consent Mode Blocking the Event
If analytics_storage is denied when the purchase event fires, GA4 sends a cookieless ping. This ping does not include the full event payload. Purchase data from these pings may not appear in e-commerce reports. Depending on your consent implementation timing, a meaningful percentage of purchases may fire in the denied state.
5. Data Layer Timing: Event Fires Before Data Is Ready
On platforms like Shopify, the order confirmation page loads asynchronously. The GTM trigger fires on page load. But the order data (transaction ID, value, items) is injected into the data layer via a script that loads after the page. Your tag reads the data layer, finds the values are not yet present, and sends a purchase event with value: undefined and transaction_id: undefined.
This is especially common on headless commerce platforms where the checkout is an iframe or a third-party hosted page. The data layer push timing is unpredictable.
6. Cross-Domain Checkout Breaks Client ID
If your checkout is on a different domain (e.g., checkout.shopify.com), the GA4 client ID does not carry over unless cross-domain tracking is configured. The purchase event fires with a new client ID. GA4 records the purchase but cannot attribute it to the original session. It appears in raw event data but attribution reports show it as a direct/none conversion.
7. GA4 Collection Rate Limits
GA4 has undocumented rate limits on event collection. During flash sales or high-traffic events, if your site sends more than approximately 500 events per second from a single client, GA4 begins sampling. Purchase events during these peak periods may be dropped entirely. You will never see an error — the events simply do not arrive.
How to Detect These Failures
Compare three data sources daily:
- Payment processor: Total transactions and revenue (ground truth)
- GA4 real-time + BigQuery export: Purchase events received by GA4
- Tag monitor: Purchase event fires observed in the browser
If the tag monitor shows 100 purchase fires but GA4 shows 80, the 20 missing events were rejected by GA4 (malformed data, rate limits). If the payment processor shows 120 transactions but the tag monitor shows 100, the 20 missing events never fired in the browser (consent block, page load failure, tag error).
This three-way comparison isolates the failure point. Without it, you are guessing.
The Revenue Cost in INR When Purchase Events Fail
A 20% purchase-event failure rate on a ₹6 crore/month Indian e-commerce business translates to ₹1.2 crore/month of untracked revenue. The first-order impact is cosmetic — reports look wrong. The second-order impact is where the actual money leaks. Meta’s Advantage+, Google’s Smart Bidding, and every optimisation algorithm you run trains on the purchase events it receives. When 20% of events are missing — especially from specific audiences (mobile users, consent-denied sessions, users on slower networks) — the algorithm starts under-bidding on those exact cohorts. Over a 90-day window, we have measured 14–22% ROAS degradation directly attributable to systematic purchase-event data gaps.
The CFO impact is sharper. If your finance team uses GA4 revenue numbers for monthly reporting and the actual payment-processor revenue is 20% higher, you are under-reporting to the board. When the gap is finally caught during an audit, the discrepancy has to be reconciled, historical campaigns re-attributed, and bonus calculations potentially revised. Three finance leaders we have worked with spent six weeks each unwinding a single quarter of GA4-payment-processor discrepancy once it was surfaced.
Case Study: A BFSI Marketplace with Shopify Checkout
A mid-size Indian BFSI (banking, financial services, insurance) marketplace selling subscription financial products had a persistent 34% gap between GA4 reported purchases and their internal order management system. The CTO had run Tag Assistant, GTM Preview, and a manual walkthrough twice in the preceding quarter. All three tools reported the purchase event was firing correctly.
The root cause was three simultaneous failures, any one of which would have been survivable individually: (1) currency was being set to "INR " with a trailing space from a string-concatenation bug in their thank-you page template, causing GA4 to silently reject the currency as non-ISO-compliant and exclude the event from monetary reports. (2) transaction_id was being generated client-side with Math.random(), producing collisions on approximately 2% of same-second transactions that GA4 deduplicated as repeats. (3) For users who completed checkout through the Razorpay iframe flow, the cross-domain redirect stripped the GA4 client ID, so the purchase was attributed to a new session with no context. Each failure alone contributed 8–15% of the gap. Together they accounted for the full 34%.
Remediation took 6 engineering hours: trim the currency string server-side, switch to UUID for transaction IDs, and configure cross-domain tracking with Razorpay’s iframe redirect URL in the GA4 referral exclusion list. Within 14 days, the GA4-OMS gap narrowed to 3.2%, which was approximately the expected ad-blocker and consent-denial floor. Recovered attribution confidence allowed finance to approve a 25% increase in performance marketing budget, knowing the numbers were trustworthy.
Step-by-Step Detection Playbook for Purchase Events
- Pull last 7 days of transactions from your payment processor (Razorpay, Stripe, PayU, CCAvenue). Note the count and total value.
- In GA4 → Reports → Monetization → Ecommerce purchases, note the purchase count and total revenue for the same window.
- Compute the gap: (payment processor − GA4) / payment processor. Any gap greater than 8% deserves investigation.
- Open GA4 DebugView. Trigger a test purchase on your staging environment. Confirm the event appears with all required parameters:
transaction_id,value,currency,items[]. - Inspect the network request in DevTools. Look for the
v=2parameter in the GA4 collect URL. Verifyen=purchase,epn.value,ep.currency, andep.transaction_id. - In BigQuery (if GA4 is exporting), run:
SELECT event_name, COUNT(*), COUNT(DISTINCT ecommerce.transaction_id) FROM project.dataset.events_* WHERE event_name = 'purchase'. If count of events differs from count of distinct transaction IDs, you have duplicates or collisions. - Test with 5 different browsers: Chrome, Safari, Firefox, Chrome Mobile, Samsung Internet. Record which platforms the event fires correctly on.
- Test with an ad blocker enabled (uBlock Origin). Confirm whether the event still fires (and whether GA4 receives it).
- Test with consent denied. The event should fire in cookieless mode (
gcs=G100), which GA4 still receives but logs differently. - If gap persists: export
/debug/eventsdata from GA4 and cross-reference with a sample of 50 transactions from the payment processor. For each missing GA4 event, capture the user’s user-agent and session context.
Common Mistakes Teams Make
Trusting GTM Preview Mode for Production Verification
Preview mode tests the tag in your browser, with your connection, without your customers’ conditions. A purchase event that fires in Preview can fail on 20% of production sessions because of race conditions, consent, or cross-domain issues that Preview cannot reproduce.
Using Client-Side Transaction ID Generation
Generating transaction IDs in JavaScript with Math.random() or Date.now() produces collisions at scale. Always use a server-generated UUID or your order management system’s unique order ID. Client-side generation is a data-quality landmine.
Not Sending Currency as ISO 4217
“Rs”, “₹”, “Rupees”, or INR with any whitespace all fail GA4’s currency validation. The event still arrives but is excluded from revenue reports. Always hardcode currency: 'INR' in your GTM configuration rather than pulling from a template variable that might have formatting issues.
Relying on Single-Domain Tracking for Multi-Domain Journeys
If users cross from www.example.com to checkout.example.com or to a third-party payment iframe, the GA4 client ID needs to be passed via the linker parameter. Without cross-domain configuration, every payment-return session is counted as a new direct/none acquisition.
Ignoring Rate Limits During Flash Sales
GA4 undocumented rate limits throttle at approximately 500 events/second per property during peak traffic. Flash sales, Diwali campaigns, and viral moments all risk silent sampling. Have a server-side measurement protocol backup configured to catch rate-limited events.
Not Reconciling Weekly
If you reconcile GA4 against the payment processor once a quarter, you will discover broken tracking 45 days into the breakage. Weekly automated reconciliation with a 5% tolerance alert catches issues within 7 days.
FAQ for Head of Growth
Why is Smart Bidding underperforming even though my CPA targets are conservative?
If your purchase event has missing value or incorrect currency, Smart Bidding trains on inflated or zeroed conversion values. Check that every purchase event has a non-zero value and a valid ISO 4217 currency code.
Should I use enhanced measurement or custom events for purchases?
Custom. Enhanced measurement’s automatic e-commerce tracking is too generic for most real checkout flows. A custom purchase event with a fully populated items[] array gives you parameter-level control and cleaner debugging.
How do I handle refunds and partial returns?
Send a refund event with the same transaction_id. GA4 will reconcile the revenue. Do not adjust historical values — GA4’s revenue calculation handles refunds natively when the event is sent correctly.
What if my payment processor sends webhook confirmations minutes after the user lands on the thank-you page?
Use the Measurement Protocol (server-side) in addition to the client-side event. Send the server-side event on webhook confirmation with the same transaction_id so GA4 deduplicates. This catches purchases where the user closed the browser before the thank-you page loaded.
Validation Framework for Purchase Event Integrity
Purchase event validation is a multi-layered check that must run continuously, not as a one-time audit. Layer 1: Event Arrival. Did the event reach GA4? Check via DebugView for test orders and via BigQuery export for production volume. Missing events appear as gaps in expected volume.
Layer 2: Parameter Completeness. Are all required parameters present? transaction_id, value, currency, items, tax, shipping. Items array must contain complete item_name, item_id, price, quantity for each line. Missing parameters degrade reporting and downstream feeds to Google Ads.
Layer 3: Value Accuracy. Does the event value match the actual order value? Reconcile daily against order management system (OMS). Gaps over 2% indicate a data mapping issue. Common culprits: tax included vs excluded, shipping included vs excluded, GST treatment mismatches for Indian businesses.
Layer 4: Deduplication. Is the transaction_id unique per order? Duplicate transaction_ids cause GA4 to double-count revenue or drop events entirely (GA4 is inconsistent on this behaviour). Duplicates often come from user-triggered page refreshes on the thank-you page; guard against this with session-scoped flag checks.
Layer 5: Attribution. Does the session have a valid traffic source? Events with source=direct/medium=(none) for 100% of traffic indicate referrer loss during the redirect chain. Investigate payment gateway configurations; many payment gateways in India (Razorpay, CCAvenue, PayU) require explicit referrer preservation settings.
Indian E-Commerce Specific Edge Cases
OTP Interruption: UPI and net-banking flows often include an OTP step that moves the user out of your site and back. Some gateways return the user to a different URL than the one initiating the payment. Purchase events fire on the returning URL only if GTM is configured on that URL. This is a common source of 15-30% missing purchase events in Indian e-commerce.
Multiple Currency Display: Sites showing INR to domestic users and USD to NRI users must ensure the currency parameter matches the actual charge currency, not the display currency. Currency mismatches corrupt Google Ads Smart Bidding by feeding inflated or deflated values.
Partial Payments: EMI and BNPL products charge a portion today and the rest over time. The purchase event should fire at commitment time (when the user completes the purchase intent), not at full payment time. Track the expected total value as the event value; track actual collected amount separately for financial reporting.
Cash on Delivery: COD orders have a cancellation rate of 15-25% in India. Fire the purchase event at order placement, then fire a refund event at cancellation. Do not wait for delivery confirmation to fire the purchase event; Google Ads Smart Bidding needs the feedback within the 30-day attribution window.
Bottom Line
Purchase event tracking is the most consequential tag in any e-commerce container. When it fails, it fails silently — the event “fires,” but GA4 does not count it, or counts it incorrectly. Three-way reconciliation between browser monitoring, GA4, and the payment processor is the only way to know. A 20% gap between payment processor and GA4 is not an analytics problem — it is a revenue-visibility crisis that compounds weekly through mis-optimised bidding.
TagDrishti monitors this automatically
Across every tag, every page, 24/7. Set it up in 5 minutes. No GTM dependency. No developer required.
Start 14-day free trial →