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.

Overview

Verifactu::queryInvoices() sends a ConsultaFactuSistemaFacturacion SOAP request to AEAT and returns the matching invoice records already stored in the AEAT ledger. This is useful for:
  • Reconciling your local invoice database against what AEAT has accepted.
  • Verifying the submission status of a specific invoice.
  • Checking which invoices are on record for a given tax period.
  • Paginating through large result sets.
The method accepts an InvoiceQuery model and returns a QueryResponse object.
Querying does not modify any data at AEAT. It is a read-only operation and can be called as often as needed without side effects.

Building an InvoiceQuery

Required Fields

use eseperio\verifactu\models\InvoiceQuery;

$query = new InvoiceQuery();
$query->year   = '2024';   // Tax year (Ejercicio) — four-digit string
$query->period = '07';     // Period (Periodo) — two-digit month ('01'–'12') or quarter
The period field follows the AEAT period notation. Supply the two-digit month string that corresponds to the billing period you want to query (e.g. '07' for July, '01' for January).

Optional Filters

$query->seriesNumber = 'FA2024';      // Filter to invoices whose series starts with this value
$query->issueDate    = '2024-07-15';  // Filter to a specific issue date (YYYY-MM-DD)
$query->externalRef  = 'REF-QUERY-123'; // Match on your own external reference

Filter by Counterparty

Narrow results to invoices involving a specific buyer or seller NIF. Pass the NIF and optionally the name:
$query->setCounterparty('A12345678', 'Cliente Ejemplo SL');
The method signature is:
$query->setCounterparty(string $nif, ?string $name = null): static

Filter by Issuer Party

To filter by a specific issuer NIF (useful for multi-obligor setups), call setIssuerparty():
$query->setIssuerparty('B12345678', 'Empresa Ejemplo SL');

Set System Information

The query must include the software system details, just like other operations:
$query->setSystemInfo('ERP Company', '1.0');
This is a simplified setter that accepts the system name and version:
$query->setSystemInfo(string $system, string $version): static

Pagination

AEAT may return results in pages. To request a specific page, call setPaginationKey():
$query->setPaginationKey(1, 50); // page number, records per page
Retrieve the paginationKey from the previous QueryResponse and pass it back on the next call to continue from where you left off. Check $result->paginationKey in the response to get the key for the next page.

Validate and Submit

use eseperio\verifactu\Verifactu;

// Validate before sending
$validationResult = $query->validate();
if ($validationResult !== true) {
    foreach ($validationResult as $property => $errors) {
        echo "Error in '$property': " . implode(', ', $errors) . "\n";
    }
    exit;
}

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

Handling the QueryResponse

QueryResponse has no status constant. Check the queryResult string returned by AEAT directly — a successful query returns 'Correcto':
if ($result->queryResult === 'Correcto') {
    echo "Records found: " . count($result->foundRecords) . "\n";

    foreach ($result->foundRecords as $record) {
        echo "Invoice: "  . $record['NumSerieFactura']      . "\n";
        echo "Date: "     . $record['FechaExpedicionFactura'] . "\n";
        echo "Issuer: "   . $record['NombreRazonEmisor']    . " (" . $record['NIF'] . ")\n";
        echo "Amount: "   . $record['ImporteTotal']         . " EUR\n";
        echo "CSV: "      . $record['CSV']                  . "\n";
        echo "Status: "   . $record['EstadoRegistro']       . "\n";
        echo "---\n";
    }

    // Pagination — check if more records are available
    if (!empty($result->paginationKey)) {
        echo "More records available. Use paginationKey to fetch the next page.\n";
        // Pass $result->paginationKey to your next query
    }
} else {
    echo "Query status: " . $result->queryResult . "\n";
    echo "Pagination indicator: " . $result->paginationIndicator . "\n";
}
QueryResponse propertyTypeDescription
queryResultstringResult code returned by AEAT
paginationIndicatorstringWhether pagination is in effect
foundRecordsarrayArray of matching invoice record arrays
paginationKeyarray|nullKey to pass for the next page of results
headerarrayResponse header metadata
periodarrayThe imputation period of the queried records

Paginating Through All Results

$allRecords = [];
$page = 1;

do {
    $query = new InvoiceQuery();
    $query->year   = '2024';
    $query->period = '07';
    $query->setSystemInfo('ERP Company', '1.0');

    if ($page > 1) {
        $query->setPaginationKey($page, 50);
    }

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

    if ($result->queryResult === 'Correcto') {
        $allRecords = array_merge($allRecords, $result->foundRecords ?? []);
    }

    $page++;
} while (!empty($result->paginationKey));

echo "Total records retrieved: " . count($allRecords) . "\n";

Complete Working Example

<?php

use eseperio\verifactu\Verifactu;
use eseperio\verifactu\models\InvoiceQuery;
use eseperio\verifactu\models\QueryResponse;

// Configure once at application bootstrap
Verifactu::config(
    '/path/to/certificate.pfx',
    'certificate-password',
    Verifactu::TYPE_CERTIFICATE,
    Verifactu::ENVIRONMENT_SANDBOX
);

$query = new InvoiceQuery();

// Required
$query->year   = '2024';
$query->period = '07';   // July

// Optional filters
$query->seriesNumber = 'FA2024';
$query->issueDate    = '2024-07-01';

// Filter by counterparty NIF
$query->setCounterparty('A12345678', 'Cliente Ejemplo SL');

// System info
$query->setSystemInfo('ERP Company', '1.0');

// Pagination (first page of 50 records)
$query->setPaginationKey(1, 50);

// Validate
$validationResult = $query->validate();
if ($validationResult !== true) {
    print_r($validationResult);
    exit;
}

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

// Process results
if ($result->queryResult === 'Correcto') {
    $records = $result->foundRecords ?? [];
    echo "Total records found in this page: " . count($records) . "\n";

    foreach ($records as $record) {
        echo "Invoice: "  . ($record['NumSerieFactura'] ?? 'N/A')        . "\n";
        echo "Date: "     . ($record['FechaExpedicionFactura'] ?? 'N/A') . "\n";
        echo "Amount: "   . ($record['ImporteTotal'] ?? 'N/A')           . " EUR\n";
        echo "CSV: "      . ($record['CSV'] ?? 'N/A')                    . "\n";
        echo "Status: "   . ($record['EstadoRegistro'] ?? 'N/A')         . "\n";
        echo "---\n";
    }

    if (!empty($result->paginationKey)) {
        echo "More records available — adjust setPaginationKey() to fetch the next page.\n";
    }
} else {
    echo "Query completed with status: " . $result->queryResult . "\n";
    echo "Pagination indicator: " . $result->paginationIndicator . "\n";
}

Exception Handling

try {
    $result = Verifactu::queryInvoices($query);
} catch (\InvalidArgumentException $e) {
    // Validation failed — year/period missing, or invalid values
    echo "Query validation error: " . $e->getMessage();
} catch (\SoapFault $e) {
    // SOAP communication error
    echo "SOAP error: " . $e->getMessage();
} catch (\Exception $e) {
    echo "Unexpected error: " . $e->getMessage();
}

Build docs developers (and LLMs) love