Advanced GA4 Ecommerce Tracking
GA4 ecommerce tracking from first event to refund handling. The standard schema, item-array parameters, currency requirements, multi-step funnels, and the subscription-renewal patterns most setups miss.
The GA4 ecommerce schema in one place
GA4 ecommerce expects a specific event taxonomy and a specific item-array structure. Setups that match the schema get reporting for free. Setups that don't get partial reporting, missing revenue, and item-level data that never surfaces in standard reports. Follow the schema. Customize on top of it, not against it.
The seven core events
| Event | When it fires | Required params |
|---|---|---|
| view_item_list | Category, collection, or search results page loads | item_list_id, item_list_name, items[] |
| select_item | User clicks an item from a list | item_list_id, items[] |
| view_item | Product detail page loads | currency, value, items[] |
| add_to_cart | User adds item to cart | currency, value, items[] |
| begin_checkout | User initiates checkout | currency, value, items[] |
| add_shipping_info / add_payment_info | User fills shipping or payment | currency, value, items[], shipping_tier or payment_type |
| purchase | Order completes | transaction_id, currency, value, items[], optionally tax, shipping |
Plus refund for refunded orders (pass the same transaction_id with negative value or the refunded subset of items).
The items array
Every ecommerce event accepts an items array. Each item is an object with these keys (use as many as apply):
- item_id — your SKU or variant ID. Stable identifier.
- item_name — display name of the product.
- item_brand — brand if multi-brand store.
- item_category, item_category2, item_category3, item_category4, item_category5 — hierarchical categories.
- item_variant — size, color, configuration.
- price — unit price.
- quantity — number of units.
- discount — per-item discount.
- coupon — coupon code applied to the item.
- affiliation — store or sub-brand for multi-source orders.
- item_list_id, item_list_name — for tracking which list the item came from.
- index — position in the list.
- location_id — physical store ID for omnichannel.
Currency: the parameter most setups skip
GA4 requires currency on every ecommerce event in ISO 4217 format (USD, EUR, GBP, JPY). Without it, GA4 logs the event but does not include revenue in reports. For multi-currency stores, pass the order's actual currency; GA4 handles conversion to your property's reporting currency automatically.
Implementation through GTM
- Push to dataLayer on every ecommerce event. Use the standard schema.
window.dataLayer.push({ event: 'view_item', ecommerce: { currency: 'USD', value: 49.99, items: [{ item_id: 'SKU-123', ... }] } }); - Set up a GTM dataLayer variable for the ecommerce object.
- Create a GA4 Event tag for each event type. Event name matches the dataLayer event. Parameters reference the dataLayer variable.
- Trigger on Custom Event matching the event name.
- Test in GTM Preview and GA4 DebugView. Confirm the event arrives in GA4 with all parameters and the items array intact.
Refund handling
Two approaches.
- Full refund: fire
refundwith the same transaction_id and value as the original purchase. Optionally include items[] for clarity. - Partial refund: fire
refundwith the subset of items being refunded, plus the partial value.
Refunds fire from the server when the refund is processed in your subscription processor, payment processor, or CRM. Send via Measurement Protocol (server-side) since they do not happen in a browser.
Subscription renewal handling
Renewals happen server-side and never fire a client event. The pattern:
- Subscription processor (Recharge, Bold, Skio, Loop, Stripe Billing) fires a webhook on successful renewal.
- Your server forwards the renewal to GA4 via Measurement Protocol with event_name "purchase," the renewal transaction_id, the renewal value, items, and the original customer's client_id (passed through from the initial signup).
- GA4 attributes the renewal correctly.
Common ecommerce tracking failure modes
Currency missing
Most common cause of "GA4 shows the event but no revenue." Add currency to every ecommerce event in the dataLayer.
Items array as an object, not an array
GA4 requires items to be an array, even for single-item events. Single item: items: [{...}], not items: {...}. The latter silently fails.
item_id and item_name inconsistent across events
If view_item reports item_id "SKU-123" but add_to_cart reports item_id "PROD-123" for the same SKU, GA4 cannot join the funnel. Use the same item_id everywhere.
transaction_id not unique
GA4 deduplicates purchases by transaction_id. If the same order ID fires twice, GA4 counts it once but logs an error. Use unique order IDs.
Subscription renewals not tracked
The most-missed pattern. Without webhook-driven server-side firing, GA4 reports look like every customer made one purchase and never bought again. LTV reporting becomes useless.
What to read next
Sister pages: GA4 best practices, GA4 enhanced conversions, GA4 BigQuery export. For implementation infrastructure, read advanced GTM setup. For Shopify-specific patterns, read Shopify GA4 + GTM tracking.