How to run a Stripe affiliate program, from click to payout
A 9-step Stripe affiliate program setup: tag each referral in Checkout, create commission when invoices are paid, handle refunds, then pay affiliates.
Muzahid Maruf, Founder
On this page
Explore with AI
Opens this article inside the chosen assistant with a ready-made prompt.
A Stripe affiliate program comes down to four jobs: capture the referral when someone clicks, carry the affiliate's ID into Stripe Checkout or onto the subscription, create commission from money Stripe has collected (invoice.paid for Stripe Billing subscriptions), and take it back when a charge is refunded.
Stripe stores the payment, but nothing on a charge says who sent the customer.
Key takeaways
- Stripe copies metadata between objects in only four cases, so decide early which object holds the affiliate ID: the Checkout Session, the subscription or the customer.
- A Payment Link or Buy Button needs a handler that copies the ID onto the subscription, or the second month's invoice has no affiliate on it.
- Pay commission on paid invoices and before tax. At 20% on a $49 plan the right base pays $9.80, and a 19% VAT-inclusive base pays $11.66.
- Hold payouts longer than your 30-day refund window, because card chargebacks typically arrive within 120 days.
- Paying 40 affiliates $150 each costs $120 on Stripe Connect and $60 on Global Payouts with US bank transfers.
What Stripe records and where an affiliate ID can live
Stripe gives you three places to attach a referral. A Checkout Session takes a client_reference_id. Payment Links, Buy Buttons and pricing tables accept the same value as a URL parameter or an HTML attribute.
Customers, subscriptions and PaymentIntents take metadata, which Stripe's metadata guide caps at 50 key-value pairs, with keys up to 40 characters and values up to 500.
The same guide says metadata does not copy automatically between related objects, with four exceptions, so an ID set on the wrong object never arrives on the invoice you will pay commission on.
| Where you set it | Reaches renewal invoices? | What Stripe does with it |
|---|---|---|
Checkout Session: client_reference_id | No | Stays on the Checkout Session and appears in checkout.session.completed |
Payment Link, Buy Button or pricing table: client_reference_id | No | Passed to the session as a URL parameter or attribute of up to 200 characters; invalid values are silently dropped |
Subscription: metadata via subscription_data | Yes | Copied to each new invoice as parent.subscription_details.metadata |
Customer: metadata | By lookup | Nothing is copied; you fetch the customer from the invoice |
PaymentIntent: metadata via payment_intent_data | One-time payments only | Snapshotted onto the Charge once; later edits are not |
Sources: Stripe documentation on metadata, Payment Links URL parameters, pricing tables and Buy Buttons, October 2026.
Step 1: Set the terms your Stripe data has to support
Four numbers decide what the rest of the setup must record: the commission rate, how long renewals keep earning it, the hold before commission becomes payable, and the click-to-purchase window.
The affiliate benchmark tables list 20% for 12 months as the median B2B SaaS program and 60 days as the median cookie window.
| Monthly plan | Monthly commission | Commission over 12 months | Gross profit left |
|---|---|---|---|
| $19 | $3.80 | $45.60 | $136.80 |
| $49 | $9.80 | $117.60 | $352.80 |
| $99 | $19.80 | $237.60 | $712.80 |
| $199 | $39.80 | $477.60 | $1,432.80 |
20% for 12 months, customer retained all 12, 80% gross margin assumed.
Write the commission base into the terms as well: the invoice amount after discounts and before tax.
Step 2: Capture the click on your own domain
Set the referral cookie from your server, on your own domain.
WebKit documents that Intelligent Tracking Prevention deletes cookies created in JavaScript after 7 days without user interaction, so a 60-day window written by a script shrinks to a week for Safari visitors who do not return.
A cookie on your domain is also invisible on buy.stripe.com, so the ID has to travel in the URL or through your checkout code. The cookie side has its own guide: affiliate tracking without third-party cookies.
Step 3: Pass the affiliate ID into Stripe
When your server creates the Checkout Session, read the cookie and set two fields in one call. client_reference_id labels the Checkout Session. subscription_data.metadata writes the ID onto the subscription Stripe creates, which is what lets renewals find their affiliate.
// aff_id was set when the visitor clicked the affiliate linkconst affiliateId = (await cookies()).get("aff_id")?.value; const session = await stripe.checkout.sessions.create({ mode: "subscription", line_items: [{ price: "price_123", quantity: 1 }], client_reference_id: affiliateId, subscription_data: { metadata: { affiliate_id: affiliateId } }, success_url: "https://example.com/welcome",});For a Payment Link, append ?client_reference_id= and the affiliate's ID to the URL, and Stripe sends the value in checkout.session.completed after payment. Pricing tables and Buy Buttons take a client-reference-id attribute.
A link-level subscription_data.metadata is set once when you create the link, so it names an affiliate only if you create one Payment Link per affiliate through the API. Stripe Payment Links attribution covers that flow.
A podcast read has no clickable link, so give that affiliate a promotion code. Stripe's Promotion Code object has a metadata field and a max_redemptions limit, so one code per affiliate can name its owner and cap its use.
Coupon code tracking for affiliate programs covers the rest.
Step 4: Make the ID survive to renewals
A Payment Link or Buy Button puts the ID on the Checkout Session only, so the month-2 invoice arrives with no trace of the referral.
Close the gap in your checkout.session.completed handler by writing the ID to the subscription and the customer.
const session = event.data.object;const affiliateId = session.client_reference_id; if (affiliateId && session.subscription) { await stripe.subscriptions.update(session.subscription, { metadata: { affiliate_id: affiliateId }, });}if (affiliateId && session.customer) { await stripe.customers.update(session.customer, { metadata: { affiliate_id: affiliateId }, });}Stripe's webhook guide says not to depend on event order, so the first invoice.paid can arrive before this write lands. For an invoice with a billing_reason of subscription_create, list the Checkout Sessions with a subscription filter and read client_reference_id there.
For later invoices, check parent.subscription_details.metadata, then the subscription, then the customer.
Step 5: Create commission from collected money
Create the commission for a subscription on invoice.paid, which Stripe's subscription webhook guide describes as sent when an invoice is successfully paid.
Skip any invoice with an amount_paid of 0, and use the invoice ID as the commission row's unique key so a replayed event changes nothing.
| Stripe event | What it tells you | What to do |
|---|---|---|
invoice.paid | An invoice, first or renewal, was paid | Create commission if amount_paid is above 0 |
checkout.session.completed | Checkout finished; payment_status is paid, unpaid or no_payment_required | Copy the affiliate ID (step 4); commission only on one-time Checkout Sessions marked paid |
checkout.session.async_payment_succeeded | A bank debit or voucher settled, which Stripe says can take 2 to 14 days | Create the one-time commission |
charge.refunded | A refund, partial ones included | Recompute commission (step 7) |
charge.dispute.created | A cardholder opened a dispute | Freeze the commission (step 7) |
customer.subscription.updated | A renewal, plan change or discount | Update the base for future invoices |
customer.subscription.deleted | The subscription ended | Stop accruing |
Event names and descriptions from Stripe's subscription webhook, refund, Checkout fulfillment and Payment Links guides, October 2026.
A free trial can look like a sale
Stripe's Checkout reference says a free-trial session reports payment_status paid once the $0 trial invoice is processed. A handler that creates commission on checkout.session.completed without checking the amount pays for trial signups that never convert.
Verify the signature and return a 2xx before slow work. Stripe's webhook documentation says failed deliveries are retried for up to three days in live mode and the same event can arrive twice, so log event IDs.
Then choose one amount per invoice and keep to it. total_excluding_tax is the invoice total after discounts and before tax; subtotal is before invoice-level discounts. Take a $49.00 plan billed to a customer in Germany with 19% VAT added.
Commission on the VAT-inclusive total overpays by the tax rate on every renewal.
| Commission base | Invoice amount | 20% commission | Overpayment |
|---|---|---|---|
Before tax (total_excluding_tax) | $49.00 | $9.80 | None |
| Invoice total with 19% VAT | $58.31 | $11.66 | $1.86 a renewal |
A $49.00 monthly plan; $58.31 is $49.00 plus $9.31 of VAT.
Step 6: Follow upgrades, downgrades and cancellations
Commission follows invoices, so most plan changes need no special code: an upgrade makes the next invoice larger and a cancellation stops invoices. What needs a rule is the recurring window.
Count paid invoices per subscription using billing_reason (subscription_create for the first, subscription_cycle for each renewal, subscription_update for a mid-cycle change) and stop at your term.
Decide whether proration invoices earn commission, and whether the term counts months or invoices; annual plans make that difference visible.
| Scenario | Paid invoices | Affiliate earns |
|---|---|---|
| Stays on the $49 plan all year | 12 invoices of $49.00 | 12 × $9.80 = $117.60 |
| Upgrades to $99 after month 4 | 4 of $49.00, then 8 of $99.00 | $39.20 + $158.40 = $197.60 |
| Cancels after month 3 | 3 invoices of $49.00 | 3 × $9.80 = $29.40 |
| Starts a trial and cancels before the first charge | One $0.00 invoice | $0.00 |
Worked example: $49 monthly plan, 20% commission for 12 months, proration ignored.
Step 7: Reverse refunds and hold for chargebacks
Stripe sends charge.refunded for every refund, partial ones included. Recompute the commission from the charge's cumulative refunded amount so a replayed event cannot subtract twice.
If the original commission was already paid, carry the difference as a negative balance on the next payout; the refunds and upgrades guide walks through the ledger states.
| Case | Event | Commission before | Commission after |
|---|---|---|---|
| Full refund of a $49 renewal | charge.refunded | $9.80 | $0.00 |
| $100 partial refund on a $300 annual charge | charge.refunded | $60.00 | $40.00 |
| Dispute opened on a $49 renewal | charge.dispute.created | $9.80 | Frozen until charge.dispute.closed, then reversed if lost |
At a 20% rate. The partial refund leaves $200 of net revenue, and 20% of $200 is $40.
Holds and chargebacks run on different clocks
Stripe's dispute documentation says card networks typically let a cardholder file within 120 days of the payment, and a dispute can take 2 to 3 months to resolve. A 30-day hold covers refund requests; a chargeback filed in month 3 or 4 arrives after it. Hold as long as your affiliates will accept, and state the hold length in the terms before the first payout.
Step 8: Pay the affiliates
Stripe settles your customers' payments to you. Getting part of that money to affiliates is a separate job, and the rails differ in reach and cost.
| Payout rail | Who it reaches | Cost for 40 affiliates at $150 | Catch |
|---|---|---|---|
| CSV export, then PayPal or Wise | Wherever PayPal or Wise operates | Provider fees only | You run each payout cycle by hand |
| Stripe Connect transfers | Affiliates in your platform's region, with limited cross-border reach | $80 account fees + $25 payout fees + $15 routing = $120 (2.0%) | Each affiliate onboards with Stripe; a transfer fails if your balance is short |
| Stripe Global Payouts | Recipients without Stripe accounts; the sender must be a US or UK business | 40 US bank payouts at $1.50 = $60 (1.0%) | Requires Stripe Treasury; you manage your own compliance, which Stripe says can call for a Money Transmitter License |
| Rewardful Managed Payouts | Optional on every Rewardful plan | 3% of $6,000 = $180 (3.0%) | Affiliates may also pay withdrawal fees |
| Tolt auto payouts | PayPal, Wise, local bank, crypto and wire | 2% of $6,000 = $120 (2.0%) | Growth plan ($99) or higher |
Forty payouts of $150 is $6,000 in a month. Connect rates from Stripe's Connect pricing page: $2 per monthly active account, 0.25% + $0.25 per payout, plus the 0.25% of payout volume funds-routing line in its feature table. Global Payouts rate from Stripe's Global Payouts pricing. Vendor rates from Rewardful and Tolt, October 2026.
Stripe Connect pays out of your own Stripe balance: Stripe's separate charges and transfers guide says a transfer fails when it exceeds your available balance, with no automatic retry.
Transfers cross borders only between the US, Canada, the UK, the EEA and Switzerland. Stripe's docs now call Standard, Express and Custom accounts legacy and model a payee as a v2 Account with a recipient configuration.
Stripe restricts transfers to permitted use cases, and its cross-border payouts page lists affiliate payouts under Global Payouts, so ask Stripe whether your program fits before you build.
For a first program I would start with the CSV route: Stripe Connect's $2 monthly account fee alone is 1.3% of a $150 payout, and every affiliate must finish Stripe onboarding.
Collect a W-9 or W-8 before the first payout on any rail; how to pay affiliates covers methods and tax forms.
Step 9: Test the whole path with test clocks
Stripe's test clocks (called simulations in the current docs) move a test subscription through time, so a month-2 renewal takes minutes.
Run one referred customer through a payment, a renewal, an upgrade, a partial refund and a cancellation, then compare the ledger with the invoice list.
Repeat with a Payment Link and an API-created Checkout Session, which take different paths through steps 3 and 4, and run an unreferred customer to confirm nothing is paid.
Build it yourself or use an affiliate tool
Steps 3 to 7 stay yours to maintain while the program runs: event order, replays, disputes and payout files all land on you.
A tool connects to Stripe, reads the same events and runs the ledger, and those steps are the checklist for judging one: how it finds the affiliate on a renewal, what it pays on, and what it does with a partial refund.
Entry plans differ on what they cap and how they pay.
| Tool | Entry plan, monthly price | What the entry plan caps | Payouts |
|---|---|---|---|
| Rewardful | Starter, $49 | $7,500/month of affiliate-attributed revenue, 1 campaign | PayPal and Wise mass payouts; Managed Payouts optional at 3% |
| FirstPromoter | Starter, $49 | $5,000/month of affiliate revenue, 1,000 affiliates | Mass payouts through PayPal, Wise, Stripe, crypto and bank transfer |
| Tolt | Basic, $69 | $10,000/month earned through affiliates | Manual payouts on Basic; auto payouts from Growth at $99 |
| PromoteKit | Pro, from $29 (yearly toggle shown); free plan covers 3 referrals | $10,000/month from affiliates | One-click global payouts on Pro |
| TrackRev | Starter, $39 | $10,000 of commission tracked monthly, 1 workspace | Wise or PayPal CSV export, then mark paid |
Prices from each vendor's pricing page, October 2026. Rewardful, FirstPromoter and Tolt each offer a 14-day trial.
Caps are measured differently. The first four cap affiliate-driven revenue; TrackRev caps the commission it tracks, so at a 20% rate $10,000 of commission corresponds to $50,000 of affiliate revenue.
The full comparison is in best affiliate software for Stripe in 2026.
How TrackRev handles the Stripe side
TrackRev connects with a restricted Stripe key and syncs paid charges hourly; an optional webhook cuts the delay to seconds. It never writes metadata onto your customers or subscriptions, which removes step 4.
Its pixel sets a first-party visitor ID and adds it as client_reference_id to Payment Links, Buy Buttons and pricing tables on your pages; for Checkout Sessions you create, pass it as client_reference_id or metadata.vid.
Without either, it matches the customer's email, and renewals inherit the affiliate from the first charge on the subscription.
Per program you set a percentage or flat amount, first sale only or renewals too, a 60-day click window by default, a hold of 0 to 180 days (default 0) and a payout schedule from weekly to quarterly.
Paying means exporting a PayPal or Wise CSV and marking each payout paid.
Against the build above, TrackRev reads charges, so the base is the charge amount, including any tax Stripe collected. Any refund, partial ones too, reverses that charge's whole commission with no proration. The program needs a paid plan.
| TrackRev plan | Monthly price | Commission tracked monthly | Workspaces |
|---|---|---|---|
| Free | $0 | None; link tracking only, with 50 links and 1,000 events | 1 |
| Starter | $39 | Up to $10,000 | 1 |
| Growth | $99 | Up to $100,000 | Unlimited |
| Scale | $199 | Unlimited | Unlimited |
Annual billing charges for 9 months and gives 12.
Pick Tolt (Growth, $99) when automatic payouts with 1099 filing matter more than the entry price, PromoteKit when Stripe is your only billing system and price decides it, and TrackRev when the affiliate program should sit beside link tracking and channel attribution on one data model.
The affiliate program page lists what partners see in their portal.
Found this useful? Share it.
Frequently asked questions
- Stripe's docs describe no affiliate feature: a charge carries an amount, a customer and whatever IDs and metadata you attach, so Stripe affiliate tracking is your code or a tool's. Stripe does list affiliates among the recipients of Global Payouts.
- Yes. Tools such as Rewardful ($49 and up) and TrackRev ($39 and up) connect to Stripe through an authorization grant or a restricted API key and run the ledger for you. You still set the terms and run the tests before inviting anyone.
- Start near the benchmark medians, 20% for B2B SaaS and 15% for B2C. Then check the rate against margin. At 80% gross margin, a 20% commission leaves 60% of every payment as gross profit, and 30% would leave 50%.
- Longer than your refund window and shorter than your affiliates' patience. A 30-day refund policy points to a hold of 30 to 45 days. Stretching it to 120 days catches most chargebacks but delays every first payout by four months. TrackRev's hold is set per program from 0 to 180 days and defaults to 0.
- Nothing is created until Stripe collects the money. A failed attempt produces no invoice.paid event, so a $49 renewal that fails twice and clears on the third attempt earns its $9.80 at 20% once, on that day. One that never clears earns nothing.
- A 12-month term counted in months covers one invoice on an annual plan. A $490 plan at 20% earns $98.00 once, when that invoice is paid. If affiliates should earn on the second year too, define the term in renewals or years and put it in writing.
- No. The free plan covers link tracking only, with 50 links, 1,000 tracked events monthly and 1 workspace, and the affiliate program stays locked. It starts on Starter at $39.

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