Skip to main content
Stripe → MetaIntegration guide

Connecting Stripe to Meta Conversions API

Stripe knows exactly what was paid, by whom, and when — down to the cent, including the refund three weeks later. Meta's pixel loses a large share of conversions to Safari, ad blockers and iOS, and the Conversions API exists because of it. The join between them succeeds or fails on one thing: whether the fbc and fbp values reaches the Stripe Customer or PaymentIntent before the conversion happens, because Meta can only match a conversion it can tie back to a click it recognizes.

Free30 minutesNo obligation

Specific to this pair

Stripe to Meta: one event_id shared by the pixel and the webhook

Add {CHECKOUT_SESSION_ID} to Stripe's success_url, fire the Meta pixel Purchase with it as event_id, and send the Conversions API event from checkout.session.completed with the same ID, plus fbc, fbp and hashed email from customer_details. Put fbc and fbp in payment_intent_data.metadata if you use PaymentIntent events. Do not send subscription_cycle renewals as Purchases.

Stripe gives you a clean deduplication key if you use it. Add {CHECKOUT_SESSION_ID} to the success_url, have the thank-you page fire the pixel's Purchase with that ID as event_id, and send the server event from checkout.session.completed with the same session ID. Both sides then name the same conversion without passing anything extra between them.

If your handler listens to payment_intent.succeeded instead, note that Checkout Session metadata does not carry over to the PaymentIntent; set fbc and fbp under payment_intent_data.metadata as well. For match quality, Stripe Checkout collects the buyer's email in customer_details, and the billing address if you require it, which you can hash and send alongside fbc and fbp.

Subscriptions split into two kinds of event. The first checkout is a Purchase or Subscribe at the first charge's value; renewals arrive as invoice.paid with a billing_reason of subscription_cycle and should not be sent as new Purchases, because no fresh click sits behind them and they would credit old campaigns with recurring revenue. Meta also cannot retract a Purchase, so refunds reported by Stripe have to come out of your own ROAS calculations.

Stripe → Meta · what breaks

Why this join usually fails — Stripe → Meta

  1. 01

    The fbc and fbp values never reaches Stripe

    Stripe Checkout runs on checkout.stripe.com, a different origin from your site, so nothing you set in a first-party cookie travels with the customer. The identifier has to be put into PaymentIntent metadata (or the Checkout Session's metadata) at the moment you create the session, server-side. If you create sessions from a price link or a Buy Button, there is no server step and no metadata — that setup cannot be joined to advertising at all without changing how sessions are created.

  2. 02

    Checkout is on Stripe's domain, so the browser loses the thread

    The customer leaves your site, pays somewhere else, and comes back to a thank-you page. Anything relying on browser continuity across that hop is unreliable, and on Safari it is worse than unreliable.

  3. 03

    Double counting from broken deduplication

    Server and browser both fire, the event_id does not match, and Meta counts two purchases. Reported ROAS goes up, the business does not, and it can run for months before anyone reconciles against the store.

  4. 04

    Nothing reconciles afterwards

    Events Manager shows received events, the deduplication state and an Event Match Quality score per event. Events arriving is not the test — deduplicated, well-matched events arriving is. Compare Meta's reported purchases against your store's order count for the same window.

Stripe → Meta · the fix

What working looks like — Stripe → Meta

  • The fbc and fbp values on every payment

  • Conversions arriving in Meta

    Events appear in Events Manager with an Event Match Quality score, deduplicated against the pixel, and become usable for optimization and attribution.

  • Reconciled against the source

Stripe → Meta · architecture

How Stripe and Meta actually connect

  1. 1

    Stripe holds the truth

    Every succeeded PaymentIntent is a real conversion with a real amount and currency. No sampling, no modeling, no consent gate. This is the number the business actually banked, which is why it is the reference rather than anything either ad platform reports.

  2. 2

    Meta Conversions API needs a click to match

    Meta matches server events to people using whatever identifiers you send, hashed with SHA-256 where required. There is no single mandatory key, which is why quality varies so much between implementations — a poorly matched event is accepted, reported, and worth much less than it appears.

  3. 3

    The identifier is the bridge

    The fbc and fbp values, captured at the visit and carried into the Stripe Customer or PaymentIntent.

  4. 4

    The event source decides reliability

    Subscribe to payment_intent.succeeded (or checkout.session.completed) and send from your server when Stripe fires it. Reading a payment from the thank-you page instead means every customer who closes the tab on the redirect is a conversion you never counted, and Stripe redirects after taking money, not before.

  5. 5

    Deduplication at the destination

    Meta deduplicates on the pair of event_name and event_id. The browser pixel and the server event must send the same event_id for the same conversion, within the deduplication window. Get this wrong and every purchase counts twice — which is by far the most common Conversions API failure and it inflates reported ROAS rather than reducing it.

Stripe → Meta · normal or broken

Working vs. broken, side by side — Stripe → Meta

Normal, not a fault

  • The fbc and fbp values present on the payment

  • Sent from the Stripe webhook

  • Deduplicated correctly

    Meta deduplicates on the pair of event_name and event_id. The browser pixel and the server event must send the same event_id for the same conversion, within the deduplication window. Get this wrong and every purchase counts twice — which is by far the most common Conversions API failure and it inflates reported ROAS rather than reducing it.

  • Reconciles with the source

Actually broken

  • The fbc and fbp values missing on most records

    Stripe Checkout runs on checkout.stripe.com, a different origin from your site, so nothing you set in a first-party cookie travels with the customer. The identifier has to be put into PaymentIntent metadata (or the Checkout Session's metadata) at the moment you create the session, server-side. If you create sessions from a price link or a Buy Button, there is no server step and no metadata — that setup cannot be joined to advertising at all without changing how sessions are created.

  • Sent from the browser

    The customer leaves your site, pays somewhere else, and comes back to a thank-you page. Anything relying on browser continuity across that hop is unreliable, and on Safari it is worse than unreliable.

  • Counted twice, or not at all

    Server and browser both fire, the event_id does not match, and Meta counts two purchases. Reported ROAS goes up, the business does not, and it can run for months before anyone reconciles against the store.

  • Nobody has reconciled it

Stripe → Meta · diagnose

Diagnostic order — Stripe → Meta

  1. 01

    Count payments carrying the fbc and fbp values

  2. 02

    Follow one real conversion end to end

  3. 03

    Open one succeeded PaymentIntent in the Stripe dashboard

    Look at its Metadata section. If the identifier is not there, nothing downstream can fix it — the join is broken at the source and every other check is wasted effort.

  4. 04

    Read Event Match Quality and the deduplication indicator

    In Events Manager, open the event and check both. A low match quality means weak identifiers; a missing deduplication indicator means your event_id pairing is broken.

  5. 05

    Read the match rate, not the success message

    Events Manager shows received events, the deduplication state and an Event Match Quality score per event. Events arriving is not the test — deduplicated, well-matched events arriving is. Compare Meta's reported purchases against your store's order count for the same window.

Stripe → Meta · prove

How it gets proven — Stripe → Meta

Source of truth

The Stripe dashboard — Payments, filtered to succeeded, for the same window.

  • The fbc and fbp values confirmed present on a live payment.
  • Deduplication confirmed in Events Manager on a real purchase, with the browser and server events visibly paired rather than counted twice.
  • A refunded payment confirmed to reverse, not just the sale confirmed to send.

Stripe → Meta · free audit

What we check in the free audit

  • Count payments carrying the fbc and fbp values
  • Follow one real conversion end to end
  • Open one succeeded PaymentIntent in the Stripe dashboard
  • Read Event Match Quality and the deduplication indicator
  • Read the match rate, not the success message

Checked against

The Stripe dashboard — Payments, filtered to succeeded, for the same window.

Get my free conversion tracking audit

Free30 minutesNo obligation

Stripe → Meta · when not to buy

When you don't need this — Stripe → Meta

  • Measure identifier coverage yourself first — pull your last hundred payments and count how many carry the fbc and fbp values.
  • If you take payment through a hosted Payment Link or Buy Button and have no server creating sessions, this needs a development change on your side before any tracking work is worth commissioning. Worth knowing before you pay for tracking.
  • If you are not running Meta ads at meaningful spend, the Conversions API is effort without payoff. It earns its cost when the optimization signal is worth improving.

Stripe → Meta · what we deliver

What gets built — Stripe → Meta

  • Identifier capture into Stripe

    Read the click identifier from the landing page, persist it in a first-party cookie, and pass it into the create-session call as metadata. Stripe carries metadata onto the resulting PaymentIntent and Charge, so it survives to the webhook and to any later refund.

  • Event source: the Stripe webhook

    Subscribe to payment_intent.succeeded (or checkout.session.completed) and send from your server when Stripe fires it. Reading a payment from the thank-you page instead means every customer who closes the tab on the redirect is a conversion you never counted, and Stripe redirects after taking money, not before.

  • Dispatch to Meta

    Server-side events to the Conversions API with the event_id matched to the pixel, action_source set correctly, and customer data hashed before it leaves your infrastructure.

  • Reversals where they exist

    Stripe fires charge.refunded and charge.dispute.created. Both carry the original metadata, so a refund can be sent as a negative or a retraction wherever the destination supports it. Most setups never wire this and quietly overstate revenue by the refund rate.

Written guarantee

95%+ tracking accuracy

Measured against The Stripe dashboard — Payments, filtered to succeeded, for the same window.. Miss it and we keep working at no cost.

Get my free conversion tracking audit

From $400 · See packages

FAQ

Stripe → Meta questions, answered

The Checkout Session ID, available to the success page through the {CHECKOUT_SESSION_ID} placeholder and to the server in the webhook. Both sides then send the same value.

Free · 30 min

Get Stripe and Meta joined properly

Free30 minutesNo obligation