DEV Community

Mihir kanzariya
Mihir kanzariya

Posted on

Five affiliate fraud patterns I caught on Stripe (and the queries that found them)

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;
Enter fullscreen mode Exit fullscreen mode

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")
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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")
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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:

  1. Before any payout batch, run the self-referral and shared-fingerprint checks.
  2. Flag any affiliate whose referrals have a first-cycle cancel rate above 50%.
  3. Flag conversion rates below 0.1% on high-click affiliates.
  4. 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)