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.
Corrective invoices (facturas rectificativas) are the correct mechanism to fix billing errors in VERI*FACTU. Instead of cancelling the original record, you issue a new RegistrationRecord with a corrective invoice type (R1–R5) that references the invoice being corrected. This keeps the billing chain intact and gives the AEAT full auditability of the correction.
Invoice types for corrective invoices
Verifactu-PHP uses the InvoiceType enum to identify the legal basis of the correction:
| Case | Value | Legal basis |
|---|
R1 | R1 | Art. 80.1, 80.2 and right-based error (error fundado en derecho) |
R2 | R2 | Art. 80.3 |
R3 | R3 | Art. 80.4 |
R4 | R4 | All other corrective cases (Resto) |
R5 | R5 | Corrective simplified invoice (factura rectificativa simplificada) |
Choose the type that matches the legal article that authorises the correction. When in doubt, R4 covers the remaining cases not covered by R1–R3.
InvoiceType::R5 is the corrective equivalent of InvoiceType::Simplificada — it is used for simplified invoices. Like Simplificada, R5 records cannot have recipients.
Two corrective modes
Every corrective invoice must declare a CorrectiveType that describes how the correction relates to the original:
| Case | Value | Behaviour |
|---|
Differences | I | Supplements the original invoice with only the delta amounts. The original invoice remains valid. |
Substitution | S | Fully replaces the original invoice. The original is considered annulled. |
Correction by differences
Use CorrectiveType::Differences when you are issuing a corrective invoice that only states the difference between the original and the corrected amounts. The original invoice remains legally valid.
For this mode correctedBaseAmount and correctedTaxAmount must be left null — they are not used when correcting by differences.
<?php
use DateTimeImmutable;
use josemmo\Verifactu\Models\Records\BreakdownDetails;
use josemmo\Verifactu\Models\Records\CorrectiveType;
use josemmo\Verifactu\Models\Records\FiscalIdentifier;
use josemmo\Verifactu\Models\Records\InvoiceIdentifier;
use josemmo\Verifactu\Models\Records\InvoiceType;
use josemmo\Verifactu\Models\Records\OperationType;
use josemmo\Verifactu\Models\Records\RegimeType;
use josemmo\Verifactu\Models\Records\RegistrationRecord;
use josemmo\Verifactu\Models\Records\TaxType;
// Original invoice that is being corrected
$originalInvoiceId = new InvoiceIdentifier();
$originalInvoiceId->issuerId = 'A00000000';
$originalInvoiceId->invoiceNumber = 'FACT-2026-010';
$originalInvoiceId->issueDate = new DateTimeImmutable('2026-02-01');
// Corrective invoice identifier
$correctiveId = new InvoiceIdentifier();
$correctiveId->issuerId = 'A00000000';
$correctiveId->invoiceNumber = 'REC-2026-001';
$correctiveId->issueDate = new DateTimeImmutable('2026-03-20');
// Build the corrective registration record
$record = new RegistrationRecord();
$record->invoiceId = $correctiveId;
$record->issuerName = 'Acme Servicios, S.A.';
$record->invoiceType = InvoiceType::R1; // Art. 80.1/80.2
$record->description = 'Rectificación por diferencias de precio';
// Corrective mode and reference to the corrected invoice
$record->correctiveType = CorrectiveType::Differences;
$record->correctedInvoices = [$originalInvoiceId];
// correctedBaseAmount and correctedTaxAmount must remain null for Differences
// Recipient (required for R1–R4, not for R5)
$record->recipients[] = new FiscalIdentifier('Cliente Español, S.L.', 'B12345678');
// Breakdown — only the delta amounts
$line = new BreakdownDetails();
$line->taxType = TaxType::IVA;
$line->regimeType = RegimeType::C01;
$line->operationType = OperationType::Subject;
$line->baseAmount = '-50.00'; // negative delta
$line->taxRate = '21.00';
$line->taxAmount = '-10.50';
$record->breakdown[] = $line;
$record->totalTaxAmount = '-10.50';
$record->totalAmount = '-60.50';
// Chain, hash, and validate
$record->previousInvoiceId = null; // set to previous record in chain
$record->previousHash = null;
$record->hashedAt = new DateTimeImmutable();
$record->hash = $record->calculateHash();
$record->validate();
Correction by substitution
Use CorrectiveType::Substitution when the corrective invoice entirely replaces the original. In this case you must also provide correctedBaseAmount and correctedTaxAmount, which represent the amounts from the original invoice that are being replaced.
<?php
use DateTimeImmutable;
use josemmo\Verifactu\Models\Records\BreakdownDetails;
use josemmo\Verifactu\Models\Records\CorrectiveType;
use josemmo\Verifactu\Models\Records\FiscalIdentifier;
use josemmo\Verifactu\Models\Records\InvoiceIdentifier;
use josemmo\Verifactu\Models\Records\InvoiceType;
use josemmo\Verifactu\Models\Records\OperationType;
use josemmo\Verifactu\Models\Records\RegimeType;
use josemmo\Verifactu\Models\Records\RegistrationRecord;
use josemmo\Verifactu\Models\Records\TaxType;
$originalInvoiceId = new InvoiceIdentifier();
$originalInvoiceId->issuerId = 'A00000000';
$originalInvoiceId->invoiceNumber = 'FACT-2026-010';
$originalInvoiceId->issueDate = new DateTimeImmutable('2026-02-01');
$correctiveId = new InvoiceIdentifier();
$correctiveId->issuerId = 'A00000000';
$correctiveId->invoiceNumber = 'REC-2026-002';
$correctiveId->issueDate = new DateTimeImmutable('2026-03-20');
$record = new RegistrationRecord();
$record->invoiceId = $correctiveId;
$record->issuerName = 'Acme Servicios, S.A.';
$record->invoiceType = InvoiceType::R1;
$record->description = 'Rectificación por sustitución total de la factura';
// Corrective mode — substitution requires the original amounts
$record->correctiveType = CorrectiveType::Substitution;
$record->correctedInvoices = [$originalInvoiceId];
$record->correctedBaseAmount = '1000.00'; // base amount from the original invoice
$record->correctedTaxAmount = '210.00'; // tax amount from the original invoice
$record->recipients[] = new FiscalIdentifier('Cliente Español, S.L.', 'B12345678');
// Breakdown — the new, corrected amounts
$line = new BreakdownDetails();
$line->taxType = TaxType::IVA;
$line->regimeType = RegimeType::C01;
$line->operationType = OperationType::Subject;
$line->baseAmount = '950.00'; // corrected base
$line->taxRate = '21.00';
$line->taxAmount = '199.50';
$record->breakdown[] = $line;
$record->totalTaxAmount = '199.50';
$record->totalAmount = '1149.50';
$record->previousInvoiceId = null;
$record->previousHash = null;
$record->hashedAt = new DateTimeImmutable();
$record->hash = $record->calculateHash();
$record->validate();
Replaced invoices (Sustitutiva — F3)
InvoiceType::Sustitutiva (F3) is a special invoice type used to issue a full invoice in replacement of one or more simplified invoices that were previously issued and declared. It is not a corrective invoice type — it does not require a correctiveType.
Instead of correctedInvoices, you use the replacedInvoices array:
use josemmo\Verifactu\Models\Records\InvoiceType;
$record->invoiceType = InvoiceType::Sustitutiva;
// Reference the simplified invoices being replaced
$simplifiedId = new InvoiceIdentifier();
$simplifiedId->issuerId = 'A00000000';
$simplifiedId->invoiceNumber = 'TICKET-2026-005';
$simplifiedId->issueDate = new DateTimeImmutable('2026-01-10');
$record->replacedInvoices = [$simplifiedId];
replacedInvoices is only valid for InvoiceType::Sustitutiva. Setting it on any other invoice type will cause validate() to fail.