# Arquitetura de Webhooks Seguros & Pagamentos (Enterprise)

Esta arquitetura foi desenhada para processar milhões de pagamentos, garantindo 100% de consistência de dados, prevenção contra fraude, falhas de rede e ataques de repetição (Replay Attacks).

## 1. Princípios de Segurança Implementados

### 1.1 Assinatura e Validação de Origem
Nenhum webhook é processado sem validação criptográfica (HMAC SHA-256).
- **Stripe:** Utiliza `Stripe-Signature` header.
- **PayPal:** Utiliza `PAYPAL-AUTH-ALGO`, `PAYPAL-CERT-URL`, e `PAYPAL-TRANSMISSION-SIG`.
- **Ifthenpay/Easypay:** Validação por chaves simétricas partilhadas.

### 1.2 Proteção Contra Replay Attacks
- Verificamos sempre o timestamp do evento. Eventos com mais de 5 minutos de atraso (`timestamp_evento < agora - 5m`) são rejeitados automaticamente.
- Garantia de que o atacante não pode intercetar e reenviar o mesmo payload para duplicar ações.

### 1.3 Idempotência Absoluta
- A base de dados MySQL tem um constraint único (`uk_provider_event`) na tabela `webhook_logs` (campo `external_event_id`).
- Se a Gateway enviar o mesmo evento 3 vezes por erro de timeout, o MySQL rejeita os duplicados a nível de driver (Erro 1062 - Duplicate Entry), impedindo processamento duplo.

### 1.4 Fila de Processamento Assíncrono (Event-Driven)
- O Webhook Endpoint **nunca** processa a lógica de negócio pesada (gerar faturas, enviar emails).
- Ele apenas **guarda na BD** (tabela `webhook_logs` com status `pending`) e retorna HTTP `200 OK` imediatamente para a Gateway.
- Um cron job (ou worker de fila como Redis/BullMQ/RabbitMQ) consome a tabela `webhook_logs` e executa as ações em background.

---

## 2. Fluxo de Eventos de Pagamento

### Evento: `payment_success` / `charge.succeeded`
**Lógica de Negócio (Worker):**
1. Atualizar `payments.status` = 'succeeded'.
2. Chamar Stored Procedure `sp_process_order_payment(order_id)`.
   - Atualiza `orders.status` = 'pago'.
   - Desconta `products.stock`.
   - Regista `financial_transactions`.
3. Integração com Faturação (ex: Moloni/Invoicexpress) para emitir `invoices`.
4. Disparar notificação de email via Resend ao cliente.
5. Marcar `webhook_logs.status` = 'processed'.

### Evento: `payment_failed` / `charge.failed`
1. Atualizar `payments.status` = 'failed'.
2. Atualizar `orders.status` = 'falha'.
3. Alertar admin e cliente para tentarem novamente.
4. Libertar cativações de stock caso existam regras temporárias.

### Evento: `refund` / `charge.refunded`
1. Atualizar `payments.status` = 'refunded'.
2. Atualizar `orders.status` = 'reembolsado'.
3. Restaurar stock no catálogo (`products.stock = products.stock + quantity`).
4. Emitir nota de crédito fiscal na integração de faturação.
5. Registar saída em `financial_transactions`.

### Evento: `dispute_created` / `chargeback`
1. Atualizar `payments.status` = 'disputed'.
2. Bloquear conta/perfil por segurança caso as regras do Tenant exijam.
3. Inserir em `fraud_alerts` com criticidade 'high'.
4. Notificar equipa de fraude/admin imediatamente.

---

## 3. Esquema Backend Recomendado (Node.js/Express)

```typescript
// 1. Endpoint do Webhook
app.post('/api/webhooks/stripe', express.raw({type: 'application/json'}), async (req, res) => {
    const signature = req.headers['stripe-signature'];
    
    try {
        // Validação Criptográfica (rejeita atacantes imediatamente)
        const event = stripe.webhooks.constructEvent(req.body, signature, process.env.STRIPE_WEBHOOK_SECRET);
        
        // Inserção Idempotente na Base de Dados (Falha se já existir o ID do evento)
        await db.execute(
            `INSERT INTO webhook_logs (id, tenant_id, provider, event_type, external_event_id, payload) 
             VALUES (UUID(), ?, 'stripe', ?, ?, ?)`, 
            [tenantId, event.type, event.id, JSON.stringify(event)]
        );
        
        // Responde rápido para evitar timeouts da Gateway
        return res.status(200).send('OK');

    } catch (err) {
        if (err.code === 'ER_DUP_ENTRY') return res.status(200).send('Already Processed');
        console.error('Webhook Security Error:', err.message);
        return res.status(400).send('Webhook Error: Invalid Signature or Payload');
    }
});

// 2. Background Worker (CRON ou Daemon)
async function processPendingWebhooks() {
    // Bloquear a linha na DB (Pessimistic Locking) para evitar concorrência
    const [events] = await db.query(
        `SELECT * FROM webhook_logs WHERE status = 'pending' FOR UPDATE SKIP LOCKED LIMIT 10`
    );

    for (const event of events) {
        try {
            await handleEventLogic(event);
            await db.execute(`UPDATE webhook_logs SET status = 'processed', processed_at = NOW() WHERE id = ?`, [event.id]);
        } catch (error) {
            await db.execute(`UPDATE webhook_logs SET status = 'failed', error_message = ? WHERE id = ?`, [error.message, event.id]);
        }
    }
}
```

## 4. Mitigação e Rollback

- **Timeouts**: Se a integração de faturação falhar, o status do webhook passa a `failed`. O sistema de retry apanha webhooks com `failed` após X minutos (com backoff exponencial).
- **Consistência**: Toda a lógica de negócio (Worker) executa num bloco `START TRANSACTION; ... COMMIT;`. Se algo falhar, o `ROLLBACK` garante que nada foi gravado pela metade na base de dados MySQL.
