Skip to content

VeriFactu Module for Laravel/Filament

A comprehensive, Veri*Factu module for the E2TMK framework that ensures full compliance with Spanish Tax Agency (AEAT) requirements for invoice verification and integrity chain management.

Index

  1. Introduction
  2. Requirements
  3. Installation
  4. Configuration
  5. Basic Usage
  6. Components
  7. API
  8. Customization
  9. Troubleshooting
  10. FAQ

Introduction

The VeriFactu module provides comprehensive integration with the Spanish Tax Agency (AEAT) VeriFactu system for Laravel applications using Filament. It enables businesses to manage electronic invoicing with full compliance to Spanish tax regulations, ensuring legal compliance and seamless invoice verification through the integrity chain management system.

VeriFactu (Verification of Invoices) is a mandatory system implemented by the Spanish Tax Agency that requires all businesses to register their invoices in real-time with AEAT servers. This system aims to prevent tax fraud and ensure transparency in commercial transactions by creating an unbreakable chain of invoice verification.

The module is specifically designed for the MM (Module Manager) ecosystem by e2tmk, providing automatic integration with Filament panels and specialized APIs for Point of Sale (POS) and Terminal Point of Sale (TPV) systems.

Key Features

Automatic Filament Integration - Self-registers with Filament panels via module.json configuration
Complete VeriFactu QR Code Generation - Full API for generating AEAT-compliant QR codes
POS/TPV Specialized APIs - Dedicated endpoints for retail point-of-sale systems
Thermal Printer Support - Generate tickets for 58mm and 80mm thermal printers
Batch QR Processing - Generate up to 50 QR codes in a single API call
Multiple Output Formats - Support for PNG, JPG, and SVG QR code formats
Real-time AEAT Communication - Direct integration with Spanish Tax Agency servers
Digital Certificate Management - Secure handling of PKCS#12 certificates
Invoice Lifecycle Management - Complete F1, F2, R1-R5 invoice type support
Comprehensive Validation - Strict AEAT format validation and error handling
Multi-tenant Architecture - Built-in support for multi-company environments
Audit Trail & Logging - Complete traceability of all operations

Requirements

Server Requirements

  • PHP 8.2 or higher
  • Laravel 11.x or higher
  • Filament 3.3
  • Module Manager (MM) by e2tmk
  • PHP Extensions:
    • ext-openssl (required for certificate handling)
    • ext-curl (required for AEAT communication)
    • ext-libxml (required for XML processing)
    • ext-dom (required for XML manipulation)
    • ext-simplexml (required for XML parsing)

Certificate Requirements

  • Valid digital certificate issued by Spanish certification authority
  • Certificate in PKCS#12 format (.p12 or .pfx)
  • Certificate must include both public and private keys
  • Certificate must be compatible with AEAT requirements
  • Supported CAs: AC FNMT Usuarios, AC Administración Pública

Database Support

  • MySQL 8.0+ (recommended for production)
  • PostgreSQL 13+ (excellent performance for large datasets)
  • SQLite 3.35+ (suitable for development and testing)

Installation

1. Install via Module Manager

The VeriFactu module is designed to work within the MM ecosystem. Install using the Module Manager command:

bash
php artisan mm:require-module verifactu

Alternatively, you can install directly via Composer:

bash
composer require e2tmk/verifactu-module

2. Run migrations

bash
php artisan migrate

The module will automatically create the following tables:

  • verifactu_certificates - Digital certificate storage
  • verifactu_invoices - Main invoice data
  • verifactu_tax_items - Tax calculation details
  • verifactu_invoice_entries - Invoice line items
  • verifactu_invoice_cancellations - Cancellation records
  • verifactu_settings - Module configuration
  • company_invoice_table - Multi-tenant support

3. Publish configuration

bash
php artisan vendor:publish --tag=verifactu-config
php artisan vendor:publish --tag=verifactu-translations
php artisan vendor:publish --tag=verifactu-assets

4. Automatic Filament Integration

Important: The VeriFactu module automatically registers with Filament panels through the MM ecosystem. No manual plugin registration is required.

The module uses the filament_plugins configuration in module.json:

json
{
    "name": "VeriFactu",
    "filament_plugins": ["Modules\\VeriFactu\\Filament\\Plugin\\VeriFactuPlugin"]
}

The plugin automatically discovers and registers:

  • Resources: Invoice management, Certificate management
  • Pages: VeriFactu Reports, Settings configuration
  • Widgets: Dashboard statistics and monitoring

Configuration

Environment Variables

Optionally you can add the following variables to your .env file:

env
# VeriFactu Certificate Configuration
VERIFACTU_CERTIFICATE_PATH=/path/to/your/certificate.p12
VERIFACTU_CERTIFICATE_PASSWORD=your_certificate_password
VERIFACTU_CERTIFICATE_SERIAL=certificate_serial_number
VERIFACTU_CERTIFICATE_THUMBPRINT=certificate_thumbprint

# AEAT Configuration
VERIFACTU_AEAT_ENDPOINT=https://prewww1.aeat.es/wlpl/TIKE-CONT/ValidarQR
VERIFACTU_TEST_MODE=true

# Facturae Configuration
VERIFACTU_FACTURAE_VERSION=3.2.2
VERIFACTU_FACE_ENDPOINT=https://face.gob.es
VERIFACTU_FACEB2B_ENDPOINT=https://faceb2b.gob.es

# Logging Configuration
VERIFACTU_LOGGING_ENABLED=true
VERIFACTU_LOGGING_LEVEL=info

# Table Prefix (optional)
VERIFACTU_TABLE_PREFIX=verifactu_

Config File

The published configuration file config/verifactu.php contains:

php
<?php

return [
    'name' => 'VeriFactu',
    
    'table_prefix' => env('VERIFACTU_TABLE_PREFIX', 'verifactu_'),
    
    // Certificate configuration
    'certificates' => [
        'path' => env('VERIFACTU_CERTIFICATE_PATH'),
        'password' => env('VERIFACTU_CERTIFICATE_PASSWORD'),
        'serial' => env('VERIFACTU_CERTIFICATE_SERIAL'),
        'thumbprint' => env('VERIFACTU_CERTIFICATE_THUMBPRINT'),
    ],
    
    // AEAT configuration
    'aeat' => [
        'endpoint' => env('VERIFACTU_AEAT_ENDPOINT', 'https://prewww1.aeat.es/wlpl/TIKE-CONT/ValidarQR'),
        'test_mode' => env('VERIFACTU_TEST_MODE', true),
    ],
    
    // Facturae configuration
    'facturae' => [
        'version' => env('VERIFACTU_FACTURAE_VERSION', '3.2.2'),
        'face_endpoint' => env('VERIFACTU_FACE_ENDPOINT'),
        'faceb2b_endpoint' => env('VERIFACTU_FACEB2B_ENDPOINT'),
    ],
    
    // Logging configuration
    'logging' => [
        'enabled' => env('VERIFACTU_LOGGING_ENABLED', true),
        'level' => env('VERIFACTU_LOGGING_LEVEL', 'info'),
    ],
];

Basic Usage

Working with Invoices

The module provides automatic Filament resources for invoice management. Once installed, you'll have access to:

Invoice Resource

The Invoice resource provides complete CRUD functionality with specialized actions:

php
// The module automatically provides these actions in the Invoice resource:

// Send to AEAT Action
SendToAeatAction::make()
    ->visible(fn (Invoice $record) => $record->status === InvoiceStatus::DRAFT)
    ->requiresConfirmation()
    ->modalHeading('Send Invoice to AEAT')
    ->successNotificationTitle('Invoice sent successfully');

// Generate QR Code Action  
QrCodeAction::make()
    ->visible(fn (Invoice $record) => $record->status === InvoiceStatus::SENT)
    ->modalHeading('VeriFactu QR Code')
    ->qrSize(300);

// Download Facturae Action
FacturaeDownloadAction::make()
    ->visible(fn (Invoice $record) => $record->status === InvoiceStatus::SENT)
    ->facturaeVersion('3.2.2');

// Cancel Invoice Action
InvoiceCancelAction::make()
    ->visible(fn (Invoice $record) => $record->status === InvoiceStatus::SENT)
    ->requiresConfirmation();

Invoice Model Integration

To use your existing invoice models with VeriFactu, ensure they have the required fields:

php
// Migration example
Schema::create('invoices', function (Blueprint $table) {
    $table->id();
    $table->foreignId('company_id')->constrained()->onDelete('cascade');
    
    // Invoice identification
    $table->string('series')->default('');
    $table->string('number');
    $table->string('full_number', 50);
    $table->date('issue_date');
    $table->string('invoice_type'); // F1, F2, R1, etc.
    $table->string('status')->default('draft');
    
    // Seller information (required for VeriFactu)
    $table->string('seller_nif');
    $table->string('seller_name');
    $table->text('seller_address')->nullable();
    $table->string('seller_postal_code')->nullable();
    $table->string('seller_city')->nullable();
    $table->string('seller_province')->nullable();
    $table->string('seller_country')->default('ES');
    
    // Buyer information
    $table->string('buyer_nif')->nullable();
    $table->string('buyer_name')->nullable();
    $table->text('buyer_address')->nullable();
    $table->string('buyer_postal_code')->nullable();
    $table->string('buyer_city')->nullable();
    $table->string('buyer_province')->nullable();
    $table->string('buyer_country')->default('ES');
    
    // Financial information
    $table->decimal('subtotal', 10, 2)->default(0);
    $table->decimal('total_tax', 10, 2)->default(0);
    $table->decimal('total', 10, 2)->default(0);
    $table->string('currency', 3)->default('EUR');
    $table->text('description')->nullable();
    
    // VeriFactu specific fields
    $table->string('hash')->nullable();
    $table->string('csv')->nullable();
    $table->text('qr_code')->nullable();
    $table->json('aeat_response')->nullable();
    $table->string('error_code')->nullable();
    $table->text('error_description')->nullable();
    $table->timestamp('sent_at')->nullable();
    
    $table->timestamps();
    $table->softDeletes();
    
    // Indexes for performance
    $table->index('full_number');
    $table->index(['company_id', 'issue_date']);
    $table->index(['status', 'sent_at']);
});

Model Configuration

php
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Modules\VeriFactu\Enums\InvoiceStatus;
use Modules\VeriFactu\Enums\InvoiceType;
use ModuleManager\ModuleManager\Concerns\HasTenant;

class Invoice extends Model
{
    use HasTenant, SoftDeletes;
    
    protected $fillable = [
        'company_id', 'series', 'number', 'full_number', 'issue_date',
        'invoice_type', 'status', 'seller_name', 'seller_nif',
        'seller_address', 'seller_postal_code', 'seller_city', 
        'seller_province', 'seller_country', 'buyer_name', 'buyer_nif',
        'buyer_address', 'buyer_postal_code', 'buyer_city', 
        'buyer_province', 'buyer_country', 'subtotal', 'total_tax', 
        'total', 'currency', 'description', 'sent_at', 'aeat_response',
        'csv', 'qr_code', 'error_code', 'error_description'
    ];
    
    protected function casts(): array
    {
        return [
            'issue_date' => 'date',
            'invoice_type' => InvoiceType::class,
            'status' => InvoiceStatus::class,
            'subtotal' => 'decimal:2',
            'total_tax' => 'decimal:2',
            'total' => 'decimal:2',
            'sent_at' => 'datetime',
            'aeat_response' => 'array',
        ];
    }
    
    // Relationships
    public function company(): BelongsTo
    {
        return $this->belongsTo(Company::class);
    }
    
    public function taxItems(): HasMany
    {
        return $this->hasMany(TaxItem::class);
    }
    
    public function entries(): HasMany
    {
        return $this->hasMany(InvoiceEntry::class);
    }
}

Components

The module provides several Filament components that are automatically available:

SendToAeatAction

Sends invoices to AEAT servers with comprehensive validation:

php
SendToAeatAction::make()
    ->label('Send to AEAT')
    ->icon('heroicon-o-paper-airplane')
    ->color('success')
    ->requiresConfirmation()
    ->modalHeading('Send Invoice to AEAT')
    ->modalDescription('This will submit the invoice to the Spanish Tax Agency.')
    ->successNotificationTitle('Invoice sent successfully')
    ->failureNotificationTitle('Failed to send invoice')
    ->visible(fn (Invoice $record) => $record->status === InvoiceStatus::DRAFT);

QrCodeAction

Generates and displays VeriFactu QR codes:

php
QrCodeAction::make()
    ->label('Generate QR Code')
    ->icon('heroicon-o-qr-code')
    ->color('primary')
    ->modalHeading('VeriFactu QR Code')
    ->modalWidth('md')
    ->qrSize(300)
    ->qrMargin(10)
    ->visible(fn (Invoice $record) => $record->status === InvoiceStatus::SENT);

FacturaeDownloadAction

Downloads Facturae XML documents:

php
FacturaeDownloadAction::make()
    ->label('Download Facturae')
    ->icon('heroicon-o-document-arrow-down')
    ->color('secondary')
    ->facturaeVersion('3.2.2')
    ->includeDigitalSignature(true)
    ->visible(fn (Invoice $record) => $record->status === InvoiceStatus::SENT);

InvoiceCancelAction

Cancels invoices in the AEAT system:

php
InvoiceCancelAction::make()
    ->label('Cancel Invoice')
    ->icon('heroicon-o-x-circle')
    ->color('danger')
    ->requiresConfirmation()
    ->modalHeading('Cancel Invoice')
    ->modalDescription('This will cancel the invoice in AEAT system.')
    ->cancellationReason('Error in invoice data')
    ->visible(fn (Invoice $record) => $record->status === InvoiceStatus::SENT);

InvoiceQueryAction

Queries invoice status from AEAT:

php
InvoiceQueryAction::make()
    ->label('Query Status')
    ->icon('heroicon-o-magnifying-glass')
    ->color('info')
    ->modalHeading('Invoice Status Query')
    ->visible(fn (Invoice $record) => $record->status === InvoiceStatus::SENT);

API

The VeriFactu module provides comprehensive APIs specifically designed for POS/TPV integration and external system connectivity.

QR Code Generation API

POST /api/verifactu/qr/generate

Generates a VeriFactu-compliant QR code with full AEAT validation.

Request Parameters:

ParameterTypeRequiredDescriptionValidation
nifstringYesSpanish NIF (Tax ID)8 digits + 1 letter (e.g., 12345678Z)
seriestringYesInvoice seriesMax 20 chars, alphanumeric + hyphens/underscores
numberstringYesInvoice numberMax 20 chars, alphanumeric + hyphens/underscores
datestringYesInvoice dateYYYY-MM-DD format, not future, after 2024-01-01
amountnumberYesInvoice total amountMin: 0, Max: 999999999.99
sizeintegerNoQR code size in pixelsMin: 100, Max: 1000, Default: 300
marginintegerNoQR code margin in pixelsMin: 0, Max: 50, Default: 10
formatstringNoImage formatpng, jpg, svg, Default: png
company_namestringNoCompany nameMax 255 chars
customer_namestringNoCustomer nameMax 255 chars
descriptionstringNoInvoice descriptionMax 500 chars

Example Request:

json
{
    "nif": "B12345678",
    "serie": "FAC",
    "number": "001",
    "date": "2025-08-07",
    "amount": 121.00,
    "size": 300,
    "margin": 10,
    "format": "png",
    "company_name": "Mi Empresa S.L.",
    "customer_name": "Cliente Ejemplo"
}

Success Response (200):

json
{
    "success": true,
    "data": {
        "qr_url": "https://prewww1.aeat.es/wlpl/TIKE-CONT/ValidarQR?nif=B12345678&serie=FAC&numero=001&fecha=07-08-2025&importe=121.00",
        "qr_image_base64": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...",
        "validation": {
            "is_valid": true,
            "errors": []
        },
        "metadata": {
            "generated_at": "2025-08-07T22:00:00.000000Z",
            "format": "PNG",
            "encoding": "base64",
            "size": 300,
            "margin": 10
        }
    },
    "message": "QR code generated successfully"
}

Error Response (422 - Validation Failed):

json
{
    "success": false,
    "error": {
        "code": "VALIDATION_FAILED",
        "message": "The provided data is invalid.",
        "details": {
            "nif": ["The NIF must follow Spanish format: 8 digits followed by 1 letter."],
            "amount": ["The amount must be a valid number."]
        },
        "help": {
            "nif_format": "NIF must be 8 digits + 1 letter (e.g., 12345678Z)",
            "date_format": "Date must be YYYY-MM-DD format",
            "amount_format": "Amount must be a positive number with up to 2 decimal places"
        }
    }
}

POST /api/verifactu/qr/info

Returns QR code information without generating the image (faster for validation purposes).

Request Parameters: Same as /generate endpoint

Response: Same structure as /generate but without qr_image_base64 field

POST /api/verifactu/qr/validate

Validates a VeriFactu QR URL format according to AEAT specifications.

Request Parameters:

json
{
    "qr_url": "https://prewww1.aeat.es/wlpl/TIKE-CONT/ValidarQR?nif=B12345678&serie=FAC&numero=001&fecha=07-08-2025&importe=121.00"
}

Response:

json
{
    "success": true,
    "data": {
        "is_valid": true,
        "errors": [],
        "url_components": {
            "base_url": "https://prewww1.aeat.es/wlpl/TIKE-CONT/ValidarQR",
            "parameters": {
                "nif": "B12345678",
                "serie": "FAC",
                "numero": "001",
                "fecha": "07-08-2025",
                "importe": "121.00"
            }
        },
        "aeat_info": {
            "environment": "test",
            "verification_portal": "AEAT VeriFactu Portal"
        }
    },
    "message": "QR URL is valid"
}

POST /api/verifactu/qr/batch

Generates multiple QR codes in a single request (optimized for high-volume POS systems).

Request Parameters:

json
{
    "invoices": [
        {
            "nif": "B12345678",
            "serie": "FAC",
            "number": "001",
            "date": "2025-08-07",
            "amount": 121.00
        },
        {
            "nif": "B12345678",
            "serie": "FAC", 
            "number": "002",
            "date": "2025-08-07",
            "amount": 242.00
        }
    ],
    "size": 300,
    "margin": 10,
    "include_images": true
}

Constraints:

  • Maximum 50 invoices per batch
  • include_images: Boolean to include base64 images (default: true)
  • Common size and margin applied to all QR codes

Response:

json
{
    "success": true,
    "data": {
        "results": [
            {
                "index": 0,
                "invoice_id": "FAC001",
                "qr_url": "https://prewww1.aeat.es/wlpl/TIKE-CONT/ValidarQR?...",
                "qr_image_base64": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAA...",
                "validation": {
                    "is_valid": true,
                    "errors": []
                }
            }
        ],
        "errors": [],
        "summary": {
            "total_requested": 2,
            "successful": 2,
            "failed": 0,
            "success_rate": 100.00
        }
    },
    "message": "Batch QR generation completed"
}

GET /api/verifactu/status

Returns API status and configuration information.

Response:

json
{
    "success": true,
    "data": {
        "api_version": "1.0.0",
        "service_status": "operational",
        "aeat_environment": "test",
        "aeat_base_url": "https://prewww1.aeat.es/wlpl/TIKE-CONT/ValidarQR",
        "supported_formats": ["PNG", "JPG", "SVG"],
        "max_qr_size": 1000,
        "max_batch_size": 50,
        "rate_limits": {
            "requests_per_minute": 60,
            "batch_requests_per_hour": 100
        },
        "last_check": "2025-08-07T22:00:00.000000Z"
    },
    "message": "VeriFactu QR API is operational"
}

POS Integration Services

The module provides specialized services for Point of Sale integration:

POS Ticket Generation

php
use Modules\VeriFactu\Services\PosIntegrationService;

$posService = app(PosIntegrationService::class);

// Generate standard POS ticket
$ticketData = $posService->generatePosTicket($invoice, [
    'terminal_id' => 'POS-001',
    'cashier_id' => 'CASHIER-123',
    'location' => 'Store Main',
    'payment_method' => 'cash',
    'test_environment' => true
]);

// Generate thermal printer ticket (58mm or 80mm)
$thermalTicket = $posService->generateThermalTicket($invoice, [
    'width' => 58, // or 80
    'terminal_id' => 'POS-001',
    'support_info' => 'Tel: 900-123-456'
]);

// Generate digital receipt for email
$digitalReceipt = $posService->generateDigitalReceipt($invoice, [
    'generate_pdf' => true,
    'email_template' => 'custom_template'
]);

Thermal Printer Integration

The module provides specialized formatting for thermal printers:

php
// 58mm thermal printer format
$thermal58 = $posService->generateThermalTicket($invoice, [
    'width' => 58,
    'layout' => 'compact',
    'include_logo' => false,
    'qr_size' => 200
]);

// 80mm thermal printer format  
$thermal80 = $posService->generateThermalTicket($invoice, [
    'width' => 80,
    'layout' => 'standard',
    'include_logo' => true,
    'qr_size' => 300
]);

The thermal ticket data includes:

  • Header: Company information, address, date/time
  • Lines: Product/service details with quantities and prices
  • Totals: Subtotal, taxes, and total amount
  • QR Section: VeriFactu QR code with verification instructions
  • Footer: Thank you message and support information

Customization

Custom QR Code Styling

php
use Modules\VeriFactu\Services\QrCodeService;

$qrService = app(QrCodeService::class);

// Custom QR generation with advanced options
$customQr = $qrService->generateQrImageBase64($qrUrl, [
    'size' => 400,
    'margin' => 20,
    'format' => 'svg',
    'error_correction' => 'H', // High error correction
    'foreground_color' => '#000000',
    'background_color' => '#FFFFFF',
    'border_size' => 2
]);

Custom POS Templates

Create custom templates for different POS layouts:

php
// Custom thermal template
$customThermal = $posService->generateThermalTicket($invoice, [
    'template' => 'custom_thermal_58mm',
    'custom_fields' => [
        'promotion_code' => 'SUMMER2025',
        'loyalty_points' => 150
    ],
    'branding' => [
        'logo_path' => '/path/to/logo.png',
        'colors' => ['primary' => '#FF6B35']
    ]
]);

Custom Validation Rules

Extend the validation system for specific business requirements:

php
use Modules\VeriFactu\Http\Requests\GenerateQrRequest;

class CustomQrRequest extends GenerateQrRequest
{
    public function rules(): array
    {
        $rules = parent::rules();
        
        // Add custom validation rules
        $rules['custom_field'] = 'required|string|max:100';
        $rules['business_type'] = 'required|in:retail,wholesale,service';
        
        return $rules;
    }
    
    public function messages(): array
    {
        return array_merge(parent::messages(), [
            'custom_field.required' => 'Custom field is required for this business type.',
            'business_type.in' => 'Business type must be retail, wholesale, or service.'
        ]);
    }
}

Custom AEAT Integration

For advanced use cases, you can extend the AEAT service:

php
use Modules\VeriFactu\Services\AeatService;

class CustomAeatService extends AeatService
{
    protected function buildRequest(Invoice $invoice): array
    {
        $request = parent::buildRequest($invoice);
        
        // Add custom fields for specific business requirements
        $request['custom_identifier'] = $this->generateCustomId($invoice);
        $request['business_category'] = $invoice->business_category;
        
        return $request;
    }
    
    protected function processResponse(array $response, Invoice $invoice): array
    {
        $processed = parent::processResponse($response, $invoice);
        
        // Custom response processing
        if (isset($response['custom_data'])) {
            $processed['custom_processing'] = $this->handleCustomData($response['custom_data']);
        }
        
        return $processed;
    }
    
    private function generateCustomId(Invoice $invoice): string
    {
        return $invoice->company_id . '-' . $invoice->full_number . '-' . time();
    }
}

Troubleshooting

Common Installation Issues

Problem: Module not appearing in Filament panel

Solution: Verify that the Module Manager is properly installed and the module is registered:

bash
# Check if module is registered
php artisan mm:list

# Clear cache if needed
php artisan cache:clear
php artisan config:clear
php artisan view:clear

Problem: Database migration errors

Solution: Ensure proper migration order and check for conflicts:

bash
# Run migrations with verbose output
php artisan migrate --verbose

# Check migration status
php artisan migrate:status

Certificate Configuration Issues

Problem: Certificate validation fails

Solution: Verify certificate format and accessibility:

bash
# Test certificate file accessibility
ls -la /path/to/certificate.p12

# Verify certificate format using OpenSSL
openssl pkcs12 -info -in /path/to/certificate.p12 -noout

Problem: Certificate password errors

Solution: Ensure proper environment configuration:

env
# Use quotes if password contains special characters
VERIFACTU_CERTIFICATE_PASSWORD="your_password_with_special_chars"

API Integration Issues

Problem: QR generation returns validation errors

Solution: Check NIF format and date validation:

php
// Validate NIF format
if (!preg_match('/^[0-9]{8}[A-Z]$/', $nif)) {
    throw new InvalidArgumentException('Invalid NIF format');
}

// Validate date range
$date = Carbon::parse($invoiceDate);
if ($date->isFuture() || $date->isBefore('2024-01-01')) {
    throw new InvalidArgumentException('Invalid invoice date');
}

Problem: Batch API timeouts

Solution: Reduce batch size and implement proper error handling:

php
// Reduce batch size for better performance
$batchSize = 25; // Instead of maximum 50

// Implement retry logic for failed requests
$retryAttempts = 3;
$retryDelay = 1000; // milliseconds

AEAT Communication Issues

Problem: Connection timeout to AEAT servers

Solution: Check network connectivity and configure timeouts:

bash
# Test AEAT connectivity
curl -I https://prewww1.aeat.es/wlpl/TIKE-CONT/ValidarQR

# Check DNS resolution
nslookup prewww1.aeat.es

Problem: SSL certificate verification errors

Solution: Update CA certificates and verify SSL configuration:

bash
# Update CA certificates (Ubuntu/Debian)
sudo apt-get update && sudo apt-get install ca-certificates

# Test SSL connection
openssl s_client -connect prewww1.aeat.es:443 -servername prewww1.aeat.es

Performance Issues

Problem: Slow QR code generation

Solution: Implement caching and optimize image generation:

php
// Cache QR codes to avoid regeneration
Cache::remember("qr_code_{$invoiceId}", 3600, function() use ($invoice) {
    return $this->qrCodeService->generateQrCode($invoice);
});

// Optimize image size for better performance
$optimizedSize = min($requestedSize, 500); // Limit maximum size

Problem: Database performance issues with large datasets

Solution: Optimize queries and add proper indexing:

php
// Add database indexes for frequently queried fields
Schema::table('verifactu_invoices', function (Blueprint $table) {
    $table->index(['company_id', 'issue_date']);
    $table->index(['status', 'sent_at']);
    $table->index('full_number');
    $table->index(['seller_nif', 'issue_date']);
});

FAQ

General Questions

Q: Does the VeriFactu module require manual Filament plugin registration?

A: No, the VeriFactu module automatically registers with Filament panels through the MM ecosystem. The module uses the filament_plugins configuration in module.json to auto-register resources, pages, and widgets.

Q: What invoice types are supported by the module?

A: The module supports all AEAT-defined invoice types:

  • F1: Simplified invoices (typically for retail/POS)
  • F2: Complete invoices (detailed B2B invoices)
  • R1-R5: Various rectification/correction invoice types

Q: Can I use this module with existing invoice systems?

A: Yes, the module provides flexible APIs and services that can integrate with existing invoice management systems. You can use the QR generation APIs independently or integrate the full invoice lifecycle management.

Q: Is the module compatible with multi-tenant applications?

A: Yes, the module includes built-in multi-tenant support using the HasTenant trait from Module Manager, ensuring proper data isolation between companies.

Technical Questions

Q: What's the maximum number of QR codes I can generate in a batch request?

A: The batch API supports up to 50 invoices per request. For larger volumes, split into multiple batch requests or use individual generation endpoints.

Q: Which thermal printer formats are supported?

A: The module supports both 58mm and 80mm thermal printer formats with specialized templates for each width, including proper text formatting and QR code sizing.

Q: How do I handle certificate expiration?

A: The module includes automatic certificate monitoring. Configure expiration alerts in your environment:

env
VERIFACTU_CERTIFICATE_EXPIRATION_WARNING_DAYS=30
VERIFACTU_CERTIFICATE_NOTIFICATION_EMAIL=admin@company.com

Q: Can I customize the QR code appearance?

A: Yes, the API supports multiple customization options:

  • Size: 100-1000 pixels
  • Margin: 0-50 pixels
  • Format: PNG, JPG, SVG
  • Error correction levels
  • Custom colors (for SVG format)

Integration Questions

Q: How do I integrate with my existing POS system?

A: Use the specialized POS APIs:

bash
# Generate QR for POS receipt
POST /api/verifactu/qr/generate

# Batch generation for multiple sales
POST /api/verifactu/qr/batch

# Check API status
GET /api/verifactu/status

Q: What authentication is required for API access?

A: Currently, the APIs are open for integration. For production use, consider implementing API key authentication by extending the GenerateQrRequest class.

Q: How do I handle offline POS scenarios?

A: The module supports offline operation by:

  1. Generating QR codes locally when possible
  2. Queuing AEAT submissions for when connectivity returns
  3. Using Laravel's queue system for reliable processing

Compliance Questions

Q: Does this module ensure full AEAT VeriFactu compliance?

A: Yes, the module implements all AEAT VeriFactu requirements including:

  • Proper NIF validation (8 digits + 1 letter)
  • Date validation according to VeriFactu regulations
  • Amount formatting per AEAT specifications
  • QR URL construction following official standards

Q: How long should I retain VeriFactu data?

A: Spanish tax law requires retaining invoice data for at least 4 years. Configure appropriate retention policies:

php
'logging' => [
    'retention_days' => 1460, // 4 years
    'archive_old_records' => true,
],

Q: Can I use this module for international invoices?

A: The VeriFactu system is specific to Spanish tax regulations. For international invoices, you may need additional compliance modules or systems depending on the destination country's requirements.

Q: What happens if AEAT servers are unavailable?

A: The module includes robust error handling and retry mechanisms:

  • Automatic retry with exponential backoff
  • Queue-based processing for reliability
  • Comprehensive error logging and monitoring
  • Graceful degradation for non-critical operations