-
Notifications
You must be signed in to change notification settings - Fork 8
Description
Describe the bug
The library is currently unusable for its main purpose, registering an invoice, due to two critical issues:
-
Overly Strict Initial Validation: The
InvoiceSubmission::validate()
method, which is called internally byVerifactu::registerInvoice()
, requires several properties to be non-empty strings even before they can be logically generated by the library itself (e.g.,xmlSignature
) or are optional according to the documentation (e.g.,operationDate
,invoiceAgreementNumber
). This creates a catch-22 situation where it's impossible to submit an invoice because the validation fails before the submission process can even begin. -
Violation of Encapsulation: The
HashGeneratorService
attempts to directly access the protected property$invoiceId
on theInvoiceSubmission
object ($invoice->invoiceId
). This violates PHP's encapsulation rules and causes a fatal error, preventing the hash generation from completing. The service should use a public getter method instead.
These two issues combined make it impossible to follow the basic usage example in the README.md
without patching the vendor code.
To Reproduce
Steps to reproduce the behavior:
-
Setup a new Symfony 7 project with PHP 8.3 and install the library:
composer require eseperio/verifactu-php:dev-master
-
Create a console command to execute the "Register an Invoice" example from the
README.md
examples within the library. Create the filesrc/Command/TestVerifactuCommand.php
with the following content:<?php namespace App\Command; use eseperio\verifactu\models\BreakdownDetail; use eseperio\verifactu\models\Chaining; use eseperio\verifactu\models\ComputerSystem; use eseperio\verifactu\models\enums\HashType; use eseperio\verifactu\models\enums\InvoiceType; use eseperio\verifactu\models\enums\OperationQualificationType; use eseperio\verifactu\models\enums\YesNoType; use eseperio\verifactu\models\InvoiceId; use eseperio\verifactu\models\InvoiceSubmission; use eseperio\verifactu\models\LegalPerson; use eseperio\verifactu\Verifactu; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; #[AsCommand(name: 'verifactu:test')] class TestVerifactuCommand extends Command { protected function execute(InputInterface $input, OutputInterface $output): int { $io = new SymfonyStyle($input, $output); Verifactu::config( '/path/to/your/test-certificate.p12', // Adjust path 'testpassword', Verifactu::TYPE_CERTIFICATE, Verifactu::ENVIRONMENT_SANDBOX ); $invoice = new InvoiceSubmission(); $invoiceId = new InvoiceId(); $invoiceId->issuerNif = '99999999R'; $invoiceId->seriesNumber = 'FA2025/POC001'; $invoiceId->issueDate = '2025-07-16'; $invoice->setInvoiceId($invoiceId); $invoice->issuerName = 'Empresa Ejemplo POC SL'; $invoice->invoiceType = InvoiceType::STANDARD; $invoice->operationDescription = 'Venta de productos de prueba POC'; $invoice->taxAmount = 21.00; $invoice->totalAmount = 121.00; $breakdownDetail = new BreakdownDetail(); $breakdownDetail->taxRate = 21.0; $breakdownDetail->taxableBase = 100.00; $breakdownDetail->taxAmount = 21.00; $breakdownDetail->operationQualification = OperationQualificationType::SUBJECT_NO_EXEMPT_NO_REVERSE; $invoice->addBreakdownDetail($breakdownDetail); $chaining = new Chaining(); $chaining->setAsFirstRecord(); $invoice->setChaining($chaining); $computerSystem = new ComputerSystem(); $computerSystem->systemName = 'Test ERP'; $computerSystem->version = '1.0'; $computerSystem->providerName = 'Test Software Provider'; $computerSystem->systemId = '01'; $computerSystem->installationNumber = '1'; $computerSystem->onlyVerifactu = YesNoType::YES; $computerSystem->multipleObligations = YesNoType::NO; $provider = new LegalPerson(); $provider->name = 'Test Software Provider SL'; $provider->nif = 'B12345678'; $computerSystem->setProviderId($provider); $invoice->setSystemInfo($computerSystem); $invoice->recordTimestamp = (new \DateTime())->format('Y-m-d\TH:i:sP'); $invoice->hashType = HashType::SHA_256; $invoice->hash = hash('sha256', time()); try { $io->writeln('Submitting invoice...'); Verifactu::registerInvoice($invoice); $io->success('Done.'); } catch (\Exception $e) { $io->error($e->getMessage()); $io->writeln($e->getTraceAsString()); return Command::FAILURE; } return Command::SUCCESS; } }
-
Run the command:
php bin/console verifactu:test
-
See the first error (Validation). The command fails because
Verifactu::registerInvoice()
internally callsvalidate()
, which throws an exception.InvoiceSubmission validation failed: Array
(
[xmlSignature] => Array
(
[0] => Must be a string.
)
[operationDate] => Array
(
[0] => Must be a string.
)
...
) -
Attempt a workaround. Modify the command to set dummy values to bypass the validation.
// ... inside the command, before the try-catch block $invoice->operationDate = '2025-07-16'; $invoice->xmlSignature = ''; $invoice->invoiceAgreementNumber = ''; $invoice->systemAgreementId = '';
-
Run the command again.
-
See the second error (Protected Property Access). The process now fails at the hash generation stage.
In HashGeneratorService.php line 41:
Cannot access protected property eseperio\verifactu\models\InvoiceSubmission::$invoiceId
Expected behavior
- The
validate()
method should not require fields that are generated later in the process (likexmlSignature
) or are clearly optional. The validation logic should be separated: first validate user input, then, after internal processing, validate the final model if necessary. - The
HashGeneratorService
should access theinvoiceId
property via a public getter method (e.g.,$invoice->getInvoiceId()
) to respect encapsulation. - The basic example from the
README.md
should work out-of-the-box, successfully sending a request to the AEAT sandbox and returning a response, whether it's a success or a functional error from the AEAT services.
Desktop
- OS: Linux
- PHP Version: 8.3.23
- Framework: Symfony 7.3.1
- Library Version:
dev-master