How to attribute Stripe revenue to marketing channels
Stripe attribution writes a visitor's source onto the payment in 6 steps. See where Stripe stores it, what to count as revenue and which tools do the join.
Muzahid Maruf, FounderUpdated
On this page
Explore with AI
Opens this article inside the chosen assistant with a ready-made prompt.
Stripe attribution means attaching the marketing source of a visit to the Stripe payment it produced, so revenue can be totaled by channel.
A Checkout Session has no UTM field, and the Payment Links tracking guide describes UTM codes coming back on the redirect URL.
Your side has to carry the source from the browser to the charge, which takes a first-party cookie and a few server lines, then a report that groups paid invoices by what you stored.
In the illustrative numbers below, paid social brings 57.1% of the trials on a $39 plan and 19.1% of the net revenue, so ranking by signups would misplace budget.
Key takeaways
- Stripe records no campaign for a sale. The source rides in client_reference_id (200 characters) or metadata (50 keys) that your server writes.
- Metadata copies between Stripe objects only in a few cases, so write the source onto the Subscription or PaymentIntent whose events you will read.
- Count revenue from invoice.paid, subtract the amount on each charge.refunded event, and wait for checkout.session.async_payment_succeeded on delayed methods such as ACH debits.
- In the worked example (illustrative figures), paid social brings 57.1% of the trials on a $39 plan and 19.1% of net revenue. Crediting only first payments would cut the newsletter's share from 48.4% to 39.0%.
What Stripe stores about a sale
A sale spreads across a Customer, a Checkout Session, a PaymentIntent, a Charge and, for subscriptions, an Invoice each billing period. Each has a metadata field, and the metadata guide says it copies between objects only in the cases below.
Only secret-key requests return it, so your server does the writing.
| Field | Limit | Where the value ends up |
|---|---|---|
| client_reference_id on a Checkout Session | 200 characters | The Session and its checkout.session.completed event |
| metadata on any object | 50 keys, 40-character keys, 500-character values | The object you set it on |
| payment_intent_data.metadata in Checkout | Same as metadata | The PaymentIntent, then a one-time copy on its Charge |
| subscription_data.metadata in Checkout | Same as metadata | The Subscription, then a copy on every later Invoice |
| Metadata on a Payment Link | Same as metadata | A snapshot on each Checkout Session the link creates |
| UTM codes on a Payment Link URL | 5 codes, 150 characters each | Your redirect URL |
From Stripe's metadata, Checkout and Payment Links documentation, October 2026.
Metadata set on a Customer does not copy to its Charges. Write the source onto the object whose events you will read, when you create it.
Four ways to join a payment to a source
The first and third routes need no checkout code, but the second is the one I would build, because only it stores a journey you own.
Google's Measurement Protocol guide asks for server events within 48 hours of the browser event, so renewals strain the fourth route. The Google Analytics guide has the build.
| Route | Code | Keeps | Breaks when |
|---|---|---|---|
| One Payment Link per channel | None | The channel, as link metadata | Placements outnumber links |
| Visitor ID in Stripe Checkout | A few server lines | The stored journey | Payments skip your server |
| Customer email | None | Any flow with matching emails | Signup and payment emails differ |
| GA4 purchase event | A webhook | Google's channel reports | Renewals arrive late |
How Stripe attribution works, step by step
The visitor ID route has 6 links, and a break in any one sends revenue to an unattributed row.
Step 1: Tag the link and save the source on your own domain
Give each placement its own lowercase UTM values: Google's Analytics Help page says parameter values are case sensitive, so Newsletter and newsletter count as 2 sources. Have your server set the cookie.
WebKit's cookie rules say Intelligent Tracking Prevention deletes JavaScript-created cookies after 7 days without interaction, and caps cookies set in an HTTP response at 7 days only when it detects third-party CNAME or IP address cloaking.
Step 2: Tie the visitor to an account at signup
Store the visitor ID, first-touch source and email on the user record. A buyer who clicks on a phone and pays on a laptop holds 2 cookies, and the email links them.
Step 3: Write the visitor ID into Stripe when checkout starts
Create the Stripe Checkout Session when the buyer clicks Buy, because it stays open for 30 minutes to 24 hours and one built at page load carries whatever the cookie held then.
Put the ID in client_reference_id and in metadata, with subscription_data.metadata for subscriptions and payment_intent_data.metadata for one-time payments. The Checkout attribution guide has the route and the metadata setup guide has key names.
A Payment Link skips your server, so append client_reference_id to its URL. Stripe accepts letters, digits, dashes and the _ character up to 200 characters, which fits a 36-character UUID. The Payment Links attribution guide covers the rest.
Step 4: Read the money from webhooks
Stripe's subscription webhook guide says invoice.paid is sent when an invoice is successfully paid. Each new invoice gets a copy of the subscription's metadata under parent.subscription_details.metadata, so payment 12 of a $39 plan carries the source written for payment 1.
Live mode retries a failed delivery for up to 3 days, so store each event ID under a unique index.
| Event | Fires when | Action |
|---|---|---|
| checkout.session.completed | Stripe Checkout finishes | Link visitor ID to customer |
| invoice.paid | Any invoice is paid | Book the revenue once |
| charge.refunded | A full or partial refund | Subtract the amount |
Events from Stripe's subscription and refund documentation, October 2026.
A completed Checkout Session is not always a settled sale
Stripe's Checkout fulfillment guide says ACH direct debit and other delayed methods fire checkout.session.async_payment_succeeded only once the payment succeeds, so credit a channel on that event.
Step 5: Group paid invoices by source, net of refunds
Credit one amount and write the rule down. A Checkout Session reports amount_subtotal, amount_total and total_details.amount_tax, so the pre-tax figure is easy to isolate.
Stripe's refund guide says a charge can take several refunds and the original processing fee is not returned, so keep gross and net in separate columns.
-- payments: one row per paid invoice or one-time charge, filled from the webhook-- refunds: one row per charge.refunded amount, linked to the paymentwith refunded as ( select payment_id, sum(amount_cents) as cents from refunds group by payment_id)select coalesce(p.source, 'unattributed') as channel, count(distinct p.customer_id) as customers, sum(p.amount_cents) / 100.0 as gross, sum(coalesce(r.cents, 0)) / 100.0 as refunded, sum(p.amount_cents - coalesce(r.cents, 0)) / 100.0 as netfrom payments pleft join refunded r on r.payment_id = p.idwhere p.livemode and p.paid_at >= date '2026-07-01'group by 1order by net desc;Step 6: Test the chain in a sandbox
Click a tagged link in a private window, sign up and pay with Stripe's test card 4242 4242 4242 4242 and a future expiry such as 12/34. Then refund it: the channel should gain the amount and lose it again.
What the numbers look like after the join
Take a $39 monthly plan and 90 days of sales from 3 channels. The figures are invented to show the arithmetic, so replace them with your own.
| Channel | Trials (share) | Paid invoices | Refunded invoices | Net revenue (share) |
|---|---|---|---|---|
| Paid social | 400 (57.1%) | 34 | 4 | $1,170 (19.1%) |
| Search ads | 180 (25.7%) | 54 | 3 | $1,989 (32.5%) |
| Newsletter | 120 (17.1%) | 78 | 2 | $2,964 (48.4%) |
| Total | 700 | 166 | 9 | $6,123 |
Net revenue is paid invoices minus refunded invoices, times $39, over 90 days. Illustrative figures.
Paid social leads on trials, with 400 of 700. It earns $1,170 of the $6,123, while the newsletter's 120 trials return $2,964. Per trial, that is $24.70 for the newsletter, $11.05 for search ads and under $3 for paid social.
Paid social also refunded 4 of 34 invoices, or 11.8%, against 2.6% for the newsletter, so gross revenue would have flattered it by $156.
Renewals shift the picture again. If a report credits only each customer's first payment, the newsletter's share falls from 48.4% to 39.0%, because its customers paid 2.6 invoices each against 1.7 for paid social.
The subscription LTV guide covers 3 ways to credit renewals.
| Channel | Paying customers | Invoices per customer | First payments only (share) | Net revenue (share) |
|---|---|---|---|---|
| Paid social | 20 | 1.7 | $780 (26.0%) | $1,170 (19.1%) |
| Search ads | 27 | 2.0 | $1,053 (35.1%) | $1,989 (32.5%) |
| Newsletter | 30 | 2.6 | $1,170 (39.0%) | $2,964 (48.4%) |
First payments are paying customers times $39, before refunds. Same 90 days of sales as above.
Choosing a window and a credit rule
TrackRev's attribution benchmarks put the median time from first click to first paid charge at 6.3 days and the 90th percentile at 48.2 days. Those counts include only conversions inside each workspace's window, so the true tail runs longer.
Pull the click-to-payment gap for your last 100 customers and set the window just past the 90th percentile.
Last touch gives the closing click all the credit, first touch gives it to the introducer, and linear splits it evenly, so a sale touched by 2 channels counts 0.5 on each.
In the Q2 2026 data, 64% of TrackRev workspaces default to last touch, 22% to linear and 14% to first touch. The attribution models comparison matches each rule to a funnel.
Stripe attribution tools compared
The Sigma guide says Stripe Dashboard financial reports can include metadata and Sigma's templates leave it out, with a Metadata to column template to add it.
Sigma runs ANSI SQL, shows up to 1,000 rows, exports all of them to CSV and costs nothing in a sandbox.
| Stripe Sigma billing | Charges included a month | Price a month | Each additional charge |
|---|---|---|---|
| Monthly | Not stated | $15 | 6¢ |
| Annual | 250 | $10 | 4¢ |
| Annual | 2,500 | $60 | 2.5¢ |
| Annual | 10,000 | $225 | 2.5¢ |
| Annual | 25,000 | $450 | 2¢ |
From Stripe's Sigma pricing page, October 2026. New users get a 30-day free trial.
| Approach | Sees the click | Renewals and refunds | Effort |
|---|---|---|---|
| Stripe Dashboard reports or Stripe Sigma | No, only stored metadata | Yes, in Stripe's data | A query plus the metadata write |
| Your own webhook and database | Yes, once you build the click log | Yes, from invoice.paid and charge.refunded | An endpoint, 2 tables and a report |
| Google Analytics 4 with the Measurement Protocol | Yes, inside GA4 | Limited by the 48-hour window | A webhook and a GA4 property |
| A billing-side tool such as TrackRev | Yes, via pixel and tracked links | Yes, renewals credit the first click | A pixel and a restricted key |
Where the chain breaks
| Symptom | Cause | Fix |
|---|---|---|
| Your total differs from Stripe's report | Stripe Dashboard reports default to your local time zone, Sigma templates use UTC, and a day's balance report publishes by 12:00 pm the next day | Match the time zone and compare after noon |
| Test payments inflate a channel | Test events arrive with livemode false | Skip them in production queries |
| One buyer shows up as 2 channels | A phone click and a laptop payment leave 2 cookies | Join on the email saved at signup |
Causes from Stripe's reporting and Sigma documentation, October 2026.
What TrackRev does with a Stripe charge
TrackRev Revenue Attribution reads Stripe with a restricted key, so checkout code can stay as it is.
The first sync reads the previous 30 days of charges, a scheduled job picks up new ones, and a webhook that TrackRev registers when the key allows it starts a sync on a successful charge, a refund or a completed Session.
For each charge it checks a vid key in the charge metadata, then the Session's client_reference_id or metadata, then the buyer's email, which the pixel binds to the visitor at signup or checkout.
A renewal inherits the first order's credit, and a charge with any refund is marked refunded on the next sync, so even a partial refund removes the whole charge.
Credit defaults to last touch, with first touch and linear available, and the window runs from 1 to 365 days, 30 by default.
Revenue figures need a paid plan, from Starter at $39 a month. The free plan is link tracking only, with 50 links and 1,000 tracked events a month. The Stripe integration page covers the pixel and the key permissions.
Pick something else when your payments run through none of Stripe, Paddle, Polar or Lemon Squeezy, or when you close a handful of sales a month. A metadata write and a spreadsheet will answer the question.
Found this useful? Share it.
Frequently asked questions
- No. Stripe keeps no referrer, landing page or campaign, so channel revenue exists only after you write a source into client_reference_id or metadata.
- Partly. One Payment Link per channel needs no code, and a billing-side tool can match payments to clicks by email. A visitor ID passed through Stripe Checkout is the most reliable match.
- invoice.paid for subscription payments, first and renewal alike, and checkout.session.completed with a paid status for one-time purchases. Subtract the amount on charge.refunded, which also covers partial refunds.
- Use the 90th percentile of days from first click to first payment in your own data, rounded up. The benchmark's 48.2 days counts only conversions inside each workspace's window.
- Yes, when every payment already carries a source in metadata and you only need totals. Stripe Sigma, from $15 a month, reads what Stripe holds, so a buyer who reached checkout without a stored source stays unattributed.
- No. The free plan covers link tracking with 50 links and 1,000 tracked events a month. Revenue views begin on a paid plan, and Starter costs $39 a month.

Written by
Founder, TrackRev.io & Contant.io
Muzahid Maruf is the founder of TrackRev.io and Contant.io. He writes about marketing attribution, link tracking, and revenue analytics for SaaS teams.
Writes about Marketing attribution · Link tracking · Revenue analytics · SaaS growth
Stop guessing where your revenue comes from.
Set up TrackRev in about five minutes. The free plan covers 1,000 events a month, no card needed.
Start free