GameTopBase
Início

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

  1. 1O jogador abre seu link de voto com o nome da conta dele no jogo.
  2. 2O GameTopBase verifica que é uma pessoa e que ela não votou nas últimas 12 horas (nem pelo IP dela nem com essa conta).
  3. 3O voto é salvo e conta no ranking.
  4. 4Se você definiu uma URL de postback, o GameTopBase avisa seu servidor com uma notificação assinada com seu segredo.
  5. 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âmetroDescrição
usernameOpcional. 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 bot
    Verificação do Cloudflare Turnstile, um formulário assinado que expira e uma armadilha invisível para bots.
  • Um voto por IP
    Um 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 conta
    Uma 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 verdadeiro
    O 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 assinada
    Cada postback leva uma assinatura HMAC-SHA256 feita com seu segredo, que cobre todos os dados: não dá para inventá-la nem alterá-la.
  • Privacidade
    Os 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âmetroTipoDescrição
usernametextoConta do jogo que o jogador digitou (vazio se não digitou nenhuma).
iptextoIP de onde veio o voto (IPv4 ou IPv6).
timestampinteiroMomento do envio em segundos Unix. Muda a cada nova tentativa.
vote_idtextoIdentificador único do voto. Não muda entre tentativas nem entre suas URLs.
signaturetextoAssinatura HMAC-SHA256 em hexadecimal (64 caracteres).
https://seu-jogo.com/vote.php?username=jugador123&ip=203.0.113.7&timestamp=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:

  1. 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).
  2. 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.
  3. Conta: se username estiver vazio, responda 200 e não dê nada.
  4. 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.
  5. Recompensa: entregue-a a username.
  6. 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ódigoDescrição
2xxNotificação recebida. Não é enviada de novo.
Outro código ou sem resposta em 5 sServidores 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âmetroDescrição
vote_idEste voto existe? Sem limite de tempo. A forma mais segura de confirmar um postback.
usernameEsta conta votou nas últimas 12 horas? Sem diferenciar maiúsculas.
ipEste 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"
}
CampoDescrição
votedtrue se um voto foi encontrado.
voteIdIdentificador do voto (o mesmo que chega no postback), ou null.
usernameConta com que se votou, ou null.
votedAtData do voto (ISO 8601, UTC), ou null.
nextVoteAtQuando poderá votar de novo, ou null.
CódigoDescrição
200Consulta bem-sucedida (voted pode ser true ou false).
400Falta vote_id, username ou ip, ou o formato não é válido.
401O 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.