FloorSync / Guide for Stripe founders
Why Meta shows no revenue for your Stripe renewals, and the webhook fix
By the FloorSync team. You run Meta ads for a subscription. Stripe says 40 people are paying. Meta Ads Manager credits a handful of purchases, and almost none after month one. The cause is simple: a renewal happens on Stripe’s servers, and no browser is there to run your pixel.
Short answer: send each paid Stripe event from your webhook to Meta’s Conversions API, with the buyer’s fbp/fbc kept at checkout and the Stripe id as event_id. Then check it in Test Events, and compare a closed month in Stripe and Meta.
Why renewals never reach Meta
The Meta pixel is JavaScript in a page. It fires when a page loads. Look at when your subscription money arrives:
- Trial to paid. Day 8, Stripe charges the card. The buyer is asleep. No page loads.
- Every renewal. Same thing, every month. No page loads.
- First checkout, too, if the buyer doesn’t come back to your thank-you page: Checkout runs on Stripe’s domain, and a closed tab or an ad blocker means no Purchase.
So the pixel can only ever see a part of the first payments. Meta can’t credit what it never hears about. Propulse describes the same gap for GA4: browser tracking won’t catch recurring payments.
The fix, in five steps
01Keep the buyer’s ids at checkout
When your server creates the Checkout session, read the _fbp and _fbc cookies (or the fbclid in the landing URL) and GA’s _ga cookie, and store them with the Stripe customer: in the session metadata or your database. Renewals come later with only a customer id, so this is the only moment you can capture them. Without them Meta matches on hashed email alone, and GA4 sees a visitor from nowhere.
02Send from the webhook
Handle checkout.session.completed (when payment_status is paid) and invoice.paid, after checking Stripe’s signature. Post a Purchase to Meta’s Conversions API and the matching event to GA4’s Measurement Protocol. Look up the ids from step 01 by customer.
03Use the Stripe id as event_id
Checkout session id (cs_…) for the first sale, invoice id (in_…) for renewals. If the browser pixel also fired, Meta merges the two when they carry the same event_id. A webhook Stripe retries counts once. In GA4 use the same id as transaction_id.
04Check it in Test Events
In Events Manager, open your pixel, then Test events, and send an event with the test_event_code shown there. You see the match keys Meta read. For GA4, /mp/collect answers 204 whether the event is right or wrong, so send the same body to /debug/mp/collect first: it returns validationMessages.
05Compare a closed month
Take last month. Count paid Stripe invoices and sum them. Then count Meta purchases and value for the same month, same time zone, and look at the gap. Expect Meta to differ a little: it counts what it can attribute to ads, and its attribution windows are its own. A gap of one third or one tenth tells you what to fix. Don’t trust anyone’s “+30%” claim; your own two numbers are the test.
Should renewals go to Meta at all?
Honest answer: it depends, and the experts disagree. Tracking Consulting warns that sending every renewal as a new Purchase can inflate your numbers: there’s no new ad click behind month five, so old campaigns get credit for money that is just the same customer paying again. The case for sending them is that the campaigns that bring people who stay should get credit for it, and Meta’s optimisation learns from buyers who keep paying.
FloorSync’s choice: it sends a renewal as a Purchase marked system_generated (not website), matched on the ids from the first checkout, with the invoice id as event_id. Its ledger keeps new_paid and renewals apart, so you read new money and recurring money separately. If you want Meta to see first sales only, skip invoice.paid events whose billing_reason is subscription_cycle: one if in your handler. Either way, decide on purpose.
If an AI agent runs your ads
Meta’s ads MCP server (open beta since April 29, 2026) lets an agent read performance, edit campaigns and run signal diagnostics. As far as the published comparisons go, it does not send Conversions API events: that stays with the Marketing API. So an agent can see that revenue is missing but can’t fix it through that server. The agent needs a webhook path that sends the events.
Try it on a renewal, right here
Switch the buyer’s ids on and off: you see exactly what Meta and GA4 would get, and the notes say what’s missing. It’s worked out in your browser; nothing is sent.
Build it, or use FloorSync
Meta’s Conversions API is free. If Meta is your only ad platform and you or your agent can write and maintain the five steps, do that: it’s a few hundred lines. FloorSync does the same steps for $29 a month, with 7 free days: your webhook passes each verified Stripe event on, and FloorSync sends it to Meta and GA4 with the right ids, deduplicated, and keeps every send and its answer in a log. Add ?dry_run=1 and nothing is sent: you get the payloads back. Docs.
See your own renewals, free
A free key runs the dry run on your own Meta pixel and GA4. Just your email, no card. $29 a month per business after 7 free days, only when you start the plan.