Appearance
📚 Documentación del Módulo Verifactu
Versión en Castellano
📋 Índice
- Introducción
- Requisitos del Sistema
- Instalación
- Configuración Inicial
- Gestión de Certificados
- Creación de Facturas
- Gestión de Líneas de Factura
- Confirmación de Facturas
- Registro en AEAT
- Encadenamiento de Facturas
- Cancelación de Facturas
- Consultas a AEAT
- Generación de Documentos
- Códigos QR de Verificación
- Manejo de Errores
- API y Servicios
- Troubleshooting
1. Introducción
¿Qué es Verifactu?
Verifactu es el sistema de la Agencia Tributaria Española (AEAT) para la verificación y trazabilidad de facturas electrónicas. Este módulo implementa la integración completa con Verifactu según la normativa vigente.
Características Principales
- ✅ Gestión completa de facturas conformes a normativa Verifactu
- ✅ Encadenamiento criptográfico mediante hashes SHA-256
- ✅ Integración directa con AEAT mediante SOAP
- ✅ Certificados digitales para firma electrónica
- ✅ Códigos QR de verificación
- ✅ Multi-tenant con soporte para múltiples empresas
- ✅ Auditoría completa de todas las operaciones
- ✅ Gestión de errores y reintentos automáticos
Normativa Aplicable
- Reglamento que regula las obligaciones de facturación
- Orden HAC/1177/2024 de Verifactu
- Especificaciones técnicas de la AEAT
2. Requisitos del Sistema
Requisitos Técnicos
PHP >= 8.4
Laravel >= 12.x
Filament >= 4.x
OpenSSL Extension
SOAP Extension
GD/Imagick Extension (para QR)Requisitos Legales
- Certificado digital válido emitido por autoridad certificadora reconocida
- NIF de empresa española
- Alta en el sistema Verifactu de AEAT
Base de Datos
- SQLite, MySQL, PostgreSQL o SQL Server
- Soporte para transacciones
- Charset UTF-8
3. Instalación
Paso 1: Instalación del Módulo
bash
# Clonar o copiar el módulo en /Modules
cd Modules
git clone [repositorio] VerifactuPaso 2: Instalar Dependencias
bash
composer require bacon/bacon-qr-code
composer require chillerlan/php-qrcodePaso 3: Ejecutar Migraciones
bash
php artisan migratePaso 4: Publicar Configuración (Opcional)
bash
php artisan vendor:publish --tag=verifactu-config4. Configuración Inicial
Variables de Entorno
Añade al .env:
env
VERIFACTU_ENVIRONMENT=sandbox # sandbox | production
VERIFACTU_SOAP_ENDPOINT=https://prewww2.aeat.es/wlpl/TIKE-CONT/ws/SuministroInformacion
VERIFACTU_QR_URL=https://prewww2.aeat.es/wlpl/TIKE-CONT/ValidarQREntornos Disponibles
Sandbox (Pruebas)
SOAP: https://prewww2.aeat.es/wlpl/TIKE-CONT/ws/SuministroInformacion
QR: https://prewww2.aeat.es/wlpl/TIKE-CONT/ValidarQRProducción
SOAP: https://www2.agenciatributaria.gob.es/wlpl/TIKE-CONT/ws/SuministroInformacion
QR: https://www2.agenciatributaria.gob.es/wlpl/TIKE-CONT/ValidarQR5. Gestión de Certificados
5.1 Formatos Soportados
- P12/PFX: Formato estándar (recomendado)
- PEM: Conversión automática interna
5.2 Crear un Certificado
- Acceder a Verifactu → Certificados
- Clic en Crear Certificado
- Completar datos:
- Nombre: Identificador interno
- Descripción: Opcional
- Archivo P12: Subir certificado
- Contraseña: Contraseña del certificado
- Entorno: Sandbox o Producción
5.3 Datos del Sistema
Configurar información del software:
Nombre del Sistema: ERP Building MM
Versión: 1.0
ID del Sistema: 01
Número de Instalación: 1
Proveedor NIF: [NIF de tu empresa]
Solo Verifactu: Sí
Múltiples Obligaciones: No5.4 Validación del Certificado
El sistema valida automáticamente:
- ✅ Formato del certificado
- ✅ Validez temporal
- ✅ NIF del emisor
- ✅ Contraseña correcta
6. Creación de Facturas
6.1 Estados de una Factura
DRAFT → CONFIRMED → REGISTERED → [CANCELLED]- Draft: Borrador editable
- Confirmed: Confirmada con hash generado
- Registered: Registrada en AEAT
- Cancelled: Anulada
6.2 Crear Borrador de Factura
- Acceder a Verifactu → Facturas
- Clic en Crear Factura
- Completar datos básicos:
Datos Obligatorios
✓ Certificado Verifactu
✓ Número de serie (ej: FA2025/001)
✓ Fecha de emisión
✓ NIF del emisor
✓ Nombre del emisor
✓ Tipo de factura
✓ Descripción de la operaciónTipos de Factura
| Código | Descripción |
|---|---|
| F1 | Factura completa (con destinatario) |
| F2 | Factura simplificada |
| F3 | Factura sin destinatario identificado |
| R1 | Rectificativa (error fundado en derecho) |
| R2 | Rectificativa (Art. 80.1, 80.2, 80.6 LIVA) |
| R3 | Rectificativa (Art. 80.3 LIVA) |
| R4 | Rectificativa (resto) |
| R5 | Rectificativa en facturas simplificadas |
6.3 Encadenamiento Automático
El sistema automáticamente:
- ✅ Detecta si es la primera factura
- ✅ Obtiene datos de la factura anterior
- ✅ Asigna el hash de encadenamiento
7. Gestión de Líneas de Factura
7.1 Añadir Líneas
Desde el borrador de factura:
- Ir a pestaña Líneas de factura
- Clic en Añadir línea
- Completar datos:
✓ Descripción
✓ Cantidad
✓ Unidad (ud, h, kg, etc)
✓ Precio unitario
✓ Descuento % (opcional)
✓ Tipo de impuesto (IVA, IGIC, IPSI)
✓ Porcentaje de IVA (21%, 10%, 4%)
✓ Calificación de operación7.2 Calificaciones de Operación
| Código | Descripción | Cuándo usar |
|---|---|---|
| S1 | Sujeta y NO exenta sin inversión | Operaciones normales (95% casos) |
| S2 | Sujeta y NO exenta con inversión | Oro, chatarra, construcción |
| S3 | Sujeta y exenta | Sanidad, educación, seguros |
| N1 | NO sujeta (Art. 7, 14) | Exportaciones fuera UE |
| N2 | NO sujeta (Art. 20-24) | Entregas intracomunitarias |
7.3 Cálculos Automáticos
El sistema calcula automáticamente:
- ✅ Subtotal = (Precio × Cantidad) - Descuento
- ✅ IVA = Subtotal × (Tipo IVA / 100)
- ✅ Total línea = Subtotal + IVA
- ✅ Totales de factura (se actualizan en tiempo real)
7.4 Desglose de Impuestos
Generado automáticamente agrupando líneas por:
- Tipo de impuesto (IVA, IGIC, IPSI)
- Porcentaje aplicado
- Calificación de operación
8. Confirmación de Facturas
8.1 Requisitos para Confirmar
Antes de confirmar, verificar:
- ✅ Al menos 1 línea de factura
- ✅ Totales calculados correctamente
- ✅ Destinatario (si es factura F1)
- ✅ Desglose de impuestos generado
8.2 Proceso de Confirmación
- Desde la factura en borrador
- Clic en Confirmar factura
- Revisar datos
- Confirmar acción
8.3 Acciones Automáticas al Confirmar
El sistema realiza:
1. Validación de datos obligatorios
2. Detección de encadenamiento:
- Si es primera factura → first_record = true
- Si no → Obtiene hash de factura anterior
3. Generación del hash SHA-256
4. Cambio de estado a CONFIRMED
5. Creación del registro de cadena
6. Log de auditoría8.4 Hash de Encadenamiento
El hash se genera con:
- NIF del emisor
- Número de serie
- Fecha y hora de emisión
- Importes
- Hash de la factura anterior (si no es la primera)
⚠️ Importante: Una vez confirmada, la factura NO puede editarse.
9. Registro en AEAT
9.1 Envío a AEAT
Desde una factura confirmada:
- Clic en Registrar en AEAT
- El sistema envía automáticamente vía SOAP
- Esperar respuesta
9.2 Respuestas Posibles
✅ Registro Exitoso
json
{
"status": "ok",
"csv": "ABC123XYZ789",
"timestamp": "2025-11-03T10:30:00"
}Se genera:
- CSV (Código Seguro de Verificación)
- Código QR
- Estado cambia a REGISTERED
❌ Error en Registro
json
{
"status": "error",
"errors": [
{
"code": "1207",
"description": "Error interno en el servidor",
"type": "soap_fault",
"level": "fatal"
}
]
}9.3 Reintentos Automáticos
El sistema gestiona:
- Contador de errores
- Contador de reintentos
- Último error registrado
- Botón manual de reintento
9.4 Errores Comunes
| Código | Descripción | Solución |
|---|---|---|
| 1207 | Error interno servidor | Reintentar más tarde |
| 2001 | Certificado inválido | Verificar certificado |
| 3001 | Datos incorrectos | Revisar formato de datos |
| 4001 | Hash inválido | Verificar encadenamiento |
10. Encadenamiento de Facturas
10.1 Concepto
Cada factura contiene el hash de la anterior, creando una cadena inmutable:
FA2025/001 → Hash A
FA2025/002 → Hash B (incluye Hash A)
FA2025/003 → Hash C (incluye Hash B)10.2 Primera Factura
La primera factura de un certificado:
first_record = true
previous_hash = null
previous_series_number = null10.3 Facturas Subsiguientes
Las siguientes facturas automáticamente toman:
first_record = false
previous_hash = [hash de factura anterior]
previous_series_number = [serie de factura anterior]
previous_issuer_nif = [NIF de factura anterior]
previous_issue_date = [fecha de factura anterior]10.4 Validación de Cadena
El sistema valida:
- ✅ Existencia de factura anterior
- ✅ Hash coincide con el registrado
- ✅ Mismo certificado
- ✅ Orden cronológico
10.5 Tabla de Cadenas
verifactu_invoice_chains registra:
- ID de factura actual
- ID de factura anterior
- Posición en la cadena
- Validación de integridad
11. Cancelación de Facturas
11.1 Requisitos para Cancelar
- ✅ Factura registrada en AEAT (status = registered)
- ✅ No tener cancelación pendiente
- ✅ No estar ya cancelada
11.2 Proceso de Cancelación
- Desde factura registrada
- Clic en Cancelar Factura
- Completar formulario:
✓ Motivo de cancelación (texto libre)
✓ Tipo de cancelación:
- Error en los datos
- Factura duplicada
- Solicitud del cliente
- Otro motivo- Confirmar cancelación
11.3 Registro de Cancelación
Se crea registro independiente con:
- Referencia a factura original
- Motivo y tipo de cancelación
- Encadenamiento propio
- Hash de cancelación
- Envío a AEAT
11.4 Efectos de la Cancelación
- Estado factura → CANCELLED
- La factura permanece en la cadena
- Se registra en AEAT
- Se genera CSV de cancelación
12. Consultas a AEAT
12.1 Consultar Estado de Factura
Para facturas con CSV:
- Clic en Consultar Estado
- El sistema consulta AEAT en tiempo real
- Muestra resultado:
- Estado actual
- Fecha de registro
- CSV
12.2 Consulta Detallada
Proporciona:
- Estado completo del registro
- Indicador de paginación
- Registros encontrados
- Errores si existen
12.3. Actualizar Estado
Refresca el estado desde AEAT:
- Verifica si sigue registrada
- Actualiza información local
- Detecta cambios
12.4 Consulta Masiva
Para múltiples facturas:
- Seleccionar facturas
- Acción masiva → Actualizar todos los estados
- El sistema consulta cada una
- Muestra resumen:
- Actualizadas
- Errores
13. Generación de Documentos
13.1 Formatos Disponibles
- XML Verifactu (sin firmar)
- XML firmado (con certificado digital)
- PDF (representación visual)
- Ticket (formato 80mm para TPV)
13.2 Descargar XML
php
// Sin firma
$xml = $invoice->generateFacturaeXml();
// Con firma
$signedXml = $invoice->generateSignedFacturaeXml();Acciones en UI:
- Descargar Facturae XML
- Descargar Facturae Firmado
13.3 Generar PDF
Representación visual profesional con:
- Datos de emisor
- Datos de receptor
- Líneas de factura
- Desglose de impuestos
- Totales
- Código QR (si está registrada)
- CSV de verificación
13.4 Generar Ticket
Formato optimizado para impresoras térmicas 80mm:
- Información simplificada
- Líneas de productos
- Totales
- QR Code grande
- Diseño monocromo
13.5 Descarga Masiva
Para múltiples facturas:
- Seleccionar facturas
- Descargar Facturas
- Elegir formato:
- XML sin firmar
- XML firmado
- ZIP (todos los formatos)
Se genera archivo ZIP con estructura:
/xml/
facturae_FA2025-001.xml
facturae_FA2025-002.xml
/xml_signed/
facturae_FA2025-001_signed.xml
/pdf/
factura_FA2025-001.pdf14. Códigos QR de Verificación
14.1 Generación de QR
Automática al registrar en AEAT:
- Contiene URL de verificación AEAT
- Incluye CSV
- Formato PNG
14.2 Datos en el QR
https://www2.aeat.es/wlpl/TIKE-CONT/ValidarQR?CSV=ABC123XYZ78914.3 Visualizar QR
Desde factura registrada:
- Ver QR: Modal con código
- Descargar QR: Archivo PNG
Opciones de descarga:
- Formato: PNG o SVG
- Tamaño: 200px, 300px, 400px, 500px
14.4 Verificación Pública
Cualquier persona puede:
- Escanear QR con móvil
- Accede a web AEAT
- Verifica autenticidad de factura
15. Manejo de Errores
15.1 Tipos de Errores
Errores de Cabecera
- Datos del emisor incorrectos
- Certificado inválido
- Formato de fecha incorrecto
Errores de Registro
- Datos de factura incorrectos
- Hash inválido
- Encadenamiento roto
Errores SOAP Fault
- Error de servidor AEAT
- Timeout de conexión
- Certificado SSL inválido
15.2 Sistema de Reintentos
Configuración automática:
Max reintentos: 3
Intervalo: Exponencial (1min, 5min, 15min)
Después de 3 fallos: Reintento manual15.3 Logs y Auditoría
Cada operación registra:
- Timestamp
- Usuario
- Acción realizada
- Datos modificados
- Resultado (éxito/error)
- Mensaje de error (si aplica)
Acceso a logs: Verifactu → Auditoría
15.4 Notificaciones
El sistema notifica:
- ✅ Registro exitoso
- ❌ Errores de envío
- ⚠️ Advertencias
- 🔄 Actualizaciones de estado
16. API y Servicios
16.1 Servicios Principales
VerifactuService
php
use Modules\Verifactu\Services\VerifactuService;
// Configurar
VerifactuService::config([
'certPath' => '/path/to/cert.p12',
'certPassword' => 'password',
'soapEndpoint' => 'https://...',
]);
// Registrar factura
$response = VerifactuService::registerInvoice($invoiceSubmission);
// Consultar
$response = VerifactuService::queryInvoices($query);
// Cancelar
$response = VerifactuService::cancelInvoice($cancellation);HashGeneratorService
php
use Modules\Verifactu\Services\HashGeneratorService;
$hash = HashGeneratorService::generate($invoiceSubmission);
// Retorna: SHA-256 hash en hexadecimalQrGeneratorService
php
use Modules\Verifactu\Services\QrGeneratorService;
$qr = QrGeneratorService::generateQr(
$invoiceRecord,
$baseUrl,
$destination, // STRING | FILE
$size, // 200-500px
$renderer // GD | SVG
);XmlSignerService
php
use Modules\Verifactu\Services\XmlSignerService;
$signedXml = XmlSignerService::signXml(
$xmlString,
$certPath,
$certPassword
);16.2 Facades
php
use Modules\Verifactu\Facades\Verifactu;
// Configurar para empresa
Verifactu::forCompany($companyId);
// Registrar
$response = Verifactu::registerInvoice($submission);
// Generar QR
$qr = Verifactu::generateInvoiceQr($record);16.3 Modelos Eloquent
VerifactuInvoice
php
$invoice = VerifactuInvoice::find($id);
// Estados
$invoice->isDraft();
$invoice->isConfirmed();
$invoice->isRegistered();
$invoice->isCancelled();
// Acciones
$invoice->confirm();
$invoice->recalculateTotals();
$invoice->updateBreakdownFromLines();
// Relaciones
$invoice->lines;
$invoice->breakdowns;
$invoice->recipients;
$invoice->certificate;
$invoice->cancellation;VerifactuCertificate
php
$cert = VerifactuCertificate::find($id);
$cert->getCertificatePath();
$cert->getDecryptedPassword();
$cert->isValid();
$cert->expiresIn(); // días restantes16.4 Eventos
php
// Evento cuando se actualizan totales
event(new InvoiceTotalsUpdated($invoice));
// Listener
public function handle(InvoiceTotalsUpdated $event)
{
$invoice = $event->invoice;
// ... lógica
}17. Troubleshooting
17.1 Problemas Comunes
Los totales no se actualizan
Problema: Añado líneas pero los totales siguen en 0€
Solución:
php
// En Tinker
$invoice = VerifactuInvoice::find($id);
$invoice->recalculateTotals();Verificar que VerifactuInvoiceLine tiene el evento saved:
php
static::saved(function ($line) {
$line->invoice?->recalculateTotals();
});El encadenamiento no funciona
Problema: La factura no toma el hash anterior
Solución:
- Verificar que la factura anterior está confirmada (tiene hash)
- Verificar que usan el mismo certificado
- Confirmar orden cronológico
php
// Verificar en Tinker
$prev = VerifactuInvoice::where('status', 'confirmed')
->where('verifactu_certificate_id', $certId)
->latest('issue_date')
->first();
$prev->hash; // Debe tener valorError 1207 de AEAT
Problema: "Error interno en el servidor"
Solución:
- Es un error del servidor sandbox de AEAT
- Reintentar más tarde
- En producción es muy raro
Certificado no válido
Problema: "Certificate verification failed"
Solución:
- Verificar formato P12/PFX
- Verificar contraseña correcta
- Verificar que no ha expirado
- Verificar que es de representación AEAT
QR no se genera
Problema: El QR aparece vacío
Solución:
- Verificar que la factura está registrada
- Verificar que tiene CSV
- Verificar extensión GD instalada:
bash
php -m | grep gd17.2 Comandos Útiles
bash
# Limpiar caché
php artisan cache:clear
php artisan config:clear
php artisan route:clear
php artisan view:clear
# Regenerar autoload
composer dump-autoload -o
# Verificar migraciones
php artisan migrate:status
# Tinker para debug
php artisan tinker17.3 Logs
Ubicación de logs:
storage/logs/laravel.logBuscar errores de Verifactu:
bash
tail -f storage/logs/laravel.log | grep Verifactu17.4 Debugging en Tinker
php
// Ver última factura
$invoice = VerifactuInvoice::latest()->first();
// Ver estructura completa
$invoice->load(['lines', 'breakdowns', 'recipients', 'certificate']);
dd($invoice);
// Recalcular totales
$invoice->recalculateTotals();
// Ver errores
$invoice->aeat_errors;
// Confirmar manualmente
$invoice->confirm();
// Ver cadena
$invoice->chainRecord;📞 Soporte
Para soporte técnico:
- Email: soporte@qbitdynamics.com
- Documentación AEAT: https://sede.agenciatributaria.gob.es
📚 Verifactu Module Documentation
English Version
📋 Table of Contents
- Introduction
- System Requirements
- Installation
- Initial Configuration
- Certificate Management
- Invoice Creation
- Invoice Lines Management
- Invoice Confirmation
- AEAT Registration
- Invoice Chaining
- Invoice Cancellation
- AEAT Queries
- Document Generation
- Verification QR Codes
- Error Handling
- API and Services
- Troubleshooting
1. Introduction
What is Verifactu?
Verifactu is the Spanish Tax Agency (AEAT) system for electronic invoice verification and traceability. This module implements complete Verifactu integration according to current regulations.
Main Features
- ✅ Complete invoice management compliant with Verifactu regulations
- ✅ Cryptographic chaining using SHA-256 hashes
- ✅ Direct AEAT integration via SOAP
- ✅ Digital certificates for electronic signature
- ✅ Verification QR codes
- ✅ Multi-tenant with support for multiple companies
- ✅ Complete audit trail of all operations
- ✅ Error handling and automatic retries
Applicable Regulations
- Invoicing obligations regulations
- Order HAC/1177/2024 on Verifactu
- AEAT technical specifications
2. System Requirements
Technical Requirements
PHP >= 8.4
Laravel >= 12.x
Filament >= 4.x
OpenSSL Extension
SOAP Extension
GD/Imagick Extension (for QR)Legal Requirements
- Valid digital certificate issued by recognized certification authority
- Spanish company Tax ID (NIF)
- Registration in AEAT Verifactu system
Database
- SQLite, MySQL, PostgreSQL or SQL Server
- Transaction support
- UTF-8 charset
3. Installation
Step 1: Module Installation
bash
# Clone or copy module to /Modules
cd Modules
git clone [repository] VerifactuStep 2: Install Dependencies
bash
composer require bacon/bacon-qr-code
composer require chillerlan/php-qrcodeStep 3: Run Migrations
bash
php artisan migrateStep 4: Publish Configuration (Optional)
bash
php artisan vendor:publish --tag=verifactu-config4. Initial Configuration
Environment Variables
Add to .env:
env
VERIFACTU_ENVIRONMENT=sandbox # sandbox | production
VERIFACTU_SOAP_ENDPOINT=https://prewww2.aeat.es/wlpl/TIKE-CONT/ws/SuministroInformacion
VERIFACTU_QR_URL=https://prewww2.aeat.es/wlpl/TIKE-CONT/ValidarQRAvailable Environments
Sandbox (Testing)
SOAP: https://prewww2.aeat.es/wlpl/TIKE-CONT/ws/SuministroInformacion
QR: https://preww2.aeat.es/wlpl/TIKE-CONT/ValidarQRProduction
SOAP: https://www2.agenciatributaria.gob.es/wlpl/TIKE-CONT/ws/SuministroInformacion
QR: https://www2.agenciatributaria.gob.es/wlpl/TIKE-CONT/ValidarQR5. Certificate Management
5.1 Supported Formats
- P12/PFX: Standard format (recommended)
- PEM: Automatic internal conversion
5.2 Create a Certificate
- Access Verifactu → Certificates
- Click Create Certificate
- Complete data:
- Name: Internal identifier
- Description: Optional
- P12 File: Upload certificate
- Password: Certificate password
- Environment: Sandbox or Production
5.3 System Data
Configure software information:
System Name: ERP Building MM
Version: 1.0
System ID: 01
Installation Number: 1
Provider NIF: [Your company NIF]
Only Verifactu: Yes
Multiple Obligations: No5.4 Certificate Validation
System automatically validates:
- ✅ Certificate format
- ✅ Temporal validity
- ✅ Issuer NIF
- ✅ Correct password
6. Invoice Creation
6.1 Invoice States
DRAFT → CONFIRMED → REGISTERED → [CANCELLED]- Draft: Editable draft
- Confirmed: Confirmed with generated hash
- Registered: Registered in AEAT
- Cancelled: Cancelled
6.2 Create Invoice Draft
- Access Verifactu → Invoices
- Click Create Invoice
- Complete basic data:
Required Fields
✓ Verifactu Certificate
✓ Series number (e.g.: FA2025/001)
✓ Issue date
✓ Issuer NIF
✓ Issuer name
✓ Invoice type
✓ Operation descriptionInvoice Types
| Code | Description |
|---|---|
| F1 | Complete invoice (with recipient) |
| F2 | Simplified invoice |
| F3 | Invoice without identified recipient |
| R1 | Rectifying (legally founded error) |
| R2 | Rectifying (Art. 80.1, 80.2, 80.6 LIVA) |
| R3 | Rectifying (Art. 80.3 LIVA) |
| R4 | Rectifying (other) |
| R5 | Rectifying simplified invoices |
6.3 Automatic Chaining
The system automatically:
- ✅ Detects if it's the first invoice
- ✅ Gets previous invoice data
- ✅ Assigns chaining hash
7. Invoice Lines Management
7.1 Add Lines
From invoice draft:
- Go to Invoice lines tab
- Click Add line
- Complete data:
✓ Description
✓ Quantity
✓ Unit (ud, h, kg, etc)
✓ Unit price
✓ Discount % (optional)
✓ Tax type (VAT, IGIC, IPSI)
✓ VAT percentage (21%, 10%, 4%)
✓ Operation qualification7.2 Operation Qualifications
| Code | Description | When to use |
|---|---|---|
| S1 | Subject and NOT exempt without inversion | Normal operations (95% cases) |
| S2 | Subject and NOT exempt with inversion | Gold, scrap, construction |
| S3 | Subject and exempt | Health, education, insurance |
| N1 | NOT subject (Art. 7, 14) | Exports outside EU |
| N2 | NOT subject (Art. 20-24) | Intra-community deliveries |
7.3 Automatic Calculations
System automatically calculates:
- ✅ Subtotal = (Price × Quantity) - Discount
- ✅ VAT = Subtotal × (VAT Rate / 100)
- ✅ Line total = Subtotal + VAT
- ✅ Invoice totals (update in real-time)
7.4 Tax Breakdown
Automatically generated by grouping lines by:
- Tax type (VAT, IGIC, IPSI)
- Applied percentage
- Operation qualification
8. Invoice Confirmation
8.1 Requirements to Confirm
Before confirming, verify:
- ✅ At least 1 invoice line
- ✅ Totals calculated correctly
- ✅ Recipient (if F1 invoice)
- ✅ Tax breakdown generated
8.2 Confirmation Process
- From invoice in draft
- Click Confirm invoice
- Review data
- Confirm action
8.3 Automatic Actions on Confirm
The system performs:
1. Validation of required data
2. Chaining detection:
- If first invoice → first_record = true
- If not → Gets previous invoice hash
3. SHA-256 hash generation
4. Status change to CONFIRMED
5. Chain record creation
6. Audit log8.4 Chaining Hash
Hash is generated with:
- Issuer NIF
- Series number
- Issue date and time
- Amounts
- Previous invoice hash (if not first)
⚠️ Important: Once confirmed, invoice CANNOT be edited.
9. AEAT Registration
9.1 Send to AEAT
From confirmed invoice:
- Click Register in AEAT
- System automatically sends via SOAP
- Wait for response
9.2 Possible Responses
✅ Successful Registration
json
{
"status": "ok",
"csv": "ABC123XYZ789",
"timestamp": "2025-11-03T10:30:00"
}Generated:
- CSV (Secure Verification Code)
- QR Code
- Status changes to REGISTERED
❌ Registration Error
json
{
"status": "error",
"errors": [
{
"code": "1207",
"description": "Internal server error",
"type": "soap_fault",
"level": "fatal"
}
]
}9.3 Automatic Retries
System manages:
- Error counter
- Retry counter
- Last recorded error
- Manual retry button
9.4 Common Errors
| Code | Description | Solution |
|---|---|---|
| 1207 | Internal server error | Retry later |
| 2001 | Invalid certificate | Verify certificate |
| 3001 | Incorrect data | Check data format |
| 4001 | Invalid hash | Verify chaining |
10. Invoice Chaining
10.1 Concept
Each invoice contains the hash of the previous one, creating an immutable chain:
FA2025/001 → Hash A
FA2025/002 → Hash B (includes Hash A)
FA2025/003 → Hash C (includes Hash B)10.2 First Invoice
First invoice of a certificate:
first_record = true
previous_hash = null
previous_series_number = null10.3 Subsequent Invoices
Following invoices automatically take:
first_record = false
previous_hash = [previous invoice hash]
previous_series_number = [previous invoice series]
previous_issuer_nif = [previous invoice NIF]
previous_issue_date = [previous invoice date]10.4 Chain Validation
System validates:
- ✅ Previous invoice existence
- ✅ Hash matches recorded
- ✅ Same certificate
- ✅ Chronological order
10.5 Chains Table
verifactu_invoice_chains records:
- Current invoice ID
- Previous invoice ID
- Position in chain
- Integrity validation
11. Invoice Cancellation
11.1 Requirements to Cancel
- ✅ Invoice registered in AEAT (status = registered)
- ✅ No pending cancellation
- ✅ Not already cancelled
11.2 Cancellation Process
- From registered invoice
- Click Cancel Invoice
- Complete form:
✓ Cancellation reason (free text)
✓ Cancellation type:
- Data error
- Duplicate invoice
- Customer request
- Other reason- Confirm cancellation
11.3 Cancellation Record
Independent record created with:
- Original invoice reference
- Reason and cancellation type
- Own chaining
- Cancellation hash
- AEAT submission
11.4 Cancellation Effects
- Invoice status → CANCELLED
- Invoice remains in chain
- Registered in AEAT
- Cancellation CSV generated
12. AEAT Queries
12.1 Query Invoice Status
For invoices with CSV:
- Click Query Status
- System queries AEAT in real-time
- Shows result:
- Current status
- Registration date
- CSV
12.2 Detailed Query
Provides:
- Complete registration status
- Pagination indicator
- Found records
- Errors if any
12.3 Update Status
Refreshes status from AEAT:
- Verifies if still registered
- Updates local information
- Detects changes
12.4 Bulk Query
For multiple invoices:
- Select invoices
- Bulk action → Update all statuses
- System queries each one
- Shows summary:
- Updated
- Errors
13. Document Generation
13.1 Available Formats
- Verifactu XML (unsigned)
- Signed XML (with digital certificate)
- PDF (visual representation)
- Ticket (80mm format for POS)
13.2 Download XML
php
// Unsigned
$xml = $invoice->generateFacturaeXml();
// Signed
$signedXml = $invoice->generateSignedFacturaeXml();UI Actions:
- Download Facturae XML
- Download Signed Facturae
13.3 Generate PDF
Professional visual representation with:
- Issuer data
- Recipient data
- Invoice lines
- Tax breakdown
- Totals
- QR Code (if registered)
- Verification CSV
13.4 Generate Ticket
Format optimized for 80mm thermal printers:
- Simplified information
- Product lines
- Totals
- Large QR Code
- Monochrome design
13.5 Bulk Download
For multiple invoices:
- Select invoices
- Download Invoices
- Choose format:
- Unsigned XML
- Signed XML
- ZIP (all formats)
ZIP file generated with structure:
/xml/
facturae_FA2025-001.xml
facturae_FA2025-002.xml
/xml_signed/
facturae_FA2025-001_signed.xml
/pdf/
factura_FA2025-001.pdf14. Verification QR Codes
14.1 QR Generation
Automatic when registering in AEAT:
- Contains AEAT verification URL
- Includes CSV
- PNG format
14.2 Data in QR
https://www2.aeat.es/wlpl/TIKE-CONT/ValidarQR?CSV=ABC123XYZ78914.3 View QR
From registered invoice:
- View QR: Modal with code
- Download QR: PNG file
Download options:
- Format: PNG or SVG
- Size: 200px, 300px, 400px, 500px
14.4 Public Verification
Anyone can:
- Scan QR with mobile
- Access AEAT website
- Verify invoice authenticity
15. Error Handling
15.1 Error Types
Header Errors
- Incorrect issuer data
- Invalid certificate
- Incorrect date format
Record Errors
- Incorrect invoice data
- Invalid hash
- Broken chain
SOAP Fault Errors
- AEAT server error
- Connection timeout
- Invalid SSL certificate
15.2 Retry System
Automatic configuration:
Max retries: 3
Interval: Exponential (1min, 5min, 15min)
After 3 failures: Manual retry15.3 Logs and Audit
Each operation records:
- Timestamp
- User
- Performed action
- Modified data
- Result (success/error)
- Error message (if applicable)
Access logs: Verifactu → Audit
15.4 Notifications
System notifies:
- ✅ Successful registration
- ❌ Submission errors
- ⚠️ Warnings
- 🔄 Status updates
16. API and Services
16.1 Main Services
VerifactuService
php
use Modules\Verifactu\Services\VerifactuService;
// Configure
VerifactuService::config([
'certPath' => '/path/to/cert.p12',
'certPassword' => 'password',
'soapEndpoint' => 'https://...',
]);
// Register invoice
$response = VerifactuService::registerInvoice($invoiceSubmission);
// Query
$response = VerifactuService::queryInvoices($query);
// Cancel
$response = VerifactuService::cancelInvoice($cancellation);HashGeneratorService
php
use Modules\Verifactu\Services\HashGeneratorService;
$hash = HashGeneratorService::generate($invoiceSubmission);
// Returns: SHA-256 hash in hexadecimalQrGeneratorService
php
use Modules\Verifactu\Services\QrGeneratorService;
$qr = QrGeneratorService::generateQr(
$invoiceRecord,
$baseUrl,
$destination, // STRING | FILE
$size, // 200-500px
$renderer // GD | SVG
);XmlSignerService
php
use Modules\Verifactu\Services\XmlSignerService;
$signedXml = XmlSignerService::signXml(
$xmlString,
$certPath,
$certPassword
);16.2 Facades
php
use Modules\Verifactu\Facades\Verifactu;
// Configure for company
Verifactu::forCompany($companyId);
// Register
$response = Verifactu::registerInvoice($submission);
// Generate QR
$qr = Verifactu::generateInvoiceQr($record);16.3 Eloquent Models
VerifactuInvoice
php
$invoice = VerifactuInvoice::find($id);
// States
$invoice->isDraft();
$invoice->isConfirmed();
$invoice->isRegistered();
$invoice->isCancelled();
// Actions
$invoice->confirm();
$invoice->recalculateTotals();
$invoice->updateBreakdownFromLines();
// Relationships
$invoice->lines;
$invoice->breakdowns;
$invoice->recipients;
$invoice->certificate;
$invoice->cancellation;VerifactuCertificate
php
$cert = VerifactuCertificate::find($id);
$cert->getCertificatePath();
$cert->getDecryptedPassword();
$cert->isValid();
$cert->expiresIn(); // remaining days16.4 Events
php
// Event when totals are updated
event(new InvoiceTotalsUpdated($invoice));
// Listener
public function handle(InvoiceTotalsUpdated $event)
{
$invoice = $event->invoice;
// ... logic
}17. Troubleshooting
17.1 Common Issues
Totals not updating
Problem: I add lines but totals remain at 0€
Solution:
php
// In Tinker
$invoice = VerifactuInvoice::find($id);
$invoice->recalculateTotals();Verify VerifactuInvoiceLine has saved event:
php
static::saved(function ($line) {
$line->invoice?->recalculateTotals();
});Chaining not working
Problem: Invoice doesn't take previous hash
Solution:
- Verify previous invoice is confirmed (has hash)
- Verify they use the same certificate
- Confirm chronological order
php
// Verify in Tinker
$prev = VerifactuInvoice::where('status', 'confirmed')
->where('verifactu_certificate_id', $certId)
->latest('issue_date')
->first();
$prev->hash; // Must have valueAEAT Error 1207
Problem: "Internal server error"
Solution:
- It's a sandbox AEAT server error
- Retry later
- Very rare in production
Invalid certificate
Problem: "Certificate verification failed"
Solution:
- Verify P12/PFX format
- Verify correct password
- Verify not expired
- Verify it's AEAT representation
QR not generated
Problem: QR appears empty
Solution:
- Verify invoice is registered
- Verify it has CSV
- Verify GD extension installed:
bash
php -m | grep gd17.2 Useful Commands
bash
# Clear cache
php artisan cache:clear
php artisan config:clear
php artisan route:clear
php artisan view:clear
# Regenerate autoload
composer dump-autoload -o
# Check migrations
php artisan migrate:status
# Tinker for debug
php artisan tinker17.3 Logs
Log location:
storage/logs/laravel.logSearch Verifactu errors:
bash
tail -f storage/logs/laravel.log | grep Verifactu17.4 Debugging in Tinker
php
// View latest invoice
$invoice = VerifactuInvoice::latest()->first();
// View complete structure
$invoice->load(['lines', 'breakdowns', 'recipients', 'certificate']);
dd($invoice);
// Recalculate totals
$invoice->recalculateTotals();
// View errors
$invoice->aeat_errors;
// Confirm manually
$invoice->confirm();
// View chain
$invoice->chainRecord;📞 Support
For technical support:
- Email: soporte@qbitdynamics.com
- AEAT Documentation: https://sede.agenciatributaria.gob.es
© 2025 Qbit Dynamics SL - All rights reserved