The primary file produced by this prompt is shown below in its original language.
HarborMug ecommerce measurement plan
Build a consent-gated GA4 funnel from product view through confirmed paid order. Use one router-owned page-view implementation, application success callbacks for commerce events, USD on all monetary events, and stable transaction IDs. A guest who declines optional consent must generate zero analytics or advertising requests. The operational order system remains the source of truth for all sales and refunds.
This plan uses synthetic supplied business data and configuration observations; no storefront integration, GA4 account, GTM container, CMP implementation, or measured business improvement has been verified.
Decisions and scope
The supplied journey is /products/harbormug → /cart → /checkout in a React SPA on a single US storefront. The core business decision is where consenting shoppers leave the journey and whether successful paid orders are measured accurately. USD is the proposed currency based on the supplied US storefront; the implementation owner must confirm it against the order system before release. Shipping and payment step events below are conditional because separate steps are not specified.
The brief lists Google Ads and Meta, but the context explicitly excludes paid-channel activation and purchase optimization. Therefore this delivery specifies GA4 measurement only: no Ads imports, conversion linker, Meta pixel, CAPI, remarketing audiences, or enhanced conversions. Purchase is a GA4 key event for measurement, with no ad-platform activation. No cross-domain or server-side analytics is planned. Guest user_id omission is intentional; do not create an identifier or require login for measurement.
Evidence confidence: verified in supplied materials means documented input, not observed telemetry; proposed means implementation design; unconfirmed means a release dependency. The supplied dual page-view sources, thank-you load trigger, missing currency and unset consent default are documented design risks, not findings from a live audit.
Business questions and reporting definitions
| Decision | Measurement | Interpretation |
|---|
| Does product interest turn into cart intent? | Consented sessions with view_item then add_to_cart / consented sessions with view_item | Same-session ordered funnel; successful additions, not clicks |
| Does the cart lead to checkout? | Sessions with view_cart then begin_checkout / sessions with view_cart | A cart drawer or skipped cart route may bypass this step; keep a separate direct-to-checkout segment |
| Does checkout result in payment? | Sessions with begin_checkout then purchase / sessions with begin_checkout | Payment failures and exits are not paid orders; cross-session purchases also appear in an open funnel |
| Are sales and amounts trustworthy? | Distinct paid transaction IDs, purchased item quantities, merchandise purchase revenue | Reconcile to eligible order records on identical date, currency and revenue basis |
| Has money actually been returned? | Confirmed refunded merchandise and quantities by original transaction | Return requests and refund approvals are not refunds |
Use a closed, same-session product → add → checkout → purchase funnel for the main journey. Use an open funnel alongside it for direct checkout and returning shoppers. Do not divide raw event counts to estimate shopper conversion: repeated actions and quantities differ. Pick and record the property reporting timezone with operations before launch, and use it for ledger reconciliation.
For orientation only, supplied prior/current totals are 100/120 orders and 4,000/4,800 sessions. Orders per session are 2.5% in both periods; gross average order value is $24 in both. Orders, sessions and gross sales each rise 20%; these examples do not show improved conversion. Gross sales less supplied refunds are $2,280 and $2,736. Refund amounts are 5% of gross in each period, not a refunded-order rate. These are supplied all-business totals, not a consented GA4 funnel or evidence of tracking performance. Calculations are in supporting/calculations.json.
Customer action → event contract
Use Google's recommended ecommerce names, including view_item, begin_checkout and purchase, to populate ecommerce reporting. These intentionally supersede the Skill generator's generic names product_viewed, checkout_started and checkout_completed; do not send aliases alongside them. The generated search event is excluded because no search surface is supplied. Google ecommerce guide
All rows require analytics consent granted at the time of the action and immediately before dispatch. Commerce rows use currency, value, and items unless stated otherwise.
| Customer action | GA4 event | Successful trigger and counting rule |
|---|
| Views a rendered route | page_view | Initial render or completed navigation, once per navigation; not rerenders |
| Sees the HarborMug product | view_item | Product fetch succeeds and product content renders, once per product per navigation |
| Adds mugs | add_to_cart | Cart mutation succeeds; quantity/value describe only the increment |
| Removes mugs | remove_from_cart | Cart mutation succeeds; quantity/value describe only the decrement |
| Views cart | view_cart | Cart snapshot renders on /cart, once per navigation; full current cart |
| Enters valid checkout | begin_checkout | Nonempty cart accepted and checkout ready, once per checkout attempt, including direct valid entry |
| Saves shipping selection, if a separate step exists | add_shipping_info | Valid selection persisted; once per distinct saved selection per attempt; add shipping_tier |
| Supplies an accepted payment method, if a separate step exists | add_payment_info | Checkout service accepts method; once per distinct accepted method per attempt; add payment_type |
| Completes payment | purchase | Trusted order endpoint confirms paid/captured state; add transaction_id, shipping, tax; one logical event per order |
| Receives an actual refund, future workflow | refund | Provider confirms refund executed; original transaction ID and refunded items/value; inactive at launch |
An attempt begins when the checkout service creates or resumes a checkout token; resuming the same token is the same attempt. A new service token after expiry or a deliberate new checkout is a new attempt. Tokens remain internal and are never sent to GA4. Failed validation, failed cart requests, pending payment and authorization without capture produce no success event. Do not infer abandonment by sending a purchase failure on every exit.
The complete portable contract is event-contract.csv; consent-granted example payloads for every event are in data-layer-examples.json. Neither is evidence of actual actions. No new customer-facing copy is needed.
Parameter dictionary and financial basis
| Field | Type and rule | Source |
|---|
currency | String, uppercase ISO currency; proposed USD; required with every monetary value | Validated cart/order currency |
value | Number in major currency units; sum of discounted unit price × quantity; excludes tax and shipping | Accepted cart mutation/snapshot or paid order lines |
items | Array of item objects; required, nonempty except an empty view_cart | Product/cart/order API |
item_id | String HM-BLUE-001; stable SKU | Product catalog |
item_name | String Insulated stainless steel mug 500ml | Product catalog |
price | Number, actual unit merchandise price after discounts; current list price 24 | Accepted line item |
quantity | Positive integer for nonempty lines; additions/removals use positive changed quantity | Accepted line item |
transaction_id | Immutable unique opaque order reference; nonempty, no personal data; purchase/refund only | Order system, never URL or random browser ID |
shipping, tax | Nonnegative numbers for purchase; actual charged amounts, explicit zero only when genuinely zero | Paid order totals |
shipping_tier | Controlled string such as standard; optional step only | Saved checkout selection |
payment_type | Controlled generic method such as card; no financial instrument data | Accepted checkout method |
page_location | Sanitized absolute current URL | Router state |
page_title | Nonpersonal current route title, ready before event | Route metadata |
page_referrer | Sanitized previous committed route; first entry uses sanitized external referrer if available | Router navigation state |
Use actual paid amounts, never the brief's $24 hint as a constant purchase value. For one mug without discounts: merchandise value=24, shipping 5, and tax from the order API; total charged is 29 + tax. Two mugs give value=48, shipping 5; three give value=72, shipping 0 because the free-shipping threshold is $50. Do not confuse the merchant's $4 shipping cost with the $5 customer shipping charge. The example payload's zero tax is a test scenario, not a tax assumption. If discounts are introduced, confirm threshold and discount-allocation rules with operations before implementation. Recommended event parameter definitions
Clear ecommerce with dataLayer.push({ecommerce:null}) before each commerce push to prevent old order fields merging into cart events. Supply a complete fresh ecommerce object, and explicitly reset irrelevant optional fields. Keep route fields fresh on every event; send sanitized page fields on commerce hits as well. Reject malformed purchase payloads instead of sending partial revenue. Log a minimal reason in first-party operational diagnostics without personal or payment data.
SPA page views and attribution
Choose the application router as the only page-view source. Set the Google tag's send_page_view=false on every initialization and disable Enhanced Measurement history-based page views. Remove all GTM History Change page-view triggers and hardcoded analytics sends. The router emits after the committed route, data and title are ready, including the initial route. Deduplicate by a navigation token kept internal; React Strict Mode, state updates and fragment changes without new content must not create another navigation. Back/forward to a rendered route is a new view. Google manual page-view guidance
On delayed consent grant, emit one current route view and current visible product/cart snapshot only. Do not replay prior routes or past mutations. Use the prior committed route as virtual referrer; never use checkout tokens or order references as page URLs or titles. Preserve approved nonpersonal external campaign parameters on the current landing URL through tag initialization when consent permits. Do not persist prior browsing or campaign history while consent is denied.
For future campaign hygiene, normalize the supplied example to ?utm_source=google&utm_medium=cpc&utm_campaign=mug-launch. Keep approved campaign names lowercase and nonpersonal. Do not add UTMs to internal product/cart/checkout links or invent source values for direct traffic. Allowlist campaign parameters in the URL sanitizer; strip email, addresses, tokens, sensitive fragments and arbitrary query values. No account linking or paid-channel activation is part of this delivery.
Consent handling: basic mode, fail closed
Use basic consent mode rather than the generator's advanced-mode default. Default analytics_storage, ad_storage, ad_user_data, and ad_personalization to denied before any measurement tag can run. Defaults also deny optional functionality and personalization storage; strictly necessary security and cart functions are separate from analytics. Use a CMP template with GTM's consent APIs (setDefaultConsentState and updateConsentState), on Consent Initialization. A plain data-layer object containing consent strings is not an API update. Google consent implementation guide
The CMP must update consent before emitting a separate analytics_ready orchestration event. The Google tag loads only after analytics consent is granted; every GA4 event has both an explicit current-consent trigger condition and an additional analytics_storage consent requirement. Default-denied flags alone are insufficient to enforce zero requests: Google's advanced behavior can send cookieless pings. Basic mode blocks Google measurement tags before consent. Basic versus advanced consent behavior
| State | Required behavior |
|---|
| Unknown, CMP unavailable, or decline all | No GA4 library, collect hit, analytics cookie, dedup browser storage or advertising request; no queue for later replay |
| Analytics granted only | Initialize Google tag once; send current view and future eligible events; all advertising flags remain denied |
| Optional marketing accepted | Still no ad tags; only configured analytics may run, according to analytics choice |
| Analytics revoked | Update API immediately, block future tags, discard unsent events, clear analytics cookies and analytics-only guards according to their configured domains/paths; retain only necessary CMP preference |
| Later re-grant | Resume current view and future actions; never backfill payment or browsing that occurred while denied |
Check consent in the application before constructing analytics payloads and again in GTM. Serialize CMP transitions and dispatch so an event queued before revocation cannot escape afterward. Necessary order records and consent preferences are not an alternate analytics route. Do not send the declined guest's order through Measurement Protocol or any other analytics channel. The plan deliberately accepts incomplete GA4 coverage.
Transaction deduplication and delivery limits
The paid order endpoint supplies one stable transaction ID per order. The callback must validate payment status, ID, currency, items and totals. Never fire purchase from a thank-you route mount, refresh, click, payment attempt ID or browser-generated random ID. Repeated captures belonging to the same business order do not create new purchases; a genuinely separate order has a new ID.
After consent, use an in-memory single-flight lock plus an analytics-consented browser guard per transaction to stop duplicate callbacks, rerenders and reloads. Cross-tab/reopened-device protection must use an atomic first-party order-system claim keyed by transaction ID. The claim records only order measurement eligibility/handoff status, and must not send anything to GA4 or run for denied users. This is an order API requirement, not server-side tagging. If that endpoint cannot support the claim, document the residual cross-device duplication risk and do not treat local storage as sufficient.
Dispatch only after a successful claim, set the local handoff guard, and retain the same ID throughout. GA4's purchase deduplication is an additional defense; it does not justify repeated sends or protect every possible multi-user case. Never use a blank ID or reuse one ID for different orders. Google transaction-ID deduplication
The claim and GTM handoff cannot guarantee GA4 delivery. A blocked request or closed tab after claim can leave an eligible order unmeasured. Record it as delivery-unconfirmed in operational reconciliation, not as a proven GA4 receipt. A GTM callback is not a receipt either. Do not automatically replay on page load or introduce a server analytics fallback. Any later retry design needs an explicit delivery policy, current consent and the original ID. Prioritize zero duplicate logical purchases in controlled acceptance tests and transparent reporting of missing deliveries.
Returns and refunds
The supplied return was requested 14 calendar days after delivery and is unopened, within the stated 30-day window. Eligibility does not establish approval or payment. The recorded refund requires merchant approval and has not been paid: emit no refund event and deduct no amount for this individual return yet. The supplied weekly refund totals are separate aggregate examples.
Keep refund instrumentation inactive until there is a confirmed-refund source and consent-aware delivery route. A future refund sends the original transaction ID, actual refunded merchandise value and quantities, and only any tax/shipping actually refunded. Never send negative purchase events. Guard future partial refunds by an internal unique refund-operation reference; do not assume purchase transaction-ID deduplication handles multiple legitimate partial refunds. Browser-only measurement will miss refunds executed away from the shopper's session, so the refund ledger remains authoritative.
GTM build specification
This is a build specification, not an importable or deployed container. Use a separate test property/container environment before production; no real measurement identifiers are included.
| Tag | Trigger | Configuration |
|---|
CMP - Defaults and updates | Consent Initialization — All Pages; CMP change callback | Audited CMP template using consent APIs; defaults before measurement, then readiness orchestration |
Google tag - GA4 | Custom Event analytics_ready, only when analytics granted | Measurement destination constant; send_page_view=false; once per document; no all-pages load |
GA4 - page_view | Custom Event exact page_view, analytics granted and initialization complete | Fixed event name; sanitized location/title/referrer variables |
GA4 - EVENT for each of the eight active commerce rows | Exact Custom Event with matching fixed event name; analytics granted and initialization complete | One tag per event; parameters from that row; optional step tags disabled if no such step exists |
GA4 - refund | None at launch | Disabled until confirmed-refund workflow passes acceptance |
The eight active commerce rows are view_item, add_to_cart, remove_from_cart, view_cart, begin_checkout, add_shipping_info, add_payment_info, and purchase. Readiness initializes the Google tag before the app emits current-view events. Hold only post-grant events briefly while initialization completes; discard on revoke. The app must not send a second gtag implementation beside GTM.
| Variable | GTM type / exact source | Use |
|---|
CON - GA4 destination | Constant set during account creation | Google tag and all GA4 event tags |
DLV - currency/value/items | Three Version 2 variables: ecommerce.currency, ecommerce.value, ecommerce.items | Commerce tags |
DLV - transaction_id/shipping/tax | Three Version 2 variables under ecommerce with corresponding key | Purchase; refund only when applicable |
DLV - shipping_tier/payment_type | Two Version 2 variables under ecommerce | Corresponding optional step only |
DLV - page_location/page_title/page_referrer | Three Version 2 variables at root, already sanitized | All event tags |
JS - analytics allowed | Boolean from current CMP state, default false | Explicit trigger gate; never trust a stale data-layer value |
JS - analytics initialized | Boolean set only after Google tag initialization | Dispatch ordering gate |
CON - debug mode | Boolean true in test environment; absent in production | debug_mode on test events only |
Map value to DLV - value, items to DLV - items, and each other named parameter to its same-named variable. Configure ecommerce parameters explicitly; do not also turn on a second automatic ecommerce mapping that duplicates them. No click, generic form submit, DOM-ready or all-pages purchase trigger is allowed.
GA4 configuration and acceptance
Create one web stream for the storefront, confirm currency and reporting timezone, and configure one Google tag destination. Keep Google signals, advertising personalization and account links inactive for this scope. Use built-in ecommerce dimensions and metrics; do not register SKU, value, transaction ID or guest user_id as custom dimensions. No custom dimension is required for the core plan. If analysts need shipping_tier or payment_type and the required report does not expose them, register event-scoped definitions before testing those reports. Use a proposed two-month event-data retention setting, subject to the merchant's retention policy. Keep internal/developer traffic filters in testing until verified; use a separate test property to avoid permanent exclusion mistakes.
Use purchase as the sole commerce key event and count once per event so distinct orders in one session count separately. No funnel micro-event or pending return is a key event. Treat the key-event setup as prospective; do not promise historical backfill.
acceptance-checks.csv contains 16 executable test scenarios with explicit pass criteria and developer/analyst ownership. None has been run against a storefront. Required release gates are:
- Implementation owner, before staging handoff: implement consent-first dispatch, one router source, successful cart/checkout callbacks and paid-order validation; confirm currency, tax fields, checkout attempts and shared order claim.
- Developer, before analyst handoff: pass D01–D11, including decline all, delayed consent, revocation, Strict Mode, failed mutations, pending payments, reloads, two tabs, distinct orders, malformed payloads, shipping thresholds and the unapproved return.
- Analyst role, before launch approval: pass A01–A05. Inspect application payload → GTM Preview variables/tag firing → network event payload → GA4 DebugView → processed reports. Count event payloads rather than HTTP requests because collection can batch events. Zero duplicates and correct financial values are mandatory for controlled eligible orders.
- Operations owner, at release review: review the test evidence and reconcile eligible paid orders by transaction ID. Keep all-order totals, denied orders, consented expected orders, observed GA4 orders and delivery-unconfirmed orders separate. Do not expect GA4 to equal every store order.
- Analyst role, after the first complete reporting day and processing delay: recheck processed ecommerce reports, route attribution and operational reconciliation; investigate discrepancies by consent, blocking, timing, ID, filters and value basis before changing tags. Pause measurement if consent leakage or duplicate purchases occur, correct in staging, then repeat affected checks.
Evidence to retain for release: sanitized payload examples, GTM tag/consent traces, sanitized network event counts, DebugView observations, processed report counts and a synthetic transaction reconciliation table. Do not attach raw URLs, cookies, customer records or credentials. Troubleshoot from app to reports; adding another tag to fix a reporting delay risks duplicates.
Completed deliverables and remaining dependency
The finished plan includes event-contract.csv, data-layer-examples.json, acceptance-checks.csv, and supporting calculation, generator-review and verification evidence. It is ready for implementation. Live acceptance remains dependent on creating the planned GA4/GTM/CMP setup and wiring the order API; the supplied example domain and configuration cannot establish tag firing or delivery. Until the confirmed refund route exists, measure refunds in the operational ledger only.
Merchant business data is synthetic. Website observations, when present, come from the public URLs listed in the report.
No reviews yet.