Attribution
10 min read
Stripe Google Analytics integration: purchases and refunds
Follow a Stripe Checkout payment into GA4, preserve analytics identity, handle refunds, and choose when to use Grometrics for source-to-payment reporting.
Choose the report you need
GA4 supports purchase and refund events. Stripe confirms the payment; your integration must connect it to the visitor and deliver the appropriate events. A success-page view alone is not payment confirmation.
This is an implementation walkthrough for your own Stripe-to-GA4 integration, not a built-in Grometrics event exporter. Grometrics is a separate option for connecting payment records to acquisition context. Installing Grometrics does not perform the GA4 steps below.
1. Prepare a separate test setup
Use a Stripe test environment, a GA4 web stream with the Google tag installed, and a server that can create Checkout Sessions, receive webhooks and store delivery records. Create a Measurement Protocol API secret for the stream and keep it on the server. Keep test traffic out of your production reporting.
Respect the visitor’s consent choices before collecting analytics identifiers. Do not send names, email addresses or payment details to GA4. Follow Google’s Measurement Protocol setup for your web stream.
2. Preserve identity before opening Checkout
Read client_id and session_id through the Google tag’s get command after analytics consent and tag initialization. Send them to your authenticated checkout endpoint. Store them with an internal order ID, the acquisition context and the capture time. If analytics is unavailable, let checkout continue and record the attribution gap; do not invent a client ID.
Create the Stripe Checkout Session on your server with client_reference_id set to that internal order ID. Store the returned Session ID alongside the order. Resolve amounts, prices and identity ownership on the server rather than trusting browser values. The internal order joins the webhook to the pre-checkout visit.
3. Confirm payment and prevent duplicate delivery
Verify the Stripe webhook signature against the raw request body. For checkout.session.completed, retrieve the Session and check payment_status before reporting revenue. For delayed payment methods, wait for checkout.session.async_payment_succeeded; an unpaid Session or async failure is not a purchase. See Stripe’s fulfilment flow.
Persist verified events before acknowledging them, then process them through a retryable worker. Use a unique order/payment key for purchases and a unique Stripe refund ID for refunds. Multiple event types can describe the same payment, so deduplicating only the webhook event ID is insufficient. Keep delivery status and reconcile ambiguous sends; an HTTP timeout does not prove that GA4 rejected the event.
4. Map the confirmed purchase into GA4
Post JSON from your server to /mp/collect on www.google-analytics.com with measurement_id and api_secret in the query string. The example below is a payload template, not a complete webhook handler. Replace identifiers and item values with the verified order data. Never expose the API secret in browser code.
Use a stable transaction_id and the original client_id. Set currency with value; value is the sum of item price times quantity, excluding tax and shipping. Convert Stripe minor units using the currency’s rules, rather than dividing every currency by 100. Keep the item IDs consistent for later refunds. See GA4 ecommerce events.
For events that qualify for the original session, include the captured session_id and follow Google’s session-attribution timing requirements. Do not fabricate engagement time or attach a renewal months later to the checkout session. Record the actual event time within Google’s timestamp limits. Sending an event does not guarantee attribution to the original campaign.
{
"client_id": "CAPTURED_CLIENT_ID",
"events": [
{
"name": "purchase",
"params": {
"transaction_id": "ORDER_123",
"currency": "USD",
"value": 49,
"items": [
{
"item_id": "course_01",
"item_name": "Course",
"price": 49,
"quantity": 1
}
]
}
}
]
}5. Handle refunds and renewals explicitly
When a Stripe refund succeeds, resolve its original order and send a refund event with the original transaction_id. Send only the newly refunded amount, not the cumulative refunded total on every update. For item reporting, include the refunded item IDs and quantities. Track each refund ID so retries do not repeat the adjustment.
For this example, a full refund uses the same USD 49 value and course_01 item as the purchase. A renewal is a new successful payment with its own transaction ID, not a replay of the original purchase. Define how paid invoices enter your pipeline without also counting their Checkout or PaymentIntent events as separate sales.
{
"client_id": "CAPTURED_CLIENT_ID",
"events": [
{
"name": "refund",
"params": {
"transaction_id": "ORDER_123",
"currency": "USD",
"value": 49,
"items": [
{
"item_id": "course_01",
"price": 49,
"quantity": 1
}
]
}
}
]
}6. Validate the complete payment lifecycle
First send the payload to Google’s /debug/mp/collect validation endpoint and inspect validationMessages. Debug validation does not ingest the event into reports. For a consented test device, send a separate test event to /mp/collect with debug_mode enabled and inspect GA4 DebugView. A successful HTTP response alone is not evidence that the event was valid or reported. See event validation.
Complete one test purchase, replay its webhook, reload the return page, and issue a full refund. Expect one purchase and one refund with the same transaction ID. Also exercise delayed payment success/failure, partial refunds and a subscription renewal. Compare your delivery ledger with Stripe records and GA4’s processed reports.
Missing source: inspect captured identity, consent and timing. Duplicate revenue: inspect multiple emitters and payment keys. Wrong value: inspect currency conversion, tax and shipping. Missing refund: inspect status transitions, order matching and refund delivery. Keep unmatched records visible instead of assigning a guessed source.
Use Grometrics for the source-to-payment review
If your main question is which acquisition source produced Stripe revenue, follow the Stripe revenue setup guide. Install tracking before checkout, preserve visitor/customer identity, connect Stripe in Integrations, and verify the source attached to a recent payment in Attribution. A payment import cannot recover acquisition context that was never captured.
Keep GA4 when its traffic and advertising reports help you. Compare its configured event reporting with provider records using the same dates, currency and definitions. Neither tool proves what caused an individual buying decision. For a tool-selection checklist, read the Stripe analytics comparison.
Try it in your data
Connect Stripe to review payment revenue by source in Grometrics.
Start tracking for free →Related guides
Setup
8 min read
Set up revenue tracking with Stripe
Connect Stripe revenue so Grometrics can attribute new sales, renewals, and refunds back to acquisition.
Attribution
8 min read
Where did that sale actually come from?
Trace a sale back to the source, page, and session that created it without guessing from payment data alone.