turnley.dev

PHP and MySQL code for faucet operators

Tools & Calculators

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.

PHP
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.

Also in Referrals