Appearance
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
- Introduction
- Requirements
- Installation
- Configuration
- Basic Usage
- Components
- API
- Customization
- Troubleshooting
- 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 storageverifactu_invoices
- Main invoice dataverifactu_tax_items
- Tax calculation detailsverifactu_invoice_entries
- Invoice line itemsverifactu_invoice_cancellations
- Cancellation recordsverifactu_settings
- Module configurationcompany_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:
Parameter | Type | Required | Description | Validation |
---|---|---|---|---|
nif | string | Yes | Spanish NIF (Tax ID) | 8 digits + 1 letter (e.g., 12345678Z) |
serie | string | Yes | Invoice series | Max 20 chars, alphanumeric + hyphens/underscores |
number | string | Yes | Invoice number | Max 20 chars, alphanumeric + hyphens/underscores |
date | string | Yes | Invoice date | YYYY-MM-DD format, not future, after 2024-01-01 |
amount | number | Yes | Invoice total amount | Min: 0, Max: 999999999.99 |
size | integer | No | QR code size in pixels | Min: 100, Max: 1000, Default: 300 |
margin | integer | No | QR code margin in pixels | Min: 0, Max: 50, Default: 10 |
format | string | No | Image format | png, jpg, svg, Default: png |
company_name | string | No | Company name | Max 255 chars |
customer_name | string | No | Customer name | Max 255 chars |
description | string | No | Invoice description | Max 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
andmargin
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:
- Generating QR codes locally when possible
- Queuing AEAT submissions for when connectivity returns
- 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