validar-webhook-pixlisted
Install: claude install-skill roldaobatista/roldao-method
# validar-webhook-pix
Handler de webhook Pix robusto: assinatura HMAC, idempotência por EndToEndId, lock distribuído, tratamento de status.
## Estrutura padrão
```typescript
import { Request, Response } from 'express';
import crypto from 'crypto';
import { Redis } from 'ioredis';
import { db } from './db';
const redis = new Redis(process.env.REDIS_URL!);
export async function pixWebhookHandler(req: Request, res: Response) {
// 1. Validar assinatura HMAC — PIX-EXT-002, primeira linha
if (!validarAssinatura(req)) {
return res.status(401).send();
}
const payload = req.body;
const e2eId = payload.endToEndId;
if (!e2eId) {
return res.status(400).send({ error: 'endToEndId obrigatorio' });
}
// 2. Lock distribuido por E2EID
const lockKey = `pix-webhook:lock:${e2eId}`;
const lock = await redis.set(lockKey, '1', 'EX', 30, 'NX');
if (!lock) {
// outro processo ja esta tratando — retornar 200 (idempotente)
return res.status(200).send();
}
try {
// 3. Idempotencia: ja processado?
const existente = await db.pixEvent.findUnique({ where: { e2eId } });
if (existente?.status === 'processado') {
return res.status(200).send();
}
// 4. Persistir antes de qualquer side effect — PIX-EXT-003
await db.pixEvent.upsert({
where: { e2eId },
create: {
e2eId,
txId: payload.txid,
valor: payload.valor,
chavePagador: payload.pagador?.cpf || payload.pagador?.cnpj || null,
nomeP