Stripe Affiliate Tracking: Attribute Subscriptions Without a Pixel
Anderro · · 5 min read
Any conversion pixel that fires from a browser can be fired by anyone with a browser console, which is why stripe affiliate tracking built on a thank-you-page script is a payout liability. The fix is to split the job in two. The browser carries a referral code from the landing page to signup, and nothing more. Stripe's own server-side events decide whether money moved and how much commission it earned.
This post walks through that data flow for a subscription product, in order, with the settings I'd use and the four tests I'd run before letting a single affiliate in.
Why a browser-reported conversion can be forged
A typical pixel setup looks like this: the checkout success page loads a script that calls track('purchase', {amount: 99, ref: 'alex'}). The affiliate platform receives that request and creates a commission.
The problem is that the request is just an HTTP call. An affiliate named alex can open the network tab, copy it, change the amount to 999, and replay it 40 times. No card was charged. The platform has no way to tell a forged call from a real one because both arrive from an ordinary browser with an ordinary IP.
A Stripe webhook is different in three ways:
- It is sent by Stripe's servers, not the visitor's browser.
- It is signed with a webhook secret that only you and Stripe hold, so a forged payload fails signature verification.
- It describes money that actually settled: an amount, a currency, a customer ID and an invoice ID you can look up.
So the rule for any stripe affiliate system is simple: clicks can come from the browser, commissions cannot.
Steps 1 and 2: capture the code, then bind it at signup
Step 1: store the referral code in a first-party cookie. An affiliate shares yoursite.com?ref=alex. A small JS snippet on your site reads the ref parameter, records a click, and writes the code to a cookie on your own domain. Set the cookie on the root domain (.yoursite.com) so it survives the hop from marketing site to app.yoursite.com.
Pick a window and a model up front. My default is 30 days, last click. If your sales cycle runs long or you sell annual plans after a trial, a longer window makes sense; I go through the tradeoffs in Affiliate Cookie Duration: How Long Should Your Window Be?.
Step 2: link the customer to the referral at signup. This is the one moment the browser's information gets promoted to trusted data. When the visitor creates an account, your backend reads the cookie and writes the code somewhere permanent:
- On your user record (
referred_by = 'alex'). - On the Stripe customer as metadata (
metadata.ref = 'alex'), or passed asclient_reference_idwhen you create a Checkout Session.
Once the referral lives on the Stripe customer, every future payment from that customer carries it. The cookie can expire, the user can switch laptops, and renewal number 14 still attributes correctly.
Step 3: create commissions from charge and invoice.paid
Now the server side takes over. Subscribe to invoice.paid for subscription renewals and charge events for one-off payments. On each event:
- Verify the webhook signature. Reject anything that fails.
- Look up the customer's referral code.
- Deduplicate on the invoice or payment ID so a retried webhook never pays twice.
- Create a commission in a pending state.
Worked example: a $49/mo plan at 20% recurring creates a $9.80 commission on every invoice.paid. If the customer stays 12 months, that's 12 events and $117.60 to the affiliate, with no browser involved after signup. If you're still deciding the rate, What Is a Good Affiliate Commission Rate for SaaS? Do the Math runs the numbers.
You can build this yourself in a few hundred lines, or use a tool that already does it. Anderro, for example, connects to Stripe through Stripe Connect OAuth, creates commissions only from charge and invoice.paid events, and never creates a commission from a browser-reported payment.
Step 4: reverse on refunds and disputes, then pay outside Stripe
Pending commissions should sit through a hold period before approval. Set it at least as long as your refund policy: with a 30-day money-back guarantee, a 30-day hold means most refunds land before you owe anything.
Handle reversals by state:
| Event | Commission not yet paid | Commission already paid |
|---|---|---|
| Full refund | Cancel it | Negative adjustment on next payout |
| Partial refund of $24.50 on $49 | Reduce $9.80 to $4.90 | Claw back $4.90 next payout |
| Dispute opened | Reverse it | Reverse it |
| Dispute won | Restore it | Restore it |
One thing that surprises founders: Stripe does not pay your affiliates. Stripe affiliate payouts are a separate step. Once a month, you total approved commissions above a minimum threshold (I use $50), send the money via PayPal or Wise, and mark the payout as paid. Anderro handles the ledger side of this, including netting negative adjustments, while you send the actual payment through PayPal, Wise or Paystack on a monthly, quarterly or manual schedule.
Test four cases in Stripe test mode before launch
Don't open your stripe affiliate program until these four pass end to end. Create a test affiliate, click their link in a private window, and sign up with a fresh email.
- Test-mode purchase. Pay with Stripe's
4242 4242 4242 4242test card. Confirm one pending commission at the right amount, attributed to the right affiliate. - Renewal. Use a Stripe test clock to advance the subscription a month. Confirm a second commission appears from
invoice.paidand no duplicate was created. - Partial refund. Refund half of one invoice from the dashboard. Confirm the commission drops proportionally.
- Dispute. Pay with Stripe's documented dispute test card. Confirm the commission reverses, then confirm it comes back if you simulate winning.
Also try the forgery case on purpose: replay the success-page request from the browser and confirm nothing gets created.
Where this goes wrong
The webhook half is rarely the failure point. Attribution breaks in the gap between click and signup.
- Cookie on the wrong domain. Set on
www.yoursite.com, it won't be readable onapp.yoursite.com. Every referral silently drops. - OAuth signups. Google or GitHub sign-in redirects through a third party. If your callback doesn't read the cookie before creating the user, the referral is lost.
- Customer created too early. Some apps create the Stripe customer before signup completes and never update metadata. Write the ref at creation or patch it immediately after.
- $0 invoices. Free trials and 100% coupons fire
invoice.paidwith zero amount. Skip commissions on zero, or you'll clutter reports with $0.00 rows. - Double counting. Listening to both charge and
invoice.paidfor the same subscription payment pays twice unless you dedupe on a shared ID. - Self-referrals. An affiliate buying through their own link passes every technical check. Compare the customer email with the affiliate's account and payout emails; the other patterns are in Affiliate Fraud Detection for SaaS: Six Patterns, Six Controls.
If you're setting this up as part of a wider launch, How to Start an Affiliate Program for Your SaaS, in Order covers what comes before and after tracking.
Your next move: open your codebase, find where the Stripe customer is created, and confirm the referral code is written to it on that same request. If it isn't, that's the bug to fix before anything else.
Frequently asked questions
Can Stripe pay my affiliates directly?
In a typical SaaS setup, no. Stripe is your billing source of truth and supplies the payment events that create commissions, but the commission ledger and the actual affiliate payments live elsewhere. Most programs total approved commissions monthly and send them through PayPal or Wise, then mark each payout as paid in their affiliate tool so balances and clawbacks stay accurate.
Should I pay recurring commissions or first payment only?
For subscription products I default to recurring, because affiliates who send customers that stay deserve to share in that retention, and it rewards quality over volume. First payment only suits low-price plans with high churn or annual-only billing. Run the numbers: a $49 plan at 20% recurring costs $117.60 per customer over a year, versus $9.80 with first payment only.
What happens to a referral if the customer signs up with Google?
It depends on your OAuth callback. The referral cookie still sits on your domain when Google redirects back, so your callback handler must read it before creating the user and the Stripe customer. Many apps create the account in a separate code path for OAuth and forget this step, which silently drops every social-login referral. Test it explicitly before launch.
How long should the commission hold period be?
Match it to your refund window at minimum. With a 30-day money-back guarantee, a 30-day hold means most refunds arrive while the commission is still unpaid and can simply be cancelled. If you see chargebacks arriving later than that, extend the hold. Longer holds annoy affiliates, so in my judgment anything beyond 60 days needs a clear reason.
Run your affiliate program on Anderro
Stripe, Paddle and SamCart tracking, recurring commissions, refund clawbacks and fraud checks on every plan. 14-day trial.
Start your trial