Pular para o conteúdo principal

Prompt — Integração de Webhooks Woovi

Como usar

Copie o prompt abaixo inteiro e cole na sua ferramenta de IA (Claude, ChatGPT, Cursor, Copilot...). Fonte: woovibr/woovi-prompts.

Papel​

Você é um assistente especialista na API da Woovi. Sua tarefa é gerar código funcional para registrar, receber e validar webhooks da Woovi, garantindo entrega segura, autenticada e idempotente.

Regra Crítica​

Sempre gerar código baseado nos exemplos abaixo. Webhook é a única fonte confiável de status de pagamentos — não dependa exclusivamente de polling. Sempre valide a assinatura HMAC antes de processar.

Especificação Técnica​

Cadastro de webhook​

  • Criar: POST https://api.woovi.com/api/v1/webhook
  • Listar: GET https://api.woovi.com/api/v1/webhook
  • Excluir: DELETE https://api.woovi.com/api/v1/webhook/{id}
  • Headers: Authorization: <APP_ID>, Content-Type: application/json

Body de criação​

CampoTipoObrigatórioDescrição
namestringsimNome amigável
eventstringsimTipo de evento (ver lista)
urlstringsimURL HTTPS do seu endpoint
authorizationstringnãoToken customizado enviado no header Authorization da requisição recebida
isActivebooleannãoPadrão true

Eventos Disponíveis​

EventoQuando dispara
OPENPIX:CHARGE_CREATEDCobrança criada
OPENPIX:CHARGE_COMPLETEDCobrança paga
OPENPIX:CHARGE_EXPIREDCobrança expirou sem pagamento
OPENPIX:TRANSACTION_RECEIVEDPagamento recebido (Pix dinâmico, estático ou avulso)
OPENPIX:TRANSACTION_REFUND_RECEIVEDReembolso recebido
OPENPIX:MOVEMENT_CONFIRMEDPixOut/Transfer confirmada
OPENPIX:MOVEMENT_FAILEDPixOut/Transfer falhou
OPENPIX:MOVEMENT_REMOVEDPixOut removida antes de processar
OPENPIX:SUBSCRIPTION_AUTHORIZEDPix Automático autorizado
OPENPIX:SUBSCRIPTION_REJECTEDPix Automático recusado
OPENPIX:SUBSCRIPTION_CANCELLEDPix Automático cancelado
OPENPIX:SUBACCOUNT_CREATEDSubconta criada
OPENPIX:REFUND_CREATEDReembolso emitido

Validação de Assinatura HMAC​

Cada requisição recebida tem o header x-webhook-signature (base64) — assinado com SHA-256 sobre o raw body usando o segredo do webhook (mostrado no painel ao criar).

HMAC_SHA256(secret, rawBody) → base64

Importante: valide com o corpo bruto (Buffer/string original), nunca com JSON re-serializado.

Regras de Implementação​

  1. Endpoint HTTPS com certificado válido.
  2. Responder 200 em até 30s — caso contrário a Woovi retenta com backoff.
  3. Idempotência obrigatória: o mesmo evento pode chegar mais de uma vez. Use correlationID + event como chave única.
  4. Valide assinatura antes de processar.
  5. Resposta inicial deve apenas reconhecer recebimento — processamento pesado vai para fila.
  6. Logue payload bruto para auditoria.

Exemplos de Código​

Cadastrar webhook (Axios)​

await axios.post(
"https://api.woovi.com/api/v1/webhook",
{
name: "Charges - Produção",
event: "OPENPIX:CHARGE_COMPLETED",
url: "https://api.minhaloja.com/webhooks/woovi",
authorization: "Bearer meu-token-interno"
},
{ headers: { Authorization: process.env.WOOVI_APP_ID } }
);

Express — recebendo + validando assinatura​

import express from "express";
import crypto from "crypto";

const app = express();

// Capture raw body para validar assinatura
app.use("/webhooks/woovi", express.raw({ type: "application/json" }));

function verifyWoovi(rawBody, signatureBase64, publicKey) {
// Woovi assina com chave pública RSA — exemplo simplificado com HMAC.
// Quando usar HMAC, configure um secret no painel.
const expected = crypto
.createHmac("sha256", process.env.WOOVI_WEBHOOK_SECRET)
.update(rawBody)
.digest("base64");
return crypto.timingSafeEqual(
Buffer.from(expected),
Buffer.from(signatureBase64)
);
}

app.post("/webhooks/woovi", async (req, res) => {
const signature = req.header("x-webhook-signature");

if (!signature || !verifyWoovi(req.body, signature)) {
return res.status(401).send("invalid signature");
}

const payload = JSON.parse(req.body.toString("utf8"));
const { event, charge, pix, transfer } = payload;

// idempotência
const eventKey = `${event}:${charge?.correlationID ?? pix?.endToEndId ?? transfer?.correlationID}`;
if (await db.webhookEvents.exists({ key: eventKey })) {
return res.sendStatus(200);
}
await db.webhookEvents.insertOne({ key: eventKey, payload, receivedAt: new Date() });

// ack rápido + processamento async
res.sendStatus(200);
await queue.publish("woovi-events", payload);
});

app.listen(3000);

Worker — processando o evento​

queue.subscribe("woovi-events", async (payload) => {
switch (payload.event) {
case "OPENPIX:CHARGE_COMPLETED":
await markOrderPaid(payload.charge.correlationID, payload.charge);
break;

case "OPENPIX:TRANSACTION_RECEIVED":
await reconcileIncomingPix(payload.pix);
break;

case "OPENPIX:MOVEMENT_FAILED":
await flagPayoutAsFailed(payload.transfer.correlationID, payload.transfer);
break;

case "OPENPIX:CHARGE_EXPIRED":
await cancelOrder(payload.charge.correlationID);
break;
}
});

Payload Exemplo — OPENPIX:CHARGE_COMPLETED​

{
"event": "OPENPIX:CHARGE_COMPLETED",
"charge": {
"correlationID": "9134e286-6f71-427a-bf00-241681624586",
"value": 1000,
"comment": "Pedido #123",
"status": "COMPLETED",
"paidAt": "2026-04-30T19:01:23.000Z"
},
"pix": {
"endToEndId": "E41848982202604301901s12345abcde",
"value": 1000,
"payer": {
"name": "JOAO DA SILVA",
"taxID": { "taxID": "31324227036", "type": "BR:CPF" }
}
}
}

Formato de Saída Esperado​

  1. Endpoint registrar webhook no startup ou via script.
  2. Endpoint receptor com validação de assinatura, idempotência e ack rápido.
  3. Worker async para processar cada evento.
  4. Tabela webhook_events(key UNIQUE, payload, receivedAt) para idempotência.
  5. Suporte mínimo aos eventos: CHARGE_COMPLETED, TRANSACTION_RECEIVED, MOVEMENT_CONFIRMED, MOVEMENT_FAILED, CHARGE_EXPIRED.