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

# Webhooks

> Receba atualizações automáticas sempre que um checkout mudar de estado.

# Webhooks

Pense no webhook como o jeito da TroqPay te avisar que alguma coisa mudou.

Em vez de perguntar para a API o tempo todo se um checkout foi pago, você cadastra uma URL no seu sistema e a TroqPay envia um `POST` sempre que um evento relevante acontecer.

## Por que usar webhooks

Sem webhook, seu backend teria que ficar consultando a API repetidamente para descobrir se o pagamento foi confirmado.

Com webhook, a TroqPay te avisa assim que isso acontecer.

Na prática, isso ajuda você a:

* atualizar o status de um pedido
* liberar acesso a um produto
* registrar a confirmação do pagamento
* reduzir polling desnecessário

## Onde cadastrar no app

No fluxo mais comum, você faz isso no app da TroqPay:

<Steps>
  <Step title="Abra a área de integração">
    Entre no app e vá até a área de integração da sua conta.
  </Step>

  <Step title="Cadastre a URL do endpoint">
    Informe a URL pública que vai receber os eventos da TroqPay.
  </Step>

  <Step title="Escolha quais eventos esse endpoint recebe">
    No cadastro você seleciona os eventos (`checkout.created`, `checkout.paid`, `checkout.expired`). A TroqPay só dispara para o endpoint os eventos marcados — os demais são ignorados para essa URL.
  </Step>

  <Step title="Guarde o segredo do webhook">
    Você vai usar esse segredo para validar o header `x-troqpay-signature`.
  </Step>

  <Step title="Teste no sandbox antes de subir para produção">
    Valide o endpoint com eventos de teste antes de trocar a chave e a URL final.
  </Step>
</Steps>

## Como funciona na prática

<Steps>
  <Step title="Você cria um endpoint no seu sistema">
    Exemplo: `https://seusistema.com/webhooks/troqpay`
  </Step>

  <Step title="Você cadastra essa URL na sua conta">
    A TroqPay passa a enviar eventos para esse endpoint.
  </Step>

  <Step title="Seu servidor recebe o evento">
    O corpo do `POST` informa o tipo do evento e os dados do checkout.
  </Step>

  <Step title="Seu sistema processa e responde 2xx">
    Depois disso, seu fluxo segue normalmente no seu sistema.
  </Step>
</Steps>

## O que a TroqPay espera do seu endpoint

<CardGroup cols={2}>
  <Card title="Receba JSON por HTTPS">
    Seu endpoint precisa aceitar `POST` com corpo JSON e estar disponível publicamente por HTTPS.
  </Card>

  <Card title="Valide a assinatura">
    Use `x-troqpay-signature` e o segredo do webhook para confiar no evento.
  </Card>

  <Card title="Responda 2xx rápido">
    Faça o ack assim que a entrega for aceita. Deixe processamento pesado para sua fila interna.
  </Card>

  <Card title="Espere reentregas">
    Trate o modelo de entrega como `at least once`: o mesmo evento pode chegar mais de uma vez.
  </Card>
</CardGroup>

## Eventos disponíveis hoje

| Evento             | Quando acontece                        |
| ------------------ | -------------------------------------- |
| `checkout.created` | Quando o checkout é criado com sucesso |
| `checkout.paid`    | Quando o pagamento Pix é confirmado    |
| `checkout.expired` | Quando o checkout expira sem pagamento |

## Ordem, timeout e reentregas

* para o mesmo checkout, o fluxo esperado costuma ser `checkout.created` seguido de `checkout.paid` ou `checkout.expired`
* não assuma ordem estrita entre eventos de checkouts diferentes
* a TroqPay faz **1 tentativa imediata** e, se ela falhar, até **4 reentregas** (total de **5 tentativas**), com backoff `60s → 5m → 15m → 60m` entre as reentregas
* o timeout de cada tentativa é de **5 segundos**
* se o seu endpoint não responder `2xx` dentro desse prazo, a entrega é retentada
* guarde `event.id` para garantir processamento idempotente — o mesmo `evt_...` pode chegar mais de uma vez

## Restrições do endpoint

A TroqPay valida algumas coisas quando você cadastra a URL no app:

* precisa ser uma URL absoluta válida
* precisa usar `http://` ou `https://`
* não pode ter credenciais embutidas (`https://user:pass@host/...` é rejeitado)
* em produção (`trq_live_`), precisa ser `https://` e **não pode** ser endereço local ou de rede privada (RFC 1918, loopback, link-local)

<Warning>
  O entregador da TroqPay **não segue redirects**. Se o seu endpoint responder `301`, `302`, `307` ou `308`, a entrega é considerada falha e consome uma das 5 tentativas. Cadastre sempre a URL final (sem redirect na frente, mesmo que via CDN).
</Warning>

## Exemplo de payload

```json theme={null}
{
  "id": "evt_4a8b3c1d5e6f2a9b0c1d2e3f",
  "type": "checkout.paid",
  "createdAt": "2026-04-25T14:02:10.000Z",
  "livemode": false,
  "data": {
    "checkout": {
      "id": "chk_4a8b3c1d5e6f2a9b0c1d",
      "livemode": false,
      "status": "PAID",
      "amount": 12990,
      "currency": "BRL",
      "description": "Plano Pro",
      "externalId": "order_1001",
      "checkoutUrl": "https://pay.troqpay.com/pay/chk_4a8b3c1d5e6f2a9b0c1d",
      "createdAt": "2026-04-25T14:00:00.000Z",
      "expiresAt": "2026-04-25T14:30:00.000Z",
      "paidAt": "2026-04-25T14:02:10.000Z",
      "customer": {
        "name": "Maria Silva",
        "email": "maria@example.com",
        "document": null
      },
      "pix": {
        "copyPaste": "00020101021226840014br.gov.bcb.pix...",
        "qrCodeUrl": "https://api.openpix.com.br/openpix/charge/brcode/image/chk_4a8b3c1d5e6f2a9b0c1d.png"
      },
      "settlement": {
        "currency": "BRL",
        "status": "AVAILABLE",
        "amount": "129.90"
      },
      "metadata": {
        "plan": "pro"
      }
    }
  }
}
```

`data.checkout` é exatamente a mesma forma retornada por `GET /v1/checkouts/{checkoutId}`. Inclui `customer` (sem `phone`), `pix`, `settlement` e `metadata`.

## Assinatura

Cada endpoint cadastrado no app tem o seu próprio segredo de assinatura, mostrado uma vez no momento do cadastro.

Toda entrega inclui o header:

```text theme={null}
x-troqpay-signature
```

O valor é `HMAC-SHA256` do corpo bruto da requisição, codificado em hex, usando esse segredo. Em pseudo-fórmula:

```text theme={null}
x-troqpay-signature = HMAC_SHA256(rawBody, signingSecret).hex()
```

Compare a assinatura calculada do seu lado com o header recebido **antes** de processar o evento.

## Exemplo de verificação em Node.js

```js theme={null}
import crypto from "node:crypto";

const signature = request.headers["x-troqpay-signature"];
const rawBody = request.rawBody; // os bytes exatos recebidos, antes de qualquer parser

const expected = crypto
  .createHmac("sha256", process.env.TROQPAY_WEBHOOK_SIGNING_SECRET)
  .update(rawBody)
  .digest("hex");

// Compare em tempo constante para evitar timing attack.
// timingSafeEqual exige buffers do mesmo tamanho, então cheque o
// comprimento antes (uma assinatura ausente ou de tamanho errado já é inválida).
const signatureBuffer = Buffer.from(signature ?? "", "hex");
const expectedBuffer = Buffer.from(expected, "hex");

if (
  signatureBuffer.length !== expectedBuffer.length ||
  !crypto.timingSafeEqual(signatureBuffer, expectedBuffer)
) {
  throw new Error("Invalid signature");
}
```

O segredo é único por endpoint cadastrado. Se você tem mais de um endpoint na mesma conta, cada um tem o seu segredo próprio.

## O que responder para a TroqPay

| Resposta do endpoint | O que isso significa                                                         |
| -------------------- | ---------------------------------------------------------------------------- |
| `2xx`                | A entrega foi aceita pelo seu sistema                                        |
| `4xx` ou `5xx`       | A entrega falhou e seu endpoint precisa ser investigado                      |
| Timeout              | Trate como falha de entrega e prepare seu endpoint para reprocessar o evento |

## Boas práticas de processamento

<CardGroup cols={2}>
  <Card title="Responda 2xx rápido">
    Faça o ack da entrega rapidamente e deixe o processamento pesado para sua fila interna.
  </Card>

  <Card title="Use idempotência">
    Guarde `event.id` para garantir que a mesma entrega não seja processada duas vezes.
  </Card>

  <Card title="Espere reentregas">
    Retry faz parte da vida de qualquer integração séria. Seu endpoint precisa lidar bem com isso.
  </Card>

  <Card title="Logue com contexto">
    Registre `event.id`, `checkout.id`, `externalId` e `livemode` para investigar problemas sem dor.
  </Card>
</CardGroup>

<Tip>
  Use `livemode` para separar eventos de teste e produção no seu pipeline.
</Tip>

<Tip>
  O campo `customer.phone` aceito no envio do checkout não é retornado dentro de `data.checkout.customer`. Se você precisa do telefone para conciliação, salve-o do seu lado quando criar o checkout.
</Tip>

## Próximos passos

* [Modelar os checkouts Pix](/flows/checkouts-pix)
* [Subir para produção](/production)
* Abra a aba `API Reference` para consultar a referência completa dos eventos e recursos

## Precisa de ajuda para seguir?

<CardGroup cols={3}>
  <Card title="Voltar ao Quickstart" href="/quickstart">
    Se você ainda não criou a primeira cobrança, volte e valide o fluxo completo.
  </Card>

  <Card title="Revisar erros" href="/flows/errors">
    Veja como investigar falhas de autenticação, payload ou resposta do endpoint.
  </Card>

  <Card title="Abrir o app" href="https://app.troqpay.com">
    Use o app da TroqPay para revisar suas URLs, chaves e o ambiente que você está usando.
  </Card>
</CardGroup>
