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
Invoice registration (Alta) is the process of submitting a new invoice record to Spain’s AEAT via the Verifactu SOAP endpoint. When you callVerifactu::registerInvoice(), the library automatically:
- Validates all model properties against the AEAT XSD rules.
- Calculates the SHA-256 hash (Huella) using the official AEAT field ordering.
- Serialises the data into an XML document matching the
RegistroAltaschema. - Signs the XML block with XAdES Enveloped using your digital certificate.
- Transmits the signed payload to AEAT over SOAP.
- Parses the response into a typed
InvoiceResponseobject.
You do not need to calculate the hash yourself.
Verifactu::registerInvoice() calls
HashGeneratorService::generate() internally before sending the request. Do not pre-set
$invoice->hash; leave it empty and let the library fill it in.Building an InvoiceSubmission Step by Step
The
InvoiceId object uniquely identifies an invoice within the issuer’s records. It is required on every submission.use eseperio\verifactu\models\InvoiceId;
use eseperio\verifactu\models\InvoiceSubmission;
$invoice = new InvoiceSubmission();
$invoiceId = new InvoiceId();
$invoiceId->issuerNif = 'B12345678'; // Spanish NIF of the issuing entity
$invoiceId->seriesNumber = 'FA2024/001'; // Series + invoice number (up to 60 chars)
$invoiceId->issueDate = '2024-07-01'; // Issue date in YYYY-MM-DD format
$invoice->setInvoiceId($invoiceId);
use eseperio\verifactu\models\enums\InvoiceType;
use eseperio\verifactu\models\enums\YesNoType;
$invoice->issuerName = 'Empresa Ejemplo SL'; // Must match AEAT census data
$invoice->invoiceType = InvoiceType::STANDARD; // F1 – standard invoice
$invoice->operationDescription = 'Venta de productos';
$invoice->taxAmount = 21.00; // Total tax (CuotaTotal)
$invoice->totalAmount = 121.00; // Grand total (ImporteTotal)
// These two are optional but recommended for clarity
$invoice->simplifiedInvoice = YesNoType::NO;
$invoice->invoiceWithoutRecipient = YesNoType::NO;
InvoiceType valueF1STANDARDF2SIMPLIFIEDF3REPLACEMENTR1RECTIFICATION_1R2RECTIFICATION_2R3RECTIFICATION_3R4RECTIFICATION_4R5RECTIFICATION_SIMPLIFIEDThe
Breakdown aggregates one or more BreakdownDetail lines, each describing a tax bucket
(rate, base, amount, and VAT regime).use eseperio\verifactu\models\Breakdown;
use eseperio\verifactu\models\BreakdownDetail;
use eseperio\verifactu\models\enums\TaxType;
use eseperio\verifactu\models\enums\RegimeType;
use eseperio\verifactu\models\enums\OperationQualificationType;
$breakdown = new Breakdown();
$detail = new BreakdownDetail();
$detail->taxType = TaxType::IVA; // 01 – IVA
$detail->regimeKey = RegimeType::GENERAL; // 01 – general
$detail->operationQualification = OperationQualificationType::SUBJECT_NO_EXEMPT_NO_REVERSE; // S1
$detail->taxRate = 21.00; // percentage
$detail->taxableBase = 100.00; // BaseImponible
$detail->taxAmount = 21.00; // CuotaRepercutida
$breakdown->addDetail($detail);
$invoice->setBreakdown($breakdown);
SUBJECT_NO_EXEMPT_NO_REVERSES1SUBJECT_NO_EXEMPT_REVERSES2NOT_SUBJECT_ARTICLEN1NOT_SUBJECT_LOCATIONN2For exempt operations, set
$detail->exemptOperation (an ExemptOperationType instance)
instead of operationQualification. Exactly one of the two must be provided.Chaining links each record to the previous one via its hash, forming a tamper-evident chain.
Use
firstRecord only for the very first invoice ever submitted by this issuer.use eseperio\verifactu\models\Chaining;
$chaining = new Chaining();
// ── Option A: first invoice ever ──────────────────────────────────────────────
$chaining->firstRecord = 'S'; // or call $chaining->setAsFirstRecord()
// ── Option B: subsequent invoices ─────────────────────────────────────────────
$chaining->setPreviousInvoice([
'issuerNif' => 'B12345678',
'seriesNumber' => 'FA2024/000',
'issueDate' => '2024-06-30',
'hash' => '<sha256-hash-of-previous-invoice>',
]);
$invoice->setChaining($chaining);
The library stores the hash returned in the AEAT response after each successful submission.
Persist
$response->csv and the generated $invoice->hash in your database so you can
reference them when chaining the next invoice.ComputerSystem describes the invoicing software. AEAT requires this block on every record.
The providerId (LegalPerson) identifies the software developer (not the invoice issuer).use eseperio\verifactu\models\ComputerSystem;
use eseperio\verifactu\models\LegalPerson;
use eseperio\verifactu\models\enums\YesNoType;
$provider = new LegalPerson();
$provider->name = 'Software Provider SL';
$provider->nif = 'B87654321';
$computerSystem = new ComputerSystem();
$computerSystem->systemName = 'ERP Company';
$computerSystem->version = '1.0';
$computerSystem->providerName = 'Software Provider SL';
$computerSystem->systemId = '01';
$computerSystem->installationNumber = '1';
$computerSystem->onlyVerifactu = YesNoType::YES;
$computerSystem->multipleObligations = YesNoType::NO;
$computerSystem->hasMultipleObligations = YesNoType::NO;
$computerSystem->setProviderId($provider);
$invoice->setSystemInfo($computerSystem);
For F1 standard invoices, at least one recipient is required. Use
addRecipient() to attach
one or more LegalPerson objects. Validation will fail if the recipients list is empty for F1.$recipient = new LegalPerson();
$recipient->name = 'Cliente Ejemplo SL';
$recipient->nif = 'A98765432';
$invoice->addRecipient($recipient);
Validate Before Submission
Callvalidate() to catch errors before hitting the AEAT endpoint. It returns true on success,
or an associative array of property-keyed error messages.
Submit the Invoice
Handle the InvoiceResponse
InvoiceResponse property | Type | Description |
|---|---|---|
submissionStatus | string | "Correcto" on success (InvoiceResponse::STATUS_OK) |
csv | string | AEAT-issued Código Seguro de Verificación |
lineResponses | array | Per-record details, including error codes |
waitTime | string | Suggested wait time before next submission |
submissionData | array | Presentation metadata returned by AEAT |
Complete Working Example
Special Cases
Simplified Invoices (F2)
Simplified invoices (InvoiceType::SIMPLIFIED) do not require a recipient unless you
explicitly identify the buyer. Set invoiceWithoutRecipient = YesNoType::YES to omit
recipients entirely, or YesNoType::NO if you choose to include one.
Rectification Invoices (R1–R5)
Rectification invoices must declare which original invoice(s) they rectify. UseaddRectifiedInvoice() to reference each original invoice. R1–R4 types require a recipient;
R5 (simplified rectification) must not have one.
Invoices Issued by a Third Party
If a third party issues the invoice on behalf of the obligated issuer, setissuedBy and
setThirdParty():
FechaFinVeriFactu (v1.2.1+)
The optionalfechaFinVeriFactu field signals voluntary termination of Verifactu submissions.
It must be the 31st of December of the current or previous year, formatted as DD-MM-YYYY.
fechaFinVeriFactu corresponds to AEAT validation rule 31.1.3. The library validates that
the date is always December 31st before serialising the XML.