Stripe + Google Ads: bid on confirmed revenue, not checkout events
Stripe knows which payments cleared, failed or were refunded. Store the GCLID on the PaymentIntent or Subscription, upload a conversion only when money clears, and retract refunds within Google's 54-day adjustment window. From June 15, 2026, Google moves offline conversion uploads to the Data Manager API, so build new integrations there.
A 47-point written audit of your Google, Meta or Shopping account, back in five business days.
Get your auditBook a callWhat this integration unlocks
- Conversions based on cleared payments rather than checkout page loads
- Refund and dispute adjustments so bidding sees net revenue
- Subscription renewal value fed back to Google Ads from invoice.paid events
- Accurate value signals for value-based bidding
- A revenue truth layer between the front end and the ad platform
Setup steps
- 1
Put the GCLID on the object your webhook will read
Capture the GCLID from the landing page URL and pass it to your server when you create the Stripe payment. Where you put it matters, because Stripe metadata does not copy automatically between related objects. Metadata set on a Checkout Session stays on the session. To reach the payment itself, set payment_intent_data.metadata for one-time payments, or subscription_data.metadata for subscriptions, when you create the session or Payment Link.
Stripe then copies a one-time snapshot of PaymentIntent metadata to the Charge it creates, and of Subscription metadata to each Invoice under parent.subscription_details.metadata. Later edits do not flow through. Metadata allows 50 keys, with 40-character keys and 500-character values, and Stripe says never to store sensitive data such as card details there.
- 2
Listen for the events that mean money cleared
For one-time Checkout payments, handle checkout.session.completed and check the session's payment_status before counting anything. Delayed methods such as ACH direct debit complete Checkout before the funds arrive, so wait for checkout.session.async_payment_succeeded, and treat checkout.session.async_payment_failed as no sale. For subscriptions, use invoice.paid; Stripe also advises confirming the subscription status is active.
Stripe warns that your handler can receive the same event more than once, even concurrently, and in live mode it retries failed webhook deliveries for up to 3 days. Record which payments you have already uploaded so a retry never creates a second conversion.
- 3
Upload the conversion through the Data Manager API
Google's Help Center says that from June 15, 2026, offline conversion imports and enhanced conversions for leads uploads move to the Data Manager API and are blocked in the Google Ads API. The Google Ads API docs put it more narrowly: from that date, UploadClickConversions requests fail for developer tokens that had not already been sending these uploads. Either way, a new Stripe integration should use the Data Manager API, which does not need a developer token. If you have not built offline imports before, Google recommends starting with enhanced conversions for leads, which matches on hashed email or phone.
Send the GCLID, the conversion time, the cleared value and currency, and a unique order ID such as the PaymentIntent ID. Google does not import conversions uploaded more than 90 days after the last click, or more than 63 days for enhanced conversions for leads. After creating a new conversion action, wait 4 to 6 hours before the first upload.
- 4
Retract or restate refunds and disputes
Stripe sends charge.refunded for full or partial refunds and charge.dispute.created when a customer disputes a charge. Map each back to the conversion you uploaded, using the order ID, and send a conversion adjustment: a retraction for a full refund, or a restatement with the new value for a partial one.
Google allows adjustments within 54 days of the conversion, but only adjustments made within 7 days of the conversion being recorded are used by automated bidding. A refund issued after 54 days cannot be reflected at all. Google identifies the conversion by order ID, or by GCLID plus conversion time; for clicks that came in with a GBRAID, only the order ID works. For file uploads, Google also requires adjustments and new conversions in separate files.
- 5
Close the failed payment gap
A failed payment is not a refund, and treating it like one is a common logic error. If you only upload conversions after money clears, a failed payment never produced a conversion, so there is nothing to retract. Sending an adjustment for it is wrong.
The real gap is upstream. If anything uploads a conversion on checkout.session.completed while payment_status is still unpaid, or when a subscription is created in incomplete status, a later failure leaves a conversion in Google Ads for money that never arrived. Gate every upload on a paid state. For renewals, invoice.payment_failed means no renewal conversion yet; if a later retry succeeds, invoice.paid fires and you upload then.
Common issues
Fix. The GCLID was set as metadata on the Checkout Session, which does not copy to the PaymentIntent or Subscription. Set it with payment_intent_data.metadata or subscription_data.metadata when creating the session, or read it back from the session in your handler.
Fix. Uploads fire on checkout.session.completed without checking payment_status. Delayed methods such as ACH complete Checkout before funds clear. Upload only when payment_status is paid or on checkout.session.async_payment_succeeded, and on invoice.paid for subscriptions.
Fix. Adjustments must arrive within 54 days of the conversion, and bidding only uses those made within 7 days. Process charge.refunded events promptly, and upload each conversion with an order ID so the adjustment can find it.
Fix. Google moved offline conversion uploads to the Data Manager API, and UploadClickConversions requests now fail for developer tokens that had not used them before. Move the upload to the Data Manager API.
Fix. Use one source of truth for the purchase conversion. If Stripe sends the confirmed conversion, make sure the front-end tag is not also counting the same order as a primary conversion.
Common questions
- Google Ads Help: About offline conversion imports
- Google Ads API: Manage offline conversions
- Data Manager API: Upgrade from the Google Ads API
- Google Ads Help: Guidelines for importing offline conversions
- Google Ads Help: Set up offline conversions using GCLID
- Google Ads Help: Upload conversion adjustments
- Stripe Docs: Metadata
- Stripe Docs: Fulfill orders (Checkout)
- Stripe Docs: Using webhooks with subscriptions
Want this checked on your own account?
A 47-point written audit of your Google, Meta, or Shopping account. Five business days, no sales call.

