Server-side tracking voor Shopify met de Measurebase-app
Shopify is waar normale tagging-setups stilletjes stuklopen. De checkout is een afgesloten omgeving waar je geen eigen scripts kunt plaatsen, pixels draaien in een sandbox die de verkeerde pagina-URL rapporteert, en tegen de tijd dat een betaalprovider de shopper heeft rondgestuurd zijn de sessie en de attributie vaak weg. De Measurebase Shopify-app bestaat om precies die gaten te dichten: hij verbindt je winkel met je eigen Measurebase tagging-host en meet de hele funnel server-side · storefront, checkout en de order zelf.
Drie vlakken, één stroom
De app meet je winkel vanaf drie plekken tegelijk, en elk daarvan rapporteert aan dezelfde tagging-host met dezelfde identifiers:
- Storefront · thema-embed. Eén schakelaar in je thema-editor. Hij pusht de standaard GA4-e-commerce-events naar de dataLayer voor je web-GTM-container, geladen via je eigen first-party tagging-host.
- Checkout · app-pixel. Automatisch geïnstalleerd met de app · geen code om te plakken. Hij dekt de hele checkoutfunnel vanuit Shopify's checkout, waar je eigen scripts niet mogen komen.
- Orders · server-webhooks. De order zelf, server-to-server gerapporteerd: aankopen, refunds met een correcte volledig-of-gedeeltelijk-classificatie, en (optioneel) abonnementsorders. Dit vlak blijft werken wanneer de browser nooit de kans kreeg · een device met adblocker, een gesloten tabblad, een betaalapp die de shopper nooit terugbracht naar je site.
Elk event dat de app verstuurt
Alle events gebruiken de standaardnamen en -vormen van GA4, dus ze werken zonder vertaalslag met je bestaande tags, doelgroepen en rapporten. Storefront-events, vanuit de thema-embed:
| Event | Vuurt wanneer |
|---|---|
view_item | Een productpagina wordt bekeken · met de prijs van de gekozen variant |
view_item_list | Een collectie- of zoekresultatenpagina wordt bekeken · items met lijstnaam en positie |
select_item | Een product wordt aangeklikt in een collectie- of zoeklijst · werkt met alle kaartlayouts, ook thema's die de kaart niet in een link wikkelen |
add_to_cart | Elke toevoeging aan de winkelwagen · quantity is het zojuist toegevoegde aantal, nooit het lopende winkelwagentotaal |
remove_from_cart | Elke verwijdering of aantalverlaging · berekend door de winkelwagen te vergelijken |
view_cart | De winkelwagenpagina wordt bekeken, met de volledige inhoud |
search | Een zoekresultatenpagina wordt bekeken · met search_term en search_results |
| Je eigen events | Alles wat op Shopify's analytics-bus wordt gepubliceerd · een quiz, een verlanglijst, het event van een app · gespiegeld naar de dataLayer onder een naam die jij kiest |
Checkout-events, vanuit de app-pixel:
| Event | Vuurt wanneer |
|---|---|
begin_checkout | De checkout wordt geopend |
add_shipping_info | De verzendstap wordt afgerond |
add_payment_info | De betaalstap wordt afgerond |
apply_discount_code | Een kortingscode wordt toegepast · met de code en het kortingsbedrag |
purchase | De order wordt geplaatst · het event-ID is het order-ID, herhalingen van de bedankpagina worden onderdrukt |
nc_purchase / rc_purchase | Optionele metgezellen van de purchase voor nieuwe en terugkerende klanten · nooit geteld als tweede conversie, alleen verstuurd wanneer Shopify het segment echt kent |
Server-events, vanuit order-webhooks:
| Event | Vuurt wanneer |
|---|---|
purchase | De order wordt aangemaakt · als het eigenaar-event of als gededupliceerde kopie, afhankelijk van de purchase-modus die je hieronder kiest |
refund | Een terugbetaling wordt uitgevoerd · geclassificeerd als volledig of gedeeltelijk door het terugbetaalde bedrag te vergelijken met het ordertotaal, zodat een refund van alleen verzendkosten nooit de hele order terugdraait |
subscription_started | De eerste order van een abonnement wordt geplaatst (opt-in) |
subscription_renewed | Een terugkerende abonnementsorder wordt gefactureerd (opt-in) · als annotatie-event, of als telbare purchase als je verlengingen in je omzet wilt |
Elke parameter, en waarom die er is
Het e-commerce-object
Elk commerce-event draagt het volledige GA4-e-commerce-object: currency, value, transaction_id, tax, shipping, coupon en het orderbrede discount, plus een items array met item_id, item_name, item_brand, item_category, item_variant, price, quantity en per item discount. De bron van het item-ID is instelbaar · product-ID, variant-ID, SKU of barcode · en dezelfde keuze geldt op elk vlak, zodat je productfeed matcht van de eerste view_item tot de server-side purchase. Bedragen zijn altijd een schoon getal, nooit een opgemaakte tekst en nooit floating-point-ruis.
Identiteit en matching
Events dragen klantidentifiers in Googles user-provided-data-schema, en altijd uitsluitend als SHA-256-hashes · e-mail, telefoon of adres komen nooit onversleuteld in de dataLayer en verlaten nooit de winkel:
user_data.sha256_email_addressensha256_phone_numberuser_data.addressmet gehashte voornaam, achternaam en straat, plus stad, regio, postcode en land als leesbare waarden (het schema vereist geografie onversleuteld · een gehashte vorm bestaat er niet voor)user_id· het Shopify-klant-ID voor ingelogde shoppers, waarmee GA4's cross-device User-ID aangaat
Identiteit wordt in de dataLayer gezet voordat GTM laadt, op de storefront én in de checkout · zo draagt de allereerste hit van de Google-tag al het user-ID en de matchsleutels, in plaats van alleen de events die toevallig vuren nadat alles geladen is. In de checkout verversen de hashes bij elke stap zodra de shopper meer velden invult, dus advanced matching wacht niet op de purchase.
Click-ID's die de conversie halen
Click-identifiers van Google, Microsoft, Meta en TikTok · gclid, gbraid, wbraid, msclkid, ttclid, plus de _fbp/_fbc cookies · worden vastgelegd op de landingspagina, op de winkelwagen gestempeld en meegestuurd met checkout- en server-events. De iOS-varianten gbraid en wbraid doen er het meest toe: ze bestaan juist voor de bezoeken waar elke andere identifier al is gestript, en de meeste setups laten ze volledig vallen.
Context op elk event
mb_marketenmb_language· welke Shopify Markets-storefront, en de taal die de shopper daadwerkelijk koos (browsertaal alleen kan je dat op een meertalige winkel niet vertellen)logged_inencustomer_type· ingelogd-status als expliciete true/false op elk storefront-event, en nieuw-versus-terugkerend afgeleid uit echte ordergeschiedenis · de twee dimensies die doelgroepopbouw echt nodig heeftpage_locationenpage_referrer· de echte pagina-URL, ook binnen de checkout, waar de pixel-sandbox anders Shopify's interne worker-adres als pagina zou rapporteren- Server-events voegen de landingspagina van de sessie toe met de UTM's intact, de oorspronkelijke referrer, en de user agent en het IP van de koper · zo krijgt een server-side purchase alsnog device en geo in plaats van eruit te zien als een hit uit een datacenter
Boekhouding die de tellingen eerlijk houdt
event_id· het order-ID op de purchase, identiek op alle drie de vlakken · de sleutel waarop GA4 en elk advertentieplatform deduplicerenmb_checkout_token· koppelt een browsersessie aan haar webhook-order, zodat de serverkopie de browseridentiteit draagtnew_customer· op de purchase zelf, onder de standaardnaam die Google Ads' biedstrategie voor nieuwe klanten uitleestmb_countable· expliciet nul op metgezel- en identiteitsevents, zodat niets verderop een annotatie kan aanzien voor een tweede conversiemb_purchase_ownerenmb_source· welk vlak de purchase bezit en welk vlak elk event stuurde, zodat elk getal terug te herleiden is naar zijn oorsprong
Eén aankoop, nooit twee
Met drie vlakken die dezelfde order kunnen zien, is het allerbelangrijkste dat je rapporten hem maar één keer tellen. De app geeft elke aankoop één event-ID · het order-ID zelf · op alle drie de vlakken, en laat je kiezen wie de purchase bezit:
- Browser · de checkout-pixel rapporteert de purchase, de webhook blijft stil.
- Browser + server · de pixel rapporteert hem en de webhook stuurt een server-side kopie met hetzelfde event-ID en dezelfde browseridentiteit, zodat GA4 en elk advertentieplatform schoon dedupliceren terwijl jij de betrouwbaarheid van een server-event wint.
- Server · de webhook bezit de purchase volledig; de browser levert alleen identiteit. De sterkste optie tegen blockers, want de conversie hangt nergens af van de browser van de shopper.
Herladen bedankpagina's, dubbel geplakte pixels en opnieuw afgespeelde webhooks worden allemaal onderdrukt · verdediging in de diepte op elk vlak, geen aanname dat er nooit iets misgaat.
Waarom dit zo'n sterke opzet is
Elk onderdeel hierboven is op zichzelf nuttig; de kracht zit in wat ze samen optellen. Een paar claims die de meeste Shopify-tracking niet kan maken, en waarom deze opzet dat wel kan:
- De conversie hangt niet af van de browser van de shopper. Ad blockers, ITP, een gesloten tabblad, een in-app browser die nooit terugkeert van de betaalprovider · geen van alle kan een aankoop wissen, want de order-webhook rapporteert hem server-to-server met de browseridentiteit eraan gekoppeld via het checkout-token. Setups met alleen een pixel verliezen precies deze orders en merken het nooit.
- Redundantie zonder dubbeltelling. De meeste "dubbel is beter"-setups blazen omzet stilletjes op, omdat beide lagen allebei de verkoop rapporteren. Hier gebruikt elk vlak het order-ID als event-ID en markeert wie de purchase bezit, dus je krijgt de betrouwbaarheid van drie rapporteurs met de rekensom van één.
- Matchsleutels arriveren mét het event, niet erna. Enhanced conversions en CAPI-achtige matching staan of vallen met de vraag of de hashes op de hit aanwezig zijn. Identiteit wordt gezet voordat GTM start en bij elke checkoutstap ververst, dus zelfs de eerste paginaweergave van een ingelogde shopper draagt een matchsleutel · en de purchase altijd, vanaf welk vlak hij ook geleverd wordt.
- Attributie overleeft de moeilijkste routes. De gekozen taal, de Markets-storefront, de echte checkout-pagina-URL, de landingspagina met UTM's intact, en de volledige click-ID-set inclusief
gbraid/wbraid· de routes waar attributie normaal afglijdt naar "direct / none" houden hier hun bron. - First-party van begin tot eind, gehost in de EU. Storefront-container, checkoutverkeer en server-events lopen allemaal via je eigen subdomein op EU-infrastructuur · met Enhanced Ad Blocker Protection tot in de checkout, zodat zelfs filterlijsten op padniveau niets stabiels hebben om op te matchen.
- Consent-bewust by design. De consentstatus van de shopper reist mee met de events, en identifiers worden bij de bron gehasht in plaats van "ergens later" · de opzet is sterk omdat hij zuinig met data omgaat, niet ondanks dat.
- Niets is een black box. Elk event, elke parameter en elk product op elke hit is te inspecteren in het Test-tabblad van de app, en elke telling is te herleiden tot het vlak dat hem produceerde. Ziet een cijfer er vreemd uit, dan kun je uitzoeken waarom · in plaats van gokken.
Gebouwd voor jouw GTM-setup, niet eromheen
Je houdt je eigen web- en servercontainers. Het dashboard genereert een kant-en-klaar te importeren GTM-template in Shopify-modus: elk e-commerce-veld expliciet gemapt (de checkout-sandbox negeert de gebruikelijke "lees uit de dataLayer"-route), een user-provided-data-variabele gekoppeld aan de gehashte velden, en de echte pagina-URL ingemapt zodat checkout-events de checkoutpagina rapporteren in plaats van Shopify's interne sandbox-adres.
Shopify Markets wordt per storefront afgehandeld: een winkel met meerdere domeinen tagt elk domein via zijn eigen host, automatisch bepaald · één app, één pixel, elke markt first-party.
Alles wat je winkel publiceert via Shopify's eigen analytics-events · een afgeronde quiz, een toevoeging aan een verlanglijst, het custom event van een app · kan naar de dataLayer worden gespiegeld onder een naam die jij kiest, vanuit het Events-tabblad van de app. En Enhanced Ad Blocker Protection werkt door tot in de checkout: containerverkeer vertrekt ook daar gemaskeerd.
Zien wat er gebeurt
Het Test-tabblad van de app toont elk event dat je tagging-host in de laatste 30 minuten bereikte · welk vlak het stuurde, de waarde, de producten stuk voor stuk, en elke parameter die het droeg, inclusief verkeer dat via de adblocker-maskering vertrok. Een console-debugmodus en een server-preview-header zijn één schakelaar verwijderd, en beide schakelen zichzelf na 24 uur automatisch uit, zodat een debugsessie nooit stilletjes je productiedata kan aantasten. De Setup-pagina controleert doorlopend elk onderdeel · embed, pixel, webhooks · en vertelt je wanneer iets aandacht nodig heeft.
Aan de slag
- Installeer de Measurebase-app op je winkel en verbind hem met je Measurebase-domein.
- Zet de thema-embed aan in je thema-editor voor storefront-events.
- Importeer de gegenereerde GTM-template vanuit je dashboard, of map de events in je bestaande containers.
De checkout-pixel installeert zichzelf met de app, en order-webhooks worden automatisch geregistreerd. Van de eerste paginaweergave tot de purchase en de bijbehorende refund loopt alles via je eigen domein · first-party, gededupliceerd en gehost in de EU, zoals alles op Measurebase.