Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/Eseperio/verifactu-php/llms.txt

Use this file to discover all available pages before exploring further.

Verifactu PHP surfaces errors at several distinct layers: pre-flight model validation, business rejections returned by AEAT in a structured response, and lower-level SOAP or runtime exceptions. Understanding which layer an error comes from determines both how to catch it and how to recover.

1. Model Validation Errors

Every model (InvoiceSubmission, InvoiceCancellation, InvoiceQuery, …) exposes a validate() method. Call it before submitting to AEAT to catch field-level problems early. Return value: An array — empty ([]) when the model is valid, or array<string, string[]> mapping each fully-qualified property key (e.g. ClassName::$property) to an array of error messages when invalid.
use eseperio\verifactu\models\InvoiceSubmission;

$invoice = new InvoiceSubmission();
// ... set properties ...

$result = $invoice->validate();

if (!empty($result)) {
    // $result is an associative array: [ 'ClassName::$propertyName' => ['error message', ...], ... ]
    foreach ($result as $property => $errors) {
        echo "Property '{$property}' has errors: " . implode(', ', $errors) . "\n";
    }
    // Stop here — do not submit an invalid model
}
VerifactuService also calls validate() internally before building the SOAP request and will throw an InvalidArgumentException if !empty($validation), so checking manually gives you a cleaner error surface.

2. AEAT Response Errors

A successful SOAP call does not always mean AEAT accepted the invoice. The response is parsed into an InvoiceResponse object whose submissionStatus field reflects the overall outcome.

Checking the Submission Status

use eseperio\verifactu\Verifactu;
use eseperio\verifactu\models\InvoiceResponse;

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

if ($response->submissionStatus !== InvoiceResponse::STATUS_OK) {
    // STATUS_OK constant = 'Correcto'
    // One or more invoices in the batch were rejected by AEAT
    foreach ($response->lineResponses as $line) {
        echo "Error code: "     . ($line['CodigoErrorRegistro']    ?? 'N/A') . "\n";
        echo "Error message: "  . ($line['DescripcionErrorRegistro'] ?? 'N/A') . "\n";
        echo "Error location: " . ($line['IDFactura']              ?? 'N/A') . "\n";
    }
}

Using the getErrors() Helper

InvoiceResponse provides a convenience method that collects all non-empty error codes and their descriptions into a flat array:
$errors = $response->getErrors();
// Returns: [ 'errorCode' => 'description', ... ]

foreach ($errors as $code => $description) {
    echo "AEAT error {$code}: {$description}\n";
}

Querying for Errors

For QueryResponse objects (returned by Verifactu::queryInvoices()), check the queryResult property instead:
use eseperio\verifactu\models\QueryResponse;

$result = Verifactu::queryInvoices($query);

if ($result->queryResult !== 'ConDatos') {
    // No records returned or an error condition was indicated
    echo "Query result: " . $result->queryResult . "\n";
}

3. SOAP Communication Errors

When the SOAP layer itself fails — network timeouts, TLS errors, malformed XML, or AEAT rejecting the SOAP envelope before even parsing the business payload — a SoapFault exception is thrown. The AEAT SOAP gateway also returns numeric fault codes inside the fault message. The most common ones are:
CodeDescription
100The SOAP request signature is not valid
101The SOAP request is empty
102The SOAP request is not well-formed: SOAP Envelope not found
103The SOAP request is not well-formed: SOAP Body not found
104The SOAP request is not well-formed: SOAP Header not found
106The certificate used in the SOAP signature is on a blocklist or is a test certificate
Code 106 appears when you present a certificate to the production endpoint that AEAT has revoked, flagged, or that is recognized only as a test/homologation certificate. Check that you are using ENVIRONMENT_SANDBOX during development.

4. Exception Types

The library throws standard PHP exception types with descriptive messages:
ExceptionWhen It Is Thrown
InvalidArgumentExceptionInvalid configuration parameter, unsupported environment value, or model validation failure
RuntimeExceptionFile-system problems (certificate not readable), errors calling the AEAT service after a SoapFault
SoapFaultLow-level SOAP transport error or protocol fault from AEAT
ExceptionUnexpected or uncategorized error

5. Complete Try/Catch Example

Always wrap AEAT calls in a try/catch block that handles all exception types in order from most specific to most general:
use eseperio\verifactu\Verifactu;
use eseperio\verifactu\models\InvoiceResponse;

try {
    // Validate the model before calling AEAT
    $validation = $invoice->validate();
    if (!empty($validation)) {
        foreach ($validation as $property => $errors) {
            echo "Validation error in '{$property}': " . implode(', ', $errors) . "\n";
        }
        return; // do not proceed
    }

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

    if ($response->submissionStatus === InvoiceResponse::STATUS_OK) {
        // Invoice accepted — store the CSV for your records and QR generation
        echo "Accepted. AEAT CSV: " . $response->csv . "\n";
    } else {
        // AEAT returned a business rejection
        foreach ($response->lineResponses as $line) {
            echo "Rejected — code: " . ($line['CodigoErrorRegistro'] ?? '')
               . " — " . ($line['DescripcionErrorRegistro'] ?? '') . "\n";
        }
    }

} catch (\InvalidArgumentException $e) {
    // Bad input — certificate path wrong, unsupported environment, etc.
    error_log('Configuration or input error: ' . $e->getMessage());

} catch (\RuntimeException $e) {
    // File-system issue or AEAT service call failure
    error_log('Runtime error: ' . $e->getMessage());

} catch (\SoapFault $e) {
    // SOAP-level error — check the fault code in the message
    error_log('SOAP fault: ' . $e->getMessage());
    // faultcode and faultstring are also available on SoapFault directly
    error_log('Fault code: ' . $e->faultcode);

} catch (\Exception $e) {
    // Catch-all for any other unexpected errors
    error_log('Unexpected error: ' . $e->getMessage());
}

6. The ErrorRegistry Dictionary

All AEAT error codes have official Spanish-language descriptions. The library ships the complete official mapping in src/dictionaries/ErrorRegistry.php. You can look up any code programmatically:
use eseperio\verifactu\dictionaries\ErrorRegistry;

// Look up by numeric code
$description = ErrorRegistry::getDescription(1108);
// => 'El NIF del IDEmisorFactura debe ser el mismo que el NIF del ObligadoEmision.'

// Alias method — identical behaviour
$description = ErrorRegistry::getErrorMessage(4104);
// => 'Error en la cabecera: el valor del campo NIF del bloque ObligadoEmision no está identificado.'

Error Code Ranges

RangeCategory
1100 – 1284Individual invoice field errors — invoice is rejected
2000 – 2008Warnings — invoice is accepted but must be corrected
3000 – 3004Duplicate or permission errors
3500 – 3503Database technical errors
4102 – 4140Submission-level errors — entire batch is rejected

7. NIF Census Mismatch Error

Error 4104 — ObligadoEmision NIF not identified in AEAT censusIf AEAT returns error code 4104 (“El valor del campo NIF del bloque ObligadoEmision no está identificado”), it means the issuerNif and/or issuerName you provided in the invoice do not exactly match the data held by the AEAT for that tax ID.To fix this:
  1. Log in to the AEAT website and navigate to “Mis datos censales”.
  2. Verify that the NIF and the exact legal name (NombreRazon) match character-for-character — including punctuation, abbreviations such as S.L. vs SL, and accented characters.
  3. Update your integration to use the exact values returned by the AEAT census.
The same rule applies to TEST_ISSUER_NIF and TEST_ISSUER_NAME when running sandbox integration tests.

8. Best Practices

  • Validate before submitting. Call $model->validate() explicitly so you see field-level errors without making a network round-trip.
  • Always use try/catch. SOAP calls are inherently I/O operations — network failures, TLS issues, and timeouts will occur in production.
  • Log the raw SOAP payload on errors. When debugging a SoapFault, the raw request XML ($client->__getLastRequest()) and response ($client->__getLastResponse()) are the most valuable diagnostic artefacts.
  • Store the CSV. On STATUS_OK, persist $response->csv immediately — it is the AEAT’s immutable reference for the invoice.
  • Consult ErrorRegistry for user-facing messages. AEAT error codes are terse; translate them via ErrorRegistry::getDescription($code) before displaying them to end-users or support staff.

Build docs developers (and LLMs) love