Skip to content

Webhooks — Eventos ASAAS

O fast_deliv recebe notificacoes do ASAAS quando transferencias Pix sao processadas. Voce pode expor um endpoint proprio para receber esses eventos e reagir a eles no seu sistema.


Configuracao

URL do webhook

POST https://fast-deliv-backend.vercel.app/api/v1/webhooks/asaas

Este endpoint nao requer JWT. A autenticidade e verificada via assinatura HMAC-SHA256.

Header de autenticidade

Header Descricao
asaas-signature HMAC-SHA256 do body com o ASAAS_WEBHOOK_SECRET

Seguranca

Sempre valide a assinatura antes de processar o payload. Nunca confie apenas no IP de origem.


Validando a assinatura

import hashlib
import hmac
from fastapi import Request, HTTPException

ASAAS_WEBHOOK_SECRET = "seu_webhook_secret"

async def verify_asaas_signature(request: Request) -> bytes:
    body = await request.body()
    signature = request.headers.get("asaas-signature", "")

    expected = hmac.new(
        ASAAS_WEBHOOK_SECRET.encode(),
        body,
        hashlib.sha256,
    ).hexdigest()

    if not hmac.compare_digest(expected, signature):
        raise HTTPException(status_code=401, detail="Assinatura invalida")

    return body
import crypto from "crypto";

const ASAAS_WEBHOOK_SECRET = process.env.ASAAS_WEBHOOK_SECRET;

function verifySignature(rawBody, signature) {
  const expected = crypto
    .createHmac("sha256", ASAAS_WEBHOOK_SECRET)
    .update(rawBody)
    .digest("hex");

  return crypto.timingSafeEqual(
    Buffer.from(expected),
    Buffer.from(signature)
  );
}

// Express handler
app.post("/webhooks/asaas", express.raw({ type: "application/json" }), (req, res) => {
  const sig = req.headers["asaas-signature"];
  if (!verifySignature(req.body, sig)) {
    return res.status(401).json({ error: "Assinatura invalida" });
  }

  const event = JSON.parse(req.body.toString());
  handleEvent(event);
  res.status(200).json({ received: true });
});
# Gerar assinatura para testar manualmente
BODY='{"event":"TRANSFER_DONE","payment":{"id":"tra_xxx"}}'
SECRET="seu_webhook_secret"

SIGNATURE=$(echo -n "$BODY" | openssl dgst -sha256 -hmac "$SECRET" | awk '{print $2}')
echo "asaas-signature: $SIGNATURE"
require 'openssl'

ASAAS_WEBHOOK_SECRET = ENV['ASAAS_WEBHOOK_SECRET']

def verify_signature(raw_body, signature)
  expected = OpenSSL::HMAC.hexdigest('SHA256', ASAAS_WEBHOOK_SECRET, raw_body)
  ActiveSupport::SecurityUtils.secure_compare(expected, signature)
  # Ou sem ActiveSupport:
  # expected.bytes.zip(signature.bytes).all? { |a, b| a == b }
end

Eventos

TRANSFER_DONE / TRANSFER_APPROVED

Transferencia Pix concluida com sucesso. O withdrawal.status e atualizado para completed.

{
  "event": "TRANSFER_DONE",
  "payment": {
    "id": "tra_xxxxxxxxxxxx",
    "status": "DONE",
    "value": 50.00,
    "description": "Saque fast_deliv — motorista Carlos Silva",
    "dateCreated": "2026-05-19",
    "confirmedDate": "2026-05-19"
  }
}

TRANSFER_FAILED / TRANSFER_CANCELLED

Transferencia falhou ou foi cancelada. O withdrawal.status e atualizado para failed.

{
  "event": "TRANSFER_FAILED",
  "payment": {
    "id": "tra_xxxxxxxxxxxx",
    "status": "FAILED",
    "value": 50.00,
    "description": "Saque fast_deliv — motorista Carlos Silva",
    "failReason": "Chave Pix invalida ou inexistente",
    "dateCreated": "2026-05-19"
  }
}

Resposta esperada

O endpoint deve retornar 200 OK em menos de 5 segundos. Qualquer outro status fara o ASAAS tentar reenviar.

{"received": true}

Processamento assincrono

Se o processamento for demorado, responda 200 imediatamente e enfileire o evento em background (Celery, RQ, etc.).


Tabela de eventos

Evento Descricao Acao no sistema
TRANSFER_DONE Saque concluido withdrawal.status = completed
TRANSFER_APPROVED Saque aprovado pelo banco Idem (alias)
TRANSFER_FAILED Saque falhou withdrawal.status = failed, saldo revertido
TRANSFER_CANCELLED Saque cancelado Idem ao FAILED