Analytics

How to Track Your Checkout Funnel Properly in GA4

 ·   ·  ~14 min read

A five-column bar chart of the GA4 checkout funnel events in order: view_cart, begin_checkout, add_shipping_info, add_payment_info and purchase. Each column shows the share of shoppers reaching that event and the characteristic tracking failure at that step: view_cart fires on every cart drawer open, begin_checkout fires on click or on page load, add_shipping_info is drawn as an empty dashed bar because pre-selected shipping gives it no trigger, add_payment_info drops because express wallets skip it, and purchase shows GA4 counting 32 percent against 26 percent of real orders because confirmation-page reloads double count. Two figures below: five events fired once each in order, and the 1,000 denied events per day for seven days that consent-mode modelling requires before it fills any gap
The five-event GA4 checkout funnel and the tracking failure that most often breaks each step. A funnel with a step missing looks plausible and points at the wrong problem.
TL;DR - Key Takeaways
  • Five events, in order, once each. view_cart, begin_checkout, add_shipping_info, add_payment_info, purchase, each with currency, value and an items array. purchase also needs a unique transaction_id.
  • The middle of the funnel is where implementations fail. Most stores have purchase working because revenue is what everyone checks. add_shipping_info is the step most often missing entirely.
  • Express wallets distort everything. Apple Pay, Google Pay and Shop Pay skip shipping and payment entry, so their buyers vanish between begin_checkout and purchase. Segment them out before you diagnose a drop.
  • Clear the ecommerce object before every push. Without ecommerce: null, the previous event's items leak into the next one and the funnel becomes subtly wrong rather than obviously broken.
  • The direction of the GA4 vs processor gap tells you the cause. Higher means duplication. Lower means events are not arriving.
  • Consent mode removes part of the funnel. Below Google's published thresholds, denied EU and UK traffic is simply absent, not modelled.

A complete GA4 checkout funnel is five events fired in order: view_cart, begin_checkout, add_shipping_info, add_payment_info, purchase. Each carries a currency, a value, and an items array; purchase additionally needs a unique transaction_id. Implement those five correctly and you can see which step of your checkout is losing money. Implement four of them, or fire one at the wrong moment, and you get a funnel that looks plausible and points at the wrong problem.

That second outcome is the common one. Most stores have purchase working, because revenue is the thing everyone checks. The middle of the funnel, the part that would actually tell you where shoppers give up, is where implementations quietly fall apart, and the failures are specific and predictable. This guide covers the full event chain, what breaks at each step, and how to tell the difference between a checkout problem and a tracking problem.

A note on sourcing: the event schema and the consent thresholds below come from Google's own documentation, Shopify's own Web Pixels reference and Adobe Commerce's release notes, all listed in the Sources section. Coverage statistics that circulate about GA4 implementations ("only a quarter of stores track shipping info") could not be traced to a primary methodology, so they are not here.

5 events in a complete GA4 checkout funnel, fired once each and in order
1,000 denied events per day, for 7 days, before consent-mode modelling starts (Google)
200 maximum elements in an items array; each item can carry up to 27 custom parameters
2.4.8 Adobe Commerce release (April 2025) that fixed the configurable-product data layer

What is the GA4 checkout funnel?

GA4 replaced Universal Analytics' numbered checkout steps with a fixed set of recommended ecommerce events. There are no configurable step numbers any more. You build the funnel from named events, and the names are not negotiable if you want GA4's built-in reports to work.

The GA4 checkout event chain, from Google's ecommerce measurement reference
Event Fire when Required parameters Notable optional parameters
view_cart Shopper views the cart currency, value, items coupon
begin_checkout Shopper enters the checkout flow currency, value, items coupon
add_shipping_info Shopper selects a delivery option currency, value, items shipping_tier, coupon
add_payment_info Shopper submits payment details currency, value, items payment_type, coupon
purchase Order is completed transaction_id, currency, value, items tax, shipping, coupon
refund Order is refunded transaction_id, currency value, items, tax, shipping

Two rules from Google's documentation are worth stating plainly, because breaking them is the single most common cause of a funnel that looks empty.

Set currency at the event level whenever you send value. Without it, the revenue data has nowhere to go in standard reports.

Populate every parameter you have data for, even the optional ones. shipping_tier and payment_type cost nothing to add and are the difference between "people drop at the shipping step" and "people drop at the shipping step when the only option is a €12 courier". Each item can carry the prescribed fields plus up to 27 custom parameters, and the items array can hold up to 200 elements.


How do I set up begin_checkout in GA4?

If you are using Google Tag Manager, which you should be for anything beyond a trivial store, you push a structured object into the data layer and GTM reads it.

// Clear the previous ecommerce object first.
// Without this, parameters from the last push leak into this one.
dataLayer.push({ ecommerce: null });

dataLayer.push({
  event: "begin_checkout",
  ecommerce: {
    currency: "EUR",
    value: 89.97,          // sum of price x quantity across items
    coupon: "SPRING10",
    items: [
      {
        item_id: "SKU_12345",
        item_name: "Merino Crew Sweater",
        item_brand: "Example",
        item_category: "Knitwear",
        item_variant: "Charcoal / M",
        price: 29.99,
        quantity: 3,
        index: 0
      }
    ]
  }
});

Then in GTM: a Custom Event trigger matching begin_checkout, firing a GA4 Event tag with the event name begin_checkout and the ecommerce data loaded from the data layer.

The ecommerce: null push matters more than it looks. The data layer persists, so without clearing it, an items array or a coupon from an earlier event can attach itself to the next one. This produces funnels that are subtly wrong rather than obviously broken, which is the worst kind.

The other four events follow the same shape. add_shipping_info adds shipping_tier, add_payment_info adds payment_type, and purchase adds transaction_id, tax and shipping:

dataLayer.push({ ecommerce: null });

dataLayer.push({
  event: "purchase",
  ecommerce: {
    transaction_id: "ORD-100472",   // your real order number, never a timestamp
    currency: "EUR",
    value: 89.97,
    tax: 16.80,
    shipping: 4.95,
    coupon: "SPRING10",
    items: [ /* same shape as begin_checkout */ ]
  }
});

What breaks at each step?

This is the part no implementation guide covers, and it is where the actual work is. Each event in the chain has its own characteristic failure.

view_cart

The mildest problems live here, but two are common. Stores with a slide-out cart drawer often fire view_cart on every drawer open, including the one that happens automatically after adding an item, so view_cart outnumbers add_to_cart, which is nonsense. And stores that only fire it on a dedicated /cart page miss every shopper who goes straight from the drawer to checkout, making the first funnel step look narrower than reality.

Decide which interaction counts as viewing the cart, fire it once per interaction, and be consistent.

begin_checkout

The most misplaced event in ecommerce analytics. The question that decides everything: does it fire when the shopper clicks the checkout button, or when the checkout page loads? Those are different populations, and the gap between them (people who clicked but never arrived because the page was slow, errored, or redirected badly) is exactly the kind of loss you want visible. Fire on page load, or on entry to the checkout view in a single-page app, and you will never see it.

The second issue is re-firing. A shopper who leaves checkout to change a quantity and returns will trigger begin_checkout again. That is acceptable, since GA4 funnels count users rather than events, but it means raw event counts will overstate unique checkout entries. Read the funnel exploration, not the event count, when you want a completion rate.

The third is platform-dependent and genuinely nasty. On Shopify, the underlying checkout_started event behaves differently depending on your checkout: with Checkout Extensibility enabled it fires every time a customer enters checkout, but on non-extensible shops it only fires the first time. A store that migrated between the two will see a step change in its funnel that has nothing to do with shopper behaviour.

add_shipping_info

The step most often missing entirely, for a structural reason: on many checkouts there is no clean moment that means "the shopper chose a delivery option".

If shipping methods are pre-selected by default, the shopper may never actively choose anything, so there is no interaction to bind a trigger to. If shipping options render conditionally after an address is entered, the event can fire before the options exist. And on a one-page checkout there are no page transitions at all, so the natural trigger boundary that a multi-step flow gives you for free simply is not there. That loss of funnel granularity is one of the trade-offs we cover in our comparison of one-page and multi-step checkout.

The workable pattern is to fire it on the first meaningful commitment to a shipping choice (a radio-button change, or the blur event when the shopper moves from the shipping block to the payment block) and then guard it so it fires once per checkout session, not once per re-render. Framework-driven checkouts re-render constantly; an unguarded trigger will fire dozens of times.

If you cannot instrument this step honestly, leave it out rather than firing it on page load. A step that fires for 100% of people who reach it tells you nothing and quietly makes the next step look like the problem.

add_payment_info

Three distinct failure modes, and most stores have at least one.

Payment fields inside an iframe. Stripe Elements, Adyen components, hosted card fields from your payment service provider: your page cannot see keystrokes or submit events inside a cross-origin iframe. You need the payment SDK's own callback (change, complete, or the equivalent) to know the shopper filled it in.

Redirect gateways. If the shopper leaves for a hosted payment page, they may never return to a page of yours that could fire this event. The order still completes via webhook; the funnel step never does.

Express wallets. This is the one that sends teams chasing ghosts. A shopper paying with Apple Pay, Google Pay or Shop Pay skips shipping selection and payment entry as your checkout understands them, because the wallet supplies both. So add_shipping_info and add_payment_info never fire, and purchase does. If express wallets are a meaningful share of your orders, your funnel will show a large, alarming drop between begin_checkout and add_payment_info that represents your best-converting customers. Segment wallet checkouts out before you diagnose anything, or you will optimise a step that is not broken.

purchase

The event everyone has, and the one most likely to be double-counted.

The recurring causes are well documented across analytics practitioners: the confirmation page being reloaded or reached via the Back button, a GTM trigger without a once-per-page constraint, two tracking implementations running in parallel (a platform app plus a custom container, or a leftover tag from a previous migration), the payment gateway re-firing after a redirect, and browser retries on a slow connection. Server-side tagging adds another: the browser and the server each sending the same purchase.

GA4 does collapse purchase events arriving with an identical transaction_id close together, and that catches some of it. Do not treat it as a solution. Practitioners consistently report that the window is limited (a shopper who reloads the confirmation page the next day can still be counted twice), and it only works if transaction_id is actually present and actually stable. A blank ID, or one generated from a timestamp that changes on every page load, passes straight through. Use your real order number, fix the duplicate source, and keep transaction_id as the backstop rather than the mechanism.

Also: send a refund event with the matching transaction_id. Otherwise your GA4 revenue diverges from your books by the value of every return, permanently.


Why don't my GA4 numbers match my payment processor?

They never will exactly. The question is how far apart is acceptable, and which direction the gap runs, because the direction tells you the cause.

GA4 higher than the backend almost always means duplication. Work through the list above, and note that the shape of the discrepancy narrows it down: every order doubled points to two tags firing; some orders doubled and others clean points to confirmation-page reloads; a jump immediately after the payment redirect points to the gateway.

GA4 lower than the backend means events are not arriving. Ad blockers and tracking prevention account for a meaningful share. Redirect gateways where the shopper closes the tab before returning account for more. Orders placed by phone, in-store or through a marketplace never touch your site at all. And asynchronous payment methods (bank transfer, some buy-now-pay-later providers, invoice terms) confirm minutes or days after the session ended, so a browser-based tag cannot fire. The fix for that last category is the Measurement Protocol, firing purchase from your backend when the payment actually settles.

Reading the gap between GA4 purchases and real orders
What you see Likely cause What to do
GA4 higher, every order doubled Two implementations firing: a platform app plus a custom container, or a leftover tag from a migration Find and remove the second implementation. Check what is actually connected before adding anything
GA4 higher, some orders doubled Confirmation page reloaded or reached via Back; GTM trigger with no once-per-page constraint Constrain the trigger to once per page and fix the reload path. Keep transaction_id as the backstop, not the fix
GA4 higher, jump right after the payment redirect Gateway re-firing on return; browser and server-side tagging both sending the same purchase Send the purchase from one place, or deduplicate on the server by the real order number
GA4 lower, spread evenly across orders Ad blockers and browser tracking prevention Treat as a known, stable share. Server-side tagging recovers some of it
GA4 lower, concentrated on redirect payment methods Shopper closed the tab before returning from the hosted payment page Fire purchase from the backend via the Measurement Protocol when the webhook confirms
GA4 lower, orders confirmed hours or days later Bank transfer, invoice terms, some BNPL: the payment settles after the session ended Measurement Protocol on settlement, never a browser tag
GA4 lower, orders that never touched the site Phone, in-store and marketplace orders in the same backend Exclude them from the reconciliation before comparing anything

A small single-digit gap is normal. A gap that is large, or that changes suddenly, is a measurement bug, and you should find it before you trust any funnel built on top of it. Reconcile GA4 transactions against real orders for the same dates and the same timezone before you start, not after. The same check opens our diagnostic for a post-redesign conversion drop, for the same reason: a number you have not reconciled cannot tell you what moved.



What's different on Shopify and Magento?

Shopify. Since checkout extensibility, your theme's JavaScript does not run in checkout at all. Checkout events are only reachable through Shopify's Web Pixels system, as a fixed catalogue of standard events. Two of the ones you need, checkout_contact_info_submitted and checkout_shipping_info_submitted, are documented as available only where Checkout Extensibility is enabled. The built-in Google & YouTube channel covers the standard funnel with server-confirmed data and is the right answer for most stores; a custom pixel gives you more control but runs in a sandbox with real constraints, and merchants have long reported reliability problems when pulling from checkout.order. Whichever you use, check Settings, then Customer events, first to see what is actually connected. A large share of "my events aren't firing" cases are two implementations running at once. Our Shopify checkout optimisation service covers what each plan can and cannot instrument.

Magento 2 / Adobe Commerce. There is no complete GA4 data layer out of the box; most stores use a GTM extension that builds it. Two platform-level details are worth knowing. Adobe Commerce 2.4.8 (April 2025) fixed the GTM add-to-cart data layer for configurable products, so if your configurable-product data has looked wrong for years on an older patch level, the fix may be a core upgrade rather than anything in your container. The same release also allowed connections to region1.analytics.google.com, a Content Security Policy change that had been throwing errors for EU visitors. If you inherited a GTM container built against the old Enhanced Ecommerce format, assume it needs rebuilding rather than mapping: the schemas are genuinely different, not just renamed. Our Magento abandonment guide covers the silent JavaScript errors and slow totals calls that sit next to these in the same checkout.


How do I build the funnel report once events are firing?

In GA4, use Explore, then Funnel exploration, rather than the standard reports. Add your five events as open funnel steps in order, then do the three things most teams skip.

Segment by device. Mobile and desktop checkout completion rates are usually far enough apart that an aggregate number describes neither.

Segment by market. Payment method availability, shipping cost and consent rates all vary by country. A single blended funnel hides the one market that is broken.

Split out express-wallet checkouts. As above. Otherwise they permanently distort the middle of your funnel.

Then apply the same judgment you would apply to any measurement: a step that shows a 0% drop-off is broken, not perfect, and a step that shows 100% drop-off is almost always a missing event rather than a catastrophic UX failure.


A validation protocol you can run in an afternoon

  1. Enable GA4 DebugView and GTM Preview.
  2. Place a complete test order. Count every event and note its exact trigger moment.
  3. Confirm each event fires once, with currency, value and a populated items array.
  4. Reload the confirmation page. If a second purchase appears, you have found your duplication.
  5. Repeat on mobile, on a throttled connection.
  6. Repeat with an express wallet, and with each payment method that redirects.
  7. Repeat in each market you sell into. Payment and shipping options differ, and so do the triggers bound to them.
  8. Reconcile a full week of GA4 transactions against real orders, same dates, same timezone.
  9. Check your consent banner's actual rate, and whether you are on basic or advanced consent mode.

If steps 6 and 7 surface problems, that is the normal outcome, not a sign that something unusual is wrong. Those are the paths nobody tests. The analytics points in our 77-point checkout audit checklist are the same checks in list form, alongside the rest of the checkout.

Find out what your funnel is actually telling you

Every diagnosis in this article depends on one thing: whether your events describe reality. That is not something an external scan can check. It requires a test order, a login, and someone watching what fires. Our manual checkout audit does exactly that: an engineer places real orders through your real checkout on real devices, validates every event in the chain, reconciles your analytics against your payment processor, and tells you which of your funnel drop-offs are shopper behaviour and which are measurement bugs.

Book a Manual Checkout Audit →

Not there yet? Our free automated site audit covers everything that happens before checkout (clarity, trust signals, SEO, performance and security) and walks a guest checkout as far as it can go without placing an order. What it cannot see is which events fire along the way or how they compare to your orders, and the report says so. That boundary is the honest one.


Sources

The event schema, parameter limits, consent thresholds and platform behaviour above are drawn from the following primary sources.


Written by Checkout Experts

Checkout Experts is a checkout-only optimisation agency with 20 years of experience and 500+ projects across Shopify, WooCommerce, Magento, BigCommerce, and custom builds. Part of ZestIQ, est. 2006.

Event names, parameters and thresholds in this article follow the primary documentation listed above, which changes; check the current spec before implementing. Coverage statistics we could not trace to a methodology have been left out. Last reviewed October 2026.

Common Questions

GA4 Checkout Funnel FAQ

Quick answers on events, missing steps, duplicate purchases and consent mode. Want your own funnel validated? Book a manual checkout audit.

Push a begin_checkout event into the data layer with currency, value and an items array, preceded by an ecommerce: null push to clear the previous object. Then fire a GA4 Event tag from a Custom Event trigger matching that name. Fire it when the shopper clicks the checkout button rather than when the checkout page loads, so shoppers who click but never arrive remain visible in the funnel.
view_cart, begin_checkout, add_shipping_info, add_payment_info and purchase. Add refund so returns do not permanently inflate your reported revenue.
Usually because there is no clean trigger for it. Pre-selected shipping methods mean the shopper never actively chooses; conditionally rendered options mean the event fires before they exist; one-page checkouts have no page transitions to bind to. It also never fires for express-wallet checkouts, where the wallet supplies the shipping details.
Duplication. The most common causes are confirmation-page reloads, two tracking implementations running in parallel, a GTM trigger without a once-per-page constraint, and payment gateways re-firing after a redirect. GA4's deduplication by transaction_id catches some but not all of these, so fix the source rather than relying on it.
Not for most stores, but you do need the Measurement Protocol for purchases that confirm after the browser session ends (bank transfer, invoice terms, some BNPL), and it helps materially if redirect gateways or ad blockers are costing you a large share of events.
Yes, for EU and UK traffic. Advanced consent mode can model the gap, but only above Google's published volume thresholds; below them, the data is simply absent. Modelling is also applied differently in funnel explorations than in standard reports, so the two can legitimately disagree.
Get in Touch

Find Out What Your Funnel Is Actually Telling You

An engineer places real orders through your real checkout, validates every event in the chain and reconciles your analytics against your payment processor, so you know which drop-offs are shopper behaviour and which are measurement bugs.

Usually responds within 24 hours
I want to