PHPackages                             eseperio/verifactu-php - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. eseperio/verifactu-php

ActiveLibrary[Utility &amp; Helpers](/categories/utility)

eseperio/verifactu-php
======================

Librería para la integración del sistema de la agencia tributaria española "Veri\*factu" (verifactu)

5054624[3 issues](https://github.com/Eseperio/verifactu-php/issues)[3 PRs](https://github.com/Eseperio/verifactu-php/pulls)PHP

Since Aug 10Pushed 2mo ago16 watchersCompare

[ Source](https://github.com/Eseperio/verifactu-php)[ Packagist](https://packagist.org/packages/eseperio/verifactu-php)[ RSS](/packages/eseperio-verifactu-php/feed)WikiDiscussions master Synced 1mo ago

READMEChangelogDependenciesVersions (6)Used By (0)

Verifactu PHP Library
=====================

[](#verifactu-php-library)

Warning

VERIFACTU OBLIGATION HAS BEEN DELAYED TO 2027 FOR COMPANIES AND JULY 1 2027 FOR SELF-EMPLOYEES LA OBLIGACIÓN DE USAR VERIFACTU HA SIDO POSPUESTA HASTA 2027 PARA SOCIDEDADES Y 1 JULIO DE 2027 PARA AUTÓNOMOS.

\[ES\] Librería completa (registro, consulta, anulación) orientada a objetos para la integración del sistema de facturación digital de Verifactu.

\[EN\] Full object oriented (register, query, cancellation) library for integration of Verifactu digital invoicing system.

Disclaimer: this is an open source library, not an invoicing system. It is provided **without any warranties**; it is the sole responsibility of the integrator to verify its correct operation and compliance with legal or technical requirements. The library includes automated tests to help verify its functionality, but use in production is at your own risk.

**A modern PHP library for integrating with 🇪🇸Spain’s AEAT Verifactu system (digital invoice submission, cancellation, querying, and events) according to the official regulatory technical specification.**

Note

This library supports verifactu transactions only. For non verifactu signed transactions, such as those required when not using invoicing software, you may look for a different library.

---

Table of Contents
-----------------

[](#table-of-contents)

- [Introduction](#introduction)
- [Features](#features)
- [Installation](#installation)
- [Basic Usage](#basic-usage)
- [AEAT Workflow Overview](#aeat-workflow-overview)
- [Configuration](#configuration)
- [Main Models](#main-models)
- [Service Reference](#service-reference)
- [Error Handling](#error-handling)
- [Developing and testing](#developing-and-testing)
- [Contributing](#contributing)
- [License](#license)
- [Acknowledgements](#acknowledgements)

---

Introduction
------------

[](#introduction)

This library provides an object-oriented, strongly-typed and extensible interface to Spain’s AEAT Verifactu digital invoicing system, including:

- Invoice registration (Alta)
- Invoice cancellation (Anulación)
- Querying previously submitted invoices
- Event notification (system-level events required by law)
- QR code generation (for inclusion on invoices)
- Built-in XML signature (XAdES enveloped) and hash calculation
- Error translation using the official AEAT code dictionary

It is designed for easy Composer-based installation and seamless integration into any modern PHP 8.0+ application.

---

Features
--------

[](#features)

- PSR-4 namespaced (`eseperio\verifactu`)
- Models mapped to AEAT XSDs for strong validation and serialization
- Fully modular, easily testable architecture
- Certificate management for SOAP and XML signature
- Compatible with both production and testing AEAT endpoints
- Lightweight QR code generation (no unnecessary dependencies)
- Developer-friendly validation and error reporting

---

Installation
------------

[](#installation)

Install via Composer:

```
composer require eseperio/verifactu-php
```

```
composer require bacon/bacon-qr-code
composer require robrichards/xmlseclibs
```

---

Basic Usage
-----------

[](#basic-usage)

### 1. **Configuration**

[](#1-configuration)

Before using any service, you must configure the library with your certificate, password, certificate type, and environment.
Choose between certificate type (`certificate` or `seal`) and environment (`production` or `sandbox`) according to whether you'll be working in production or testing.

```
use eseperio\verifactu\Verifactu;

// Configure the library (do this once before any operation)
Verifactu::config(
    '/path/to/your-certificate.pfx', // Path to certificate
    'your-certificate-password',     // Certificate password
    Verifactu::TYPE_CERTIFICATE,     // Certificate type: TYPE_CERTIFICATE or TYPE_SEAL
    Verifactu::ENVIRONMENT_PRODUCTION // Environment: ENVIRONMENT_PRODUCTION or ENVIRONMENT_SANDBOX
);
```

- Use `Verifactu::TYPE_CERTIFICATE` for a personal/company certificate, or `Verifactu::TYPE_SEAL` for a seal certificate.
- Use `Verifactu::ENVIRONMENT_PRODUCTION` for real submissions, or `Verifactu::ENVIRONMENT_SANDBOX` for AEAT homologation/testing.

---

### 2. **Register an Invoice (Alta)**

[](#2-register-an-invoice-alta)

```
use eseperio\verifactu\Verifactu;
use eseperio\verifactu\models\InvoiceSubmission;
use eseperio\verifactu\models\InvoiceId;
use eseperio\verifactu\models\Breakdown;
use eseperio\verifactu\models\BreakdownDetail;
use eseperio\verifactu\models\Chaining;
use eseperio\verifactu\models\ComputerSystem;
use eseperio\verifactu\models\LegalPerson;
use eseperio\verifactu\models\enums\InvoiceType;
use eseperio\verifactu\models\enums\TaxType;
use eseperio\verifactu\models\enums\YesNoType;
use eseperio\verifactu\models\enums\HashType;
use eseperio\verifactu\models\enums\OperationQualificationType;
use eseperio\verifactu\models\enums\RegimeType;

// After calling Verifactu::config(...)

$invoice = new InvoiceSubmission();

// Set invoice ID (using object-oriented approach)
$invoiceId = new InvoiceId();
$invoiceId->issuerNif = 'B12345678';
$invoiceId->seriesNumber = 'FA2024/001';
$invoiceId->issueDate = '2024-07-01';
$invoice->setInvoiceId($invoiceId);

// Set basic invoice data
$invoice->issuerName = 'Empresa Ejemplo SL';
$invoice->invoiceType = InvoiceType::STANDARD; // Using corrected enum value
$invoice->operationDescription = 'Venta de productos';
$invoice->taxAmount = 21.00; // Total tax amount
$invoice->totalAmount = 121.00; // Total invoice amount
$invoice->simplifiedInvoice = YesNoType::NO;
$invoice->invoiceWithoutRecipient = YesNoType::NO;

// Add tax breakdown (using object-oriented approach)
$breakdown = new Breakdown();
$detail = new BreakdownDetail();
$detail->taxType = TaxType::IVA;
$detail->regimeKey = RegimeType::GENERAL; // General regime operation
$detail->taxRate = 21.00;
$detail->taxableBase = 100.00;
$detail->taxAmount = 21.00;
$detail->operationQualification = OperationQualificationType::SUBJECT_NO_EXEMPT_NO_REVERSE;
$breakdown->addDetail($detail);
$invoice->setBreakdown($breakdown);

// Set chaining data (using object-oriented approach)
$chaining = new Chaining();
$chaining->firstRecord = YesNoType::YES; // For the first invoice in a chain
// Or for subsequent invoices:
// $chaining->setPreviousInvoice([
//     'seriesNumber' => 'FA2024/000',
//     'issuerNif' => 'B12345678',
//     'issueDate' => '2024-06-30',
//     'hash' => '1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef'
// ]);
$invoice->setChaining($chaining);

// Set system information (using object-oriented approach)
$computerSystem = new ComputerSystem();
$computerSystem->systemName = 'ERP Company';
$computerSystem->version = '1.0';
$computerSystem->providerName = 'Software Provider';
$computerSystem->systemId = '01';
$computerSystem->installationNumber = '1';
$computerSystem->onlyVerifactu = YesNoType::YES;
$computerSystem->multipleObligations = YesNoType::NO;

// Set provider information
$provider = new LegalPerson();
$provider->name = 'Software Provider SL';
$provider->nif = 'B87654321';
$computerSystem->setProviderId($provider);

$invoice->setSystemInfo($computerSystem);

// Set other required fields
$invoice->recordTimestamp = '2024-07-01T12:00:00+02:00'; // Date and time with timezone
$invoice->hashType = HashType::SHA_256;
$invoice->hash = '1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef'; // Calculated hash

// Optional fields
$invoice->operationDate = '2024-07-01'; // Operation date
$invoice->externalRef = 'REF123'; // External reference
$invoice->simplifiedInvoice = YesNoType::NO; // Not a simplified invoice
$invoice->invoiceWithoutRecipient = YesNoType::NO; // Has identified recipient

// Add recipients (using object-oriented approach)
$recipient = new LegalPerson();
$recipient->name = 'Cliente Ejemplo SL';
$recipient->nif = 'A98765432';
$invoice->addRecipient($recipient);

// Validate the invoice before submission
$validationResult = $invoice->validate();
if ($validationResult !== true) {
    // Handle validation errors
    print_r($validationResult);
    exit;
}

// Submit the invoice
$response = Verifactu::registerInvoice($invoice);

if ($response->submissionStatus === \eseperio\verifactu\models\InvoiceResponse::STATUS_OK) {
    echo "AEAT CSV: " . $response->csv;
} else {
    // Check error codes and messages in $response->lineResponses
    foreach ($response->lineResponses as $lineResponse) {
        echo "Error code: " . $lineResponse->errorCode . " - " . $lineResponse->errorMessage . "\n";
    }
}
```

---

### 3. **Cancel an Invoice (Anulación)**

[](#3-cancel-an-invoice-anulación)

```
use eseperio\verifactu\Verifactu;
use eseperio\verifactu\models\InvoiceCancellation;
use eseperio\verifactu\models\InvoiceId;
use eseperio\verifactu\models\Chaining;
use eseperio\verifactu\models\ComputerSystem;
use eseperio\verifactu\models\LegalPerson;
use eseperio\verifactu\models\enums\YesNoType;
use eseperio\verifactu\models\enums\HashType;
use eseperio\verifactu\models\enums\GeneratorType;

// After calling Verifactu::config(...)

$cancellation = new InvoiceCancellation();

// Set invoice ID (using object-oriented approach)
$invoiceId = new InvoiceId();
$invoiceId->issuerNif = 'B12345678';
$invoiceId->seriesNumber = 'FA2024/001';
$invoiceId->issueDate = '2024-07-01';
$cancellation->setInvoiceId($invoiceId);

// Set chaining data (using object-oriented approach)
$chaining = new Chaining();
// For subsequent invoices in a chain:
$chaining->setPreviousInvoice([
    'seriesNumber' => 'FA2024/000',
    'issuerNif' => 'B12345678',
    'issueDate' => '2024-06-30',
    'hash' => '1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef'
]);
$cancellation->setChaining($chaining);

// Set system information (using object-oriented approach)
$computerSystem = new ComputerSystem();
$computerSystem->systemName = 'ERP Company';
$computerSystem->version = '1.0';
$computerSystem->providerName = 'Software Provider';
$computerSystem->systemId = '01';
$computerSystem->installationNumber = '1';
$computerSystem->onlyVerifactu = YesNoType::YES;
$computerSystem->multipleObligations = YesNoType::NO;

// Set provider information
$provider = new LegalPerson();
$provider->name = 'Software Provider SL';
$provider->nif = 'B87654321';
$computerSystem->setProviderId($provider);

$cancellation->setSystemInfo($computerSystem);

// Set other required fields
$cancellation->recordTimestamp = '2024-07-01T12:00:00+02:00'; // Date and time with timezone
$cancellation->hashType = HashType::SHA_256;
$cancellation->hash = '1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef'; // Calculated hash

// Optional fields
$cancellation->noPreviousRecord = YesNoType::NO; // Not a cancellation without previous record
$cancellation->previousRejection = YesNoType::NO; // Not a cancellation due to previous rejection
$cancellation->generator = GeneratorType::ISSUER; // Generated by the issuer
$cancellation->externalRef = 'REF-CANCEL-123'; // External reference

// Validate the cancellation before submission
$validationResult = $cancellation->validate();
if ($validationResult !== true) {
    // Handle validation errors
    print_r($validationResult);
    exit;
}

// Submit the cancellation
$response = Verifactu::cancelInvoice($cancellation);

if ($response->submissionStatus === \eseperio\verifactu\models\InvoiceResponse::STATUS_OK) {
    echo "AEAT CSV: " . $response->csv;
} else {
    // Check error codes and messages in $response->lineResponses
    foreach ($response->lineResponses as $lineResponse) {
        echo "Error code: " . $lineResponse->errorCode . " - " . $lineResponse->errorMessage . "\n";
    }
}
```

---

### 4. **Query Submitted Invoices**

[](#4-query-submitted-invoices)

```
use eseperio\verifactu\Verifactu;
use eseperio\verifactu\models\InvoiceQuery;
use eseperio\verifactu\models\ComputerSystem;
use eseperio\verifactu\models\LegalPerson;
use eseperio\verifactu\models\enums\YesNoType;
use eseperio\verifactu\models\enums\PeriodType;

// After calling Verifactu::config(...)

$query = new InvoiceQuery();

// Required fields
$query->year = '2024';
$query->period = PeriodType::JULY; // Using enum instead of string

// Optional filters
$query->seriesNumber = 'FA2024'; // Filter by invoice series
$query->issueDate = '2024-07-01'; // Filter by specific date

// Set counterparty (using object-oriented approach)
$counterparty = new LegalPerson();
$counterparty->name = 'Cliente Ejemplo SL';
$counterparty->nif = 'A12345678';
$query->setCounterparty($counterparty);

// Set system information (using object-oriented approach)
$computerSystem = new ComputerSystem();
$computerSystem->systemName = 'ERP Company';
$computerSystem->version = '1.0';
$computerSystem->providerName = 'Software Provider';
$computerSystem->systemId = '01';
$computerSystem->installationNumber = '1';
$computerSystem->onlyVerifactu = YesNoType::YES;
$computerSystem->multipleObligations = YesNoType::NO;

// Set provider information
$provider = new LegalPerson();
$provider->name = 'Software Provider SL';
$provider->nif = 'B87654321';
$computerSystem->setProviderId($provider);

$query->setSystemInfo($computerSystem);

// Additional optional fields
$query->externalRef = 'REF-QUERY-123'; // External reference
$query->setPaginationKey(1, 50); // Page number and records per page

// Validate the query before submission
$validationResult = $query->validate();
if ($validationResult !== true) {
    // Handle validation errors
    print_r($validationResult);
    exit;
}

// Submit the query
$result = Verifactu::queryInvoices($query);

// Process the results
if ($result->queryStatus === \eseperio\verifactu\models\QueryResponse::STATUS_OK) {
    echo "Total records found: " . count($result->foundRecords) . "\n";

    foreach ($result->foundRecords as $record) {
        echo "Invoice: " . $record->seriesNumber . " - Date: " . $record->issueDate . "\n";
        echo "Issuer: " . $record->issuerName . " (" . $record->issuerNif . ")\n";
        echo "Amount: " . $record->totalAmount . " EUR\n";
        echo "CSV: " . $record->csv . "\n";
        echo "Status: " . $record->status . "\n";
        echo "-------------------\n";
    }

    // Check if there are more pages
    if ($result->hasMoreRecords === YesNoType::YES) {
        echo "There are more records available. Use pagination to retrieve them.\n";
    }
} else {
    // Handle query errors
    foreach ($result->errors as $error) {
        echo "Error code: " . $error->code . " - " . $error->message . "\n";
    }
}
```

---

### 5. **Generate QR for an Invoice**

[](#5-generate-qr-for-an-invoice)

The library provides flexible options for generating QR codes for invoices, supporting different renderers, resolutions, and output formats.

```
use eseperio\verifactu\Verifactu;
use eseperio\verifactu\services\QrGeneratorService;
use eseperio\verifactu\models\InvoiceRecord;
use eseperio\verifactu\models\InvoiceSubmission;

// Assuming you already have a valid InvoiceSubmission or InvoiceCancellation object
// that has been submitted to AEAT and has a CSV

// Basic usage (returns raw image data using GD renderer)
$qrData = Verifactu::generateInvoiceQr($invoice);

// Save QR directly to a file
$filePath = Verifactu::generateInvoiceQr(
    $invoice,
    QrGeneratorService::DESTINATION_FILE, // Save to file instead of returning data
    300, // Resolution (size in pixels)
    QrGeneratorService::RENDERER_GD // Use GD library (default)
);
echo "QR code saved to: $filePath";

// Generate SVG format
$svgData = Verifactu::generateInvoiceQr(
    $invoice,
    QrGeneratorService::DESTINATION_STRING,
    300,
    QrGeneratorService::RENDERER_SVG
);
// Use SVG data directly in HTML
echo '' . $svgData . '';

// Generate using Imagick (if available on your server)
$pngData = Verifactu::generateInvoiceQr(
    $invoice,
    QrGeneratorService::DESTINATION_STRING,
    300,
    QrGeneratorService::RENDERER_IMAGICK
);

// Convert to base64 for embedding in HTML or PDF
$base64Data = base64_encode($pngData);
echo '';

// Higher resolution QR code
$highResQr = Verifactu::generateInvoiceQr(
    $invoice,
    QrGeneratorService::DESTINATION_STRING,
    600 // Higher resolution
);

// Complete example with a new invoice
$invoice = new InvoiceSubmission();
// ... set all required properties as shown in the Register an Invoice example ...

// First submit the invoice to get a CSV
$response = Verifactu::registerInvoice($invoice);

if ($response->submissionStatus === \eseperio\verifactu\models\InvoiceResponse::STATUS_OK) {
    // Now we can generate a QR code for the invoice
    // The QR code will include the CSV from the response
    $invoice->csv = $response->csv;

    // Generate QR code as PNG and save to file
    $qrFilePath = Verifactu::generateInvoiceQr(
        $invoice,
        QrGeneratorService::DESTINATION_FILE,
        300,
        QrGeneratorService::RENDERER_GD
    );

    echo "Invoice registered successfully with CSV: " . $response->csv . "\n";
    echo "QR code saved to: " . $qrFilePath . "\n";

    // Generate QR code as base64 for embedding in HTML
    $qrData = Verifactu::generateInvoiceQr(
        $invoice,
        QrGeneratorService::DESTINATION_STRING,
        300,
        QrGeneratorService::RENDERER_GD
    );
    $base64QrCode = base64_encode($qrData);

    echo '';
}
```

#### Available Options:

[](#available-options)

- **Destination Types**:

    - `QrGeneratorService::DESTINATION_STRING`: Returns the raw image data (default)
    - `QrGeneratorService::DESTINATION_FILE`: Saves to a temporary file and returns the file path
- **Renderer Types**:

    - `QrGeneratorService::RENDERER_GD`: Uses GD library (default, widely available)
    - `QrGeneratorService::RENDERER_IMAGICK`: Uses ImageMagick (if available)
    - `QrGeneratorService::RENDERER_SVG`: Generates SVG format (vector-based)
- **Resolution**: Size in pixels (default: 300)

---

AEAT Workflow Overview
----------------------

[](#aeat-workflow-overview)

The typical workflow to comply with AEAT Verifactu regulation is:

1. **Prepare Invoice Data:** Build an `InvoiceSubmission` model (or `InvoiceCancellation`, `EventRecord`, etc.) with all required fields.
2. **Generate Hash (Huella):** The library calculates the SHA-256 hash of the invoice using official field ordering.
3. **Serialize to XML:** The library creates an XML structure strictly matching the AEAT XSD.
4. **Digitally Sign XML:** The library signs the XML block using XAdES enveloped and your digital certificate.
5. **Transmit to AEAT:** The signed XML is sent to AEAT via SOAP using the appropriate endpoint and certificate authentication.
6. **Parse Response:** The response is parsed into a typed model (`InvoiceResponse`, `QueryResponse`, etc.), translating error codes using the official dictionary.
7. **Handle Results:** Use the CSV, status, and error details to update your ERP, notify users, or perform follow-up actions.

The same flow applies to cancellations and events, changing only the data model and XML structure.

---

Configuration
-------------

[](#configuration)

The library is configured using the `Verifactu::config()` method, which accepts the following parameters:

```
Verifactu::config(
    string $certPath,           // Path to your digital certificate (PFX or P12 format)
    string $certPassword,       // Password for the certificate file
    string $certType,           // Type of certificate: Verifactu::TYPE_CERTIFICATE or Verifactu::TYPE_SEAL
    string $environment = Verifactu::ENVIRONMENT_PRODUCTION // Environment: Verifactu::ENVIRONMENT_PRODUCTION or Verifactu::ENVIRONMENT_SANDBOX
);
```

### Certificate Types

[](#certificate-types)

- `Verifactu::TYPE_CERTIFICATE`: For a personal or company certificate
- `Verifactu::TYPE_SEAL`: For a seal certificate

### Environments

[](#environments)

- `Verifactu::ENVIRONMENT_PRODUCTION`: For real submissions to AEAT
- `Verifactu::ENVIRONMENT_SANDBOX`: For testing in AEAT's homologation environment

The library automatically selects the appropriate SOAP endpoints and QR verification URLs based on the certificate type and environment.

### Advanced Configuration

[](#advanced-configuration)

If you need more control over the configuration, you can use the `VerifactuService::config()` method directly:

```
use eseperio\verifactu\services\VerifactuService;

VerifactuService::config([
    VerifactuService::CERT_PATH_KEY => '/path/to/your/certificate.p12',
    VerifactuService::CERT_PASSWORD_KEY => 'your-certificate-password',
    VerifactuService::WSDL_ENDPOINT => 'https://custom-endpoint.example.com',
    VerifactuService::QR_VERIFICATION_URL => 'https://custom-qr-verification.example.com',
    // Add any other custom configuration options here
]);
```

---

Main Models
-----------

[](#main-models)

The library provides a comprehensive set of object-oriented models that represent different aspects of the AEAT Verifactu system. All models extend the base `Model` class, which provides validation functionality.

### Core Models

[](#core-models)

- **InvoiceRecord:** Abstract base class for invoice records (submissions and cancellations)
- **InvoiceSubmission:** For registering new invoices (Alta)
- **InvoiceCancellation:** For cancelling existing invoices (Anulación)
- **InvoiceQuery:** For querying submitted invoices
- **InvoiceResponse:** The result of a registration or cancellation
- **QueryResponse:** The result of a query/filter
- **EventRecord:** For system events as required by AEAT

### Component Models

[](#component-models)

- **InvoiceId:** Invoice identification block used within both Alta and Anulación
- **Breakdown:** Tax breakdown information for invoices
- **BreakdownDetail:** Individual tax rate breakdown item
- **Chaining:** Invoice chaining information for hash linkage
- **ComputerSystem:** Information about the computer system generating the invoice
- **LegalPerson:** Represents a legal entity (person or company) with identification
- **OtherID:** Alternative identification for non-Spanish entities
- **PreviousInvoiceChaining:** Information about the previous invoice in a chain
- **Recipient:** Invoice recipient information
- **RectificationBreakdown:** Breakdown for rectification invoices

### Enumerations

[](#enumerations)

The library uses enum classes for type-safe constants:

- **HashType:** Hash algorithm types (SHA-256)
- **InvoiceType:** Invoice types (F1, F2, R1, R2, etc.)
- **OperationQualificationType:** Tax operation qualifications
- **PeriodType:** Month or quarter periods
- **RectificationType:** Types of invoice rectifications
- **RegimeType:** Special regime keys for VAT (ClaveRegimen) - General regime, exports, travel agencies, cash basis, etc.
- **YesNoType:** Yes/No values for boolean fields
- **GeneratorType:** Invoice generator types
- **ThirdPartyOrRecipientType:** Types of third parties or recipients

### Model Validation

[](#model-validation)

All models provide validation through the `validate()` method:

```
$model = new InvoiceSubmission();
// ... set properties ...
$validationResult = $model->validate();

if ($validationResult !== true) {
    // Handle validation errors
    foreach ($validationResult as $property => $errors) {
        echo "Errors in $property: " . implode(', ', $errors) . "\n";
    }
}
```

### XML Serialization

[](#xml-serialization)

Models can be serialized to XML using the `toXml()` method, which returns a DOMDocument:

```
$invoice = new InvoiceSubmission();
// ... set properties ...
$dom = $invoice->toXml();
$xmlString = $dom->saveXML();
```

See the `/src/models` directory for PHPDoc details and validation rules for each model.

---

Service Reference
-----------------

[](#service-reference)

The library is organized into specialized services that handle different aspects of the AEAT Verifactu integration. While most operations can be performed through the main `Verifactu` facade, you can also use these services directly for more advanced use cases.

### Main Services

[](#main-services)

- **Verifactu:** The main facade class that provides a simplified API for common operations.
- **VerifactuService:** Orchestrates the main workflow (validate → hash → serialize → sign → SOAP → parse).

### Specialized Services

[](#specialized-services)

- **HashGeneratorService:** Implements AEAT-compliant SHA-256 hash calculation for invoices.

    ```
    use eseperio\verifactu\services\HashGeneratorService;

    // Calculate hash for an invoice
    $hash = HashGeneratorService::generateHash($invoice);
    $invoice->hash = $hash;
    ```
- **XmlSignerService:** Digitally signs XML blocks using XAdES Enveloped and your certificate.

    ```
    use eseperio\verifactu\services\XmlSignerService;

    // Sign an XML document
    $signedXml = XmlSignerService::signXml($xmlString, $certPath, $certPassword);
    ```
- **SoapClientFactoryService:** Configures and creates secure SOAP clients with certificates.

    ```
    use eseperio\verifactu\services\SoapClientFactoryService;

    // Create a SOAP client with certificate authentication
    $client = SoapClientFactoryService::createClient($wsdlUrl, $certPath, $certPassword);
    ```
- **QrGeneratorService:** Generates AEAT-compliant QR codes for invoices in various formats.

    ```
    use eseperio\verifactu\services\QrGeneratorService;

    // Generate a QR code for an invoice
    $qrCode = QrGeneratorService::generateQr(
        $invoice,
        QrGeneratorService::DESTINATION_STRING,
        300,
        QrGeneratorService::RENDERER_GD
    );
    ```
- **ResponseParserService:** Converts AEAT XML/SOAP responses to model objects.

    ```
    use eseperio\verifactu\services\ResponseParserService;

    // Parse a SOAP response into an InvoiceResponse object
    $response = ResponseParserService::parseInvoiceResponse($soapResponse);
    ```
- **EventDispatcherService:** Handles event submission to AEAT endpoints.

    ```
    use eseperio\verifactu\services\EventDispatcherService;

    // Submit an event to AEAT
    $response = EventDispatcherService::submitEvent($eventRecord);
    ```
- **CertificateManagerService:** Manages certificate and private key loading and validation.

    ```
    use eseperio\verifactu\services\CertificateManagerService;

    // Load a certificate and check its validity
    $certInfo = CertificateManagerService::loadCertificate($certPath, $certPassword);
    ```

Each service is designed to be used independently or as part of the overall workflow orchestrated by the `VerifactuService`. This modular design allows for flexibility and testability.

---

Error Handling
--------------

[](#error-handling)

The library provides comprehensive error handling at multiple levels:

### Model Validation Errors

[](#model-validation-errors)

When using the `validate()` method on models, validation errors are returned as an associative array:

```
$invoice = new InvoiceSubmission();
// ... set some properties but not all required ones ...

$validationResult = $invoice->validate();
if ($validationResult !== true) {
    // $validationResult is an array of errors by property
    foreach ($validationResult as $property => $errors) {
        echo "Property '$property' has errors: " . implode(', ', $errors) . "\n";
    }
}
```

### AEAT Response Errors

[](#aeat-response-errors)

Errors returned by AEAT in SOAP responses are parsed into structured objects:

```
$response = Verifactu::registerInvoice($invoice);

if ($response->submissionStatus !== \eseperio\verifactu\models\InvoiceResponse::STATUS_OK) {
    foreach ($response->lineResponses as $lineResponse) {
        echo "Error code: " . $lineResponse->errorCode . "\n";
        echo "Error message: " . $lineResponse->errorMessage . "\n";
        echo "Error location: " . $lineResponse->errorLocation . "\n";
    }
}
```

All AEAT error codes are mapped to human-readable messages using the official code dictionary in `/src/dictionaries/ErrorRegistry.php`.

Note

If you receive the error: “The value of the NIF field in the ObligadoEmision block is not identified”, it means the issuer data you provided is not correct. They must exactly match your census data at the Spanish Tax Agency (AEAT). You can verify them on the AEAT website under “Mis datos censales”.

### SOAP Communication Errors

[](#soap-communication-errors)

When making SOAP requests, any communication errors (network issues, timeouts, etc.) will throw a `SoapFault` exception. Also if soap validation does not pass in AEAT, a code will be returned by AEAT. Here is a list of common codes:

CodeDescription100The SOAP request signature is not valid101The SOAP request is empty102The SOAP request is not well-formed: SOAP Envelope not found103The SOAP request is not well-formed: SOAP Body not found104The SOAP request is not well-formed: SOAP Header not found106The certificate used in the SOAP signature is on a blocklist or is a test certificate### Exception Handling

[](#exception-handling)

The library throws exceptions for various error conditions:

```
try {
    $response = Verifactu::registerInvoice($invoice);
} catch (\InvalidArgumentException $e) {
    // Handle invalid input parameters
    echo "Invalid argument: " . $e->getMessage();
} catch (\RuntimeException $e) {
    // Handle runtime errors (file access, etc.)
    echo "Runtime error: " . $e->getMessage();
} catch (\SoapFault $e) {
    // Handle SOAP communication errors
    echo "SOAP error: " . $e->getMessage();
} catch (\Exception $e) {
    // Handle any other unexpected errors
    echo "Unexpected error: " . $e->getMessage();
}
```

### Common Error Types

[](#common-error-types)

- **Validation Errors**: Returned by the `validate()` method when model properties don't meet requirements
- **AEAT Business Errors**: Returned in the response when AEAT rejects the submission for business reasons
- **Certificate Errors**: Thrown when there are issues with the digital certificate
- **SOAP Communication Errors**: Thrown when there are network or protocol issues
- **XML Parsing Errors**: Thrown when there are issues parsing XML responses

Proper error handling is essential for a robust integration with AEAT Verifactu.

---

Contributing
------------

[](#contributing)

- Open issues or pull requests with improvements, bugfixes, or feature requests.
- Please follow PSR coding standards and document all public classes/methods with PHPDoc.
- Contributions for additional AEAT document types or regulatory changes are welcome!

---

License
-------

[](#license)

MIT License. See [LICENSE](LICENSE) file.

---

Acknowledgements
----------------

[](#acknowledgements)

- Based on public technical documentation and XSDs by AEAT.
- QR generation via [bacon/bacon-qr-code](https://github.com/Bacon/BaconQrCode)
- XML signature via [robrichards/xmlseclibs](https://github.com/robrichards/xmlseclibs)

---

*For more information on the Verifactu regulation, see the [AEAT website](https://www.agenciatributaria.es/)*

Developing and testing
----------------------

[](#developing-and-testing)

### Testing

[](#testing)

This library includes comprehensive tests to ensure code quality and that documentation examples work correctly:

```
# Run all tests
composer test

# Test only README examples
composer test-readme

# Run unit tests
composer test-unit

# Run sandbox environment tests (requires certificate)
composer test-sandbox
```

#### Setting up for Sandbox Testing

[](#setting-up-for-sandbox-testing)

To run tests that interact with the AEAT Verifactu sandbox environment, you need to provide a valid certificate. The library uses a `.env` file to securely load certificate information without committing it to the repository.

1. Copy the `.env.example` file to `.env` in the project root:

    ```
    cp .env.example .env
    ```
2. Edit the `.env` file and set the following variables:

    ```
    # Path to your certificate file (required for sandbox tests)
    VERIFACTU_CERT_PATH=/path/to/your/certificate.p12

    # Password for your certificate (required for sandbox tests)
    VERIFACTU_CERT_PASSWORD=your_certificate_password

    # Certificate type: 'certificate' or 'seal'
    VERIFACTU_CERT_TYPE=certificate

    # Environment: 'production' or 'sandbox'
    VERIFACTU_ENVIRONMENT=sandbox

    # Required by integration tests (must match your AEAT census data)
    TEST_ISSUER_NIF=B12345678
    TEST_ISSUER_NAME=Example Company SL

    ```
3. When you run any test command, the library will automatically check if the `.env` file exists and create it from `.env.example` if it doesn't. If the file is created automatically, you'll see a warning message indicating that you need to configure it.
4. Tests that require a certificate will be skipped if the `.env` file is not properly configured.

> **Note:** The `.env` file is excluded from version control by `.gitignore` to prevent accidentally committing sensitive information.

Note

To run integration tests you must define TEST\_ISSUER\_NIF and TEST\_ISSUER\_NAME in `.env`. Tests will throw an exception if they are not configured. These values must exactly match your AEAT census data (NIF and legal name). If they do not match, you will get issuer identification errors.

### Development

[](#development)

See the [CONTRIBUTING.md](CONTRIBUTING.md) file for guidelines on how to contribute to this project, including setting up your development environment, running tests, and submitting pull requests.

###  Health Score

33

—

LowBetter than 75% of packages

Maintenance56

Moderate activity, may be stable

Popularity31

Limited adoption so far

Community22

Small or concentrated contributor base

Maturity20

Early-stage or recently created project

 Bus Factor1

Top contributor holds 68.6% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

### Community

Maintainers

![](https://www.gravatar.com/avatar/b1e173755eb4a5abae378ed4c8a8e8066f578c3192105c5e3f9b8ea3ef0086e7?d=identicon)[Eseperio](/maintainers/Eseperio)

---

Top Contributors

[![Eseperio](https://avatars.githubusercontent.com/u/5459366?v=4)](https://github.com/Eseperio "Eseperio (83 commits)")[![logosur](https://avatars.githubusercontent.com/u/22943974?v=4)](https://github.com/logosur "logosur (22 commits)")[![Copilot](https://avatars.githubusercontent.com/in/1143301?v=4)](https://github.com/Copilot "Copilot (10 commits)")[![Alejandro-wzb](https://avatars.githubusercontent.com/u/153491184?v=4)](https://github.com/Alejandro-wzb "Alejandro-wzb (3 commits)")[![FabioIYT](https://avatars.githubusercontent.com/u/37196600?v=4)](https://github.com/FabioIYT "FabioIYT (2 commits)")[![abrahampo1](https://avatars.githubusercontent.com/u/53398615?v=4)](https://github.com/abrahampo1 "abrahampo1 (1 commits)")

### Embed Badge

![Health badge](/badges/eseperio-verifactu-php/health.svg)

```
[![Health](https://phpackages.com/badges/eseperio-verifactu-php/health.svg)](https://phpackages.com/packages/eseperio-verifactu-php)
```

###  Alternatives

[pocketmine/bedrock-data

Blobs of data generated from Minecraft: Bedrock Edition, used by PocketMine-MP

115418.0k5](/packages/pocketmine-bedrock-data)[nahid/qarray

QArray is a PHP abstraction for querying array

108403.2k2](/packages/nahid-qarray)[iben12/laravel-statable

Statable trait for Laravel Eloquent models

96299.8k1](/packages/iben12-laravel-statable)[flyntwp/acf-field-group-composer

52145.4k](/packages/flyntwp-acf-field-group-composer)[koriym/app-state-diagram

An Application Diagram Generator

38222.0k2](/packages/koriym-app-state-diagram)[terminal42/contao-leads

Leads extension for Contao Open Source CMS; Store and manage form data with ease!

41167.9k10](/packages/terminal42-contao-leads)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
