Blog

Stripe attribution: connect a payment to its traffic source

A practical guide by the AnalyticFast Team. Examples are illustrative; this article reports no original research or customer performance results.

When a Stripe payment has no acquisition source, inspect the identity handoff before adding more client-side purchase events. Your website knows the visit. Stripe knows the financial event. Attribution requires a deliberate connection between them.

Separate three identities

A visitor ID identifies browser analytics context. A Stripe Customer ID identifies the customer within Stripe. A PaymentIntent ID identifies a financial transaction for this integration. They serve different purposes. Do not replace an application’s client_reference_id or customer key with analytics data just because a checkout accepts a reference field.

Put metadata on the object the receiver reads

Stripe does not copy arbitrary metadata between every related object. For a one-time Checkout payment, use payment_intent_data.metadata so the successful PaymentIntent carries ml_visitor_id and ml_session_id. Session-level metadata alone is not enough. For a subscription, subscription_data.metadata can supply later invoice context. See Stripe’s propagation rules and the complete AnalyticFast Stripe setup.

// Browser, just before your existing server checkout request:
const ids = window.analyticfast?.attribution?.() ?? {};
// Send only present IDs as optional context.
// Your server validates them, keeps price selection under server control,
// and passes them through the Stripe metadata fields for the chosen mode.

Do not manufacture analytics IDs when the tracker is unavailable. Payment must continue even if attribution is missing. Keep the Stripe secret key, webhook signing secret and server API tokens out of browser code. The public Site ID is the only installation identifier your page needs.

Use the canonical financial event

The current adapter creates money from payment_intent.succeeded, keyed by the PaymentIntent ID. invoice.paid enriches attribution, product context and renewal classification; it does not add another sale. Configure both events along with refund.created and refund.updated. An invoice with no PaymentIntent reference is outside this adapter’s financial coverage. These are snapshot event contracts; thin events are unsupported. Stripe documents destination setup in its webhook guide.

Check the source allocation after matching

Matching a visitor is separate from selecting a session. If someone first arrives from Reddit, later visits through a newsletter and finally returns directly to buy, the default last non-direct model credits the newsletter. The first-touch view credits Reddit. A source change after switching models is not evidence of duplicated revenue.

Use a test matrix instead of one success-page check

Use a separate analytics test site and Stripe’s test environment. A local parser preview is useful for payload shape; it does not validate the delivery path. Once the matrix passes, reconcile live payments by currency and date without equating analytics net revenue with a payout after Stripe fees.

Add your website to AnalyticFast, or start with the installation guides and payment integrations.

Questions, answered.

Does Checkout Session metadata automatically reach the PaymentIntent?

No. For a one-time Checkout payment use payment_intent_data.metadata. Subscription mode has its own metadata path. Check Stripe’s documented propagation rules rather than assuming all related objects share metadata.

Should I record revenue again on checkout.session.completed?

Not for this integration. Its financial identity is the successful PaymentIntent. Adding another client or session-based revenue event can duplicate the same sale.

Keep exploring