Stripe & PaymentsIntermediateUpdated June 25, 2026

Stripe revenue attribution for SaaS

Connect Stripe Checkout, subscriptions, invoices, refunds, and webhooks to source, campaign, landing page, and session reports.

TL;DR

Stripe knows the payment, customer, subscription, invoice, and refund. To attribute that revenue, your app must also pass the visitor and session context that existed before checkout. The safest pattern is to copy AttribIQ identifiers into Checkout Session metadata and subscription metadata, then process Stripe webhooks idempotently.

What you will learn
  • Understand which Stripe objects matter for attribution
  • Know why metadata is copied onto checkout and subscription objects
  • Map Stripe webhook events into normalized revenue events
  • Avoid common attribution mistakes around renewals and refunds

Stripe revenue attribution connects Stripe's payment lifecycle to the visits and campaigns that happened before the customer paid.

Stripe is the source for the money event. AttribIQ is the source for the browser and campaign context. The integration works when your app carries AttribIQ identifiers into Stripe metadata before checkout starts.

Stripe documents Checkout Sessions, metadata, and subscription webhooks in its official docs: Checkout Sessions, metadata, and subscription webhooks.

The objects that matter

For a subscription SaaS product, these Stripe objects usually matter:

  • Checkout Session for the initial checkout attempt.
  • Customer for the paying account in Stripe.
  • Subscription for recurring billing state.
  • Invoice for renewals and failed payments.
  • Charge or PaymentIntent for payment settlement and refunds.

The key mistake is only thinking about the first payment. If you want revenue by campaign, you also need to decide how renewals, upgrades, downgrades, refunds, cancellations, and churn should inherit or change attribution.

The metadata bridge

The browser tracker knows the AttribIQ project, visitor, and session. Before checkout, your frontend sends that attribution object to your backend.

const attribution = window.attribiq.getAttribution()

await fetch("/api/create-checkout-session", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ price_id: "price_123", attribution })
})

The backend copies that context into Stripe:

const session = await stripe.checkout.sessions.create({
  mode: "subscription",
  line_items: [{ price: priceId, quantity: 1 }],
  success_url: "https://example.com/billing/success?session_id={CHECKOUT_SESSION_ID}",
  cancel_url: "https://example.com/pricing",
  client_reference_id: attribution.visitor_id,
  metadata: {
    attribiq_project_id: attribution.project_id,
    attribiq_visitor_id: attribution.visitor_id,
    attribiq_session_id: attribution.session_id
  },
  subscription_data: {
    metadata: {
      attribiq_project_id: attribution.project_id,
      attribiq_visitor_id: attribution.visitor_id,
      attribiq_session_id: attribution.session_id
    }
  }
})

Copying metadata to both places matters. The Checkout Session helps with the first conversion. Subscription metadata helps with future invoice and subscription events.

Choose the ingestion path

There are two Stripe ingestion paths.

For most teams, Stripe should send selected webhook events directly to AttribIQ's native receiver. AttribIQ verifies the Stripe signature when the endpoint secret is configured, ignores duplicate Stripe event IDs, extracts the AttribIQ metadata, writes the revenue event, and runs attribution.

Use a customer-owned relay only when your backend needs to normalize custom billing semantics before AttribIQ receives the event. In that path, your webhook handler should:

  1. Verify the Stripe webhook signature.
  2. Store the Stripe event ID and reject duplicate deliveries.
  3. Extract AttribIQ metadata from the event object.
  4. Normalize the event into purchase, subscription_started, renewal, upgrade, downgrade, refund, cancellation, or churn.
  5. Use Stripe object IDs for idempotency.
  6. Send the normalized revenue event to POST /v1/revenue.

Do not rely on redirect success pages as the source of truth. They are useful for UI, but webhooks are the reliable payment lifecycle feed.

Attribution rules for renewals

Most SaaS teams attribute renewals back to the original acquisition source unless they have a reason to model expansion separately. That makes reports like "MRR by campaign" stable and understandable.

Refunds should subtract from the attributed source that received the original credit. Churn should be visible by source too. A campaign that creates many initial purchases but high refunds may be worse than a quieter channel with stronger retention.

Next steps

Start with passing attribution into Stripe Checkout, then connect Stripe to AttribIQ. If you do not use Stripe or need a custom relay, use the generic Payment API guide.

FAQ

Which Stripe events should I start with?

For the native AttribIQ receiver, start with invoice.paid, charge.refunded, and customer.subscription.deleted. Add checkout.session.completed and customer.subscription.updated only after you confirm those events match your billing model.

Should metadata live only on the Checkout Session?

No. Checkout metadata is useful for the first purchase, but subscription renewals and lifecycle changes need metadata on the subscription too.

Can I use client_reference_id for attribution?

You can use it as a reconciliation pointer, but you should still copy explicit attribution identifiers into metadata so webhook processing is clear and extensible.

What if some Stripe events arrive without attribution metadata?

Store and process them as unattributed revenue rather than dropping them. Missing attribution is a data quality issue, not a reason to lose revenue records.

Connect payments to the dashboard

Install the tracker, pass attribution into checkout, and report revenue by source, campaign, landing page, and path.

Related guides