Cómo se calcula la huella SHA-256 de VeriFactu, paso a paso
La huella es un resumen SHA-256 de ocho datos del registro de facturación, que incluye la huella del registro anterior. Así cada registro queda encadenado al anterior y cualquier cambio posterior se detecta.
1. Los 8 campos y su orden
Para un registro de alta (la factura que se emite), la huella se calcula con estos campos, siempre en este orden y con estos nombres:
| # | Campo | Qué es | Ejemplo |
|---|---|---|---|
| 1 | IDEmisorFactura | NIF del emisor | 89890001K |
| 2 | NumSerieFactura | Serie y número de la factura | 12345678/G33 |
| 3 | FechaExpedicionFactura | Fecha de expedición | 01-01-2024 |
| 4 | TipoFactura | F1, F2, F3, R1…R5 | F1 |
| 5 | CuotaTotal | Suma de las cuotas de IVA | 12.35 |
| 6 | ImporteTotal | Importe total de la factura | 123.45 |
| 7 | Huella | Huella del registro anterior | vacío en el primero |
| 8 | FechaHoraHusoGenRegistro | Momento en que se genera el registro, con huso horario | 2024-01-01T19:20:30+01:00 |
2. El formato exacto de cada valor
La huella no se calcula sobre los datos, sino sobre el texto. Un solo carácter distinto produce una huella completamente diferente. Por eso:
- Mismo texto que en el XML. Cada valor debe ser idéntico al que va en el registro que se envía o se conserva. Si en el XML pones
123.40, en la huella también. - Importes con punto decimal.
123.45, no123,45. Lo más seguro es usar siempre dos decimales:123.4y123.40dan huellas distintas. - Fecha de expedición como DD-MM-AAAA:
01-01-2024, no2024-01-01. - Marca temporal en ISO 8601 con huso:
2024-01-01T19:20:30+01:00. El huso es parte del texto: la misma hora escrita en UTC (18:20:30+00:00) da otra huella. - Sin espacios al principio ni al final de cada valor. Los espacios intermedios sí cuentan.
- El campo
Huellase incluye siempre, aunque esté vacío en el primer registro de la cadena.
3. El cálculo en 4 pasos
- Construye la cadena uniendo los 8 campos como
nombre=valor, separados por&. No se codifica nada como URL: la barra de12345678/G33se queda como/. - Pásala a bytes en UTF-8. Importa si la serie lleva caracteres como la ñ o acentos.
- Aplica SHA-256 a esos bytes.
- Escribe el resultado en hexadecimal en mayúsculas: 64 caracteres del 0 al 9 y de la A a la F.
La huella resultante es la que irá en el campo Huella del siguiente registro. Así se forma la cadena.
Es el último registro de facturación generado por el mismo sistema para ese emisor, sea de alta o de anulación. No es “la factura anterior de la misma serie”: si anulas una factura, la siguiente alta encadena con la anulación.
4. Los ejemplos de la AEAT, con su resultado
Estos son los casos del documento técnico de la AEAT. Si tu implementación no da exactamente estas huellas, hay un error de formato.
Caso 1: primer registro de alta (sin huella anterior)
IDEmisorFactura=89890001K&NumSerieFactura=12345678/G33&FechaExpedicionFactura=01-01-2024&TipoFactura=F1&CuotaTotal=12.35&ImporteTotal=123.45&Huella=&FechaHoraHusoGenRegistro=2024-01-01T19:20:30+01:00
Huella: 3C464DAF61ACB827C65FDA19F352A4E3BDC2C640E9E9FC4CC058073F38F12F60
Caso 2: segundo registro de alta, encadenado con el primero
IDEmisorFactura=89890001K&NumSerieFactura=12345679/G34&FechaExpedicionFactura=01-01-2024&TipoFactura=F1&CuotaTotal=12.35&ImporteTotal=123.45&Huella=3C464DAF61ACB827C65FDA19F352A4E3BDC2C640E9E9FC4CC058073F38F12F60&FechaHoraHusoGenRegistro=2024-01-01T19:20:35+01:00
Huella: F7B94CFD8924EDFF273501B01EE5153E4CE8F259766F88CF6ACB8935802A2B97
Puedes comprobar ambos casos, y los tuyos, en el validador: calcula la huella en tu navegador y no envía los datos a ningún servidor.
5. La huella de un registro de anulación
Cuando se anula una factura ya registrada, el registro de anulación tiene su propia huella, con cinco campos:
IDEmisorFacturaAnuladaNumSerieFacturaAnuladaFechaExpedicionFacturaAnuladaHuella(la del registro anterior de la cadena)FechaHoraHusoGenRegistro
Caso 3: anulación de la factura del caso 2
IDEmisorFacturaAnulada=89890001K&NumSerieFacturaAnulada=12345679/G34&FechaExpedicionFacturaAnulada=01-01-2024&Huella=F7B94CFD8924EDFF273501B01EE5153E4CE8F259766F88CF6ACB8935802A2B97&FechaHoraHusoGenRegistro=2024-01-01T19:20:40+01:00
Huella: 177547C0D57AC74748561D054A9CEC14B4C4EA23D1BEFD6F2E69E3A388F90C68
6. Código en JavaScript, PHP y Python
JavaScript (navegador o Node.js 18+)
async function huellaAlta(c) {
const cadena =
`IDEmisorFactura=${c.nif.trim()}` +
`&NumSerieFactura=${c.numSerie.trim()}` +
`&FechaExpedicionFactura=${c.fecha.trim()}` +
`&TipoFactura=${c.tipo.trim()}` +
`&CuotaTotal=${c.cuota.trim()}` +
`&ImporteTotal=${c.importe.trim()}` +
`&Huella=${(c.huellaAnterior || '').trim()}` +
`&FechaHoraHusoGenRegistro=${c.fechaHora.trim()}`;
const bytes = new TextEncoder().encode(cadena); // UTF-8
const digest = await crypto.subtle.digest('SHA-256', bytes);
return [...new Uint8Array(digest)]
.map(b => b.toString(16).padStart(2, '0')).join('').toUpperCase();
}
PHP
function huella_alta(array $c): string {
$cadena = 'IDEmisorFactura=' . trim($c['nif'])
. '&NumSerieFactura=' . trim($c['num_serie'])
. '&FechaExpedicionFactura=' . trim($c['fecha'])
. '&TipoFactura=' . trim($c['tipo'])
. '&CuotaTotal=' . trim($c['cuota'])
. '&ImporteTotal=' . trim($c['importe'])
. '&Huella=' . trim($c['huella_anterior'] ?? '')
. '&FechaHoraHusoGenRegistro=' . trim($c['fecha_hora']);
return strtoupper(hash('sha256', $cadena)); // el texto PHP ya es UTF-8
}
Python
import hashlib
def huella_alta(c: dict) -> str:
cadena = (
f"IDEmisorFactura={c['nif'].strip()}"
f"&NumSerieFactura={c['num_serie'].strip()}"
f"&FechaExpedicionFactura={c['fecha'].strip()}"
f"&TipoFactura={c['tipo'].strip()}"
f"&CuotaTotal={c['cuota'].strip()}"
f"&ImporteTotal={c['importe'].strip()}"
f"&Huella={c.get('huella_anterior', '').strip()}"
f"&FechaHoraHusoGenRegistro={c['fecha_hora'].strip()}"
)
return hashlib.sha256(cadena.encode("utf-8")).hexdigest().upper()
Con los datos del caso 1, las tres funciones devuelven 3C464DAF…F12F60. Si tu resultado no coincide, revisa los errores más habituales.
Calcular bien la huella no basta para cumplir VeriFactu. El sistema también debe generar el registro de facturación en el formato XML de la AEAT, enviarlo a la AEAT (modalidad VERI*FACTU) o firmarlo y llevar un registro de eventos (modalidad no VERI*FACTU), incluir el código QR en la factura y contar con la declaración responsable del fabricante del software.
Comprueba tu huella en segundos
El validador calcula la huella y la URL del QR de cualquier registro, marca los errores de formato y trae cargados los ejemplos de la AEAT. Funciona en tu navegador: los datos no salen de tu equipo.