Recompense seus jogadores por votarem
Seus jogadores votam no seu servidor no GameTopBase e você dá a eles uma recompensa no jogo. Esta página explica como fazer isso com segurança: só votos reais são recompensados, e cada um só uma vez.
Como funciona
- 1O jogador abre seu link de voto com o nome da conta dele no jogo.
- 2O GameTopBase verifica que é uma pessoa e que ela não votou nas últimas 12 horas (nem pelo IP dela nem com essa conta).
- 3O voto é salvo e conta no ranking.
- 4Se você definiu uma URL de postback, o GameTopBase avisa seu servidor com uma notificação assinada com seu segredo.
- 5Seu servidor verifica a assinatura, garante que ainda não pagou esse voto e entrega a recompensa.
Dê a recompensa só quando chegar um postback com a assinatura correta, ou depois de confirmar com a API de verificação. Nunca porque o jogador diz que votou nem a partir do navegador.
1. Link de voto
Envie seus jogadores para este link (pelo site, pelo launcher, pelo Discord ou por um comando no jogo):
https://gametopbase.com/pt/servidores/<seu-servidor>/votar?username=<conta>
<seu-servidor> é o identificador do seu servidor no GameTopBase: a última parte do endereço da sua página. Por exemplo, em …/servidores/craftnation é craftnation. No seu painel, em "Integração de votos", o link já vem completo.
| Parâmetro | Descrição |
|---|---|
| username | Opcional. Nome da conta do jogo que recebe a recompensa: de 1 a 32 caracteres (letras, números, espaço, _ - .). Se o link trouxer esse dado, o campo já vem preenchido. |
Sem nome de conta o voto conta do mesmo jeito para o ranking, mas não há quem recompensar: o postback chega com username vazio.
No link só vai username, de propósito. Quem abre é o jogador, no navegador dele, então ele poderia mudar qualquer dado da URL: o GameTopBase pega o IP da conexão real e gera o resto (vote_id, data, assinatura) no próprio servidor. Seu servidor recebe tudo isso depois, no postback assinado.
No seu painel, em "Divulgue seu servidor", o botão e o link estão prontos para copiar.
2. Votos reais: o que o GameTopBase verifica
Antes de contar um voto, o GameTopBase verifica no próprio servidor (não no navegador do jogador):
- Uma pessoa, não um botVerificação do Cloudflare Turnstile, um formulário assinado que expira e uma armadilha invisível para bots.
- Um voto por IPUm IP só pode votar no seu servidor uma vez a cada 12 horas, e no máximo 20 votos por hora em todo o site.
- Um voto por contaUma conta do jogo só ganha um voto a cada 12 horas, mesmo votando de outros IPs ou com VPN. Não diferencia maiúsculas: "Jogador" e "jogador" são a mesma.
- IP verdadeiroO IP vem da conexão real, não do que o navegador envia, então não dá para falsificá-lo para votar mais vezes.
- Notificação assinadaCada postback leva uma assinatura HMAC-SHA256 feita com seu segredo, que cobre todos os dados: não dá para inventá-la nem alterá-la.
- PrivacidadeOs IPs são guardados com hash irreversível. Um IP só fica sem hash enquanto houver novas tentativas pendentes da notificação ao seu servidor (uns 40 minutos no máximo). Só o seu servidor recebe o IP de quem votou, no postback.
3. Postback: a notificação ao seu servidor
Coloque a URL do seu script no painel do seu servidor, em "Integração de votos". Depois de salvar cada voto, o GameTopBase faz uma requisição GET para essa URL com estes parâmetros:
| Parâmetro | Tipo | Descrição |
|---|---|---|
| username | texto | Conta do jogo que o jogador digitou (vazio se não digitou nenhuma). |
| ip | texto | IP de onde veio o voto (IPv4 ou IPv6). |
| timestamp | inteiro | Momento do envio em segundos Unix. Muda a cada nova tentativa. |
| vote_id | texto | Identificador único do voto. Não muda entre tentativas nem entre suas URLs. |
| signature | texto | Assinatura HMAC-SHA256 em hexadecimal (64 caracteres). |
https://seu-jogo.com/vote.php?username=jugador123&ip=203.0.113.7×tamp=1767225600&vote_id=cm4x9k2p00001&signature=<64 hex>
- Seu script tem 5 segundos para responder.
- Redirecionamentos não são seguidos: use a URL final, de preferência com https.
- Só são feitos envios para endereços públicos (nada de localhost nem redes internas).
4. Verifique cada notificação
Seu script precisa fazer estas verificações, nesta ordem, antes de dar qualquer coisa:
- Assinatura: calcule o HMAC-SHA256 com seu segredo sobre o texto abaixo e compare com signature usando uma comparação de tempo constante (hash_equals no PHP, timingSafeEqual no Node).
- Data: rejeite a notificação se timestamp estiver a mais de 5 minutos do seu horário. Assim uma notificação antiga copiada não serve.
- Conta: se username estiver vazio, responda 200 e não dê nada.
- Uma única vez: salve vote_id em uma tabela com chave única antes de pagar. Se já existia, é uma nova tentativa: responda 200 e não pague de novo.
- Recompensa: entregue-a a username.
- Resposta: responda com um código 2xx (por exemplo 200).
Texto assinado (os valores exatamente como chegam, separados por dois-pontos):
signature = HMAC-SHA256(segredo, "servidor:username:ip:timestamp:vote_id") → hex
O segredo está no seu painel, em "Integração de votos". É só para o seu servidor: nunca o coloque no launcher, no site nem no cliente do jogo.
Exemplo completo em PHP
<?php
// vote.php: recebe a notificação de voto do GameTopBase e entrega a recompensa.
// Coloque a URL deste arquivo como "URL de postback" no seu painel do GameTopBase.
const GAMETOP_SECRET = 'SEU_SEGREDO_DO_PAINEL'; // Painel > Integração de votos > Segredo
const GAMETOP_SERVER = 'seu-servidor'; // o identificador da sua página (última parte do endereço)
$user = $_GET['username'] ?? '';
$ip = $_GET['ip'] ?? '';
$ts = $_GET['timestamp'] ?? '';
$voteId = $_GET['vote_id'] ?? '';
$sig = $_GET['signature'] ?? '';
// 1. A assinatura prova que a notificação vem do GameTopBase e que ninguém alterou nenhum dado.
$expected = hash_hmac('sha256', GAMETOP_SERVER . ":$user:$ip:$ts:$voteId", GAMETOP_SECRET);
if (!is_string($sig) || !hash_equals($expected, $sig)) {
http_response_code(403);
exit('assinatura invalida');
}
// 2. Só notificações recentes: uma antiga copiada não serve.
if (!ctype_digit($ts) || abs(time() - (int) $ts) > 300) {
http_response_code(403);
exit('notificacao expirada');
}
// 3. Sem conta o voto conta para o ranking, mas não há quem recompensar.
if ($user === '') {
http_response_code(200);
exit('ok');
}
// 4. Cada voto é pago uma única vez. vote_id não muda entre tentativas: é salvo ANTES de
// pagar em uma coluna com chave única, assim duas notificações ao mesmo tempo não pagam duas vezes.
// CREATE TABLE gametop_votes (vote_id VARCHAR(40) PRIMARY KEY, username VARCHAR(32), paid_at DATETIME);
$db = new PDO('mysql:host=localhost;dbname=seu_jogo', 'usuario', 'senha');
$stmt = $db->prepare('INSERT IGNORE INTO gametop_votes (vote_id, username, paid_at) VALUES (?, ?, NOW())');
$stmt->execute([$voteId, $user]);
if ($stmt->rowCount() === 0) {
http_response_code(200); // já foi pago (é uma nova tentativa): não se dá de novo
exit('ok');
}
// 5. >>> AQUI VOCÊ ENTREGA A RECOMPENSA para a conta $user <<<
// É o único ponto em que o voto está verificado e chega pela primeira vez.
try {
dar_recompensa($user); // seu código: moedas, itens, dias de VIP...
} catch (Throwable $e) {
// Se falhar, a marca é removida e a resposta é um erro: o GameTopBase tenta de novo (servidores premium).
$db->prepare('DELETE FROM gametop_votes WHERE vote_id = ?')->execute([$voteId]);
http_response_code(500);
exit('erro');
}
// 6. Responda 2xx: o GameTopBase dá a notificação como entregue.
http_response_code(200);
echo 'ok';O mesmo exemplo em Node.js (Express)
const crypto = require("crypto");
const express = require("express");
const app = express();
const GAMETOP_SECRET = "SEU_SEGREDO_DO_PAINEL"; // Painel > Integração de votos > Segredo
const GAMETOP_SERVER = "seu-servidor";
app.get("/gametop/vote", async (req, res) => {
const q = (name) => (typeof req.query[name] === "string" ? req.query[name] : "");
const user = q("username"), ip = q("ip"), ts = q("timestamp"), voteId = q("vote_id"), sig = q("signature");
// 1. Assinatura: do GameTopBase e sem alterações
const expected = crypto.createHmac("sha256", GAMETOP_SECRET)
.update(GAMETOP_SERVER + ":" + user + ":" + ip + ":" + ts + ":" + voteId)
.digest("hex");
if (sig.length !== expected.length || !crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
return res.status(403).send("assinatura invalida");
}
// 2. Só notificações recentes
if (!/^\d+$/.test(ts) || Math.abs(Date.now() / 1000 - Number(ts)) > 300) {
return res.status(403).send("notificacao expirada");
}
// 3. Sem conta, nada a pagar
if (user === "") return res.send("ok");
// 4. Uma única vez por vote_id: salve com chave única ANTES de pagar
const firstTime = await saveVoteId(voteId, user); // seu código: false se já existia
if (!firstTime) return res.send("ok");
// 5. >>> AQUI VOCÊ ENTREGA A RECOMPENSA para a conta user <<<
try {
await giveReward(user); // seu código
} catch (e) {
await deleteVoteId(voteId); // assim a nova tentativa (premium) pode pagá-lo
return res.status(500).send("erro");
}
// 6. Resposta 2xx
res.send("ok");
});
app.listen(8080);5. Respostas e novas tentativas
| Código | Descrição |
|---|---|
| 2xx | Notificação recebida. Não é enviada de novo. |
| Outro código ou sem resposta em 5 s | Servidores premium: nova tentativa após 1, 5 e 30 minutos (4 tentativas), com o mesmo vote_id e uma assinatura nova. Cada tentativa aparece no seu painel. |
| Sem premium | É enviada uma única vez. Se o seu servidor estava fora do ar, use a API de verificação como alternativa. |
Servidores premium podem adicionar até mais 2 URLs de postback: todas recebem o mesmo vote_id, então você pode usar várias sem pagar duas vezes.
6. API de verificação
Consulte o GameTopBase sobre um voto a partir do seu servidor. Serve para confirmar um postback, para dar a recompensa com um comando no jogo ou como alternativa se uma notificação não chegou.
Envie o segredo no cabeçalho Authorization: Bearer <segredo>. Nunca na URL: ali ele ficaria registrado nos logs de acesso.
| Parâmetro | Descrição |
|---|---|
| vote_id | Este voto existe? Sem limite de tempo. A forma mais segura de confirmar um postback. |
| username | Esta conta votou nas últimas 12 horas? Sem diferenciar maiúsculas. |
| ip | Este IP votou nas últimas 12 horas? |
Use um dos três em cada consulta, junto com server=<seu-servidor>.
curl -H "Authorization: Bearer <segredo>" \
"https://gametopbase.com/api/v1/vote-check?server=<seu-servidor>&vote_id=cm4x9k2p00001"
curl -H "Authorization: Bearer <segredo>" \
"https://gametopbase.com/api/v1/vote-check?server=<seu-servidor>&username=jugador123"
{
"voted": true,
"voteId": "cm4x9k2p00001",
"username": "jugador123",
"votedAt": "2026-10-25T10:00:00.000Z",
"nextVoteAt": "2026-10-25T22:00:00.000Z"
}| Campo | Descrição |
|---|---|
| voted | true se um voto foi encontrado. |
| voteId | Identificador do voto (o mesmo que chega no postback), ou null. |
| username | Conta com que se votou, ou null. |
| votedAt | Data do voto (ISO 8601, UTC), ou null. |
| nextVoteAt | Quando poderá votar de novo, ou null. |
| Código | Descrição |
|---|---|
| 200 | Consulta bem-sucedida (voted pode ser true ou false). |
| 400 | Falta vote_id, username ou ip, ou o formato não é válido. |
| 401 | O servidor não existe ou o segredo não está correto. |
Recompensa com um comando no jogo
Se você não usa postback: quando o jogador digitar /recompensa, consulte por username. Se voted for true e esse voteId não estiver na sua tabela de votos pagos, salve-o e entregue a recompensa. É a mesma regra do passo 4: um voteId, um pagamento.
7. Lista de segurança
- O segredo só fica no seu servidor. Se vazar, clique em "Gerar novo" no seu painel: o anterior deixa de valer na hora.
- Sempre verifique a assinatura e a data, mesmo que a URL do seu script seja secreta.
- Salve vote_id com chave única antes de pagar: é isso que impede pagar duas vezes.
- Não dê recompensas a partir do navegador nem porque o jogador diz que votou.
- Use https na URL de postback.