Ads Manager shows two purchases for one buyer. Or the opposite: Stripe says the order was $144 with the upsell, but Meta and GA4 only saw $47. Both problems have the same root. In ClickFunnels, and in any funnel with one-click upsells, the main checkout and the OTO are separate purchase events on separate pages. Most pixel setups either fire one generic Purchase on every page or tag only the first sale.
I fix this weekly. Here is the mechanism, the fix pattern, and how to prove it works before you trust the numbers.
The checkout page charges the card and saves it. The buyer lands on the OTO page. One click charges the saved card again. That is two charges, two order legs, two different pages. There is no single moment where "the order" completes.
Your tracking has to mirror that structure. Any setup built on the assumption "one funnel = one purchase" breaks in one of two directions, and both cost you money in ad optimization.
Failure A: one funnel-wide Purchase script. Someone pasted a Purchase snippet into the funnel-level tracking code, so it fires on the checkout confirmation and again on the OTO thank-you, usually with the same value. Meta counts both, because browser events with different event IDs are two separate purchases to Meta. Your main offer revenue doubles and ROAS looks better than it is. GA4 does the opposite: it sees the repeated transaction_id and discards the second event, so your upsell revenue silently disappears. Same mistake, opposite errors on the two platforms.
Failure B: Purchase only on the first confirmation page. The main sale tracks fine, the OTO tracks nothing. If your $97 upsell takes at 30%, roughly 40% of real revenue never reaches Meta. The algorithm then optimizes toward people who buy the front-end offer and ignores the buyers who take upsells, which are the buyers you actually want more of.
There is a third, sneakier one: OTO accept and decline both redirect to the same thank-you page, and that page fires an upsell Purchase on load. Now decliners generate upsell revenue that never happened.
The pattern that holds up:
For a fixed-price OTO, hardcoding the value on that page is fine and more reliable than scraping it from the DOM:
// Main sale page (first page after checkout)
fbq('track', 'Purchase', {value: 47.00, currency: 'USD'}, {eventID: 'CF-38291'});
gtag('event', 'purchase', {transaction_id: 'CF-38291', value: 47.00, currency: 'USD'});
// OTO thank-you (accept path only)
fbq('track', 'Purchase', {value: 97.00, currency: 'USD'}, {eventID: 'CF-38291-OTO1'});
gtag('event', 'purchase', {transaction_id: 'CF-38291-OTO1', value: 97.00, currency: 'USD'});The order ID should come from the page's order data so the server events can reference the same IDs. The suffix pattern (-OTO1, -OTO2) keeps legs tied to one order while staying unique.
Do not assume the tags fire the way the setup screen suggests. Run the funnel and watch the events.
Once each leg reports its true value, Meta's value optimization bids toward the buyers who take upsells instead of the cheapest front-end conversions. Ads Manager ROAS starts matching Stripe, so budget decisions stop being guesses. GA4 attribution shows which campaigns produce high-AOV buyers rather than just buyers. On funnels with a strong OTO, the reported-versus-real gap is often 30-50% of revenue. That gap is what the algorithm was optimizing without.
A Purchase snippet placed in funnel-wide tracking code fires on both the checkout confirmation and the OTO thank-you. Since each firing gets a different event ID, Meta counts them as two separate purchases. Move to one Purchase per order leg, each with that leg's own value.
GA4 discards purchase events that repeat a transaction_id it has already recorded. If your OTO purchase reuses the main order's transaction_id, it fires in DebugView but never reaches reports. Give the upsell leg its own ID, for example ORDER123-OTO1.
No. Each leg carries only its own value, because Meta and GA4 sum event values. If the OTO event repeats the base value, your reported revenue and ROAS inflate and Meta optimizes on numbers that do not exist.
For reliable numbers, yes. Browser-only events on post-checkout pages get lost to iOS privacy features and ad blockers, and the upsell leg is the most fragile one. Send a server event per leg with the same event_id as its browser twin so Meta deduplicates correctly.
This is the work I do: funnel tracking rebuilt with per-leg values, CAPI dedup, and a recorded test run so you can see the numbers land correctly. If you want yours checked, email work@rytisbalys.com.
work@rytisbalys.com