REF-002
Attribute a referral with a cookie and a window
First touch or last touch, a window that expires, and the check that stops a user referring themselves.
A referral link sets a cookie, a signup reads it, and the referrer gets credited. The details decide whether the system is fair or farmed.
First touch or last touch is a real choice. First touch rewards whoever introduced the user, which is what most people mean by a referral. Last touch rewards whoever posted the link they happened to click most recently, which favours spam.
The window matters as much. Ninety days is generous and defensible; forever means a link clicked two years ago still pays, which nobody expects.
function record_referral_click(PDO $pdo, string $code): void
{
$st = $pdo->prepare('SELECT id FROM users WHERE ref_code = ? AND status = 1');
$st->execute([normalise_ref_code($code)]);
$referrerId = $st->fetchColumn();
if ($referrerId === false) {
return; // unknown code: ignore quietly
}
// FIRST touch: an existing cookie is not overwritten.
if (!empty($_COOKIE['ref'])) {
return;
}
setcookie('ref', (string) $referrerId, [
'expires' => time() + 90 * 86400,
'path' => '/',
'httponly' => true,
'secure' => ($_SERVER['HTTPS'] ?? '') !== '',
'samesite' => 'Lax',
]);
}
function referrer_for_signup(PDO $pdo, string $signupIp, string $visitorKey): ?int
{
$referrerId = (int) ($_COOKIE['ref'] ?? 0);
if ($referrerId <= 0) {
return null;
}
// Self-referral: same machine as the referrer's recent activity.
$st = $pdo->prepare(
'SELECT 1 FROM activity WHERE user_id = ? AND (ip = ? OR visitor_key = ?)
AND created_at > DATE_SUB(NOW(), INTERVAL 7 DAY) LIMIT 1'
);
$st->execute([$referrerId, $signupIp, $visitorKey]);
if ($st->fetchColumn() !== false) {
return null; // silently unattributed
}
return $referrerId;
}
Using it
Store the referrer id on the user row at signup, not the cookie value at earning time. The relationship is permanent; the cookie is not.
Accept the code in the URL and in a signup form field. People share codes as text far more often than as links.
Attribute silently when it fails. Telling someone their referral was rejected as self-referral just teaches them to use another device.
What bites people
Sharing an address with your referrer is normal in a household. Blocking it is the safer default on a site paying real money, but expect a few honest complaints.
SameSite=Lax lets the cookie survive a normal click from another site, which is exactly the case you need. Strict would break every referral link.
A cookie is deletable. Someone determined to farm referrals will clear it; the IP and device checks are what actually cost them effort.