← All Guides

Stripe Payment Tracking with Google Tag Manager: Purchase Events in GA4, Google Ads and Meta Without Duplicates

How to track successful Stripe payments from Checkout and Payment Links with Google Tag Manager, capture transaction ID, value and currency on your success page, prevent duplicate purchase events, and send the purchase to GA4, Google Ads and Meta, plus the server-side webhook option.

Atiqul Islam
Written by Atiqul Islam

Tracking & analytics specialist. 278+ GTM, GA4 and server-side tracking projects for clients in the US, UK and Australia.

Rather not do this yourself? I set this up for clients every week. Free 15-minute audit, no obligation.

Book a Free Audit

Stripe makes taking payments easy and makes tracking them deliberately your problem. Stripe Checkout and Payment Links run on Stripe's domain, the customer comes back to your success page, and nothing tells GA4, Google Ads or Meta what just happened unless you build it.

This guide shows how I track Stripe payments for client projects: capturing the transaction on the success page, preventing duplicates, sending the purchase to all three platforms through Google Tag Manager, and adding a server-side webhook for the payments the browser misses.

The Flow

Your site, then Stripe Checkout, then your success page, then GTM tags. The success page is where everything happens, so it needs real transaction data, not a hard-coded value.

Step 1: Put the Session ID in the Success URL

When you create the Checkout Session, set the success URL with Stripe's placeholder:

https://yoursite.com/thank-you?session_id={CHECKOUT_SESSION_ID}

For Payment Links, set the confirmation page to redirect to your URL and include the same placeholder if your Stripe dashboard offers it. The session ID is the key to everything that follows.

Step 2: Look Up the Session on the Success Page

On the server that renders the thank-you page, read session_id from the URL and retrieve the Checkout Session from Stripe's API with line items expanded. Then output a dataLayer push before the GTM snippet:

<script>
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({ ecommerce: null });
window.dataLayer.push({
  event: 'purchase',
  ecommerce: {
    transaction_id: 'cs_live_a1b2c3',
    value: 149.00,
    currency: 'USD',
    items: [{ item_id: 'price_123', item_name: 'Pro Plan', price: 149.00, quantity: 1 }]
  },
  user_email: 'customer@example.com',
  user_phone: '+15551234567'
});
</script>

Use the session ID or the payment intent ID as transaction_id. The amount from Stripe is in the smallest currency unit, so divide by 100 for most currencies. The email and phone come from the session's customer details and are used for Enhanced Conversions and Advanced Matching; remove them if you prefer not to expose them to the page.

If your site is static and cannot call Stripe server-side, create a small serverless function that takes the session ID and returns the purchase data, and call it from the success page before pushing to the dataLayer.

Step 3: Prevent Duplicates in the Browser

Wrap the push so a refreshed page does not fire again:

<script>
(function () {
  var id = 'cs_live_a1b2c3';
  var key = 'tracked_' + id;
  try {
    if (window.localStorage.getItem(key)) { return; }
    window.localStorage.setItem(key, '1');
  } catch (e) {}
  window.dataLayer.push({ /* purchase event from Step 2 */ });
})();
</script>

Also pass the transaction ID to every tag. GA4 ignores a second purchase with the same transaction ID in the same property, and Google Ads deduplicates on transaction ID within a conversion action.

Step 4: GTM Variables and Trigger

Data Layer Variables: ecommerce.transaction_id, ecommerce.value, ecommerce.currency, ecommerce.items, user_email, user_phone. A Custom Event trigger on purchase.

Step 5: Tags

Step 6: Server-Side Webhook for the Payments the Browser Misses

Some customers close the tab before the success page loads. Ad blockers stop some tags. Subscriptions renew with no browser at all. A Stripe webhook closes those gaps.

  1. In Stripe, create a webhook endpoint pointing at your server-side GTM container, for example https://sgtm.yoursite.com/stripe, subscribed to checkout.session.completed and, if needed, invoice.paid.
  2. In the server container, add a Client that claims requests on that path, verifies the Stripe signature header, and parses the event into a purchase with the same transaction ID, value, currency and hashed customer email.
  3. Fire the GA4, Google Ads and Meta server tags from it. Use the same transaction ID and event ID as the browser event so each platform keeps one copy.

Attribution for webhook-only purchases depends on what you stored at checkout time. Pass the Google click ID and the GA4 client ID into the Checkout Session metadata when you create it, and read them back in the webhook.

Step 7: Test

  1. Use Stripe test mode and a test card. Complete a payment and land on the success page with GTM Preview open.
  2. Confirm one purchase event with correct transaction ID, value and currency, and one fire each of GA4, Google Ads and Meta.
  3. Refresh the success page. Nothing should fire.
  4. Check GA4 DebugView for the purchase and its items. Check Meta Events Manager Test Events for the Purchase with the event ID. Inspect the Google Ads request in a tag debugger such as the Insighter Debugger extension.
  5. If the webhook is set up, check the server container Preview for the same transaction arriving once from the browser and once from Stripe, with deduplication working downstream.

Common Problems

Outcome

Every successful Stripe payment reaches GA4, Google Ads and Meta once, with its real value and currency, and customer data attached for better matching. Your return on ad spend becomes a number you can defend rather than an estimate from Stripe's dashboard and a guess about where the customer came from.

Frequently Asked Questions

Stripe Checkout happens on stripe.com. How can my site track the payment?

Stripe redirects back to a success URL you control after payment. Put the Checkout session ID in that URL, have your server look up the session to get the amount and currency, and output a purchase dataLayer event on the success page. GTM does the rest.

Can I track Payment Links the same way?

Yes. Payment Links let you set a confirmation page redirect to your own URL and can include the session ID placeholder. The rest of the setup is identical.

How do I stop a refreshed success page from counting twice?

Use the Stripe session or payment intent ID as the transaction ID in every tag, and store it in a browser cookie or local storage when the purchase event fires so the listener skips repeats. GA4 and Google Ads also deduplicate on transaction ID.

Should I send the purchase from the browser or from a Stripe webhook?

Both, ideally. The browser event is fast and carries the ad click context. The webhook through server-side GTM catches payments where the customer never returned to the success page and survives ad blockers. Deduplicate with the same transaction ID and event ID.

What about subscriptions and recurring payments?

Track the first payment as the purchase from the success page. Recurring charges happen with no browser session, so they can only be tracked through the webhook. Many businesses track only the first payment for ads and handle recurring revenue in their billing reports.

Want This Done Right the First Time?

Book a free 15-minute audit call. I'll review your setup, show you what is broken, and tell you exactly what it takes to fix it.

Chat Now