
Reconciliation
Part of Checkout integrations
Deduplicating purchase events after a redirect
Prevent duplicate purchase events when customers return, refresh or complete payment after a redirect.
Use one stable GA4 transaction_id for each qualifying purchase, and give one component ownership of sending its purchase event. A redirect return, refresh or repeated payment notification should reach the same recorded send decision, not create another send. The ID helps identify the purchase; controlling the sender prevents a handler rerun from emitting it again.
Decide when to send purchase
Define the order outcome that qualifies as a purchase. A return URL proves only that the browser returned, not that payment succeeded. With Stripe Checkout, retrieve the Checkout Session and inspect payment_status; wait if the required outcome is still pending.
Keep the store order ID, provider payment references and analytics transaction_id distinct. Derive the analytics ID from a stable order ID so a failed attempt followed by a successful retry can remain one purchase; use a new ID for a genuinely separate order. Do not send an empty ID or reuse one across separate purchases.
Google recommends transaction IDs to minimise duplicate key events. Treat transaction_id as an identifier, not a lock that makes independent senders safe: prevent the second send at its source rather than relying on a shared ID alone.
Assign an event owner
Identify every possible purchase sender: return-page code, Google Tag Manager, a storefront app and server forwarding. Select the intended trigger and remove unintended senders. In Google Tag Manager, use an exception (blocking trigger) to exclude the purchase tag once that transaction is marked as sent.
If browser and server paths both remain, make them consult the same durable send-decision record keyed by transaction_id. A Stape store is one named server-side option: check whether the ID is already recorded before a webhook sends the event, then record the sending decision so a later notification can be suppressed.
Stripe Checkout guidance notes that both a webhook and a redirect can call the server function, and that the function must handle repeated calls with the same Checkout Session ID. Use the same stable order-to-transaction_id mapping on each call; do not create a fresh analytics ID when the handler runs again.
Include transaction_id, value, currency and items in the GA4 purchase payload, with values that describe the same qualifying order on every path. Place ecommerce events in JavaScript after the Google tag, not in HTML or before the tag.
Browser vs Server-Side Event Handling for Purchase Events
- Browser Return Page
- Can trigger event but must check send decision; unreliable if user refreshes.
- Server-Side Webhook
- More reliable; checks `transaction_id` and prevents duplicate sends.
- Google Tag Manager
- Can be used but requires blocking triggers to prevent reruns.
Handle refreshes and late outcomes
Have the return page display the current order state. If payment is pending, do not send a paid purchase to complete the browser journey; when the qualifying outcome arrives, the event owner sends the purchase with the final mapped values.
Persist the send decision so a refresh or repeated notification can check it before sending. Keep a recovery path for an interrupted send, because a recorded send attempt alone does not prove GA4 received the event. Analytics does not determine whether fulfilment may begin.
GA4 & Stripe Checkout Best Practice Metrics
- Recommended transaction_id use
- Minimise duplicate key events (Google Analytics)
- Stripe Checkout Session handling
- Webhook and redirect can call same function – must be idempotent
- Event payload requirements
- `transaction_id`, `value`, `currency`, `items` required for full tracking
- Timing of event send
- After Google tag in JavaScript, not in HTML or before tag
Check the redirect paths
Test a successful redirect, a refreshed return page, a browser that never returns, and a failed attempt followed by success. For Stripe Checkout, also exercise repeated calls using the same Checkout Session ID. Check that each path reaches the same send-decision record and uses the same transaction_id for the same qualifying purchase.
Use GA4 DebugView to check that the purchase event and its event- and item-level parameters arrive. For a journey where the browser never returns, check the server-side send-decision record as well as DebugView; one clean browser test does not verify the webhook path.
Key Checks Before Launching Payment Flow
- Test successful redirectEnsure only one `purchase` event is sent.
- Test page refresh after returnEvent should not be resent if already recorded.
- Test failed attempt followed by successUse same `transaction_id` for retry; no duplicate event.
- Test repeated webhook calls with same sessionServer must handle idempotency using `transaction_id` mapping.
- Verify in GA4 DebugViewConfirm event and parameters arrive correctly across all paths.



