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

# Webhooks

> Recibe notificaciones de pago en tiempo real

## Descripción general

Los webhooks notifican a tu servidor cuando ocurren eventos de pago. Configura el `webhookUrl` al crear un pago, y Ebioro enviará un POST a esa URL cada vez que el estado del pago cambie.

## Payload del webhook

```json theme={null}
{
  "data": {
    "type": "transaction_updated",
    "id": "pt_ABC123",
    "status": "paid",
    "settlement_status": "paid",
    "amount": {
      "value": 1000,
      "currency": "USD"
    },
    "settlement_amount": 1000,
    "settlement_currency": "USDC",
    "settlement_fee": 25,
    "amount_paid": 1000,
    "amount_received": 1000,
    "amount_remaining": 0,
    "history": [
      {
        "time": 1714124827333,
        "status": "success",
        "description": "Payment created"
      },
      {
        "time": 1714124900000,
        "status": "success",
        "description": "Customer paid"
      }
    ],
    "metadata": {}
  }
}
```

## Tipos de eventos

| Tipo                  | Cuándo                                              |
| --------------------- | --------------------------------------------------- |
| `transaction_created` | Orden de pago creada                                |
| `transaction_updated` | Estado del pago cambió (pagado, parcial, liquidado) |
| `transaction_failed`  | Procesamiento del pago falló                        |

<Info>
  Un pago puede emitir **varios** eventos `transaction_updated` a medida que avanza (por ejemplo `underpaid` y luego `paid`), y la cantidad puede variar según el tipo de liquidación. No asumas un número fijo — basa tu lógica en el último `status` y `settlement_status` de cada evento, y trata `settlement_status: "paid"` como la señal de que los fondos se han liquidado en tu cuenta. Deduplica por el `id` del pago.
</Info>

## Verificación de webhooks

Cada webhook incluye un header `X-WEBHOOK-AUTH` con una firma HMAC-SHA256. **Siempre verifica esta firma** antes de procesar el webhook.

La firma se calcula sobre el **cuerpo de la solicitud sin procesar (raw)** tal como se envió. Verifica contra esos bytes sin procesar — no analices el JSON para volver a serializarlo, porque un objeto re-serializado puede diferir en el orden de las claves o en los espacios y la firma dejará de coincidir.

```javascript theme={null}
const crypto = require('crypto');
const express = require('express');
const app = express();

// Captura el cuerpo sin procesar para la ruta del webhook (NO uses express.json() aquí).
app.post('/webhook', express.raw({ type: 'application/json' }), (req, res) => {
  const signature = req.headers['x-webhook-auth'];
  const rawBody = req.body; // Buffer — los bytes exactos que Ebioro firmó

  const expected = crypto
    .createHmac('sha256', TU_API_SECRET_KEY)
    .update(rawBody)
    .digest('hex');

  const valid =
    signature &&
    expected.length === signature.length &&
    crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));

  if (!valid) {
    return res.status(401).send('Firma inválida');
  }

  const { type, status, id } = JSON.parse(rawBody.toString()).data;
  // Procesar el webhook...

  res.status(200).send('OK');
});
```

## Requisitos

* La URL del webhook debe usar **HTTPS** en producción
* Tu endpoint debe responder con código de estado **200**
* Responde dentro de **10 segundos** o la solicitud expirará

## Política de reintentos

Si tu endpoint devuelve un código de estado diferente a 200 o expira, el webhook **no se reintenta automáticamente**. Usa el [registro de entregas](#registro-de-entregas-y-reenvio) para encontrar entregas fallidas y reenviarlas.

## Registro de entregas y reenvío

Cada entrega de webhook queda registrada — exitosa o no. Usa la API de registros de webhooks para auditar y recuperar:

```bash theme={null}
# Encontrar entregas fallidas de un pago
GET /merchants/{merchantId}/webhook-logs?status=failed&checkout_id=pt_ABC123

# Inspeccionar una entrega — incluye el payload exacto enviado y la respuesta de tu endpoint
GET /merchants/{merchantId}/webhook-logs/{id}

# Reenviarla a tu endpoint
POST /merchants/{merchantId}/webhook-logs/{id}/resend
```

Cada entrada del registro guarda el tipo de evento, la URL de destino, el estado de la entrega (`success` | `failed` | `skipped`), el código HTTP, el tiempo de respuesta y el número de intentos.

Un **reenvío** reproduce el payload almacenado originalmente, firmado de nuevo con tu clave secreta **actual**:

* El payload es idéntico byte a byte al de la entrega original — tu endpoint recibe el mismo evento otra vez. **Deduplica por el contenido del evento** (id del pago + estado), no por la firma.
* Los reenvíos están limitados a **10 por minuto**.

<Warning>
  Siempre verifica la firma `X-WEBHOOK-AUTH`. Sin verificación, un atacante podría enviar eventos de webhook falsos a tu endpoint.
</Warning>
