La cadena de entrada
Para un registro de alta se concatenan, en este orden y como campo=valor unidos por &: IDEmisorFactura, NumSerieFactura, FechaExpedicionFactura, TipoFactura, CuotaTotal, ImporteTotal, Huella (la del registro anterior) y FechaHoraHusoGenRegistro. Después: bytes UTF-8, SHA-256 y hexadecimal en mayúsculas.
Para un registro de anulación: IDEmisorFacturaAnulada, NumSerieFacturaAnulada, FechaExpedicionFacturaAnulada, Huella y FechaHoraHusoGenRegistro.
Ejemplo oficial 1: primer registro
El primer registro de la cadena lleva Huella= vacío:
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
Resultado: 3C464DAF61ACB827C65FDA19F352A4E3BDC2C640E9E9FC4CC058073F38F12F60
Encadenar
El registro siguiente pone esa huella en su campo Huella. En el ejemplo oficial 2 (factura 12345679/G34, generada a las 19:20:35) el resultado es F7B94CFD8924EDFF273501B01EE5153E4CE8F259766F88CF6ACB8935802A2B97. Y la anulación de esa factura (ejemplo 3) da 177547C0D57AC74748561D054A9CEC14B4C4EA23D1BEFD6F2E69E3A388F90C68.
Código
import { createHash } from 'node:crypto';
const ALTA = ['IDEmisorFactura', 'NumSerieFactura', 'FechaExpedicionFactura',
'TipoFactura', 'CuotaTotal', 'ImporteTotal', 'Huella', 'FechaHoraHusoGenRegistro'];
export function altaHash(r) {
const s = ALTA.map((k) => `${k}=${r[k]}`).join('&');
return createHash('sha256').update(s, 'utf8').digest('hex').toUpperCase();
}Es la lógica de nuestro módulo, que reproduce los tres ejemplos oficiales en sus tests. La versión real valida además cada campo antes de calcular.
Lo que suele fallar
- Importes: el valor de la cadena tiene que ser exactamente el del XML. Nosotros siempre emitimos dos decimales (
123.45,0.05). - Fechas:
FechaExpedicionFacturaenDD-MM-AAAA;FechaHoraHusoGenRegistrocon su huso (+01:00). - Espacios: no recortes ni cambies valores al calcular; la AEAT recalcula desde el XML que recibe.
- La huella anterior: guárdala por emisor. Si la pierdes, el siguiente registro no encadena. Ver error 2007.
Si la AEAT calcula otra huella, responde con el error 2000 (aceptado con errores).
En hoja de ruta: calculadora de la huella en el navegador y auditoría de la cadena completa (Sentinel · Auditar).
Fuentes
- Orden HAC/1177/2024 (BOE-A-2024-22138)
- AEAT · Documentación técnica de Sistemas Informáticos de Facturación y VERI*FACTU
- AEAT · Lista de códigos de error (errores.properties)
Información general, no asesoramiento fiscal ni jurídico.