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.
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
ReferredByPlayerIdand 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
ReferredByPlayerIdonly 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:AwardBadgeAsyncdoesn'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.