← All articles

Blog

How to Verify Your GA4 Ecommerce Purchase Event Is Sent Correctly

ga4 purchase event not workingga4 ecommerce trackingga4 items parameterverify ga4 purchase

"I set up GA4 ecommerce, but the revenue in my Monetization report doesn't match my cart." This is one of the most common headaches in online-store measurement. Purchases are happening, yet the amount is missing, or not a single item was recorded. Most of the time the cause comes down to a flaw inside the purchase event sent at checkout.

Does any of this sound familiar?

  • The Monetization report shows purchases but zero (or blank) revenue (value)
  • "Ecommerce purchases" appears, but the items list is empty
  • Your cart dashboard and GA4 disagree on the revenue amount
  • The same order is counted twice, inflating revenue above reality

This guide organizes how to verify your GA4 purchase event is sent correctly, parameter by parameter. From a practical angle: where to look to isolate "sent vs. not sent."

First, the parameters a purchase event needs

The center of GA4 ecommerce measurement is the recommended purchase event, sent when a purchase completes. To record revenue and products correctly, these parameters must all be present.

Parameter Role Common flaw
transaction_id Uniquely identifies the order Missing → the same purchase is double-counted
value Total purchase amount Unset / sent as a string → revenue shows 0
currency Currency code (ISO 4217) Not JPY/USD, or missing → the amount isn't aggregated
items Array of purchased products Empty array or wrong schema → products aren't recorded

The key point: value and currency always go together. Without a currency, GA4 can't aggregate the amount as revenue. And items is an array where each product carries fields like item_id and item_name. If that array is empty or its structure is wrong, the purchase is still counted, but the products inside it disappear.

The items array gotcha (products not recorded)

When "purchases appear but products are empty," it's almost always an items problem. Three typical causes:

  1. Empty items array: purchase is sent, but items: []. The cart data is getting cut off somewhere in the handoff.
  2. Wrong schema: each product has neither item_id nor item_name. GA4 requires at least one of the two, so if both are missing the entry isn't treated as a product.
  3. Wrong field names: you're still sending old Universal Analytics keys like id or name. GA4 expects item_id / item_name.

Common pitfall: if you can see the purchase request fire in DevTools but no products are recorded, check the request body one item at a time — is items empty, and are the keys item_id / item_name? "The event is firing" does not mean "the products came through correctly."

The value / currency gotcha (revenue doesn't match)

Flaws around the amount quietly break your revenue reports.

  • value sent as a string: sending "3000" as a string can prevent GA4 from aggregating it as a number, so it never reaches revenue. Pass a number (3000).
  • currency missing or non-ISO: if currency is absent, or written as ¥ or yen, it won't aggregate. It must be the three-letter ISO 4217 code (JPY, USD, etc.).
  • Inconsistent tax/shipping handling: whether value includes tax and shipping must match between your cart and GA4, or it surfaces as an amount discrepancy. Either choice is fine — being consistent is what matters.

Common pitfall: if "purchase counts match but only revenue is off," suspect what you're including in value first (tax-in vs. tax-out, shipping or not). Most discrepancies live here.

The transaction_id gotcha (double-counting)

transaction_id is the parameter that uniquely identifies an order. If it's missing, or changes every time, GA4 treats the same purchase as separate ones and counts them more than once.

  • Every time the thank-you page is reloaded, purchase fires again — and without a transaction_id, each reload is counted as new revenue
  • Conversely, with a correct transaction_id, GA4 de-duplicates repeat purchases sharing the same ID

For a fuller breakdown of double-counting itself, see Why conversions get counted twice, and how to fix it. The basic rule: put a value that is unique per order and never changes — like the order number — into transaction_id.

dataLayer pitfalls (GTM implementations)

If you use Google Tag Manager to send the purchase event, the data flows through the dataLayer. Three issues here account for a large share of "purchase fires but data is wrong" tickets.

  1. Not clearing the ecommerce object before each push. GTM merges dataLayer pushes recursively. If a previous ecommerce event (e.g. add_to_cart) left product data in the ecommerce object, that stale data bleeds into your purchase push. The fix: always push { ecommerce: null } immediately before your purchase push.
  2. Trigger timing vs. dataLayer availability. If your GA4 Event tag fires on DOM Ready but the cart platform pushes the transaction data to the dataLayer slightly later, the tag fires with an empty ecommerce object. Check the order in GTM Preview mode: the purchase dataLayer push must appear before the tag fires.
  3. Using the wrong variable type. GTM has a built-in "Data Layer Variable" for ecommerce fields (e.g. ecommerce.transaction_id). If you accidentally create a custom JavaScript variable or hard-code the value, the mapping can silently break when the cart format changes.

Quick test: open GTM Preview, complete a test purchase, and look at the Variables tab for the purchase event. Every ecommerce field (transaction_id, value, currency, items) should be populated. If any is undefined, the dataLayer push is either missing it or firing after the tag. For broader GTM troubleshooting, see Why your GTM tag isn't tracking.

Payment gateway redirects and purchase-event loss

When checkout redirects the buyer to an external payment gateway (PayPal, Stripe Checkout, Klarna, etc.) and then back to your thank-you page, the purchase event is at high risk of never firing or losing its data.

  • Session break on return. The redirect creates a new navigation. If the browser treats the return as a new session (common with Safari ITP and ad-blocker heuristics), the thank-you page may load without the cart context needed to populate items or value. For more on Safari-specific data loss, see Safari ITP and conversion loss.
  • Attribution overwrite. Even when purchase fires, GA4 may attribute the conversion to the payment domain as a "referral" source instead of the original traffic source. Add the payment gateway domain to your cross-domain link list in the GA4 data stream settings to preserve the session. See Why cross-domain tracking breaks for the full setup.
  • Server-side fallback. For gateways that reliably send a server-side webhook (e.g. Stripe checkout.session.completed), consider firing the purchase event via the GA4 Measurement Protocol on your server instead of relying on the client-side script. This avoids the redirect problem entirely. For the server-side approach, see Server-side GTM verification.

The ecommerce event sequence matters

GA4 ecommerce events follow a funnel: view_itemadd_to_cartbegin_checkoutpurchase. While purchase can fire independently, breaking an upstream event can mask problems that eventually surface in purchase data.

  • If add_to_cart never populates the dataLayer ecommerce.items correctly, the same malformed items array is likely carried through to purchase.
  • Some cart platforms build the items array progressively across funnel steps. If begin_checkout is skipped or fires with an error, the array handed to purchase may be incomplete.
  • Verifying the full funnel — not just the final purchase — catches schema errors earlier and makes debugging faster.

Four ways to confirm it's being sent

You can confirm the purchase event and its contents are sent correctly, in this order:

  1. GA4 DebugView: enable debug mode, run a real (or test) purchase, and in "Admin → DebugView" watch for the purchase event and whether value, currency, and items are present. The key is to expand items and check inside.
  2. DevTools Network: in your browser's DevTools, filter for collect and confirm a request to …/g/collect fires when the thank-you page loads. In the body, inspect en=purchase, transaction_id, value, currency, and items.
  3. GA4 Monetization report: in "Reports → Monetization → Ecommerce purchases," confirm per-product quantity and revenue are accumulating (standard reports have a processing delay).
  4. Check duplicates by transaction_id: in an exploration report, verify the same order isn't counted twice by looking for duplicates at the transaction_id level.

To confirm whether the GA4 tag itself is firing at all, read How to verify your GA4 tag is working first — it makes isolation faster.

What checking on your own machine can't tell you

DebugView and DevTools are very useful, but running one test purchase on your own machine has two structural limits.

  1. Your environment isn't your visitors' environment. Login state, extensions, office-IP exclusion, and ad blockers mean purchase can fire correctly on your machine yet be missing under real buyer conditions. Especially in flows that pass through a payment gateway or redirects, the session can drop before returning to the thank-you page, leaving items empty.
  2. Revenue involving an ad click is hard to verify. To properly see "does an ad-driven purchase really get recorded as revenue," you'd need to click a live ad to recreate the path — which carries the risk of an ad-policy violation as a self-click.

Judging "it showed up in DebugView, so it's fine" tends to miss the problem of value or items going missing on the real checkout flow your buyers take. For the mindset of going past firing to whether it's actually recorded, see Why it fires but isn't recorded.

Verification checklist

  • Does the purchase event fire on the thank-you (purchase-complete) page?
  • Is value a number (not a string)?
  • Is currency an ISO 4217 code (JPY / USD, etc.)?
  • Is the items array non-empty, with item_id / item_name on each product?
  • Is transaction_id present and unique per order (to prevent double-counting)?
  • Is whether tax/shipping is included in value aligned with the cart side?
  • Is the same order not counted twice (check at the transaction_id level)?
  • Is it also not missing under real user conditions in production (don't judge from one run on your machine)?

Frequently asked questions

Q. Purchases appear, but revenue (value) is zero. Why? A. Typically value is unset or sent as a string, or currency is missing/non-ISO so it can't aggregate. Check the value and currency contents in the …/g/collect request in DevTools Network.

Q. Why are only the items not recorded? A. Most likely the items array is empty, or each product lacks item_id / item_name. Also check you aren't still sending the old UA id / name keys.

Q. Why is the same purchase counted twice? A. Typically transaction_id is missing, or it changes on every reload. Put a unique, unchanging value like the order number in it and GA4 will de-duplicate. See the duplicate-tracking article for details.

Q. Should tax and shipping be included in value? A. GA4 has no hard rule, but the most important thing is to align the cart's revenue definition with GA4's value. A mismatch always surfaces as an amount discrepancy.

Q. If I can see purchase in DebugView, am I safe? A. That confirms only that it was sent. You're safe only once you've confirmed it accumulates correctly in the standard Monetization report — and that it isn't missing on your buyers' real checkout flow in production.

Conclusion: don't stop at firing — check the contents and production recording

GA4 ecommerce purchase measurement only works correctly once the purchase event has transaction_id, value, currency, and items all in place. DebugView and DevTools confirm it's "being sent," but what matters is what comes after — whether the amount and products are recorded without loss, on the real checkout flow your buyers take, in production.

ConversionOK runs your live page in an independent, isolated browser and intercepts the purchase event actually sent (including value, currency, and items), then verifies it. No need to click your own ad, and no interference from your office environment. Start with a free static check to confirm the entry point of your ecommerce measurement.