Artículos sobre: Websites & APIs
Este artículo también está disponible en:

Cómo crear un webhook de Top.gg

Cómo crear un webhook de Top⁠.gg en Square Cloud


Los webhooks son fundamentales para integrar tu bot con servicios externos. Cada vez que un usuario vota por tu bot, Top⁠.gg envía una petición POST a tu aplicación. En Square Cloud, puedes alojar ese webhook de forma estable en el puerto 80.


Esta guía usa los webhooks v1 de Top⁠.gg. Cada petición llega firmada en la cabecera x-topgg-signature (formato t=<timestamp>,v1=<firma>), donde la firma es un HMAC SHA-256 de <timestamp>.<cuerpo crudo> hecho con tu secreto whs_. El webhook legado, que enviaba una contraseña en la cabecera Authorization, no se cubre aquí.



1. ¿Cómo funciona la comunicación?


  1. El usuario vota en Top⁠.gg.
  2. Top⁠.gg envía una petición JSON firmada a la URL de tu aplicación.
  3. Tu aplicación recalcula la firma con tu secreto, rechaza lo que no coincida y procesa el voto (data.user.platform_id es el ID de Discord de quien votó).



2. Implementación en JavaScript (Express)


Instala Express y activa la sintaxis import:


npm pkg set type=module
npm install express


El ejemplo verifica la firma con el módulo nativo crypto de Node, sin ningún paquete de Top⁠.gg (en nuestras pruebas, el listener de @top-gg/sdk 4.0.0 respondió entregas válidas con timeout, lo que hace que Top⁠.gg las reenvíe).


import crypto from "node:crypto";
import express from "express";

const app = express();
// El secreto whs_ de la página Webhooks de tu proyecto
const SECRET = process.env.TOPGG_WEBHOOK_SECRET;

function isValidSignature(rawBody, header = "") {
// Formato de la cabecera: t=<timestamp unix>,v1=<HMAC-SHA256 en hex de "<timestamp>.<cuerpo crudo>">
const parts = Object.fromEntries(header.split(",").map((item) => item.split("=")));
const timestamp = Number(parts.t);
if (!SECRET || !Buffer.isBuffer(rawBody) || !parts.v1 || !timestamp) return false;
// Rechaza peticiones con más de 5 minutos (protección contra replay)
if (Math.abs(Date.now() / 1000 - timestamp) > 300) return false;
const expected = crypto.createHmac("sha256", SECRET).update(`${parts.t}.`).update(rawBody).digest();
const received = Buffer.from(parts.v1, "hex");
return received.length === expected.length && crypto.timingSafeEqual(received, expected);
}

// express.raw mantiene exactamente los bytes que fueron firmados
app.post("/webhook", express.raw({ type: "*/*" }), (req, res) => {
if (!isValidSignature(req.body, req.get("x-topgg-signature"))) return res.sendStatus(401);

const { type, data } = JSON.parse(req.body);
// "vote.create" es un voto real; "webhook.test" viene del botón Send Test
if (type === "vote.create" || type === "webhook.test") {
console.log(`¡Voto recibido! ID del usuario: ${data.user.platform_id}`);
}
res.sendStatus(204);
});

// Square Cloud define PORT=80
const port = Number(process.env.PORT) || 80;
app.listen(port, "0.0.0.0", () => console.log(`Escuchando en el puerto ${port}`));



3. Implementación en Python (Flask)


Instala las librerías con pip install flask waitress (Waitress es un servidor de producción; no uses el servidor de desarrollo de Flask).


import hashlib
import hmac
import json
import os
import time

from flask import Flask, abort, request
from waitress import serve

app = Flask(__name__)

# El secreto whs_ de la página Webhooks de tu proyecto
SECRET = os.environ.get("TOPGG_WEBHOOK_SECRET", "")


def is_valid_signature(raw_body: bytes, header: str) -> bool:
# Formato de la cabecera: t=<timestamp unix>,v1=<HMAC-SHA256 en hex de "<timestamp>.<cuerpo crudo>">
try:
parts = dict(item.split("=", 1) for item in header.split(","))
timestamp = parts["t"]
signature = parts["v1"]
age = abs(time.time() - int(timestamp))
except (KeyError, ValueError):
return False
if age > 300: # rechaza peticiones con más de 5 minutos (protección contra replay)
return False
message = timestamp.encode() + b"." + raw_body
expected = hmac.new(SECRET.encode(), message, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected.encode(), signature.encode())


@app.route("/webhook", methods=["POST"])
def topgg_webhook():
raw_body = request.get_data()
if not SECRET or not is_valid_signature(raw_body, request.headers.get("x-topgg-signature", "")):
abort(401)

payload = json.loads(raw_body)
# "vote.create" es un voto real; "webhook.test" viene del botón Send Test
if payload["type"] in ("vote.create", "webhook.test"):
print(f'¡Voto recibido! ID del usuario: {payload["data"]["user"]["platform_id"]}')
return "", 204


if __name__ == "__main__":
# Square Cloud define PORT=80
serve(app, host="0.0.0.0", port=int(os.environ.get("PORT", 80)))



4. Configuración en Square Cloud


Para que el webhook funcione, debes configurar tu aplicación como un sitio:


  1. Deploy: Envía tu código (recuerda no enviar node_modules ni venv).
  2. Publicación web: En el formulario de envío, activa la opción "Publicación en la Web" ya en el primer envío y define el subdominio (o usa SUBDOMAIN= en el archivo de configuración). Los sitios necesitan al menos 512 MB de RAM.
  3. Subdominio: Define un nombre (ej.: topgg-webhook). La URL de tu webhook será topgg-webhook⁠.squareweb⁠.app/webhook, con HTTPS.
  4. Variables de entorno: Añade la clave TOPGG_WEBHOOK_SECRET con el secreto whs_ generado por Top⁠.gg (siguiente paso).



5. Configuración en el panel de Top⁠.gg


  1. Abre el panel de tu proyecto en Top⁠.gg y ve a Webhooks.
  2. Pega la URL de tu webhook y guarda.
  3. Top⁠.gg genera un secreto que empieza con whs_: cópialo en la variable de entorno TOPGG_WEBHOOK_SECRET en Square Cloud y mantenlo en secreto.
  4. Haz clic en Send Test y comprueba que el voto aparece en los logs de tu aplicación.



6. Buenas prácticas


  • Seguridad: Nunca expongas el secreto en el código. Usa siempre variables de entorno.
  • Logs: Mantén logs de las peticiones recibidas para depurar si un voto no se procesa.
  • Manejo de errores: Responde siempre rápido (204 o 200). Si la lógica de recompensa es lenta, procésala de forma asíncrona para evitar el timeout, que hace que Top⁠.gg reenvíe la entrega.


Para más detalles, mira el tutorial de Top⁠.gg de Square Cloud.

Actualizado el: 04/10/2026

¿Este artículo te resultó útil?

Comparte tu opinión

Cancelar

¡Gracias!