The problem every DTC brand has: Google says the ad spend generated $1.2M in revenue. Meta says $890K. TikTok says $340K. Their combined reported revenue is $2.43M — but the actual monthly revenue from Shopify is $1.6M. Somewhere along the way, everyone counted the same purchases multiple times, and none of the numbers ties back to reality.
This is the process we run on every new DTC engagement to fix it. It is not conceptual. It is a specific data pipeline: order data becomes the source of truth, click IDs get matched against orders, and every purchase is attributed exactly once. Here is how it works end to end.
Step 1: Establish Order Data as the Ground Truth
Everything starts with a clean feed of Shopify (or your equivalent) order data. Not a report from Shopify Analytics — the actual orders table, pulled via API or via a data warehouse connector. You need: order ID, timestamp, revenue, customer email, and — critically — the landing page the customer first hit before ordering (or the referrer captured on first visit).
For most Shopify stores, this data is available via the Shopify Admin API or through a tool like Fivetran syncing into BigQuery/Snowflake. If your marketing team does not have access to the raw orders table, that is the first problem to fix. Everything else depends on it.
Step 2: Capture Click IDs at First Landing
When a user clicks a Google ad, the URL gets a GCLID parameter. Meta uses FBCLID. TikTok uses TTCLID. Microsoft uses MSCLKID. These are unique click identifiers — the platform's way of matching a specific click to a specific outcome.
The critical step: capture these IDs the first time a user lands on your site, and persist them for the entire session — and ideally across sessions in a first-party cookie. Most Shopify stores capture them at checkout but not at first landing, which means all the mid-funnel touchpoints are lost.
- Use a small JavaScript snippet in your theme's <head> to read URL parameters on landing.
- Store all four click IDs (GCLID, FBCLID, TTCLID, MSCLKID) in a first-party cookie with a 90-day expiry.
- On checkout, write whichever click IDs are present into hidden fields on the Shopify order object using the order's note_attributes.
- Configure your Shopify theme to preserve these note attributes through the entire checkout, including if the user uses Shop Pay or express checkout.
Step 3: Match Orders Back to Click IDs
You now have a dataset that looks like:
- orders.order_id, orders.revenue, orders.gclid, orders.fbclid, orders.ttclid, orders.msclkid, orders.first_touch_utm_source, orders.timestamp
For each order, exactly one channel gets attribution. The rule we use: last non-direct click wins. If GCLID is present, Google gets the order. If not but FBCLID is present, Meta gets it. If neither but there is a UTM source of 'organic', organic gets it. Direct traffic (no click ID, no UTM) is attributed to whichever was the most recent paid touch in the last 90 days — usually pulled from a session-level events table.
Step 4: Send Cleaned Conversions Back to the Platforms
This is where the rebuild pays off. Instead of letting Google and Meta each track their own conversions independently (which produces double-counting), you send them your deduplicated version. Only orders attributed to Google get sent to Google Ads. Only orders attributed to Meta go to Meta.
For Google Ads: Enhanced Conversions for Web + Offline Conversion Import
Enhanced Conversions for Web catches purchases in real time but is still client-side. To get server-side accuracy, use Offline Conversion Import: upload a CSV (or use the API) with GCLID + conversion value + timestamp for each attributed order. Do this daily. Google will match the GCLID back to the original click and update its bidding signals with the correct value.
For Meta: Conversions API (CAPI)
Set up CAPI via a first-party server (either through Shopify's native Meta pixel + CAPI integration or through a custom endpoint). Send purchase events with the FBCLID and hashed email/phone. Do not double-fire the client-side pixel and CAPI without deduplication — set an event_id that matches between them so Meta discards the duplicate.
What Changes Once the Rebuild Is Live
The reported ROAS for every channel initially drops — often significantly. Google that was reporting 6.2× ROAS now shows 4.4×. Meta that was reporting 4.1× shows 3.0×. TikTok that was reporting 5.5× shows 1.8× because it turns out TikTok was picking up a lot of last-click credit for retargeted purchases that were originally driven by other channels.
This is the correct baseline. From here, actual optimization is possible. Google is now optimizing on real Google-driven revenue. Meta is optimizing on real Meta-driven revenue. Budget shifts made based on this data actually move the P&L, not just the dashboards.
