> ## Documentation Index
> Fetch the complete documentation index at: https://docs.squarecloud.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Receba webhooks de votos do Top.gg

> Webhooks de votos do Top.gg na Square Cloud: receba votos em Node.js ou Python, verifique o HMAC do x-topgg-signature com seu secret e envie um voto de teste.

## Introdução

* Este guia supõe que você tenha um bot aprovado no [top.gg](https://top.gg/) e esteja usando Node.js ou Python em seu projeto.
* Em seguida, crie uma conta na Square Cloud através da [página de cadastro](https://squarecloud.app/pt-br/signup). Você pode usar seu e-mail para se registrar.
* Por fim, é necessário ter um plano ativo na sua conta. Compare os [planos disponíveis](https://squarecloud.app/pt-br/pricing) e escolha um conforme a sua necessidade.

## Configurando o ambiente

<Tabs>
  <Tab title="Node.js">
    1. Antes de começar, verifique se você tem o Node.js e o npm instalados em seu sistema. Caso contrário, faça o download no [site oficial do Node.js](https://nodejs.org/).
    2. Inicie um novo projeto Node.js e habilite a sintaxe `import` com os comandos:

    ```bash Terminal theme={"system"}
    npm init -y
    npm pkg set type=module
    ```

    Esses comandos criam um arquivo package.json no diretório atual.

    3. Instale o Express:

    ```bash Terminal theme={"system"}
    npm install express
    ```
  </Tab>

  <Tab title="Python">
    1. Verifique se você tem Python e pip (o gerenciador de pacotes do Python) instalados em seu sistema. Caso não tenha, baixe-os no [site oficial do Python](https://www.python.org/) e no [site oficial do pip](https://pypi.org/).
    2. Instale as bibliotecas `flask` e `waitress` com o pip:

    ```bash theme={"system"}
    pip install flask waitress
    ```
  </Tab>
</Tabs>

## Configurando o projeto

**1. Obtenha o secret do webhook:**

* Escolha o subdomínio que sua aplicação vai usar na Square Cloud (o campo `SUBDOMAIN` do seu arquivo de configuração), por exemplo `mysite`. A URL do seu webhook será então `https://mysite.squareweb.app/topgg`.
* No Top.gg, abra o dashboard do seu projeto e vá em **Webhooks**.
* Cole a URL do webhook e salve. O Top.gg então gera um secret de webhook que começa com `whs_`: copie-o e mantenha-o privado. Seu código o lê da variável de ambiente `TOPGG_WEBHOOK_SECRET`.

<Info>O Top.gg assina cada requisição com o header `x-topgg-signature`, no formato `t=<timestamp>,v1=<signature>`, em que a assinatura é um HMAC SHA-256 de `<timestamp>.<raw body>` gerado com o seu secret. O código abaixo recalcula essa assinatura e rejeita qualquer requisição que não bata, assim ninguém mais consegue enviar votos falsos para a sua URL. Veja a [documentação de webhooks do Top.gg](https://docs.top.gg/webhooks/overview) para mais detalhes.</Info>

<Note>Este guia usa os webhooks v1 do Top.gg. O webhook legado v0, que envia uma senha no header `Authorization` com um payload diferente, não é abordado aqui.</Note>

**2. Implemente o listener do webhook:**

As seções abaixo trazem exemplos de código em JavaScript e em Python:

<Tabs>
  <Tab title="Node.js">
    O exemplo verifica a assinatura com o módulo nativo `crypto` do Node, então nenhum pacote do Top.gg é necessário. A classe `Webhook` do `@top-gg/sdk` 4.0.0 não é usada: nos nossos testes, o listener dela respondia entregas válidas com um erro de timeout, o que faz o Top.gg reenviá-las.

    ```javascript index.js theme={"system"}
    import crypto from "node:crypto";
    import express from "express";

    const app = express();
    // O secret whs_ da página Webhooks do seu projeto no Top.gg
    const SECRET = process.env.TOPGG_WEBHOOK_SECRET;

    function isValidSignature(rawBody, header = "") {
      // Formato do header: t=<timestamp unix>,v1=<HMAC-SHA256 em hex de "<timestamp>.<raw body>">
      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;
      // Rejeita requisições com mais de 5 minutos (proteção 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 mantém os bytes exatos que o Top.gg assinou
    app.post("/topgg", 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" é um voto real, "webhook.test" vem do botão Send Test
      if (type === "vote.create" || type === "webhook.test") {
        console.log(`Voto recebido com sucesso! ID do usuário: ${data.user.platform_id}`);
      }
      res.sendStatus(204);
    });

    // A Square Cloud define PORT=80 e encaminha o tráfego do seu subdomínio para ela
    const port = Number(process.env.PORT) || 80;
    app.listen(port, "0.0.0.0", () => console.log(`Listening on port ${port}`));
    ```
  </Tab>

  <Tab title="Python">
    Para Python, você pode usar o seguinte código:

    ```python app.py theme={"system"}
    import hashlib
    import hmac
    import json
    import os
    import time

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

    app = Flask(__name__)

    # O secret whs_ da página Webhooks do seu projeto no Top.gg
    SECRET = os.environ.get("TOPGG_WEBHOOK_SECRET", "")


    def is_valid_signature(raw_body: bytes, header: str) -> bool:
        # Formato do header: t=<timestamp unix>,v1=<HMAC-SHA256 em hex de "<timestamp>.<raw body>">
        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:  # rejeita requisições com mais de 5 minutos (proteção 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())


    # O Top.gg envia requisições POST assinadas para esta rota
    @app.route("/topgg", 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" é um voto real, "webhook.test" vem do botão Send Test
        if payload["type"] in ("vote.create", "webhook.test"):
            print(f'Voto recebido com sucesso! ID do usuário: {payload["data"]["user"]["platform_id"]}')
        return "", 204


    if __name__ == "__main__":
        # A Square Cloud define PORT=80 e encaminha o tráfego do seu subdomínio para ela
        serve(app, host="0.0.0.0", port=int(os.environ.get("PORT", 80)))
    ```
  </Tab>
</Tabs>

## Criando o arquivo de configuração da Square Cloud

Crie um arquivo `squarecloud.app` na pasta do seu projeto. Defina `SUBDOMAIN` com o subdomínio que você escolheu no passo 1:

<CodeGroup>
  ```systemd Node.js theme={"system"}
  DISPLAY_NAME=Top.gg Webhook
  MAIN=index.js
  MEMORY=512
  VERSION=recommended
  SUBDOMAIN=mysite
  ```

  ```systemd Python theme={"system"}
  DISPLAY_NAME=Top.gg Webhook
  MAIN=app.py
  MEMORY=512
  VERSION=recommended
  SUBDOMAIN=mysite
  ```
</CodeGroup>

O `SUBDOMAIN` publica o listener em `https://mysite.squareweb.app`, e aplicações web precisam de pelo menos `MEMORY=512`. O código escuta na porta da variável de ambiente `PORT`, que a Square Cloud define como `80`, e no host `0.0.0.0`.

<Card title="Aprenda como: criar o arquivo de configuração para a Square Cloud." icon="link" href="/pt-br/getting-started/config-file">
  O arquivo `squarecloud.app` é usado para definir nome, descrição, versão, arquivo principal e outras configurações da sua aplicação.
</Card>

## Enviando sua aplicação para a Square Cloud

Após seguir todos os passos, a pasta do seu projeto deve conter o seu código, o arquivo de dependências (`package.json` ou `requirements.txt`) e o arquivo de configuração. Para enviar pelo dashboard, coloque-os em um arquivo `.zip`.

Se a sua aplicação é um projeto Node.js, dê uma olhada no nosso artigo sobre [Node.js](/pt-br/articles/getting-started-with-nodejs).

Se a sua aplicação é um projeto Python, dê uma olhada no nosso artigo sobre [Python](/pt-br/articles/getting-started-with-python).

### Via dashboard

<Steps>
  <Step title="Acesse a página de upload">
    Acesse a [página de upload](https://squarecloud.app/pt-br/dashboard/new) e envie seu arquivo zip.
  </Step>

  <Step title="Configure seu ambiente">
    Após fazer o upload do seu arquivo zip, você precisará configurar o nome, o arquivo principal ou o ambiente de execução e outras configurações do seu projeto.\
    Se você estiver enviando um projeto web, certifique-se de selecionar "Publicação na Web" e definir um subdomínio para o seu projeto.
  </Step>

  <Step title="Faça o deploy do projeto">
    Por fim, clique no botão "Deploy" para hospedar seu projeto na Square Cloud.
    Após o deploy, você poderá monitorar o status e os registros do seu projeto no dashboard.

    <Frame>
      <img src="https://cdn.squarecloud.app/docs/articles/dashboard/uploading-pt-br.gif" alt="Enviando aplicação para a Square Cloud" style={{ borderRadius: "0.2rem" }} />
    </Frame>
  </Step>

  <Step title="Confirme que seu app está no ar">
    Seu primeiro deploy geralmente leva menos de um minuto. No [dashboard](https://squarecloud.app/pt-br/dashboard), aguarde o status da sua aplicação mudar para em execução e verifique os registros em busca de erros de inicialização.\
    Se você fez deploy de um site ou API, abra `https://<seu-subdominio>.squareweb.app` no navegador: você deve ver sua aplicação respondendo. Se você fez deploy de um bot, envie um comando para confirmar que ele está online.
    <Tip>Seu app não está iniciando? Veja o [guia de solução de problemas](/pt-br/platform/troubleshooting/app-wont-start) para as causas e correções mais comuns.</Tip>
  </Step>
</Steps>

### Via CLI

Para usar esse método, seu projeto precisa de um arquivo de configuração chamado `squarecloud.app` na raiz. Ele informa à Square Cloud como executar a sua aplicação.

<Card title="Guia do arquivo de configuração" icon="link" href="/pt-br/getting-started/config-file">
  Saiba como criar o arquivo de configuração `squarecloud.app` que define o ambiente da sua aplicação.
</Card>

<Steps>
  <Step title="Instale a CLI">
    Instale a [CLI da Square Cloud](/pt-br/cli-reference/installation). Se você já a tem, execute o mesmo comando para atualizá-la:

    <CodeGroup>
      ```bash macOS, Linux e WSL theme={"system"}
      curl -fsSL https://cli.squarecloud.app/install | bash
      ```

      ```bash Windows com npm theme={"system"}
      npm install -g @squarecloud/cli@latest
      ```
    </CodeGroup>
  </Step>

  <Step title="Faça login">
    Execute o comando abaixo. Ele abre o seu navegador: aprove o login lá e a CLI está pronta. Não há nenhuma chave de API para copiar. Para scripts e CI, veja [autenticação da CLI](/pt-br/cli-reference/authentication).

    ```bash theme={"system"}
    squarecloud auth login
    ```
  </Step>

  <Step title="Envie o seu projeto">
    Na pasta do seu projeto, execute o comando abaixo. A CLI compacta a pasta atual, deixando de fora o que o [`squarecloud.ignore`](/pt-br/getting-started/squarecloud-ignore) lista, e faz o upload:

    ```bash theme={"system"}
    squarecloud upload
    ```

    Para enviar um zip que você mesmo criou, passe-o com `--file`:

    ```bash theme={"system"}
    squarecloud upload --file <caminho/para/zip>
    ```
  </Step>

  <Step title="Confirme que seu app está no ar">
    Seu primeiro deploy geralmente leva menos de um minuto. Verifique o status e os registros da sua aplicação diretamente pelo terminal:

    ```bash theme={"system"}
    squarecloud app status
    squarecloud app logs
    ```

    Se você fez deploy de um site ou API, abra `https://<seu-subdominio>.squareweb.app` no navegador: você deve ver sua aplicação respondendo. Se você fez deploy de um bot, envie um comando para confirmar que ele está online.
    <Tip>Seu app não está iniciando? Veja o [guia de solução de problemas](/pt-br/platform/troubleshooting/app-wont-start) para as causas e correções mais comuns.</Tip>
  </Step>
</Steps>

## Definindo o secret do webhook

Adicione o seu secret `whs_` como a variável de ambiente `TOPGG_WEBHOOK_SECRET` da sua aplicação, de uma destas formas:

* **Dashboard**: na página de upload, abra **Configuração avançada** e adicione a variável antes do deploy. Para uma aplicação que já está no ar, adicione-a na página **Variáveis de ambiente** dela.
* **CLI**: a partir da pasta do projeto, defina-a com [`squarecloud app env set`](/pt-br/cli-reference/environment-variables#squarecloud-app-env-set) e reinicie a aplicação:

```bash theme={"system"}
squarecloud app env set TOPGG_WEBHOOK_SECRET=whs_your_secret
squarecloud app restart
```

A aplicação lê as variáveis de ambiente quando inicia, então reinicie-a após cada alteração. Enquanto o secret não estiver definido, o código rejeita todas as requisições com `401 Unauthorized`. Veja [Variáveis de ambiente](/pt-br/platform/environment-variables) para as outras formas de gerenciá-las.

## Iniciando os testes

Se você fez tudo corretamente, abra `https://mysite.squareweb.app/topgg` no navegador (troque `mysite` pelo seu subdomínio). Se aparecer "Cannot GET /topgg" (Node.js) ou "Method Not Allowed" (Python), está tudo certo: a rota só aceita as requisições `POST` que o Top.gg envia.

* No código JavaScript que criamos com `app.post("/topgg", ...)`, a rota que vai receber os votos é "/topgg". Então, se o seu site é `mysite.squareweb.app`, a URL do webhook é `https://mysite.squareweb.app/topgg`.

* No código Python que criamos com `@app.route("/topgg", methods=["POST"])`, a rota que vai receber os votos também é "/topgg". Então, a URL do webhook é a mesma `https://mysite.squareweb.app/topgg`.

Por fim, volte à página **Webhooks** do seu projeto no Top.gg e clique no botão "Send Test". Depois, verifique os logs da sua aplicação. Se tudo deu certo, a mensagem que você definiu no `console.log` ou no `print` deve aparecer nos logs.

<Frame>
  <img src="https://cdn.squarecloud.app/docs/articles/topgg/example-send.png" alt="Exemplo de envio de teste do webhook do Top.gg" style={{ borderRadius: "0.2rem" }} />
</Frame>

E assim, se tudo foi configurado corretamente, seu webhook estará pronto para enviar notificações quando o seu bot receber um voto no top.gg.

## Solução de problemas

<CardGroup cols={1}>
  ### Domínio personalizado

  <Card horizontal>
    Para usar um domínio personalizado (ex.: `meusite.com`) no lugar da URL padrão `meusite.squareweb.app`, você precisa do **plano Standard ou superior**. A URL padrão vem do campo **SUBDOMAIN** no arquivo de configuração. Para conectar o seu domínio, veja [como configurar seu próprio domínio](https://help.squarecloud.app/pt-br/article/como-configurar-seu-proprio-dominio-cnamedns-j39hh2/).
  </Card>

  ### Requisitos mínimos de RAM

  <Card horizontal>
    **Mínimo: 512MB de RAM** para sites e APIs, o suficiente para um build estático no runtime estático. Para aplicações que renderizam páginas no servidor (Next.js, Nuxt, Angular SSR e similares), recomendamos **pelo menos 1GB de RAM**. Para aplicações maiores, aloque mais RAM para evitar que a aplicação fique sem memória e trave.
  </Card>

  ### Não foi possível encontrar esse site.

  <Card horizontal>
    Verifique se o **subdomínio/domínio** corresponde ao configurado no campo **SUBDOMAIN** ou nas **configurações de domínio personalizado**. Se você acabou de enviar o site, aguarde até **60 segundos** para a Square liberar o **primeiro acesso**.
  </Card>

  ### Site demorou demais para responder...

  <Card horizontal>
    O seu servidor precisa escutar na **porta 80** e no **host 0.0.0.0**. A Square Cloud define as variáveis de ambiente **PORT** (`80`) e **HOST** (`0.0.0.0`) na sua aplicação: leia essas variáveis no código em vez de fixar outros valores. Um servidor que escuta apenas em `localhost` ou `127.0.0.1` nunca recebe requisições.
  </Card>
</CardGroup>

## Próximos passos

<CardGroup cols={3}>
  <Card title="Bot do Discord" icon="discord" href="/pt-br/tutorials/bots/discord">
    Hospede o bot que recebe os votos.
  </Card>

  <Card title="Variáveis de ambiente" icon="key" href="/pt-br/platform/environment-variables">
    Gerencie o secret do webhook e outros segredos.
  </Card>

  <Card title="A aplicação não inicia" icon="triangle-exclamation" href="/pt-br/platform/troubleshooting/app-wont-start">
    Corrija conflitos de porta, módulos faltando e outras falhas na inicialização.
  </Card>
</CardGroup>

## Contate-nos

Se você continuar enfrentando **dificuldades técnicas**, nossa **equipe de suporte especializada** está disponível para auxiliá-lo. [**Entre em contato conosco**](https://squarecloud.app/pt-br/support) e teremos prazer em ajudá-lo a resolver qualquer questão: a qualidade do suporte é uma grande parte do motivo pelo qual desenvolvedores avaliam a Square Cloud com [**4,9/5 em 402 avaliações**](https://www.trustpilot.com/review/squarecloud.app) no Google e Trustpilot.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.