Brazil's biggest MU Online portal — since 2003
Tutorial Intermediate Website

How to create a referral system in MU Online

Implement a referral system on your MU Online server's website, with unique links, Cash rewards, and protection against self-referral fraud.

GA Gabriel · Updated on Sep 14, 2025 · ⏱ 17 min read
Quick answer

A referral system turns your own players into the server's best acquisition channel. The logic is simple and powerful: each player gets a unique link; when a friend signs up through that link and actually starts playing, both earn a reward. The result is cheap, organic growth, driven by people who a

A referral system turns your own players into the server's best acquisition channel. The logic is simple and powerful: each player gets a unique link; when a friend signs up through that link and actually starts playing, both earn a reward. The result is cheap, organic growth, driven by people who already trust the server. But there's a chasm between the idea and an implementation that isn't immediately exploited by people trying to farm rewards with fake accounts. This tutorial shows how to build a referral system on your MU Online server's website that is both appealing to the honest player and resistant to fraud, with trackable links, release gated on real engagement, and automatic Cash crediting. We'll use PHP and SQL Server as the example, since they're the standard on most private servers, but the logic applies to any stack. Table and column names vary by version and emulator, so treat every query as a skeleton to adapt.

Prerequisites

The referral system lives on the website, so you need a working site integrated with the server's database. If you haven't integrated site and game yet, start with the guide on how to create a MU Online server before continuing.

  • The server's website running on PHP 7.4+ with access to the database.
  • SQL Server with the MU account table (often MEMB_INFO) accessible.
  • An account/login system already working on the site.
  • A column or table where the player's Cash (cash shop currency) is stored — the name varies by version (e.g., CashShopData, WCoinC, Credits).
  • Basic knowledge of PHP, SQL, and login sessions.

How the referral flow works

Before the code, lock in the flow. Each stage exists to close a fraud loophole:

  1. The logged-in player opens the "Refer a friend" page and sees their unique referral code and a ready-to-copy link.
  2. A new visitor clicks the link; the code is stored in their session/cookie.
  3. On signup, the new account is linked to the referrer with a pending status.
  4. The system monitors the referred player's progress. When they reach a real goal (e.g., level 150 or 3 days played), the status becomes released.
  5. Only then are the rewards credited to both sides, and the link is marked paid so it never pays twice.

The critical point is step 4: a reward released at signup is an invitation to fraud. A reward released by real engagement filters out 90% of the abuse.

Step 1 — Data structure

Create a dedicated table for referrals. It keeps an auditable history and separates the referral logic from the game tables:

CREATE TABLE Referrals (
    Id            INT IDENTITY(1,1) PRIMARY KEY,
    ReferrerAcc   VARCHAR(20) NOT NULL,   -- account that referred
    ReferredAcc   VARCHAR(20) NOT NULL,   -- referred account
    ReferredIP    VARCHAR(45) NULL,
    Status        VARCHAR(10) NOT NULL DEFAULT 'pendente', -- pending/released/paid
    CreatedAt     DATETIME NOT NULL DEFAULT GETDATE(),
    RewardedAt    DATETIME NULL,
    CONSTRAINT UQ_Referred UNIQUE (ReferredAcc) -- each account is referred only once
);

Generate the referral code from the account name itself (with a short hash) or store a random code per account. For simplicity, many servers use the username itself as the code.

Step 2 — Capture the code on arrival

When someone visits the site with ?ref=CODE, store that code before it gets lost. A long-lived cookie covers visitors who don't sign up in the same session:

<?php
// include at the top of the site's index.php
if (isset($_GET['ref']) && !isset($_COOKIE['ref_code'])) {
    $ref = preg_replace('/[^a-zA-Z0-9_]/', '', $_GET['ref']);
    if ($ref !== '') {
        // 30-day attribution window
        setcookie('ref_code', $ref, time() + 60*60*24*30, '/');
    }
}

Note the sanitization: we accept only valid account characters, which already rules out injection attempts through the URL.

In the signup processing, after successfully creating the account, record the link — but only after the anti-fraud checks:

<?php
function registerReferral(PDO $db, string $newAccount, string $newIP): void {
    if (empty($_COOKIE['ref_code'])) return;

    $ref = preg_replace('/[^a-zA-Z0-9_]/', '', $_COOKIE['ref_code']);
    if ($ref === '' || strcasecmp($ref, $newAccount) === 0) return; // self-referral

    // The referrer must actually exist
    $stmt = $db->prepare("SELECT memb___id FROM MEMB_INFO WHERE memb___id = ?");
    $stmt->execute([$ref]);
    if (!$stmt->fetch()) return;

    // Block the same IP between referrer and referred
    $ipInd = $db->prepare("
        SELECT r.ReferredIP FROM Referrals r
        WHERE r.ReferrerAcc = ? AND r.ReferredIP = ?");
    $ipInd->execute([$ref, $newIP]);
    if ($ipInd->fetch()) return; // already referred someone from this same IP

    $ins = $db->prepare("
        INSERT INTO Referrals (ReferrerAcc, ReferredAcc, ReferredIP)
        VALUES (?, ?, ?)");
    $ins->execute([$ref, $newAccount, $newIP]);
}

Step 4 — Release the reward on engagement

This is the anti-fraud core. A scheduled script (cron or Windows Task Scheduler) runs periodically, checks which referred players reached the goal, and releases the rewards. The example goal is level 150 on any character of the account — adjust the Character table name and the level column to your version:

<?php
// cron/release_referrals.php — run every 10 min
require 'db.php';

$pending = $db->query("
    SELECT Id, ReferrerAcc, ReferredAcc
    FROM Referrals WHERE Status = 'pendente'")->fetchAll(PDO::FETCH_ASSOC);

foreach ($pending as $r) {
    // Did the referred player reach the goal?
    $chk = $db->prepare("
        SELECT TOP 1 cLevel FROM Character
        WHERE AccountID = ? AND cLevel >= 150");
    $chk->execute([$r['ReferredAcc']]);

    if ($chk->fetch()) {
        creditCash($db, $r['ReferrerAcc'], 200); // referrer
        creditCash($db, $r['ReferredAcc'], 100);  // referred
        $up = $db->prepare("
            UPDATE Referrals SET Status = 'pago', RewardedAt = GETDATE()
            WHERE Id = ?");
        $up->execute([$r['Id']]);
    }
}

function creditCash(PDO $db, string $account, int $amount): void {
    // The Cash column/table name VARIES by version
    $up = $db->prepare("
        UPDATE MEMB_INFO SET Credits = Credits + ? WHERE memb___id = ?");
    $up->execute([$amount, $account]);
}

Note that the reward is credited only once, because the status becomes pago in the same logical transaction.

Step 5 — Player page

Give the logged-in player a page that shows the link and the history. That's what drives sharing:

<?php
$account = $_SESSION['username'];
$link  = "https://yoursite.com/?ref=" . urlencode($account);

$stats = $db->prepare("
    SELECT
      SUM(CASE WHEN Status = 'pago' THEN 1 ELSE 0 END) AS paid,
      COUNT(*) AS total
    FROM Referrals WHERE ReferrerAcc = ?");
$stats->execute([$account]);
$row = $stats->fetch(PDO::FETCH_ASSOC);
?>
<h2>Refer and earn</h2>
<input type="text" value="<?= htmlspecialchars($link) ?>" readonly onclick="this.select()">
<p>Friends who have already played: <?= (int)$row['paid'] ?> of <?= (int)$row['total'] ?> referred</p>

Anti-fraud protection layers

No single check solves it. Combine several and the cost of committing fraud outweighs the reward:

LayerWhat it checksLimitation
Engagement goalMinimum level/time of the referred playerFilters out throwaway accounts
Self-referral blockSame account nameStops the most obvious case
IP comparisonSame IP between the accountsDynamic IPs and VPNs get around it
HWID/hardwareSame machine (when available)Depends on client support
Unique emailSame email on both accountsOnly if email is mandatory
Per-referrer limitMaximum referrals per dayReduces mass farming

Common errors and fixes

SymptomLikely causeSolution
Rewards handed out en masse to ghost accountsRelease at signupGate it on the engagement goal in the cron
Same player earns multiple times for the same referredNo "paid" statusUse the unique constraint and mark as paid on crediting
Cash isn't creditedWrong column nameCheck the schema; the Cash column varies by version
The ?ref= code disappears before signupNo attribution cookieStore the code in a 30-day cookie
Self-referral slips throughOnly compares the nameCombine IP, HWID, and email in addition to the name
Cron doesn't run on WindowsNo scheduled taskCreate a task in Task Scheduler to call the PHP via CLI

Best practices

Always use prepared statements — the example above never concatenates user input into SQL. Log every reward release to audit disputes. Set rewards modest enough not to break the server economy, but attractive enough to motivate sharing; Cash is preferable to items because it's trivial to credit and reverse. And communicate the rules clearly on the referral page: goals, deadlines, and what counts as fraud that can lead to a ban.

Launch checklist

  • Referrals table created with a unique-referred constraint
  • Capture of the ?ref= code via a 30-day cookie
  • Link created on signup only after anti-fraud checks
  • Self-referral block by name, IP, and email
  • Cron/task releasing rewards on the engagement goal
  • Cash crediting tested with the server's real schema
  • Player page with link and history working
  • All queries using prepared statements
  • Rules published and rewards balanced with the economy
  • Release logs enabled for auditing

Frequently asked questions

When should I release the referral reward?

Never at signup. Release it only when the referred player reaches a real engagement goal, such as a minimum level or a minimum time played, to avoid fake accounts created just for the reward.

How do I stop someone from referring themselves?

Compare IP, hardware/HWID when available, and email between referrer and referred, and block rewards when they match. No single check is perfect; combine several.

Where do I store the referral code?

In a column on the accounts table or in a dedicated referrals table. The ideal is a dedicated table that records referrer, referred, status, and date, keeping an auditable history.

Can I give item rewards instead of Cash?

Yes, but Cash (the cash shop currency) is simpler and safer to credit via the website. Delivering items requires manipulating the inventory in the database, which is riskier and varies a lot by version.

Does the referral system work on any MU version?

The website logic is independent of the version. What changes is the name of the account tables and the way you credit Cash, which vary by emulator; adapt the queries to your schema.

GA
Guides & builds editor

Gabriel covers gameplay, class builds, PvP and progression. He tests every strategy on a live server before publishing.

Keep reading

Related articles