-
Notifications
You must be signed in to change notification settings - Fork 0
Fix Ads conversion value, gate on Measurement ID, unify payloads, add events and tests #8
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
85e4ab9
Fix Ads conversion value, gate on Measurement ID, unify item payloads…
next-devin bd75969
Address review: escape snippet values, guard category entries, drop a…
next-devin 3c9d8ec
Skip products whose id is null or undefined in view_item_list and vie…
next-devin 9cd6842
Parity with the GTM review: trim the measurement id in both gates, dr…
next-devin 56a6421
Review follow-ups: parent window with try/catch, send_to on every eve…
next-devin 56e9501
Configure the Ads tag from the tracker so id and label validation liv…
next-devin c9956c6
Item-list cap: say truncation, take the currency before truncating; c…
next-devin d643ea4
Line prices and checkout value exclude tax (price_excl_tax) since tax…
next-devin b37e6a6
Document the price_excl_tax fallback, the zero-price case, and discou…
next-devin File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| name: Test | ||
|
|
||
| on: | ||
| pull_request: | ||
| push: | ||
| branches: [main] | ||
|
|
||
| jobs: | ||
| test: | ||
| runs-on: ubuntu-latest | ||
| steps: | ||
| - uses: actions/checkout@v4 | ||
| - uses: actions/setup-node@v4 | ||
| with: | ||
| node-version: "22" | ||
| - run: npm test |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| # Changelog | ||
|
|
||
| ## Unreleased | ||
|
|
||
| - Google Ads conversions now send the order total as the value (was the tax) and the `AW-` tag is configured by the tracker, so `send_to` can attribute (#2). | ||
| - The snippet and tracker are gated on a non-empty Measurement ID; a store that only ticks "Enable" no longer loads `gtag/js?id=` (#3). Tracker calls are guarded so a page without the snippet never throws. | ||
| - `debug_mode` and `user_id` are passed in one config object; debug mode works for logged-in shoppers (#4). | ||
| - All money fields are numbers, unit `price`/`discount` are derived from line totals, one item shape across the funnel (`item_id` = product id, `sku`, `item_variant`), and page fields are no longer stuffed into `items[]` (#5). | ||
| - New events: `view_item_list`, `remove_from_cart`, `add_shipping_info`. New "Skip Test Orders" setting (#6). Consent Mode defaults and `add_payment_info` are not included: the platform has no payment-step event, and consent defaults need a CMP to be useful. | ||
| - Settings labels, help text and README say Google Ads and Next Commerce (#7). | ||
| - Review follow-ups: the tracker reaches the storefront through `window.parent` inside try/catch (an embedded storefront no longer throws on every event); every GA4 event sets `send_to`; checkout `value` is item revenue rather than the order grand total, and line prices exclude tax (`price_excl_tax`) since `tax` is reported separately; the Ads tag is configured by the tracker, which is the single place that normalises the id (bare digits, spaces, lower case) and validates the label shape; coupon is omitted when absent; lines without a product are dropped; item lists are truncated to GA4's 200-item limit. | ||
| - Added a Node test harness (`npm test`) and a CI workflow. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,25 +1,45 @@ | ||
| # Google Analytics 4 | ||
|
|
||
| Google Analytics 4 app for 29 Next that integrates Google Analytics 4 into any storefront theme with [Storefront Event Tracking](https://developers.29next.com/docs/themes/event-tracking/). App also includes [Enhanced Ecommerce](https://developers.google.com/analytics/devguides/collection/ga4/ecommerce?client_type=gtag) event tracking using the `gtag()` events. | ||
| Google Analytics 4 app for Next Commerce. Installs the Google tag on any storefront theme and sends GA4 ecommerce events through [Storefront Event Tracking](https://developers.nextcommerce.com/docs/storefront/event-tracking). Optionally sends a Google Ads conversion on completed orders. | ||
|
|
||
| Also includes Google Adwords Conversion tracking integration with `gtag()` conversion events. | ||
| ## Settings | ||
|
|
||
| **Google Analytics 4** | ||
| * [Installs Google Analytics 4](https://support.google.com/analytics/answer/9304153) globally with a setting for your Measurement ID | ||
| | Setting | Notes | | ||
| |---|---| | ||
| | Enable Google Analytics | Nothing loads until a Measurement ID is also set. | | ||
| | Google Analytics Measurement ID | `G-XXXXXXXXXX`. | | ||
| | Enable Google Ads Conversion Tracking | The tracker configures the `AW-` tag at startup and sends a `conversion` event on `checkout_completed`. | | ||
| | Google Ads Conversion ID | `AW-123456789`; a bare `123456789` or lower-case paste is normalised. | | ||
| | Google Ads Conversion Label | From the Ads conversion action (letters, digits, `_`, `-`). | | ||
| | Enable Debug Mode | Sends `debug_mode` so events show in GA4 DebugView. | | ||
| | Skip Test Orders | Suppresses `purchase` and `conversion` for orders flagged `is_test`. | | ||
|
|
||
| ## Events | ||
|
|
||
| **Google Ecommerce Events** | ||
| * [Product Detail Impressions](https://developers.google.com/analytics/devguides/collection/ga4/ecommerce?client_type=gtag#view_item_details) | ||
| * [Add to Cart](https://developers.google.com/analytics/devguides/collection/ga4/ecommerce?client_type=gtag#add_or_remove_an_item_from_a_shopping_cart) | ||
| * [Begin Checkout](https://developers.google.com/analytics/devguides/collection/ga4/ecommerce?client_type=gtag#initiate_the_checkout_process) | ||
| * [Purchases](https://developers.google.com/analytics/devguides/collection/ga4/ecommerce?client_type=gtag#make_a_purchase_or_issue_a_refund) | ||
| | Storefront event | GA4 event | | ||
| |---|---| | ||
| | `product_category_viewed` | `view_item_list` | | ||
| | `product_viewed` | `view_item` | | ||
| | `product_added_to_cart` | `add_to_cart` | | ||
| | `product_removed_from_cart` | `remove_from_cart` | | ||
| | `checkout_started` | `begin_checkout` | | ||
| | `checkout_shipping_method_submitted` | `add_shipping_info` | | ||
| | `checkout_completed` | `purchase` (+ Ads `conversion` when enabled) | | ||
|
|
||
| `page_view` is sent by the Google tag itself on `config`. | ||
|
|
||
| **Google Adwords Conversion Tracking** | ||
| Items share identifiers across the funnel so GA4 item reports join: `item_id` is the product id, `sku` and `item_variant` identify the child product, `price` and `discount` are per unit, and all money fields are numbers. `value` on `begin_checkout`, `add_shipping_info` and `purchase` is item revenue (the sum of the lines' `price_excl_tax`; if a payload ever lacks that field the tax-inclusive total is used, so a store seeing values that include tax should check its payloads), as GA4 defines it; `shipping` and `tax` travel in their own parameters. The Ads `conversion` value is the order total the merchant was paid. Every GA4 event carries `send_to` for the configured Measurement ID so a second Google tag on the page does not receive it. | ||
|
|
||
| * [Google Tag for Google Ads Conversion Tracking](https://support.google.com/google-ads/answer/7548399?hl=en) | ||
| ## Files | ||
|
|
||
| - `manifest.json` — settings schema, snippet location, event tracker mapping. | ||
| - `snippets/global-header.html` — loads gtag and configures the GA4 tag. Rendered only when a Measurement ID is set. | ||
| - `tracking.js` — the event tracker, which also validates the Ads settings and configures the `AW-` tag. Runs in the platform's tracker frame (a direct child of the storefront page) and calls `gtag` on the parent window; every parent access is wrapped so a page without the snippet, or an embedded storefront with a cross-origin parent, never throws. | ||
|
|
||
| See `tracking.js` for a complete detailed view of the implementation. | ||
| ## Tests | ||
|
|
||
| For more information on individual tracking events, see the [Storefront Theme Event Tracking guide](https://developers.29next.com/docs/themes/event-tracking/). | ||
| ```bash | ||
| npm test | ||
| ``` | ||
|
|
||
| `tests/tracking.test.js` runs `tracking.js` with the same globals the platform provides (`app`, `analytics`, `window.parent`) and asserts the payload of every mapped event, plus the escaping in the snippet. No dependencies; Node 22, the version CI runs. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| { | ||
| "name": "google-analytics-4", | ||
| "private": true, | ||
| "description": "Google Analytics 4 app for Next Commerce storefronts", | ||
| "scripts": { | ||
| "test": "node --test" | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,16 +1,18 @@ | ||
| {% if app.settings.google_analytics_enabled %} | ||
| {% if app.settings.google_analytics_enabled and app.settings.google_analytics_measurement_id.strip %} | ||
|
|
||
| <!-- Google tag (gtag.js) --> | ||
| <script async src="https://www.googletagmanager.com/gtag/js?id={{ app.settings.google_analytics_measurement_id }}"></script> | ||
| <script async src="https://www.googletagmanager.com/gtag/js?id={{ app.settings.google_analytics_measurement_id.strip|urlencode }}"></script> | ||
| <script> | ||
| window.dataLayer = window.dataLayer || []; | ||
| function gtag() { dataLayer.push(arguments); } | ||
| gtag('js', new Date()); | ||
|
|
||
| gtag('config', '{{ app.settings.google_analytics_measurement_id }}', | ||
| {% if user.is_authenticated %} { 'user_id': '{{ user.pk }}' }, {% endif %} | ||
| {% if app.settings.google_analytics_debug_mode %} { 'debug_mode': true }, {% endif %} | ||
| ); | ||
| gtag('config', '{{ app.settings.google_analytics_measurement_id.strip|escapejs }}', { | ||
| {% if user.is_authenticated %}'user_id': '{{ user.pk|escapejs }}',{% endif %} | ||
| {% if app.settings.google_analytics_debug_mode %}'debug_mode': true,{% endif %} | ||
| }); | ||
|
|
||
| {# The Google Ads tag is configured by tracking.js, which validates and normalises the id and label in one place. #} | ||
| </script> | ||
|
|
||
| {% endif %} | ||
| {% endif %} | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.