DEV Community

Designing a referral system that can't be gamed by throwaway accounts

I just shipped a referral system for Adsyte, my free directory for indie projects, and the design decision behind it is worth sharing because it's a pattern that applies to any growth loop with a token reward attached.

The obvious version, and why it's broken

The naive implementation: give the recruiter tokens the moment someone signs up through their link. Simple, but it has an exploit built in. Signing up costs nothing, and OAuth makes throwaway accounts trivial. Anyone can self-refer through five Discord accounts and walk away with free reward tokens without bringing a single real user to the platform.

What I did instead

The payout only fires when the recruit publishes their first listing, not when they sign up. This one change closes the loop:

  • A fake account costs nothing, but a real listing needs an actual project with a real URL.
  • The listing already has to pass duplicate-URL detection and hCaptcha, so faking one is meaningfully harder than faking a signup.
  • Every token paid out corresponds to a listing the directory actually gained, which is the metric that matters, not signups.

Implementation notes

  • Referral code is an HMAC of the user's id, derived deterministically rather than stored as a random token, so there's nothing extra to generate or leak.
  • The code lives in a cookie set on landing (?ref=CODE), read once at OAuth callback, and tied to the account via a Redis SETNX so it can only ever be set once, self-referral excluded outright.
  • Payout uses SETNX again on a per-recruit key so double-firing (retries, race conditions) can't double-pay.
  • A daily cap per recruiter stops a single compromised or bot-driven account from draining the reward pool in one sitting.

Nothing here is novel, it's the standard "pay for the outcome, not the action" principle, but I don't see it applied to referral systems as often as it should be. Most implementations I've seen reward signup because it's the easy event to hook into, and then bolt on fraud detection after the abuse shows up. Reward the outcome that actually costs the abuser something, and you don't need the fraud detection layer at all.

Curious how others have handled this, especially the daily cap number. I picked 10 fairly arbitrarily.

Read on DEV Community ↗ ← Back to News

Comments

No comments yet. Start the discussion.