GA4 & Google Tag Manager

GA4 ecommerce tracking for Magento 2 on Luma and Hyvä: a complete dataLayer for Google Tag Manager or gtag.js, Google Consent Mode, server-side refunds and a GTM container you import in one step.

  • Magento 2.4.7 – 2.4.9
  • PHP 8.2 – 8.5
  • Version 1.2.2
$159

One-off payment, with 12 months of updates.

What is included

  • 12 months of new versions and fixes; the versions released in that time stay yours
  • Install with Composer, or download a zip from your account
  • Licence for one production domain, staging and development copies included
  • 30-day money-back guarantee, refund policy

No subscription or automatic renewal. Keep using the versions included in your update period. Update and licence details

Key features

  • Google Tag Manager or gtag.js
  • All GA4 ecommerce events with full item data
  • Luma and Hyvä, no compatibility module
See all features

Composer package softaware/module-ga4-gtm

GA4 & Google Tag Manager $159
  • Revenue you can trust

    Cart changes are detected on the server and every event carries an id, so AJAX adds, page reloads and merged carts are not counted twice. Purchase is flagged in the database and never sent again.

  • No tags to build by hand

    Enter your GTM container ID and GA4 measurement ID, download the container from the admin and import it into Google Tag Manager with Merge. The tags, triggers and variables are already built.

  • Consent first

    Google Consent Mode defaults are printed before the tag. With Softaware Cookie Consent you can also hold GTM or gtag.js back until the visitor accepts Analytics or Marketing.

All features

What is included

Events

  • view_item_list and select_item (category, search, related, upsell, cross-sell)
  • view_item, add_to_cart, remove_from_cart, add_to_wishlist
  • view_cart, begin_checkout, add_shipping_info, add_payment_info
  • purchase (once per order) and refund (server-side)
  • login, sign_up and search

Tags and consent

  • GTM snippet in the head plus noscript iframe, or gtag.js
  • Custom GTM script domain for server-side tagging
  • Consent Mode defaults: from Softaware Cookie Consent, denied, granted or left to your consent tool
  • Load tags only after Analytics or Marketing consent (with Softaware Cookie Consent)
  • Debug mode: console log of every push and GA4 DebugView

Data quality

  • Cart changes compared with the stored quantity per line
  • Event ids remembered in the browser, no double pushes
  • Purchase flagged per order in the database
  • Refund value follows the purchase value rules (tax, shipping)
  • Free gifts and custom prices reported at the cart price

Admin and developers

  • Settings per website and store view
  • GTM container export in the admin and on the CLI
  • CLI: list, dry-run and send queued refunds
  • window.swGa4.push() and a server-side event queue for your own events
  • Separate ACL permissions for settings and export

Feature tour

Everything your shoppers and your team see

01 / 06

A real add_to_cart, ready for GA4

Every cart change pushes add_to_cart or remove_from_cart with currency, value and full item data: item ID, name, brand, up to five category levels, variant, price and quantity.

02 / 06

Product lists on Hyvä and Luma

view_item_list for category pages, search results, related, upsell and cross-sell products, with list ID, list name and position. A click on a product in one of these lists sends select_item.

03 / 06

The same data on every device

Events come from the page data and the private customer-data section, so they are the same on phones and desktops and safe with the full-page cache.

04 / 06

Tag, consent and debug in one place

Choose Google Tag Manager or gtag.js, set the container ID, a custom script domain for server-side tagging, the GA4 measurement ID, when tags load and the Consent Mode defaults.

05 / 06

Item data that matches your feed

SKU or product ID as item_id, the brand attribute, category path, affiliation, currency, and whether prices and the purchase value include tax and shipping.

06 / 06

Import the tags instead of building them

Softaware > GA4 & GTM > GTM Container Export downloads a container JSON with a Google tag, GA4 event tags, triggers and variables for every event the module sends.

Live demo

Try it before you install it

A full Magento store with the module installed, on Luma and on Hyvä. The admin demo signs you in with one click.

Compatibility

Requirements and compatibility

Compatibility of GA4 & Google Tag Manager
Magento2.4.7 – 2.4.9
PHP8.2 – 8.5
Latest version 1.2.2
composer.json requires php ~8.2.0||~8.3.0||~8.4.0||~8.5.0 magento/framework ~103.0.7 softaware/module-core ^1.0 magento/module-backend * magento/module-catalog * magento/module-checkout * magento/module-config * magento/module-csp * magento/module-customer * magento/module-quote * magento/module-sales * magento/module-search * magento/module-store * magento/module-tax *

Installation

Up and running in minutes

After you buy, create a Composer key in your account. Then, in the root of your Magento project:

  1. 01Add the repository and your key (once per project)

    composer config repositories.softaware composer https://repo.softawarecommerce.com
    composer config --auth http-basic.repo.softawarecommerce.com PUBLIC_KEY PRIVATE_KEY
  2. 02Install the module

    composer require softaware/module-ga4-gtm
  3. 03Enable it

    bin/magento setup:upgrade
    bin/magento setup:di:compile
    bin/magento setup:static-content:deploy
    bin/magento cache:flush

    The last three are only needed in production mode.

Prefer a zip? Every version you are entitled to can be downloaded from My modules. More about Composer access

User guide

How to set up and use GA4 & Google Tag Manager

For version 1.2.2. The same guide comes with the module, in docs/user-guide.md.

GA4 ecommerce tracking for Luma and Hyvä: Google Tag Manager or gtag.js, a complete GA4 dataLayer, Google Consent Mode, refunds sent from the server through the Measurement Protocol, and a GTM container you can import in one step. This guide covers installation, every setting and day-to-day use.

add_to_cart event in the dataLayer after adding a product on Luma
add_to_cart event in the dataLayer after adding a product on Luma

1. Requirements

MagentoOpen Source or Adobe Commerce 2.4.7 to 2.4.9
PHP8.2 to 8.5
ThemesLuma, Blank and themes based on them; Hyvä 1.3+ (no compatibility module needed)
Othersoftaware/module-core (installed automatically)
Optionalsoftaware/module-cookie-consent for the consent banner and "load after consent"
GoogleA GTM container ID and/or a GA4 measurement ID; a Measurement Protocol API secret for refunds

2. Installation

With Composer. Use the Composer keys from your account on softawarecommerce.com (see https://softawarecommerce.com/shop/composer-access/):

composer config repositories.softaware composer https://repo.softawarecommerce.com
composer config --auth http-basic.repo.softawarecommerce.com PUBLIC_KEY PRIVATE_KEY
composer require softaware/module-ga4-gtm
bin/magento setup:upgrade
bin/magento setup:di:compile            # production mode only
bin/magento setup:static-content:deploy # production mode only
bin/magento cache:flush

To update later: composer update softaware/module-ga4-gtm, then the same bin/magento commands.

Magento cron must run (it sends the refunds). Nothing else is needed on Hyvä: the module ships its own Hyvä layout handles.

3. Quick start with Google Tag Manager

  1. Go to Stores > Configuration > Softaware > GA4 & Google Tag Manager (also under Softaware > GA4 & GTM > Settings).
  2. General > Enabled = Yes, Tag Type = Google Tag Manager.
  3. Enter your GTM Container ID (for example GTM-AB12CDE) and your GA4 Measurement ID (for example G-AB12CD34EF). Save and flush the cache.
  4. Open Softaware > GA4 & GTM > GTM Container Export, pick the store view in Settings From and click Download GTM Container JSON.
  5. In Google Tag Manager open Admin > Import Container, choose the file, your workspace, Merge and "Rename conflicting tags, triggers and variables". Check the measurement ID in the variable "Const - GA4 Measurement ID", preview the container on your shop, then publish.

With gtag.js instead: set Tag Type = gtag.js (GA4 directly) and the measurement ID; there is no container to import.

GTM Container Export page
GTM Container Export page

4. Settings

All settings are under Stores > Configuration > Softaware > GA4 & Google Tag Manager and can be set per website and per store view.

General

General settings
General settings
SettingWhat it does
EnabledAdds the tag and the ecommerce events to the storefront. Default: No.
Tag TypeGoogle Tag Manager: events go to the dataLayer for your GTM tags. gtag.js (GA4 directly): events are sent straight to GA4.
GTM Container IDFor example GTM-AB12CDE (top of your GTM workspace). Shown for Google Tag Manager only.
GTM Script DomainChange only for server-side tagging (for example https://sgtm.example.com). Default: https://www.googletagmanager.com
GA4 Measurement IDFor example G-AB12CD34EF (GA4 Admin > Data streams). Required for gtag.js and refunds; also written into the GTM container export.
Load TagsImmediately (Google Consent Mode) (default), After "Analytics" consent or After "Marketing" consent. The two "after" options need Softaware Cookie Consent and are ignored when it is disabled.
Consent Mode Defaults (Without Softaware Cookie Consent)Do not set (consent tool sets them), Denied until updated or Granted (no consent required). Only used when Softaware Cookie Consent is not enabled.
Debug ModeLogs every event to the browser console and adds debug_mode so events show in GA4 DebugView. Switch off on live stores.

Ecommerce Data

Ecommerce Data settings
Ecommerce Data settings
SettingWhat it does
Item IDSKU or Product ID. Use the same identifier as your Google Merchant Center feed. Configurable products report the parent; the chosen variant SKU goes into item_variant.
Brand AttributeProduct attribute for item_brand. Default: Manufacturer.
Default BrandUsed when the product has no brand value. Leave empty to omit item_brand.
Item CategoriesDeepest assigned category (full path) or First assigned category (full path), sent as item_category to item_category5 (root category excluded).
AffiliationSent with purchases and items. Empty uses the store view name.
CurrencyCurrency the customer sees (order currency) (default) or Base currency, for value, price and tax in all events.
Item Prices Include TaxItem prices in events include tax. Default: No.
Purchase Value Includes TaxWhether value includes tax. Tax is always sent separately in the tax parameter as well.
Purchase Value Includes ShippingWhether value includes shipping. Shipping is always sent separately in the shipping parameter as well.
Maximum Items per view_item_listKeeps the dataLayer small on long category pages. Default: 50; GA4 accepts up to 200.
Enhanced Conversions (user_data)Adds SHA-256 hashed email, phone and name (plus city, postcode and country) as user_data to purchase, sign_up and login. Only enable if your privacy policy covers it.

Refunds (Measurement Protocol)

Refund settings
Refund settings
SettingWhat it does
Send RefundsSends a refund event for every credit memo. Needs the GA4 Measurement ID and an API secret. Default: No.
API SecretGA4 Admin > Data streams > your web stream > Measurement Protocol API secrets. Stored encrypted.

5. The events

EventWhenTheme
view_item_listCategory pages, search results, related, upsell and cross-sell listsLuma and Hyvä
select_itemA click on a product link inside one of these listsLuma and Hyvä
view_itemProduct pageLuma and Hyvä
add_to_cart / remove_from_cartAny cart change: product page, AJAX add from a list, mini-cart, cart page quantity or remove, wish list, reorderLuma and Hyvä
add_to_wishlistA product added to the wish listLuma and Hyvä
view_cartCart pageLuma and Hyvä
begin_checkoutCheckout pageMagento checkout
add_shipping_info / add_payment_infoShipping method and payment method chosen in the checkoutMagento checkout
purchaseOrder success page, once per orderAll
refundCredit memo created (sent from the server)Not in the browser
login / sign_up / searchCustomer login, registration, search results pageLuma and Hyvä
view_item_list on a Hyvä category page
view_item_list on a Hyvä category page

How events reach the dataLayer

  • Page events (view_item_list, view_item, view_cart, begin_checkout, purchase, search) are printed in the page as JSON and pushed when the page loads.
  • Events that belong to the visitor (cart changes, wish list, login, sign-up) are queued on the server and delivered through the private customer-data section softaware-ga4, so they work with the full-page cache.
  • Cart changes are detected by comparing each cart line with its stored quantity. An AJAX add and a full page reload are both counted once, and a guest cart merged at login is not counted.
  • Every event has an id, and the browser remembers the ids it has already pushed. If a page navigates away before the section response arrives, the next page fetches the section once.
  • Purchase is flagged in the table softaware_ga4_order and never output twice for the same order.
  • Cart events use the cart's own currency. Lines with a custom price (for example free gifts) are reported at that price, even when it is 0.

To check the events, switch on Debug Mode and open the browser console: every push is logged as [Softaware GA4] <event>. You can also inspect window.dataLayer, or use GTM Preview and GA4 DebugView.

view_item on a phone (Hyvä)
view_item on a phone (Hyvä)
  • With Softaware Cookie Consent: the Consent Mode defaults come from that module and are printed before the tag. Nothing else is needed. The GTM <noscript> iframe is left out, because a visitor without JavaScript cannot give consent. With Load Tags = After "Analytics" consent (or Marketing), GTM or gtag.js is loaded only after the visitor accepts that category.
  • With another consent tool: set Consent Mode Defaults to Denied until updated if your tool sends gtag('consent', 'update', ...) but does not set the defaults itself, or Do not set if it sets them.
  • Without consent requirements: choose Granted (no consent required).

The defaults cover ad_storage, ad_user_data, ad_personalization, analytics_storage, functionality_storage and personalization_storage. The GTM container includes a trigger "CE - softaware_consent_update" and variables for the Softaware Cookie Consent categories, for your own tags that should wait for consent.

7. Refunds

  1. Create a GA4 Measurement Protocol API secret (GA4 Admin > Data streams > your web stream).
  2. In Refunds (Measurement Protocol) set Send Refunds = Yes and paste the API Secret. The GA4 Measurement ID in General must be set.
  3. From then on, every credit memo queues a refund event with the order number as transaction_id, the value, tax, shipping, affiliation and the refunded items. Cron sends queued refunds every five minutes.

The GA client ID and session ID are saved when the order is placed, so GA4 can link the refund to the visitor. If no client ID was available (consent denied, orders created in the admin), a generated one is used: the refund is attributed to the transaction but not to a session. The refund value follows the purchase value settings (tax and shipping), so a full refund cancels the purchase revenue exactly. A failed send is retried up to five times, and a refund is claimed before it is sent, so cron and the CLI cannot send it twice.

8. CLI commands

CommandWhat it does
bin/magento softaware:ga4:refundsLists queued refunds.
bin/magento softaware:ga4:refunds --dry-runPrints the Measurement Protocol payloads without sending them.
bin/magento softaware:ga4:refunds --sendSends pending refunds now (cron does this every five minutes).
bin/magento softaware:ga4:gtm-export [--store=default] [--output=container.json]Writes the GTM container JSON, using the measurement ID of that store view.

9. For developers

  • Push your own events in the browser: window.swGa4.push('event_name', {currency: 'GBP', value: 10, items: [...]}).
  • Queue an event on the server (delivered on the next section load): Softaware\Ga4\Model\EventQueue::add($event, $params).
  • Register a product list for view_item_list and select_item: Softaware\Ga4\Model\ListRegistry::add($id, $name, $products).
  • Add page events: implement Softaware\Ga4\Api\PageEventProviderInterface and add it to the providers argument of the block softaware.ga4.datalayer in a layout handle.
  • Build items: Softaware\Ga4\Model\ItemBuilder::fromProduct(), fromQuoteItem(), fromOrderItem().

10. Permissions (ACL)

Under System > Permissions > User Roles > Role Resources > Softaware > GA4 & GTM:

ResourceGives access to
Softaware_Ga4::exportGTM Container Export
Softaware_Ga4::configThe GA4 & Google Tag Manager settings

11. Troubleshooting

  • No events in the dataLayer: check Enabled for the store view, the container or measurement ID, and flush the cache. With Load Tags set to an "after consent" option, nothing loads until the visitor accepts that category.
  • Events in the dataLayer but not in GA4: with GTM, the container must be published and contain the GA4 tags (import the export file). Use GTM Preview and GA4 DebugView with Debug Mode = Yes.
  • No add_shipping_info or add_payment_info: these are implemented for the Magento (Luma) checkout. A one-step checkout needs an adapter that calls window.swGa4.push(). add_payment_info also fires for a method the checkout pre-selects.
  • A CMS product widget sends no view_item_list: its block HTML is cached, so these lists are not reported.
  • Refunds are not arriving: check Send Refunds, the API secret and the measurement ID, make sure Magento cron runs, then run bin/magento softaware:ga4:refunds to see the queue and its last error. --dry-run shows the payload.
  • Enhanced conversions without phone numbers: Google requires E.164, so phone numbers are only sent when stored in international format (+44...).

12. Uninstall

bin/magento module:disable Softaware_Ga4
composer remove softaware/module-ga4-gtm
bin/magento setup:upgrade
bin/magento cache:flush

Then remove the imported tags from your GTM container if you no longer need them. The tables softaware_ga4_order and softaware_ga4_refund can be dropped once the module is removed.

Changelog

Release notes

1.2.2

Latest
  • docs/listing.json, docs/faq.md, docs/user-guide.md and screenshots in docs/images/.

1.2.1

  • GTM Container Export page: the download form is a standard admin fieldset (label beside the store selector, button aligned with the field), section headings and the event table use the admin styles from a small module CSS file instead of inline styles, and a notice explains when no store view has the module enabled or a store view uses gtag.js (where the container is not needed).
  • Settings: every field has a short comment with its default (Enabled, Currency, Item Prices Include Tax, Send Refunds and others), and the measurement ID comment says where to find it.

1.2.0

  • Requires softaware/module-core instead of softaware/module-base. The admin menu and ACL now sit under Softaware_Core::core ("Softaware"); roles that had access keep it (migrated by module-core). After updating all SoftAware modules, softaware/module-base can be removed.

1.1.0

  • add_to_wishlist event (Luma and Hyvä), delivered through the customer-data section; included in the GTM container export.
  • Refund value now follows *Purchase Value Includes Tax / Shipping*, like the purchase event. Before, refunds always sent the credit memo grand total, so with tax or shipping excluded a full refund made revenue negative.
  • A queued refund could be sent twice when cron and softaware:ga4:refunds --send ran at the same time; it is now claimed (optimistic lock on the attempt counter) before it is sent.
  • Cart events (add_to_cart, remove_from_cart, view_cart, begin_checkout) use the cart's currency instead of the session's display currency.
  • Cart lines with a custom price of 0 (free gifts) were reported at the catalogue price in add_to_cart.
  • The GTM <noscript> iframe is no longer printed while Softaware Cookie Consent is active: without JavaScript a visitor cannot consent, so GTM must not load.

1.0.0

  • Google Tag Manager (head + noscript, optional server-side tagging domain) or gtag.js with a GA4 measurement ID.
  • Google Consent Mode: works with Softaware Cookie Consent automatically; own defaults when no consent module is used; optional "load only after consent".
  • GA4 ecommerce events with full item data: view_item_list (category, search, related, upsell, cross-sell), select_item, view_item, add_to_cart, remove_from_cart, view_cart, begin_checkout, add_shipping_info, add_payment_info, purchase (once per order), plus login, sign_up and search.
  • Cart events for every kind of cart change (AJAX and page reloads, Luma and Hyvä) through the customer-data section softaware-ga4; de-duplicated in the browser.
  • Refunds from credit memos through the Measurement Protocol (cron, encrypted API secret).
  • Enhanced conversions: optional SHA-256 hashed user_data.
  • GTM container export (admin page and CLI).
  • Debug mode with console logging and GA4 DebugView support.

FAQ

Questions, answered

Something else on your mind? The developers who wrote the module answer before and after you buy.

Ask a question →

Already installed it? Open a support ticket

Which events does the module send?

The GA4 recommended ecommerce events: view_item_list, select_item, view_item, add_to_cart, remove_from_cart, add_to_wishlist, view_cart, begin_checkout, add_shipping_info, add_payment_info, purchase and refund, plus login, sign_up and search. Items carry item_id, item_name, item_brand, item_category to item_category5, item_variant (the child SKU of configurable products), list ID, list name and position in lists, affiliation, discount, price and quantity.

Do I need Google Tag Manager?

No. Choose Tag Type = Google Tag Manager to push the events to the dataLayer for your GTM tags, or gtag.js to send them straight to GA4 with your measurement ID. With GTM, the admin gives you a ready-made container to import.

Does it work with Hyvä?

Yes, on Hyvä 1.3 and later, without a separate compatibility module. Product lists, product pages, cart changes, wish list, login, sign-up and search are tracked on Hyvä and Luma. The Hyvä storefront uses the Luma checkout fallback, so the checkout events are the Luma ones.

Which checkouts are supported for add_shipping_info and add_payment_info?

The standard Magento (Luma) checkout, which Hyvä themes also use by default. One-step checkouts such as Hyvä Checkout need a small adapter that calls window.swGa4.push() when the shopper picks a shipping or payment method. All other events, including purchase on the success page, do not depend on the checkout.

How does it avoid counting a cart change twice?

Cart changes are detected on the server: each cart line is compared with its stored quantity, so an AJAX add and a full page reload are counted once, and a guest cart merged at login is not counted again. The events reach the browser through the private customer-data section softaware-ga4, and every event has an id that the browser remembers. The purchase event is flagged per order in the database and never output twice, even when the shopper reloads the success page.

Does it support Google Consent Mode?

Yes, including the ad_user_data and ad_personalization signals. With Softaware Cookie Consent enabled, the consent defaults come from that module and are printed before the tag. Without it, you choose the defaults in the settings: denied until updated, granted, or not set (when your own consent tool sets them). With Softaware Cookie Consent you can also load GTM or gtag.js only after the visitor accepts Analytics or Marketing.

How are refunds tracked?

When you create a credit memo, the module queues a refund event with the order number, value, tax, shipping and the refunded items. Cron sends it to GA4 through the Measurement Protocol every five minutes, using the GA client ID and session saved when the order was placed. The API secret is stored encrypted and is never sent to the browser. You can list, dry-run and send queued refunds with bin/magento softaware:ga4:refunds.

Will a refund cancel the right amount of revenue?

Yes. The refund value follows the same Purchase Value Includes Tax and Purchase Value Includes Shipping settings as the purchase event, so a full refund cancels the purchase revenue exactly.

Are orders created in the admin tracked?

The purchase event is sent from the order success page in the shopper's browser, so orders created in the admin do not send a purchase. Refunds for any order are sent from the server; when no GA client ID was saved, a generated one is used, so the refund is attributed to the transaction but not to a session.

What are enhanced conversions?

An option that adds SHA-256 hashed email, phone and name (plus city, postcode and country) as user_data to the purchase, sign_up and login events. It is off by default; only switch it on if your privacy policy covers it. Phone numbers are only sent when stored in international format (for example +44...).

Can I use server-side tagging?

Yes. Set GTM Script Domain to your tagging server (for example https://sgtm.example.com) and GTM is loaded from there.

Can I add my own events?

Yes. In the browser call window.swGa4.push('event_name', {...}). On the server, queue an event with Softaware\Ga4\Model\EventQueue::add(), or add page events by implementing Softaware\Ga4\Api\PageEventProviderInterface. The user guide has the details.

Support

Help from the developers who wrote it