Webhooks
Facturalo puede notificar a tu servidor en tiempo real cuando ocurre un evento importante. Configura una URL por entorno y tu servidor recibirá un POST con el payload del evento.
¿Qué eventos envía Facturalo via webhook?
| Evento | Descripción | Activo por defecto |
|---|---|---|
| documento.sunat.respuesta | SUNAT procesó el documento (aceptado o rechazado). | Activo |
| documento.anulado | La baja del documento fue aceptada por SUNAT. | Activo |
| documento.correo.enviado | El PDF fue enviado al correo del cliente. | Inactivo |
| documento.correo.fallido | El envío del correo al cliente falló. | Inactivo |
| guia.sunat.respuesta | SUNAT procesó la guía de remisión. | Activo |
| documento.error | Error interno al generar o enviar el documento. | Activo |
¿Cómo verificar la firma HMAC de un webhook?
Cada evento incluye el header X-Facturalo-Firma con un HMAC-SHA256 del body usando tu webhookSecret. Verifica siempre la firma antes de procesar el evento.
X-Facturalo-Firma: {hmac-sha256}
Verificación y procesamiento — PHPphp
<?php
// En tu endpoint POST /webhooks/facturalo
$rawBody = file_get_contents('php://input');
$firmaRecibida = $_SERVER['HTTP_X_FACTURALO_FIRMA'] ?? '';
$webhookSecret = 'whsec_test_xxxxxxxxxxxxxxxxxxxx'; // tu webhookSecret
// Verificar firma
$firmaEsperada = hash_hmac('sha256', $rawBody, $webhookSecret);
if (!hash_equals($firmaEsperada, $firmaRecibida)) {
http_response_code(401);
exit('Firma inválida');
}
// Procesar evento
$payload = json_decode($rawBody, true);
switch ($payload['evento']) {
case 'documento.sunat.respuesta':
$doc = $payload['documento'];
$estado = $doc['estado']; // ACEPTADA | RECHAZADA
// guardar estado en tu BD...
break;
case 'documento.anulado':
// marcar como anulado en tu BD...
break;
case 'documento.error':
$error = $payload['error']['mensaje'];
// registrar error...
break;
}
http_response_code(200);
echo json_encode(['ok' => true]);¿Qué pasa si mi servidor no responde el webhook?
Si tu servidor no responde con un código 2xx, el evento queda marcado como fallido y puedes reenviarlo manualmente desde Listar webhooks enviados. El payload reenviado es exactamente el original guardado en base de datos.