Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/josemmo/Verifactu-PHP/llms.txt

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

Verifactu-PHP uses three exception classes, all extending PHP’s built-in RuntimeException, to signal different failure modes at different stages of the billing workflow. Understanding which class is thrown — and when — lets you write targeted catch blocks and present meaningful error information to operators.

InvalidModelException

Fully qualified name: josemmo\Verifactu\Exceptions\InvalidModelException InvalidModelException is thrown by Model::validate() when one or more Symfony Validator constraints fail. Every model class (RegistrationRecord, CancellationRecord, AeatResponse, etc.) inherits validate() from the abstract josemmo\Verifactu\Models\Model base class, so this exception can be raised anywhere validate() is called.

Properties

violations
ConstraintViolationListInterface
The complete list of Symfony Validator constraint violations that caused validation to fail. Declared as public readonly — iterate over it to extract property paths and messages. Type: Symfony\Component\Validator\ConstraintViolationListInterface.

Code example

use josemmo\Verifactu\Exceptions\InvalidModelException;
use josemmo\Verifactu\Models\Records\RegistrationRecord;

$record = new RegistrationRecord();
// (populate $record fields …)

try {
    $record->validate();
} catch (InvalidModelException $e) {
    echo "Validation failed with " . count($e->violations) . " error(s):\n";

    foreach ($e->violations as $violation) {
        printf(
            "  • %s: %s\n",
            $violation->getPropertyPath(), // e.g. "totalTaxAmount"
            $violation->getMessage()       // e.g. "This value should not be blank."
        );
    }
}
InvalidModelException::__construct() also calls parent::__construct() with a pre-formatted human-readable summary, so $e->getMessage() provides a quick overview of all violations without needing to iterate $e->violations.

AeatException

Fully qualified name: josemmo\Verifactu\Exceptions\AeatException AeatException is thrown by AeatClient::send() when the AEAT server returns a SOAP fault or when the response body cannot be parsed into a valid AeatResponse object. It extends RuntimeException directly with no additional properties — use $e->getMessage() to retrieve the fault string or parse-error description.
AeatException can be thrown even when the HTTP call returns a 200 OK status code. The AEAT VERI*FACTU web service embeds SOAP faults inside a standard HTTP 200 body. Always wrap send()->wait() in a try/catch regardless of HTTP status.

Code example

use josemmo\Verifactu\Exceptions\AeatException;

try {
    $response = $client->send($records)->wait();
} catch (AeatException $e) {
    // Could be a SOAP fault string from AEAT, or a missing/invalid XML element
    echo 'AEAT communication error: ' . $e->getMessage();
}

ImportException

Fully qualified name: josemmo\Verifactu\Exceptions\ImportException ImportException is thrown by Record::fromXml() (and by extension RegistrationRecord::fromXml() and CancellationRecord::fromXml()) as well as ComputerSystem::fromXml() when a required XML element is absent or contains a value that cannot be mapped to the expected PHP type or enum. It extends RuntimeException with no additional properties — $e->getMessage() returns a description of the missing or invalid element.

Code example

use josemmo\Verifactu\Exceptions\ImportException;
use josemmo\Verifactu\Models\Records\Record;
use UXML\UXML;

$xml = UXML::fromString($rawXmlString);

try {
    $record = Record::fromXml($xml->get('sum1:RegistroAlta'));
} catch (ImportException $e) {
    echo 'Failed to parse record XML: ' . $e->getMessage();
    // e.g. "Missing <sum1:TipoFactura /> element"
    //      "Invalid value for <sum1:RechazoPrevio /> element"
}

Comprehensive catch block

The following pattern handles all three exception types in a single try block, appropriate for end-to-end send workflows:
use josemmo\Verifactu\Exceptions\AeatException;
use josemmo\Verifactu\Exceptions\ImportException;
use josemmo\Verifactu\Exceptions\InvalidModelException;
use josemmo\Verifactu\Models\Responses\ItemStatus;
use josemmo\Verifactu\Models\Responses\ResponseStatus;

try {
    // 1. Validate the record before sending
    $record->validate();

    // 2. Send and wait for the AEAT response
    $response = $client->send([$record])->wait();

    // 3. Check global status
    if ($response->status === ResponseStatus::Incorrect) {
        echo "Submission rejected by AEAT\n";
    }

    // 4. Check per-record status
    foreach ($response->items as $item) {
        if ($item->status === ItemStatus::Incorrect) {
            printf(
                "Record %s rejected: [%s] %s\n",
                $item->invoiceId->invoiceNumber,
                $item->errorCode,
                $item->errorDescription
            );
        }
    }

} catch (InvalidModelException $e) {
    // Validation failed locally — do not send to AEAT
    echo "Record validation errors:\n";
    foreach ($e->violations as $violation) {
        printf("  • %s: %s\n", $violation->getPropertyPath(), $violation->getMessage());
    }

} catch (AeatException $e) {
    // SOAP fault or unparseable response from AEAT
    // Note: this can occur even on HTTP 200 responses
    echo "AEAT server error: " . $e->getMessage() . "\n";

} catch (ImportException $e) {
    // Malformed XML when importing records from an external source
    echo "XML import error: " . $e->getMessage() . "\n";
}

Build docs developers (and LLMs) love