Your affiliate program will attract fraud before it attracts revenue. That sounds pessimistic, but the math checks out: a 30% recurring commission on a $49/mo plan pays $14.70/mo per referred customer. Fraudsters do the arithmetic faster than you do.
I build Referralful, affiliate software for SaaS on Stripe. These are five patterns I've seen in production, with the Stripe data points and queries that catch them.
1. Self-referrals
The simplest one. An affiliate signs up, generates their own referral link, opens an incognito window, and subscribes through it. They collect commission on their own purchase.
Detection signal: The affiliate's email domain, billing name, or Stripe customer metadata overlaps with the referred customer.
SELECT a.affiliate_email, c.customer_email, c.stripe_customer_id
FROM affiliates a
JOIN referrals r ON r.affiliate_id = a.id
JOIN customers c ON c.id = r.customer_id
WHERE LOWER(a.affiliate_email) = LOWER(c.customer_email)
OR a.billing_name = c.billing_name;
Check the Stripe payment method fingerprint too. Stripe assigns a unique fingerprint to each card. Same card on the affiliate account and the referred account? Self-referral.
# Compare card fingerprints via Stripe API
affiliate_methods = stripe.PaymentMethod.list(customer=affiliate_stripe_id, type="card")
customer_methods = stripe.PaymentMethod.list(customer=referred_stripe_id, type="card")
affiliate_fps = {pm.card.fingerprint for pm in affiliate_methods}
customer_fps = {pm.card.fingerprint for pm in customer_methods}
overlap = affiliate_fps & customer_fps
if overlap:
flag_referral(referral_id, reason="shared_card_fingerprint")
2. Churn-and-re-sign
An affiliate refers a real customer. The customer churns. The affiliate convinces them to cancel and re-subscribe through a fresh referral link, resetting the commission window.
Detection signal: The same Stripe card fingerprint or email appears in multiple referral records, attributed to the same affiliate, with short gaps between subscriptions.
SELECT r.affiliate_id,
c.customer_email,
COUNT(*) AS referral_count,
MIN(r.created_at) AS first_referral,
MAX(r.created_at) AS latest_referral
FROM referrals r
JOIN customers c ON c.id = r.customer_id
GROUP BY r.affiliate_id, c.customer_email
HAVING COUNT(*) > 1
AND EXTRACT(DAY FROM MAX(r.created_at) - MIN(r.created_at)) < 90;
The fix is simple: deduplicate on the customer, not the referral event. First-touch attribution, permanent. If a customer was ever referred by affiliate A, every future subscription from that customer still belongs to affiliate A's original referral, and you only pay the commission window once.
3. Cookie stuffing via redirect chains
An affiliate embeds your referral link in an iframe, image pixel, or redirect chain on a high-traffic page. Visitors never see the link or click it intentionally. The affiliate's cookie lands in their browser, and if they happen to sign up later, the affiliate gets credit.
Detection signal: Referral click volume with abnormally low conversion rates, or referral cookies set without a matching Referer header from a legitimate page.
Track these columns on every referral click:
click_timestamp | affiliate_id | ip_address | user_agent | referer_header | converted
Then flag:
SELECT affiliate_id,
COUNT(*) AS clicks,
SUM(CASE WHEN converted THEN 1 ELSE 0 END) AS conversions,
ROUND(100.0 * SUM(CASE WHEN converted THEN 1 ELSE 0 END) / COUNT(*), 2) AS conv_rate
FROM referral_clicks
WHERE click_timestamp > NOW() - INTERVAL '30 days'
GROUP BY affiliate_id
HAVING COUNT(*) > 500
AND conv_rate < 0.1;
An affiliate with 5,000 clicks and 2 conversions isn't driving real traffic.
4. Coupon code leaking
You give an affiliate a discount coupon to share with their audience. They post it on coupon aggregator sites (RetailMeNot, Honey, browser extensions). Customers who would have paid full price find the coupon, use it, and the affiliate collects commission on a discounted sale they never influenced.
Detection signal: High coupon redemption volume with referral source = coupon site, not the affiliate's actual content.
On the Stripe side, listen to customer.subscription.created and check subscription.discount.coupon.id against your affiliate coupon map. Then compare the customer.created timestamp with the referral click timestamp:
# If the customer existed BEFORE clicking the affiliate link,
# they probably found the coupon independently
if customer.created < referral_click.timestamp:
flag_referral(referral_id, reason="customer_predates_click")
The structural fix: stop tying coupons to affiliates. Use the referral link for attribution and a separate, non-affiliate discount for promotions. Or accept that coupon leaking is the cost of running discounts and price it into your commission structure.
5. Disposable card churn
Affiliates (or bots they run) create accounts using virtual/disposable cards, subscribe at the lowest tier, collect the first commission payout, then let the subscription lapse. Stripe processes the initial charge successfully, your system records a valid referral, and the commission enters your payout queue.
Detection signal: Multiple referred customers with the same IP, the same card BIN range, or subscriptions that cancel within the first billing cycle.
SELECT r.affiliate_id,
COUNT(*) AS total_referrals,
SUM(CASE WHEN s.canceled_at IS NOT NULL
AND s.canceled_at < s.current_period_end THEN 1 ELSE 0 END) AS early_cancels,
ROUND(100.0 * SUM(CASE WHEN s.canceled_at IS NOT NULL
AND s.canceled_at < s.current_period_end THEN 1 ELSE 0 END) / COUNT(*), 2) AS cancel_rate
FROM referrals r
JOIN subscriptions s ON s.id = r.subscription_id
WHERE r.created_at > NOW() - INTERVAL '60 days'
GROUP BY r.affiliate_id
HAVING COUNT(*) > 5
AND cancel_rate > 60;
An affiliate with a 70% first-cycle cancellation rate across 20 referrals isn't sending real customers.
The structural defense: hold commissions for a pending period (30, 60, or 90 days) before they become payable. If the referred customer cancels or the charge gets refunded during that window, the commission never pays out. I wrote about the ledger design for this in my previous article on clawbacks.
Run the checks before the first payout
The worst time to discover fraud is after you've paid the commission. Wire these queries into a pre-payout review step:
- Before any payout batch, run the self-referral and shared-fingerprint checks.
- Flag any affiliate whose referrals have a first-cycle cancel rate above 50%.
- Flag conversion rates below 0.1% on high-click affiliates.
- Deduplicate referrals by customer email + card fingerprint.
You don't need ML or a fraud vendor for this. A few SQL queries and a manual review queue handle it until you're processing hundreds of affiliates. Start there.
I'm building Referralful, affiliate software for SaaS startups on Stripe. Free until your first affiliate signs up.
Top comments (0)