How to stop players farming referral rewards

Referral links never expire, so paying on every join pays again and again. How to pay each invitee once and cap rewards per inviter.

warningLuaudit check: repeatable-referral-rewards

What it looks like

The game rewards players for inviting friends. It reads ReferredByPlayerId from Player:GetJoinData() when a player joins and pays the inviter, the invitee or both, but never records that the referral has been paid.

Luau
Players.PlayerAdded:Connect(function(player)
	local referrer = player:GetJoinData().ReferredByPlayerId
	if referrer and referrer ~= 0 then
		reward(player)
	end
end)

How to detect it

  • Find every read of ReferredByPlayerId and follow it to what it grants.
  • Roblox's referral system guide says a referral link never expires, and its advice on abuse is to make sure invitees are only rewarded once. Look for a saved record, keyed by the invitee's UserId, that is checked before paying and written when paying.
  • The guide's advice to prevent abuse is one-time rewards per invitee, a cooldown before an inviter can earn again, and watching for unusual activity. Check that at least the first two exist.
  • Look for a limit per inviter. Without one, an inviter with a stack of new accounts earns without end. The reward banner set up in the Creator Hub can show a reward limit, but that limit is only text. The code must enforce it.
  • Check the inviter's reward when the inviter isn't in the server. Paying them later needs the same once-only record.
  • Make sure the record is written before or with the reward, in saved data that has loaded. A record that isn't saved, or a load that failed, pays again next time.

What an exploiter could do

They leave and rejoin through their friend's link, and both get paid every time. With a few new accounts they farm the inviter's reward too. Neither needs an exploit, only the link.

How to fix it

  • Save the fact that an invitee has been referred, keyed by their UserId, and pay only the first time.
  • Cap how many referrals an inviter can be paid for, and how often.
  • Keep referral rewards modest, since new accounts are free.

Vulnerable example

Vulnerable
local Players = game:GetService("Players")
local PlayerData = require(script.Parent.PlayerData)

local inviteeReward = 100
local inviterReward = 250

Players.PlayerAdded:Connect(function(player)
	local referrerId: unknown = (player:GetJoinData() :: any).ReferredByPlayerId
	if typeof(referrerId) ~= "number" or referrerId == 0 or referrerId == player.UserId then
		return
	end
	local profile = PlayerData.waitFor(player)
	if profile then
		profile.Coins += inviteeReward
	end
	local inviter = Players:GetPlayerByUserId(referrerId)
	local inviterProfile = inviter and PlayerData.get(inviter)
	if inviterProfile then
		inviterProfile.Coins += inviterReward
	end
end)

Fixed example

Fixed
local Players = game:GetService("Players")
local PlayerData = require(script.Parent.PlayerData)

local inviteeReward = 100
local inviterReward = 250
local maxReferrals = 10

Players.PlayerAdded:Connect(function(player)
	local referrerId: unknown = (player:GetJoinData() :: any).ReferredByPlayerId
	if typeof(referrerId) ~= "number" or referrerId == 0 or referrerId == player.UserId then
		return
	end
	local profile = PlayerData.waitFor(player)
	if not profile or profile.Claimed.Referred then
		return
	end
	profile.Claimed.Referred = true
	profile.Coins += inviteeReward
	local inviter = Players:GetPlayerByUserId(referrerId)
	local inviterProfile = inviter and PlayerData.get(inviter)
	if inviterProfile and (inviterProfile.Progress.Referrals or 0) < maxReferrals then
		inviterProfile.Progress.Referrals = (inviterProfile.Progress.Referrals or 0) + 1
		inviterProfile.Coins += inviterReward
	end
end)

Common false positives

  • Reading ReferredByPlayerId only to show a welcome message, or to put friends in the same team, gives nothing away.
  • A reward saved as claimed per invitee, like the fixed example, is the fix, even if the inviter's limit is generous.
  • BadgeService:AwardBadgeAsync doesn't award a badge the player already has, though a player who deletes it can earn it again, so a referral that only awards a badge gives little to farm.