Repository navigation
Registro de Eventos
Un sistema que funciona en modo NO VERI*FACTU debe llevar un registro de eventos (Orden HAC/1177/2024, artículo 9). En modo VERI*FACTU no es obligatorio. EventRecordService genera cada evento, lo encadena con el anterior y lo firma. Los eventos nunca se envían a la AEAT.
VeriFactuConfig config = new VeriFactuConfig("Mi Empresa S.L.", "B72877814");
config.setDeveloperNif("B72877814");
EventRecordService events = new EventRecordService("/ruta/certificado.p12", "password", config);
// El primer evento de la cadena de un obligado: sin evento anterior (PrimerEvento = S)
Map<String, Object> start = events.generate(
new Event(EventType.NO_VERIFACTU_START, "B72877814", "Mi Empresa S.L."));
// Un evento posterior se encadena con el anterior
Event next = new Event(EventType.NO_VERIFACTU_END, "B72877814", "Mi Empresa S.L.")
.setPrevious(new Event.Reference(EventType.NO_VERIFACTU_START,
ZonedDateTime.parse((String) start.get("generated_at")), (String) start.get("hash")));
Map<String, Object> end = events.generate(next);| Clave | Contenido |
|---|---|
status |
success o error
|
hash |
Huella del evento (SHA-256, 64 caracteres hexadecimales en mayúsculas) |
hashInput |
Cadena exacta sobre la que se calculó la huella. Guárdala junto a la huella para reverificar la cadena con HashHelper.verify
|
signedXml |
El RegistroEvento firmado, en base64 |
generated_at |
FechaHoraHusoGenEvento usada en la huella (ISO 8601 con huso) |
event_type |
Código del tipo de evento |
message, validation_errors
|
Solo con status=error: el motivo y la lista de fallos de validación |
Para encadenar el evento siguiente, guarda de cada evento su tipo, generated_at y hash.
La huella sigue el documento de la AEAT Detalle de las especificaciones técnicas para la generación de la huella o hash de los registros (apartado 3.c). Los campos, en este orden, como nombre=valor unidos por &:
-
NIFdel sistema informático (el del productor). -
ID(vacío). -
IdSistemaInformatico. -
Version. -
NumeroInstalacion. -
NIFdel obligado. -
TipoEvento. -
HuellaEvento: la del evento anterior; vacía en el primer evento. -
FechaHoraHusoGenEvento.
Se codifica en UTF-8 y se aplica SHA-256, con la salida en hexadecimal y mayúsculas. Los datos del sistema informático salen de VeriFactuConfig. HashHelper.generateEventHash calcula la huella sin generar el XML.
EventType recoge la lista L2E del anexo de la orden:
| Código | Tipo | Datos propios (setDetails) |
|---|---|---|
| 01 | Inicio del funcionamiento como NO VERI*FACTU | — |
| 02 | Fin del funcionamiento como NO VERI*FACTU | — |
| 03 | Lanzamiento de la detección de anomalías en los registros de facturación | Event.ProcessLaunch |
| 04 | Detección de anomalías en los registros de facturación |
Event.AnomalyDetection (con setInvoice) |
| 05 | Lanzamiento de la detección de anomalías en los registros de evento | Event.ProcessLaunch |
| 06 | Detección de anomalías en los registros de evento |
Event.AnomalyDetection (con setEvent) |
| 07 | Restauración de copia de seguridad | — |
| 08 | Exportación de registros de facturación de un periodo | Event.BillingExport |
| 09 | Exportación de registros de evento de un periodo | Event.EventExport |
| 10 | Registro resumen de eventos | Event.Summary |
| 90 | Otros eventos voluntarios | — |
El tipo de anomalía de los eventos 04 y 06 es AnomalyType (lista L1E: integridad de la huella o de la firma, trazabilidad de la cadena y de las fechas). Un evento con datos propios que no corresponden a su tipo es un error de validación.
El resumen (10) debe generarse al menos una vez por cada 6 horas de funcionamiento y antes de apagar el sistema. Lo gestiona quien llama al SDK.
El RegistroEvento se firma con XAdES enveloped sobre el nodo RegistroEvento. El esquema EventosSIF.xsd exige la firma como último hijo de Evento: el SDK la coloca allí (SignatureService.signXml(xml, placeAfterXPath)). El XML generado valida contra EventosSIF.xsd, que el SDK empaqueta.
Ejemplo completo: EventRecordExample.java