Tracking
Conversion tracking for Magento 2 stores on Hyvä, for Meta, Google (GA4 + Google Ads), ChatGPT Ads and TikTok, with every purchase sent from both the browser and the server and deduplicated, consent enforced on both sides, and the value each ad platform bids on computed the way its invoice is: net.
What it does
Section titled “What it does”- Browser and server, deduplicated. Each purchase is sent by the pixel on the success page and by your server (Meta Conversions API, GA4 Measurement Protocol, ChatGPT Ads Conversions API, TikTok Events API), with the same event id. Ad blockers, Safari’s ITP and a closed tab no longer lose the conversion; the platform counts it once.
- Consent on both sides. Reads the visitor’s choice from Google Consent Mode v2 — so it works with any consent platform that sends it (Cookiebot, CookieYes, Usercentrics, OneTrust, Complianz…) — or from Magento’s Cookie Restriction Mode. Server-side sends obey the same decision as the browser tags. Without consent, nothing leaves the browser for Meta, ChatGPT Ads or TikTok, and GA4 receives only Consent Mode’s cookieless pings.
- The right value for bidding. Ad platforms usually invoice net of VAT, so by default Meta, Google Ads, ChatGPT Ads and TikTok receive product revenue without shipping and tax, derived from each order’s own totals (no tax rate in code; mixed rates and rate changes are handled). GA4 keeps the gross value with
shippingandtaxsent separately. - Lost sales come back out of GA4. A purchase is reported when the order is placed; when it is later cancelled, refused at delivery, returned or refunded, the module sends GA4 a
refundfor exactly what was reported — once, and only for sales that were reported. Your revenue in GA4 is what you kept, not what was ordered. See Returns and cancellations. - Full funnel:
view_item,add_to_wishlist,add_to_cart,begin_checkout,purchase, plussign_upfor new accounts. Add to cart and add to wishlist are captured on the server from what was actually saved (the chosen variant of a configurable product, the bundle, each product of a grouped product), for form posts and AJAX alike, on full-page-cached pages. - Brand on every product event, from the attribute you choose — for stores that sell many brands, so GA4 reports and TikTok can break results down by brand.
- Bundles that match your feed: reported as one product under the bundle’s catalog SKU, or as their components with the bundle price split across them (money conserved).
- Click ids that survive:
fbclid,gclid, ChatGPT’sopprefand TikTok’sttclidare stored in durable first-party cookies set by your server. - Performance: third-party scripts (several hundred KB) load on the first interaction or after a configurable delay, outside the critical rendering window; the success page is never delayed.
- Google Customer Reviews opt-in on the success page, behind marketing consent.
Requirements
Section titled “Requirements”| Magento Open Source / Adobe Commerce | 2.4.8 (tested on 2.4.8-p5) |
| Hyvä Theme | 1.3 or newer (tested on 1.5.3) |
| PHP | 8.3 – 8.4 (as required by Magento 2.4.8) |
| Checkout | Magento’s standard checkout page (checkout_index_index), including Hyvä’s; custom checkouts: see Custom checkout below |
The storefront templates use Hyvä’s view models and CSP integration; Luma storefronts are not supported. hyva-themes/magento2-theme-module comes from Hyvä’s own Composer repository, which every Hyvä store already has configured. Magento_Wishlist is optional: without it there is simply no add_to_wishlist event.
Installation
Section titled “Installation”Add the repository you received with your license to composer.json (once), then:
composer require slova/module-tracking:^1.0bin/magento module:enable Slova_Trackingbin/magento setup:upgradebin/magento setup:di:compile # production modebin/magento cache:flushsetup:upgrade adds three columns to sales_order (the ledger of reported values used for retractions). Nothing is sent anywhere until you enable tracking and switch Send Mode to Live (see Configuration).
Upgrading
Section titled “Upgrading”composer update slova/module-trackingbin/magento setup:upgradebin/magento setup:di:compile # production modebin/magento cache:flushRead CHANGELOG.md first; settings are kept across versions.
Uninstalling
Section titled “Uninstalling”bin/magento module:uninstall Slova_Tracking --remove-data--remove-data drops the three sales_order columns and deletes the module’s settings (slova_tracking/*, keys included — note them first if you may reinstall). Without it, columns and settings stay, harmlessly.
Configuration
Section titled “Configuration”Stores → Configuration → Slova → Tracking (Meta, Google, ChatGPT & TikTok)
Start in Send Mode = Log only: every server payload is written to var/log/slova_tracking.log and nothing is sent. Switch to Live once the keys are in place.
General
Section titled “General”| Setting | What to choose |
|---|---|
| Consent Source | Google Consent Mode v2 if your store has a consent platform; Magento Cookie Restriction Mode if you use Magento’s notice; None only where no consent is legally required. |
| Value Basis for Ad Platforms | Products, excluding tax (recommended). If you switch on a live account, recalibrate tROAS targets the same day. |
| Bundle Reporting | Match your product feed: As one product if the feed lists bundles, As its components if it lists only simple products. |
| Brand Attribute Code | The attribute holding the brand (default manufacturer). Sent as item_brand to GA4 and brand to TikTok; Meta and ChatGPT Ads read the brand from your product catalog. |
| Statuses That Retract the Sale | Your statuses for refused delivery or returns (see Returns and cancellations). Cancellation and credit memos are handled without listing them. |
| Tag Start Delay | 3500 ms recommended. 0 = tags start only on interaction. |
Pixel ID, and a Conversions API access token from Events Manager → your dataset → Settings → Conversions API. While validating, put the code from Test Events in Test Event Code, and remove it afterwards.
Google Analytics 4
Section titled “Google Analytics 4”Measurement ID (G-…) and a Measurement Protocol API secret (GA4 Admin → Data Streams → your stream).
Google Ads
Section titled “Google Ads”Conversion ID (AW-…) and the purchase conversion label. Enhanced conversions are hashed on your server; the plain values never appear in the page.
TikTok
Section titled “TikTok”The Pixel ID is only the code inside ttq.load('…') in TikTok’s base code — do not paste the whole script; the module renders the pixel itself, deferred and behind consent. For the server side, generate an Events API access token in Events Manager → your pixel → Settings → Events API. While validating, put the code from Test Events in Test Event Code, and remove it afterwards.
ChatGPT Ads
Section titled “ChatGPT Ads”In Ads Manager → Conversions, create a data source (its Pixel ID) and a Conversions API key (not the Advertiser API key), and a conversion event for Order created (order_created). Validate Only = Yes makes OpenAI check server events without saving them; set it back to No. ChatGPT Ads has no wishlist event: adds to wishlist arrive as a custom event named add_to_wishlist.
Events
Section titled “Events”| Event | Meta | GA4 / Google Ads | ChatGPT Ads | TikTok |
|---|---|---|---|---|
| Product page | ViewContent |
view_item |
contents_viewed |
ViewContent |
| Add to wishlist | AddToWishlist |
add_to_wishlist |
custom add_to_wishlist |
AddToWishlist |
| Add to cart | AddToCart |
add_to_cart |
items_added |
AddToCart |
| Checkout | InitiateCheckout |
begin_checkout |
checkout_started |
InitiateCheckout |
| Purchase (browser + server) | Purchase |
purchase |
order_created |
Purchase |
| Account created | CompleteRegistration |
sign_up |
registration_completed |
CompleteRegistration |
sign_up fires for every account created on the storefront — Magento’s registration form, login modals, social login, an account created after checkout — whether or not email confirmation is required; accounts created in admin or through the API are not reported. Wishlist and registration events are browser events, sent only with marketing consent (GA4 under Consent Mode).
Google Customer Reviews
Section titled “Google Customer Reviews”Merchant Center ID and the upper end of your delivery window in business days. Product GTINs come from GTIN Attribute Code, or from the SKU when it is the GTIN.
How consent works
Section titled “How consent works”The consent source has a browser half and a server half:
- In the browser it tells the loader whether analytics (GA4) and marketing (Meta, Google Ads, ChatGPT Ads, TikTok) are granted, and reports changes.
- It mirrors the decision into a first-party cookie,
slova_tracking_consent, which the server reads when the order is placed.
With Google Consent Mode v2 the module never sends gtag('consent', …) itself: your consent platform owns those signals. A Consent Mode default scoped to a region is applied only when it denies — the module cannot know the visitor’s region, so it never uses one to grant more than the global default.
Without a consent platform on the page, nothing is granted. Use None only where consent is not required.
Returns and cancellations
Section titled “Returns and cancellations”The purchase is reported when the order is placed — the only moment the visitor’s browser is there, and the only one that keeps the conversion signal fast. Some of those sales never happen: a card payment is never completed, a cash-on-delivery parcel is refused, goods come back. Left alone, they stay in your reports as revenue.
The module retracts them from GA4 with a server-side refund event:
| What happens to the order | What is retracted |
|---|---|
| It is cancelled — from admin, or automatically (unpaid card orders cancelled by cron) | everything still reported |
| It moves to one of your Statuses That Retract the Sale (refused delivery, returned to sender…) | everything still reported |
| A credit memo is created | the amount refunded; a partial memo retracts only the refunded items |
How it stays correct:
- Exactly what was reported. The values sent at the sale are stored on the order (three columns on
sales_order). The retraction subtracts those, not a new calculation — a tax rate or value-basis change between the sale and the return does not distort it. - Once. Retractions accumulate against what was reported: a cancellation followed by a credit memo, or a memo saved twice, never subtracts twice.
- Only reported sales. An order placed without consent was never reported, so nothing is retracted for it. Retractions run in admin and cron, where there is no visitor and no consent cookie; they are allowed because the order is in the ledger only if consent existed at the sale.
- Your workflow, your statuses. The module listens to Magento’s own order save and credit memo save, so it works with any order-status module. List your statuses for lost sales in the setting; cancellation and credit memos need no configuration.
What it does not do:
- Meta, Google Ads, ChatGPT Ads and TikTok are not corrected. Their conversion APIs have no retraction (Google Ads has one only through the Google Ads API, not included). Their ROAS stays gross of returns: to compensate, raise targets by 1 / (1 − your return rate).
- Orders placed before installation have no ledger entry, so their returns are not retracted.
- A status written straight to the database (without saving the order) does not trigger anything — deliberate, so bookkeeping corrections are not mistaken for lost sales.
Deduplication and ids
Section titled “Deduplication and ids”| Platform | Browser | Server | Dedup key |
|---|---|---|---|
| Meta | Pixel Purchase |
Conversions API | event_id = purchase.<order increment id> |
| GA4 | gtag purchase |
Measurement Protocol (same client_id and session_id) |
transaction_id |
| Google Ads | gtag conversion | — | transaction_id |
| ChatGPT Ads | oaiq order_created |
Conversions API | event_id / id |
| TikTok | ttq Purchase |
Events API 2.0 | event_id |
Item ids are SKUs — the chosen variant’s SKU for configurable products. They must match the id in your product feed.
Custom checkout
Section titled “Custom checkout”begin_checkout is emitted on the standard checkout page. For a checkout on another route, add the block to that route’s layout handle:
<!-- view/frontend/layout/<your_route>_index_index.xml --><referenceContainer name="content"> <block class="Magento\Framework\View\Element\Template" name="slova.tracking.begin_checkout" template="Slova_Tracking::event/begin_checkout.phtml"> <arguments> <argument name="tracking_config" xsi:type="object">Slova\Tracking\ViewModel\Frontend\Config</argument> <argument name="checkout" xsi:type="object">Slova\Tracking\ViewModel\Frontend\Checkout</argument> </arguments> </block></referenceContainer>Purchases need nothing: they are reported from sales_model_service_quote_submit_success and the standard success page.
Extending
Section titled “Extending”- Another consent platform: implement
Slova\Tracking\Api\ConsentSourceInterface(server half) with a template definingwindow.slovaConsentSource(browser half), and register it in your module’sdi.xmlonSlova\Tracking\Model\Consent\SourcePool(sources). The interface documents the contract. - Another ad platform (server side): implement
Slova\Tracking\Api\ProviderInterfaceand add it toSlova\Tracking\Model\TrackingService(providers). - Your own bundle split (to share it with invoicing, for example): prefer your class for
Slova\Tracking\Api\OrderLineExpanderInterface. It must conserve money. - Browser events from your own code:
window.slovaTrack(name, data)fans an event out to every enabled browser tag, under consent.
Troubleshooting
Section titled “Troubleshooting”- Nothing in the page: the browser tags render only with Enable Tracking = Yes, Send Mode = Live and at least one platform enabled. Flush the full-page cache after changing these settings.
- Server events: look at
var/log/slova_tracking.log— each line sayssent,failed: <reason>orskipped — no <category> consent. - Meta: use Events Manager → Test Events with a Test Event Code. GA4: validate payloads at
/debug/mp/collect—/mp/collectanswers 204 to anything. ChatGPT Ads: Ads Manager → Conversions shows events from roughly the last 15 minutes. - Testing in an automated browser? Meta silently drops events when it detects automation; that is not a broken pixel.
Known limitations
Section titled “Known limitations”- Meta, ChatGPT Ads and TikTok have no retraction mechanism: their ROAS stays gross of returns. Google Ads retractions would need the Google Ads API and are not included.
- A Consent Mode default scoped to a region is used only when it denies (see How consent works); in such setups GA4 may record visits that Meta and ChatGPT Ads do not.
- Phone numbers typed in local form are converted for most of the EEA, the UK, Switzerland and North America; elsewhere only numbers in international form (
+…,00…) are matched. - Product feeds are not generated by this module.
Privacy
Section titled “Privacy”Personal data (email, phone, name) sent to ad platforms is normalized and SHA-256 hashed on your server; address components are sent as the platforms require. Google Customer Reviews sends the customer’s email and order number to Google, only with marketing consent. Mention the platforms you enable in your privacy policy.
Support and license
Section titled “Support and license”Slova — a trade name of Carusel Multimedia SRL. Commercial software: see the license.