Duplicate payment events

Duplicate Payment Events: Root Cause and Fix

4 min read • install

Admaxxer is a DTC analytics platform with built-in Meta + Google ad ops. If a purchase fires from both the client-side pixel and the server-side Shopify webhook without a shared event_id, Admaxxer counts the revenue twice and every downstream metric — MER, ROAS, LTV, MMM contribution — goes wrong in the same direction. TL;DR: pick one source of truth, emit a deterministic event_id (the Shopify order id works great), and deduplicate at ingest.

Symptoms

Root cause

Admaxxer supports multiple purchase sources because different stacks need different combinations:

If two of those fire for the same order and the event_id is different, Admaxxer treats them as distinct events. The common mistakes:

  1. Firing admaxxer.track('purchase', ...) on thank-you page AND having Shopify webhook subscribed to orders/paid.
  2. Using a random event_id (uuid per beacon) instead of the order id.
  3. Sending CAPI events with the same event_id but different event_time such that the deduplication window is exceeded.

Fix

Step 1: Decide on one source of truth

For Shopify brands, the webhook is authoritative because it survives ad blockers and client-side bounces. Keep the pixel purchase firing only if you need CAPI match-rate signal, and make sure it sends the same event_id.

Step 2: Emit a deterministic event_id

On the pixel side, pass the Shopify order id (or your checkout's internal order id) as the event_id:

admaxxer('track', 'purchase', {
  event_id: 'shopify_' + orderId,
  value: total,
  currency: 'USD',
  order_id: orderId
});

The webhook should emit the same event_id format. Admaxxer deduplicates on event_id.

Step 3: Backfill deduplicate

If doubles already exist, run the Admaxxer "purge duplicate purchase events" job from the Admin panel, or contact support. Do not manually delete rows — it breaks cohort backfills and LTV recomputation.

Step 4: Audit other event types

Duplicates usually hit purchase, but add_to_cart and initiate_checkout can get doubled the same way. Use the same event_id strategy across all ecommerce events.

Step 5: Verify CAPI parity

If you send CAPI events to Meta separately, use the same event_id there too. Meta deduplicates on event_id + event_name within a window — diverging ids break CAPI match rate and Meta's delivery system will treat the same order as two separate conversions.

Step 6: Handle multi-subscription and installment orders

Some stacks split an order across multiple orders/paid events (installment plans, deferred shipping, subscription renewals). Use the parent order id plus a suffix, e.g. shopify_<order_id>_charge_<charge_id>, so each charge is unique but traceable back to the parent order. Without the suffix, renewals silently deduplicate against the original order and revenue is undercounted.

Verify the fix

Prevent it next time

Related guides

FAQs

Q: Should I turn off the pixel purchase event entirely? A: Only if you do not care about CAPI match rate. Keeping the pixel purchase is useful for Meta CAPI parity as long as the event_id is shared with the webhook.

Q: What about partial refunds — do those create duplicates? A: No. Refunds fire on orders/refunded, not orders/paid, so they get their own event_id suffix (e.g. shopify_refund_<id>) and do not double-count.

Q: How does deduplication work across Admaxxer, Meta CAPI, and Google Ads Enhanced Conversions? A: Each platform deduplicates within its own window. Using a consistent event_id across all three minimizes both under- and over-counting.

Frequently Asked Questions

Should I turn off the pixel purchase event entirely?

Only if you do not care about CAPI match rate. Keeping the pixel purchase is useful for Meta CAPI parity as long as the event_id is shared with the webhook.

What about partial refunds — do those create duplicates?

No. Refunds fire on orders/refunded with a distinct event_id suffix, so they do not double-count paid orders.

How does deduplication work across Admaxxer, Meta CAPI, and Google Ads Enhanced Conversions?

Each platform deduplicates within its own window. A consistent event_id across all three minimizes both under- and over-counting.

Put This Knowledge Into Action

Bring Meta and Google ads into one self-hosted workspace.

Get Started Free