Server-side purchase tracking solves one problem and creates another. When Stripe, PayPal or another payment gateway confirms a payment through a webhook, you finally record every sale, even from buyers who closed the tab. But the webhook comes from the payment provider's server, not from the visitor's browser. It has no cookies, no click ID and no idea which ad the buyer clicked. Unless you plan for it, those purchases arrive in GA4 as (not set) or Direct, and Google Ads and Meta cannot credit them to a campaign.
This guide explains how to carry the right identifiers through any third-party checkout. It is the missing piece behind my Stripe and PayPal tracking setups.
Which identifiers do you need to keep?
Each platform matches a purchase to an ad with its own identifiers.
| Platform | Identifier | Where it comes from |
|---|---|---|
| GA4 | client ID | The _ga cookie |
| GA4 | session ID | The _ga_<measurement ID> cookie (for example _ga_ABC123XYZ) |
| Google Ads | gclid (also gbraid / wbraid) | The landing page URL after an ad click |
| Meta | fbc | The _fbc cookie, built from the fbclid URL parameter |
| Meta | fbp | The _fbp cookie set by the Meta Pixel |
| Microsoft Ads | msclkid | The landing page URL |
| TikTok | ttclid | The landing page URL |
You do not need all of them, only the ones for the platforms you advertise on, plus GA4.
Step 1: Capture the identifiers before checkout
Read the identifiers in the browser at the moment the visitor starts checkout, while they are still on your site.
- Make sure your site keeps click IDs from the landing page. A first-party cookie set on arrival (or the cookies GA4, Google Ads and Meta already set) is the usual source.
- When the visitor clicks Buy, read the GA4 client ID and session ID, gclid,
_fbcand_fbp. - Respect consent: if the visitor declined advertising cookies, do not collect advertising identifiers. My consent and server-side GTM guide explains how this fits together.
Step 2: Link them to the payment
Now attach the identifiers to the payment, so the webhook can find them later. There are two clean patterns.
Pattern A: store them on the payment. If your server creates the payment, add the identifiers directly:
- Stripe Checkout: add them to the Checkout Session
metadata. - PayPal Orders: set a
custom_idorinvoice_idon the order that points to your stored record.
Pattern B: store a reference. Save the identifiers in your own database (or a server-side store) under a short internal ID, and pass only that ID to the checkout:
- Stripe Payment Links: add
client_reference_idto the link URL. Stripe includes it in thecheckout.session.completedwebhook. It accepts only letters, numbers, dashes and underscores (up to 200 characters), so use an internal ID, not an email. - Other gateways: most have a merchant reference or order ID field that comes back in their callback.
Pattern B keeps personal data out of the payment provider and works with almost any gateway.
Step 3: Send them with the server-side purchase
When the webhook arrives in your server-side GTM container (or backend), look up the identifiers and include them in each platform's event:
- GA4: send the purchase with the stored client ID and session ID, so it joins the original session and keeps its source.
- Google Ads: send the conversion with the gclid, plus hashed email and phone for enhanced conversions. If the conversion happens much later, an offline import with the gclid also works.
- Meta: send the Conversions API purchase with
fbc,fbp, the client IP and user agent saved at checkout, and hashed email. Use the sameevent_idas any browser Purchase.
{
"event_name": "purchase",
"transaction_id": "cs_live_a1B2c3",
"value": 199,
"currency": "USD",
"ga_client_id": "1234567890.1730419200",
"ga_session_id": "1730419200",
"gclid": "EAIaIQobChMI...",
"fbc": "fb.1.1730419200000.IwAR...",
"fbp": "fb.1.1730419100000.123456789"
}
What if the identifiers are missing?
Some buyers block cookies, switch devices or arrive without a click ID. You can still help the platforms match:
- Send hashed email and phone from the payment. Google Ads and Meta both use them for matching.
- Keep the purchase in GA4 even without a session, so revenue is complete, and accept that its source may be unknown.
- Do not invent or reuse identifiers from other visitors; that corrupts attribution.
Common mistakes
- Reading identifiers on the success page, after the visitor has already been away
- Putting an email address in Stripe
client_reference_id(it is silently dropped) - Storing identifiers for visitors who declined advertising consent
- Sending a webhook purchase without the GA4 session ID, so it shows as
(not set) - Using different transaction IDs in the browser and on the server
If your paid traffic already shows as Direct or Unassigned, read why paid traffic shows as Direct or Unassigned in GA4 as well.
Final thoughts
Server-side purchases are only half the job; attribution is the other half. Save the identifiers before the visitor leaves, link them to the payment with a clean reference and send them with every server event. My conversion tracking service includes this for Stripe, PayPal and other gateways.