← All posts

Revenue that doesn't match the backend: mistakes in the GA4 purchase event

value as text, no currency and an empty transaction_id: three mistakes in the purchase event that make GA4 lose or duplicate revenue.

Step 1 of 5 This is the purchase event the site pushes to the dataLayer when an order is confirmed.
  1. This is the purchase event the site pushes to the dataLayer when an order is confirmed.
  2. Three fields are wrong: an empty transaction_id, value as text with a decimal comma and no currency.
  3. The result: revenue GA4 cannot add up, and purchases that can't be deduplicated or matched to the backend.
  4. The fix: a unique identifier, a numeric value and the currency in ISO 4217 format.
  5. Before every push, clear the ecommerce object and validate the schema. Then compare with the backend.

It is normal for GA4 revenue not to match the order system to the cent: some users decline measurement, ad blockers exist and some orders are cancelled later. But that gap is usually stable and moderate. When it is large, erratic or changes after a release, the most frequent cause is the purchase event itself.

What happens when GA4 revenue doesn’t match the backend

The site sends a purchase for every confirmed order, but GA4 can’t make good use of its data. The typical symptoms:

  • Revenue lower than expected, or purchases that show up with zero revenue.
  • Duplicate purchases when the user reloads the confirmation page.
  • No way to match GA4 orders with the backend.

Why the purchase event loses or duplicates revenue

The event arrives with a wrong schema. The three most common faults:

  • value as text. "49,90" is a string with a decimal comma. GA4 expects a number with a decimal point (49.90); a string with a comma isn’t read as the right amount.
  • No currency. Without an ISO 4217 currency code (EUR, USD…), GA4 can’t calculate revenue metrics from value.
  • Empty or repeated transaction_id. The order ID lets GA4 deduplicate purchases and lets you match them with the backend. If it is empty, every reload of the confirmation page can be logged as a new purchase.

There is a fourth, dataLayer-specific problem: if the ecommerce object isn’t cleared between events, products from a previous event can mix with the next one.

How to audit the purchase event in DebugView

  1. In DebugView, place a test order and open the purchase event. Check the type and format of value, that currency is there and the value of transaction_id.
  2. In the browser console, type dataLayer on the confirmation page and inspect the object being sent.
  3. In an exploration, build a table with Transaction ID, purchases and revenue. Empty IDs, (not set) or IDs with more than one purchase reveal the problem.

How to fix value, currency and transaction_id

dataLayer.push({ ecommerce: null }); // clear the previous object
dataLayer.push({
  event: "purchase",
  ecommerce: {
    transaction_id: "T-10482", // unique per order
    value: 49.90,              // a number, with a decimal point
    currency: "EUR",           // ISO 4217
    items: [ /* … */ ],
  },
});
  • Build value from the order’s numeric amount, not from the formatted text shown on screen.
  • Always send currency, even if the shop sells in a single currency.
  • Use the same order ID as the backend, so both systems can be matched.

Google’s documentation: Measure ecommerce.

How to check GA4 revenue against the backend

  • Repeat the test order and check the three fields in DebugView.
  • Reload the confirmation page: no second purchase with the same transaction_id should appear in the reports.
  • For a few days, compare GA4 orders and revenue with the backend by date. The gap should settle at a small, explainable percentage.