How to build a Vote4Coins system (Gtop100, XtremeTop) in MU Online
Build a complete Vote4Coins system for your MU Online server, rewarding players who vote on lists like Gtop100 and XtremeTop via a secure callback (postback), with anti-fraud, cooldown and automatic coin crediting.
Voting lists like Gtop100 and XtremeTop are one of the cheapest organic growth engines a MU Online server can have. The more votes you accumulate, the higher you appear in the ranking, and the more new players discover the server without spending a cent on ads. The problem is that no one votes for f
Voting lists like Gtop100 and XtremeTop are one of the cheapest organic growth engines a MU Online server can have. The more votes you accumulate, the higher you appear in the ranking, and the more new players discover the server without spending a cent on ads. The problem is that no one votes for free for very long — that is why Vote4Coins exists: a system that trades votes for in-game coins. In this intermediate-level tutorial you will build that system from scratch, focusing on what really matters: crediting only votes confirmed by the list, through the callback (postback), and hardening everything against fraud.
The classic mistake of beginners is crediting the reward the moment the player clicks the "Vote" button. That is an invitation to fraud: the player clicks, gets the coin, closes the tab without completing the vote, and repeats. The right way is to understand that there are two distinct moments — the redirect (the player goes to the list) and the callback (the list confirms to your server that the vote was valid). The reward lives in the second moment. All the field, parameter and table names here are examples and vary by version of the distribution and by list; always check each site's current documentation and your database schema.
Prerequisites
- A PHP 7.4+ website (ideally 8.x) with access to the website and game databases.
- An administrator account registered on each list you are going to use (Gtop100, XtremeTop, TopG, XtremeTop100, etc.).
- Your server already registered on those lists, with the Site ID / server ID and the secret key (sometimes called "postback key", "callback key" or "incentive hash") in hand.
- A login system on the website that identifies the player's game account (to know who receives the reward).
- HTTPS active — many lists require a secure callback URL.
- A notion of how to credit coins in the game database (WCoin, VIP credits, event points — varies by version).
How the voting flow with callback works
Before coding, understand the full path of a rewarded vote. There are six steps, and each one has a clear role.
- The logged-in player clicks "Vote on Gtop100" on your site.
- Your backend records a pending vote and redirects the player to the list's URL, embedding an identifier (usually the IP or a
pingUsernamewith the player's login). - The player solves the CAPTCHA and confirms the vote on the list.
- The list validates the vote (unique IP in the period, correct CAPTCHA) and makes an HTTP request to your callback stating that the vote was successful.
- Your callback validates the origin and the secret key, finds the player by the identifier and credits the reward idempotently.
- The player returns to the site and sees the updated balance; the cooldown starts counting.
| Step | Who executes it | Trust it to credit? |
|---|---|---|
| Click "Vote" | Player | No |
| Redirect | Your backend | No |
| Confirmation on the list | Player + list | Not yet |
| Callback/postback | The list's server | Yes, after validating |
| Crediting | Your backend | — |
Modeling the votes and rewards tables
Create, in the website database, a table to track each vote. It controls the cooldown and the callback's idempotency.
CREATE TABLE votes (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
account VARCHAR(20) NOT NULL, -- game account
site VARCHAR(30) NOT NULL, -- 'gtop100', 'xtremetop', ...
ip VARCHAR(45) NOT NULL,
status ENUM('pending','confirmed') DEFAULT 'pending',
reward INT NOT NULL DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
confirmed_at DATETIME NULL,
INDEX idx_cooldown (account, site, created_at),
INDEX idx_ip (ip, site, created_at)
);
A second configuration table helps keep the rewards and cooldowns per list without touching the code:
CREATE TABLE vote_sites (
site VARCHAR(30) PRIMARY KEY,
display_name VARCHAR(60) NOT NULL,
reward INT NOT NULL, -- e.g. 200 WCoin per vote
cooldown_h INT NOT NULL DEFAULT 12,
secret_key VARCHAR(120) NOT NULL,
vote_url VARCHAR(255) NOT NULL
);
Step by step: the vote redirect
When the player clicks to vote, you record the pending vote and send them to the list. The identifier sent depends on the list: some use the player's IP to match the callback; others accept a pingUsername with the login. Generic example:
<?php
session_start();
$account = $_SESSION['account'] ?? null;
if (!$account) { header('Location: /login'); exit; }
$site = $_GET['site'] ?? '';
$cfg = buscarVoteSite($site); // reads from vote_sites
if (!$cfg) { http_response_code(404); exit; }
// check the cooldown before allowing another vote
if (emCooldown($account, $site, $cfg['cooldown_h'])) {
header('Location: /votar?erro=cooldown'); exit;
}
// record a pending vote
registrarVotoPendente($account, $site, $_SERVER['REMOTE_ADDR'], $cfg['reward']);
// redirect to the list (URL and parameters VARY by list)
$url = str_replace('{USER}', urlencode($account), $cfg['vote_url']);
header("Location: {$url}");
The vote_url for Gtop100, for example, usually follows the server page pattern with an incentive parameter (pingUsername). On XtremeTop, the match is normally by IP. Check each list's documentation for the exact parameters — they change over time.
Step by step: the callback (postback)
This is the heart of the system. The list makes a request to your callback when the vote is valid. The format varies a lot:
- Gtop100 normally sends a POST with fields like
Successful(1 = success, 0 = failure),pingUsername(the identifier you sent),pingIPand aVoteID, plus a key-based verification. - XtremeTop100 usually makes a simple GET to your callback passing the voter's IP, and you must match that IP to the most recent pending vote.
The code pattern, however, is always the same: validate, match, credit idempotently, respond.
<?php
// Gtop100 callback (example — fields vary)
$sucesso = $_POST['Successful'] ?? '0';
$user = $_POST['pingUsername'] ?? '';
$ip = $_POST['pingIP'] ?? $_SERVER['REMOTE_ADDR'];
// 1. validate the origin: secret key and/or the list server's IP
if (!validarChaveSecreta('gtop100', $_POST)) {
http_response_code(403); exit('invalid');
}
if ($sucesso === '1') {
// 2. find that player/list's pending vote
$voto = buscarVotoPendente($user, 'gtop100');
if ($voto) {
creditarVoto($voto['id']); // idempotent
}
}
echo 'ok'; // many lists expect a specific response
Note that the callback does not trust the player's browser — the request comes from the list's server. Even so, validate the secret key and, if possible, restrict by the list's IP range, because an attacker can try to call your callback directly to forge votes.
Idempotent crediting of the reward
The crediting function must ensure that the same pending vote does not become coins twice. Use the status transition as a lock:
<?php
function creditarVoto(int $voteId): void
{
global $siteDb, $gameDb;
// lock: only credit if it is still 'pending'
$upd = $siteDb->prepare(
"UPDATE votes SET status='confirmed', confirmed_at=NOW()
WHERE id=? AND status='pending'"
);
$upd->execute([$voteId]);
if ($upd->rowCount() === 0) return; // already credited
$v = /* fetch account and reward from the vote */;
// credit in the GAME database (table/column VARY by version)
$gameDb->prepare(
"UPDATE MEMB_INFO SET WCoin = WCoin + ? WHERE memb___id = ?"
)->execute([$v['reward'], $v['account']]);
}
If the callback is resent (some lists repeat on timeout), the WHERE status='pending' guarantees a single credit. The WCoin column in MEMB_INFO is the typical Season 6 case; your version may use Cash, its own points table or a VIP credit system. Varies by version — confirm before.
Anti-fraud: cooldown, IP and secret key
Vote4Coins is a magnet for abuse. Reinforce three barriers:
- Per-account and per-list cooldown: respect the list's official interval (12 or 24 h). Block a new pending vote before that deadline.
- IP control: record the IP and limit votes per IP in the period, making it harder for the same player to multi-account.
- Callback validation: a mandatory secret key and, when possible, verification of the list server's source IP. Never accept a callback just because it "looks" right.
<?php
function emCooldown(string $acc, string $site, int $horas): bool {
global $siteDb;
$q = $siteDb->prepare(
"SELECT 1 FROM votes
WHERE account=? AND site=? AND status='confirmed'
AND created_at > (NOW() - INTERVAL ? HOUR) LIMIT 1"
);
$q->execute([$acc, $site, $horas]);
return (bool)$q->fetch();
}
Voting page for the player
On the panel, show each list with a "Vote" button and the remaining cooldown time. An example structure to render it dynamically:
const sites = [
{ site: "gtop100", nome: "Gtop100", reward: 200, cooldownRestante: 0 },
{ site: "xtremetop", nome: "XtremeTop100", reward: 150, cooldownRestante: 3600 },
];
// disable the button while cooldownRestante > 0 and show a countdown
Make it clear to the player how much they earn per vote and the interval — transparency reduces support tickets. If you are building the server's entire structure, Vote4Coins is just one of the web modules; see the general guide on how to create a MU Online server to understand where it fits.
Common errors and fixes
| Symptom | Likely cause | Fix |
|---|---|---|
| Player voted but got nothing | Callback not configured or wrong URL on the list | Register the correct postback URL in the list's panel |
| Reward credited without voting | Credited on the click, not on the callback | Only credit on the confirmed callback (Successful=1) |
| Vote credited twice | No idempotency | Use UPDATE ... WHERE status='pending' as a lock |
| Callback rejected (403) | Mismatched secret key | Reconfirm the secret key in the list's panel and in vote_sites |
| Player votes several times in a row | Cooldown not applied | Check the cooldown per account and IP before the redirect |
| Callback forged by third parties | No origin validation | Validate the secret key and restrict the list's source IP |
Security and best practices
- Never expose the list's secret key on the front end or in Git.
- Treat the callback as untrusted input until you validate the key and origin.
- Log received callbacks (payload + IP) to audit for fraud.
- Matching by
pingUsernameis more reliable than matching by raw IP, when the list allows it. - If a player complains about an unpaid vote, first check whether the list actually sent the callback — often the CAPTCHA failed on their side.
- Cap the total daily reward per account to curb abuse even with multiple lists.
Launch checklist
- Server registered on each list with the Site ID and secret key saved
votesandvote_sitestables created- Callback URL configured on each list, with HTTPS
- Secret key validation implemented in each handler
- Idempotent crediting tested (a duplicate callback does not pay twice)
- Cooldown per account and per IP working
- Real coin table/column name confirmed for your game DB version
- Voting page with a cooldown countdown
- Callback logs enabled for auditing
- End-to-end test with a real vote on each list
With this system you turn every active player into a daily promoter of the server, climb the rankings organically and still give a fair reward in return — all automatically, securely and proof against anyone trying to game the vote click.
Frequently asked questions
What is Vote4Coins in MU Online?
It is a system that rewards players with in-game coins (WCoin, credits or points) whenever they vote for your server on ranking sites like Gtop100 and XtremeTop, raising your position in the lists and your exposure.
How does the voting site tell you the vote was valid?
Through a callback (also called a postback or pingback): when the vote is confirmed, the list makes an HTTP request to a URL on your site, providing the player's identifier. Only at that moment should the reward be credited.
Why shouldn't I credit as soon as the player clicks to vote?
Because the click does not guarantee a valid vote. The player may close the page, the CAPTCHA may fail or they may vote from an already-used IP. Only the callback confirmed by the list guarantees that the vote counted.
How do I stop a player from voting several times in a row?
Combine the list's official cooldown (usually 12 or 24 hours) with your own record of votes per account and per IP, plus validating the callback via the secret key and the list's source IP.
Can I use more than one voting list at the same time?
Yes, and it is recommended. Each list has its own callback and cooldown. You just create one handler per list and a unified votes table to control rewards and avoid duplication.