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
- GA4 Event
purchasewith the ecommerce data. Set the tag to read ecommerce data from the dataLayer. Markpurchaseas a key event, which it is by default. - Google Ads Conversion Tracking with conversion value, transaction ID and currency mapped to the variables. Attach a User-Provided Data variable from the email and phone for Enhanced Conversions. Conversion Linker on All Pages.
- Meta Pixel Purchase with
value,currency,content_idsandcontent_type, advanced matching from the email and phone, and aneventIDequal to the transaction ID so the Conversions API event can be deduplicated.
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.
- In Stripe, create a webhook endpoint pointing at your server-side GTM container, for example
https://sgtm.yoursite.com/stripe, subscribed tocheckout.session.completedand, if needed,invoice.paid. - 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.
- 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
- Use Stripe test mode and a test card. Complete a payment and land on the success page with GTM Preview open.
- Confirm one
purchaseevent with correct transaction ID, value and currency, and one fire each of GA4, Google Ads and Meta. - Refresh the success page. Nothing should fire.
- 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.
- 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
- Value is 100 times too high. You forgot to divide Stripe's amount by 100.
- Purchase fires with no items. The line items were not expanded in the session lookup.
- Double purchases in Meta. Browser and server events have different event IDs. Use the transaction ID for both.
- Payment Links with no session ID. Check the confirmation redirect settings, or fall back to the webhook as the only source and track a generic success page view in the browser.
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.
