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
- 01What does Stripe store about a sale?
- 02Which 4 ways join a payment to a source?
- 03How does Stripe attribution work, step by step?
- 04What do the numbers look like after the join?
- 05How do you choose a window and a credit rule?
- 06How do Stripe attribution tools compare?
- 07Where does the chain break?
- 08What does TrackRev do with a Stripe charge?
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.
The work takes a first-party cookie and a few server lines, then a report that groups paid invoices by what you stored.
This guide covers the 6 steps, the Stripe events that count as revenue and 4 ways to run the join in 2026.
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.
For example, the newsletter sends 17.1% of the trials and earns 48.4% of the revenue, as the worked table later in this guide shows.
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 does Stripe store about a sale?
A Stripe sale is a chain of objects: a Customer, a Checkout Session, a PaymentIntent, a Charge and, for subscriptions, an Invoice each billing period.
Each object has a metadata field, and the metadata guide says metadata copies between objects only in the cases below. Only secret-key requests return metadata, 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.
For example, a subscription written with subscription_data.metadata lets all 12 invoices of a year carry the source (October 2026).
Which 4 ways join a payment to a source?
Four routes join a payment to a source: a Payment Link per channel, a visitor ID in Stripe Checkout, the customer email and a GA4 purchase event.
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. For instance, a renewal billed on day 30 arrives long after that window closes.
The Google Analytics guide has the build. In practice, the visitor ID route breaks only when payments skip your server, as a Payment Link does.
| 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 does Stripe attribution work, step by step?
Stripe attribution by visitor ID is a chain of 6 links, and a break in any one sends revenue to an unattributed row. Step 1 tags the link and saves the source. Step 2 ties the visitor to an account.
Step 3 writes the ID into Stripe Checkout. Step 4 reads the money from Stripe Billing webhooks. Step 5 groups paid invoices by source. Step 6 tests the chain in a Stripe sandbox, where no real card is charged.
Each step names the exact Stripe field or event, with limits taken from Stripe's documentation of October 2026.
Step 1: How do you tag the link and save the source?
A UTM tag is a lowercase label such as utm_source=newsletter, and each placement needs its own values, stored on arrival.
Google's Analytics Help page says parameter values are case sensitive, so Newsletter and newsletter count as 2 sources in Google Analytics. Have your server set the cookie.
According to WebKit's cookie rules, Safari Intelligent Tracking Prevention deletes JavaScript-created cookies after 7 days without interaction. The same rules cap cookies set in an HTTP response at 7 days only when WebKit detects third-party CNAME or IP address cloaking.
A cookie from your own server therefore stays outside the 7-day script rule in 2026.
Step 2: Why tie the visitor to an account at signup?
A visitor record is the row that holds the visitor ID, first-touch source and email, and the user record should store all 3 values at signup.
A buyer who clicks on a phone and pays on a laptop holds 2 cookies and 1 email, and the email links the 2 cookies.
The signup row also survives a cleared cookie, so a payment made weeks later still finds its source. Lowercase the email before saving, so Jane@Example.com and jane@example.com match.
In practice, save the row at signup and read the row at payment, because the 2 events can sit days apart in Stripe Billing.
Step 3: How do you write the visitor ID into Stripe?
A Checkout Session is the Stripe object that carries the visitor ID, and the Session should be created when the buyer clicks Buy.
A Session stays open for 30 minutes to 24 hours, so one built at page load carries whatever the cookie held then. Put the ID in client_reference_id and in metadata, using the subscription or PaymentIntent field that matches the mode.
A UUID takes 36 characters, so 164 of the 200 allowed characters stay free. The Checkout attribution guide has the route and the metadata setup guide has key names.
For example, a Stripe Checkout Session created at 10:00 can still be paid 24 hours later.
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.
| Limit | Value | Why it matters |
|---|---|---|
| Checkout Session lifetime | 30 minutes to 24 hours | A Session built early holds an old cookie |
| client_reference_id | 200 characters | A 36-character UUID fits |
| Metadata keys per object | 50 | A visitor ID plus 6 UTM fields uses 7 |
| Live-mode webhook retries | Up to 3 days | Duplicate events need a unique index |
Limits from Stripe's Checkout, metadata and webhook documentation, October 2026.
Step 4: Which Stripe events count as revenue?
Revenue events are the webhooks that fire when money moves, and 3 of them cover most flows. 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.
For example, a retried invoice.paid event must not book the same $39 twice, and a 12-month plan books exactly 12 events.
| 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: How do you group paid invoices by source?
A source report is a table that totals paid invoices per 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. The SQL below totals customers, gross, refunded and net per channel from 2 tables.
In the worked example, the Newsletter row nets $2,964 of $6,123 across 166 invoices. Unattributed rows stay visible as their own channel, which shows how much of the 100% of revenue the join misses.
-- 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: How do you test the chain in a sandbox?
A sandbox test is a rehearsal that proves the chain before real revenue depends on the chain.
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 the payment: the channel should gain the amount and lose it again. Check 3 rows afterward, namely the visitor row, the payment row and the refund row.
Production queries must skip test events, as the break table below explains.
What do 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.
How do you choose a window and a credit rule?
A window is the number of days a click can still earn credit for a payment.
In our attribution benchmark data, the median time from first click to first paid charge is 6.3 days and the 90th percentile is 48.2 days. The counts cover only conversions inside each workspace's window.
Pull the click-to-payment gap for your last 100 customers and set the window just past the 90th percentile. Rounding the 48.2-day benchmark up gives a 49-day starting window, which your own figure then replaces.
Last touch gives the closing click all the credit, first touch gives the credit to the introducer, and linear splits the credit evenly, so a sale touched by 2 channels counts 0.5 on each.
In our 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.
| Credit rule | Share of TrackRev workspaces | Who gets the credit |
|---|---|---|
| Last touch | 64% | The closing click |
| Linear | 22% | Every touch, evenly |
| First touch | 14% | The introducing click |
Default credit rule per workspace in TrackRev's Q2 2026 benchmark data.
How do Stripe attribution tools compare?
Stripe attribution tools are the reports and software that total Stripe revenue by source, from Stripe Sigma to billing-side tools.
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 Stripe sandbox.
Pricing starts at $15 a month on monthly billing and falls to 2¢ per additional charge on the 25,000-charge annual tier, per Stripe (October 2026).
The second table compares 4 approaches by what each can see, from Google Analytics 4 to billing-side software.
| 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 does the chain break?
The chain breaks in 3 common places, and each break is a number that disagrees with Stripe. Check the time zone first, then test events, then split buyers.
For example, a Sigma query and a Dashboard report can disagree on one day's total near midnight (October 2026). The table lists all 3 symptoms with their causes and fixes.
| 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 does TrackRev do with a Stripe charge?
TrackRev is SaaS affiliate software with revenue attribution built in, and we built it to read 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 the sync 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. Six processors feed the same model: Stripe, Paddle Billing, Polar, Lemon Squeezy, Creem and Dodo Payments.
What does TrackRev cost, and when is another option better?
TrackRev pricing is simple: revenue figures need a paid plan, from Indie at $29 a month, and the free plan is link tracking only, with 50 links and 1,000 tracked events a month.
The pixel and key permission steps sit on our own Stripe integration page (2026).
Pick something else when your payments run through none of its 6 processors, when you want a standalone attribution product, or when you close a handful of sales a month.
In that case, a metadata write and a spreadsheet will answer the question.
| Setting | Value |
|---|---|
| First sync | Previous 30 days of charges |
| Attribution window | 1 to 365 days, 30 by default |
| Processors | 6 |
| Free plan | 50 links, 1,000 tracked events a month, no revenue figures |
| Indie plan | $29 a month, 1 workspace, 1 tracked domain |
TrackRev settings used in this guide, October 2026.
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.
- Take the 90th percentile of days from first click to first payment in your own data and round up. A benchmark median understates the tail.
- 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. Revenue views begin on a paid plan, and Indie costs $29 a month. The free plan stops at link tracking.

Written by
Founder, TrackRev.io & Contant.io
Muzahid Maruf founded TrackRev.io, SaaS affiliate software with no limit on tracked revenue, and Contant.io. He writes about affiliate programs.
Writes about Marketing attribution · Link tracking · Revenue analytics · SaaS growth
Run your affiliate program without a revenue cap.
Affiliate software for indie hackers and small SaaS teams: $29 a month flat, 0% fees, link tracking and revenue attribution built in. Free to start, no card.