diff --git a/app/Services/EDocument/Standards/Verifactu/Models/Invoice.php b/app/Services/EDocument/Standards/Verifactu/Models/Invoice.php index cad78f9bc1..be19e3a119 100644 --- a/app/Services/EDocument/Standards/Verifactu/Models/Invoice.php +++ b/app/Services/EDocument/Standards/Verifactu/Models/Invoice.php @@ -2,6 +2,10 @@ namespace App\Services\EDocument\Standards\Verifactu\Models; +use RobRichards\XMLSecLibs\XMLSecurityDSig; +use RobRichards\XMLSecLibs\XMLSecurityKey; +use Illuminate\Support\Facades\Log; + class Invoice extends BaseXmlModel { protected string $idVersion; @@ -36,6 +40,9 @@ class Invoice extends BaseXmlModel protected string $huella; protected ?string $signature = null; protected ?FacturaRectificativa $facturaRectificativa = null; + protected ?string $privateKeyPath = null; + protected ?string $publicKeyPath = null; + protected ?string $certificatePath = null; public function __construct() { @@ -411,6 +418,117 @@ class Invoice extends BaseXmlModel $this->facturaRectificativa = $facturaRectificativa; } + public function setPrivateKeyPath(string $path): self + { + $this->privateKeyPath = $path; + return $this; + } + + public function setPublicKeyPath(string $path): self + { + $this->publicKeyPath = $path; + return $this; + } + + public function setCertificatePath(string $path): self + { + $this->certificatePath = $path; + return $this; + } + + protected function signXml(\DOMDocument $doc): void + { + if (!$this->privateKeyPath || !file_exists($this->privateKeyPath)) { + throw new \RuntimeException('Private key not found or not set'); + } + + if (!$this->certificatePath || !file_exists($this->certificatePath)) { + throw new \RuntimeException('Certificate not found or not set'); + } + + Log::info('Starting XML signing process'); + Log::debug('XML before signing: ' . $doc->saveXML()); + + try { + // Create a new Security object + Log::debug('Creating XMLSecurityDSig object'); + $objDSig = new XMLSecurityDSig(); + + // Set canonicalization method + Log::debug('Setting canonicalization method'); + $objDSig->setCanonicalMethod(XMLSecurityDSig::EXC_C14N); + + // Create a new Security key + Log::debug('Creating XMLSecurityKey object'); + $objKey = new XMLSecurityKey(XMLSecurityKey::RSA_SHA256, ['type' => 'private']); + + // Load the private key + Log::debug('Loading private key from: ' . $this->privateKeyPath); + $objKey->loadKey($this->privateKeyPath, true); + + // Add reference + Log::debug('Adding reference to document'); + $objDSig->addReference( + $doc, + XMLSecurityDSig::SHA256, + ['http://www.w3.org/2000/09/xmldsig#enveloped-signature'] + ); + Log::debug('Added reference to document'); + + // Add the certificate + Log::debug('Adding certificate'); + $objDSig->add509Cert(file_get_contents($this->certificatePath)); + + // Sign the XML document + Log::debug('Signing document'); + $objDSig->sign($objKey); + + // Append the signature to the XML + Log::debug('Appending signature'); + $objDSig->appendSignature($doc->documentElement); + + Log::debug('XML after signing: ' . $doc->saveXML()); + } catch (\Exception $e) { + Log::error('Error during XML signing: ' . $e->getMessage()); + Log::error('Stack trace: ' . $e->getTraceAsString()); + throw $e; + } + } + + public function verifySignature(\DOMDocument $doc): bool + { + if (!$this->publicKeyPath || !file_exists($this->publicKeyPath)) { + throw new \RuntimeException('Public key not found or not set'); + } + + // Get the signature node + $objXMLSecDSig = new XMLSecurityDSig(); + + // Locate the signature + $objDSig = $objXMLSecDSig->locateSignature($doc); + if (!$objDSig) { + throw new \RuntimeException('Signature not found in document'); + } + + // Canonicalize the signed info + $objXMLSecDSig->canonicalizeSignedInfo(); + + // Validate references + $objXMLSecDSig->validateReference(); + + // Get the key from the certificate + $objKey = $objXMLSecDSig->locateKey(); + if (!$objKey) { + throw new \RuntimeException('Key not found in signature'); + } + + // Load the public key + $objKey->loadKey($this->publicKeyPath, false, true); + + // Verify the signature + return $objXMLSecDSig->verify($objKey) === 1; + } + public function toXml(): string { // Validate required fields first, outside of try-catch @@ -439,8 +557,8 @@ class Invoice extends BaseXmlModel $doc->formatOutput = true; // Create root element with proper namespaces - $root = $doc->createElementNS(self::XML_NAMESPACE, self::XML_NAMESPACE_PREFIX . ':RegistroAlta'); - $root->setAttribute('xmlns:ds', self::XML_DS_NAMESPACE); + $root = $doc->createElementNS(parent::XML_NAMESPACE, parent::XML_NAMESPACE_PREFIX . ':RegistroAlta'); + $root->setAttribute('xmlns:ds', parent::XML_DS_NAMESPACE); $doc->appendChild($root); // Add required elements in exact order according to schema @@ -617,12 +735,19 @@ class Invoice extends BaseXmlModel $root->appendChild($signatureElement); } - $xml = $doc->saveXML($root); - // error_log("Generated XML: " . $xml); - return $xml; + // Sign the document if private key is set + if ($this->privateKeyPath !== null) { + try { + $this->signXml($doc); + } catch (\Exception $e) { + throw new \RuntimeException('Failed to sign XML: ' . $e->getMessage()); + } + } + + return $doc->saveXML(); } catch (\Exception $e) { error_log("Error in toXml: " . $e->getMessage()); - throw $e; + throw new \RuntimeException("Error in toXml: " . $e->getMessage(), 0, $e); } } diff --git a/composer.json b/composer.json index 6c05d0fe9f..b72c55a49f 100644 --- a/composer.json +++ b/composer.json @@ -94,6 +94,7 @@ "pusher/pusher-php-server": "^7.2", "quickbooks/v3-php-sdk": "^6.2", "razorpay/razorpay": "2.*", + "robrichards/xmlseclibs": "^3.1", "sentry/sentry-laravel": "^4", "setasign/fpdf": "^1.8", "setasign/fpdi": "^2.3", diff --git a/composer.lock b/composer.lock index 8a901231a6..c7244c8c48 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "3469affb27a6e61c80c53267c6ca2889", + "content-hash": "df5805c56ea07b9fe22be83885c67387", "packages": [ { "name": "adrienrn/php-mimetyper", @@ -5169,16 +5169,16 @@ }, { "name": "laravel/framework", - "version": "v11.44.2", + "version": "v11.44.5", "source": { "type": "git", "url": "https://github.com/laravel/framework.git", - "reference": "f85216c82cbd38b66d67ebd20ea762cb3751a4b4" + "reference": "819555ccc7e88ea92945ea785ef9f0fa9b6d4931" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/framework/zipball/f85216c82cbd38b66d67ebd20ea762cb3751a4b4", - "reference": "f85216c82cbd38b66d67ebd20ea762cb3751a4b4", + "url": "https://api.github.com/repos/laravel/framework/zipball/819555ccc7e88ea92945ea785ef9f0fa9b6d4931", + "reference": "819555ccc7e88ea92945ea785ef9f0fa9b6d4931", "shasum": "" }, "require": { @@ -5380,7 +5380,7 @@ "issues": "https://github.com/laravel/framework/issues", "source": "https://github.com/laravel/framework" }, - "time": "2025-03-12T14:34:30+00:00" + "time": "2025-04-24T13:59:11+00:00" }, { "name": "laravel/octane", @@ -11027,6 +11027,48 @@ }, "time": "2025-01-21T10:13:31+00:00" }, + { + "name": "robrichards/xmlseclibs", + "version": "3.1.3", + "source": { + "type": "git", + "url": "https://github.com/robrichards/xmlseclibs.git", + "reference": "2bdfd742624d739dfadbd415f00181b4a77aaf07" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/robrichards/xmlseclibs/zipball/2bdfd742624d739dfadbd415f00181b4a77aaf07", + "reference": "2bdfd742624d739dfadbd415f00181b4a77aaf07", + "shasum": "" + }, + "require": { + "ext-openssl": "*", + "php": ">= 5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "RobRichards\\XMLSecLibs\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "A PHP library for XML Security", + "homepage": "https://github.com/robrichards/xmlseclibs", + "keywords": [ + "security", + "signature", + "xml", + "xmldsig" + ], + "support": { + "issues": "https://github.com/robrichards/xmlseclibs/issues", + "source": "https://github.com/robrichards/xmlseclibs/tree/3.1.3" + }, + "time": "2024-11-20T21:13:56+00:00" + }, { "name": "sabberworm/php-css-parser", "version": "v8.8.0", diff --git a/tests/Feature/EInvoice/Verifactu/Models/BaseModelTest.php b/tests/Feature/EInvoice/Verifactu/Models/BaseModelTest.php index 5f051d1443..452c5a3672 100644 --- a/tests/Feature/EInvoice/Verifactu/Models/BaseModelTest.php +++ b/tests/Feature/EInvoice/Verifactu/Models/BaseModelTest.php @@ -2,7 +2,7 @@ namespace Tests\Feature\EInvoice\Verifactu\Models; -use PHPUnit\Framework\TestCase; +use Tests\TestCase; use App\Services\EDocument\Standards\Verifactu\Models\BaseXmlModel; abstract class BaseModelTest extends TestCase diff --git a/tests/Feature/EInvoice/Verifactu/Models/InvoiceTest.php b/tests/Feature/EInvoice/Verifactu/Models/InvoiceTest.php index 13b235f275..c53916ef6f 100644 --- a/tests/Feature/EInvoice/Verifactu/Models/InvoiceTest.php +++ b/tests/Feature/EInvoice/Verifactu/Models/InvoiceTest.php @@ -789,10 +789,9 @@ class InvoiceTest extends BaseModelTest ->setImporteTotal(100.00) ->setFechaHoraHusoGenRegistro('2023-01-01T12:00:00') ->setTipoHuella('01') - ->setHuella('abc123...') - ->setSignature('MOCK_SIGNATURE_VALUE'); + ->setHuella('abc123...'); - // Add sistema informatico + // Add sistema informatico with all required fields $sistema = new SistemaInformatico(); $sistema ->setNombreRazon('Sistema de Facturación') @@ -800,15 +799,74 @@ class InvoiceTest extends BaseModelTest ->setNombreSistemaInformatico('SistemaFacturacion') ->setIdSistemaInformatico('01') ->setVersion('1.0') - ->setNumeroInstalacion('INST-001'); + ->setNumeroInstalacion('INST-001') + ->setTipoUsoPosibleSoloVerifactu('S') + ->setTipoUsoPosibleMultiOT('S') + ->setIndicadorMultiplesOT('S'); $invoice->setSistemaInformatico($sistema); - $xml = $invoice->toXml(); - $this->assertValidatesAgainstXsd($xml, $this->getTestXsdPath()); + // Add desglose with proper structure + $desglose = new Desglose(); + $desglose->setDesgloseIVA([ + 'Impuesto' => '01', + 'ClaveRegimen' => '02', + 'CalificacionOperacion' => 'S2', + 'BaseImponible' => 100.00, + 'TipoImpositivo' => 21, + 'Cuota' => 21.00 + ]); + $invoice->setDesglose($desglose); - // Test deserialization - $deserialized = Invoice::fromXml($xml); - $this->assertEquals('MOCK_SIGNATURE_VALUE', $deserialized->getSignature()); + // Add encadenamiento (required) + $encadenamiento = new Encadenamiento(); + $encadenamiento->setPrimerRegistro('S'); + $invoice->setEncadenamiento($encadenamiento); + + // Set up paths for certificates + $certsPath = dirname(__DIR__) . '/certs/'; + $privateKeyPath = $certsPath . 'private.pem'; + $publicKeyPath = $certsPath . 'public.pem'; + $certificatePath = $certsPath . 'certificate.pem'; + + // Check if certificate files exist and are readable + foreach (['private.pem', 'public.pem', 'certificate.pem'] as $file) { + $path = $certsPath . $file; + echo "\n$file: "; + if (!file_exists($path)) { + echo "MISSING"; + throw new \RuntimeException("Certificate file $file does not exist at $path"); + } + if (!is_readable($path)) { + echo "NOT READABLE"; + throw new \RuntimeException("Certificate file $file is not readable at $path"); + } + echo "OK (size: " . filesize($path) . " bytes)"; + } + + // Set the keys + $invoice->setPrivateKeyPath($privateKeyPath) + ->setPublicKeyPath($publicKeyPath) + ->setCertificatePath($certificatePath); + + // Generate signed XML + $xml = $invoice->toXml(); + + // Debug output + echo "\nGenerated XML with signature:\n"; + echo $xml; + echo "\n\n"; + + // Load the XML into a DOMDocument for verification + $doc = new \DOMDocument(); + $doc->loadXML($xml); + + // Verify the signature + $this->assertTrue($invoice->verifySignature($doc)); + + // Skip schema validation for signed XML since the signature schema is not part of the core invoice schema + // Instead, validate the XML before signing + $unsignedXml = $invoice->toXml(false); + $this->assertValidatesAgainstXsd($unsignedXml, $this->getTestXsdPath()); } public function testCreateAndSerializeInvoiceWithAgreementData(): void @@ -1140,4 +1198,78 @@ class InvoiceTest extends BaseModelTest throw new \DOMException('XML does not validate against schema'); } } + + public function testSignatureGeneration(): void + { + $invoice = new Invoice(); + $invoice->setIdVersion('1.0') + ->setIdFactura('TEST123') + ->setNombreRazonEmisor('Test Company') + ->setTipoFactura('F1') + ->setDescripcionOperacion('Test Operation') + ->setCuotaTotal(100.00) + ->setImporteTotal(121.00) + ->setFechaHoraHusoGenRegistro(date('Y-m-d\TH:i:s')) + ->setTipoHuella('SHA-256') + ->setHuella(hash('sha256', 'test')); + + // Set up the desglose + $desglose = new Desglose(); + $desglose->setDesgloseIVA([ + 'Impuesto' => 'IVA', + 'ClaveRegimen' => '01', + 'BaseImponible' => 100.00, + 'TipoImpositivo' => 21.00, + 'Cuota' => 21.00 + ]); + $invoice->setDesglose($desglose); + + // Set up encadenamiento + $encadenamiento = new Encadenamiento(); + $encadenamiento->setPrimerRegistro('1'); + $invoice->setEncadenamiento($encadenamiento); + + // Set up sistema informatico + $sistemaInformatico = new SistemaInformatico(); + $sistemaInformatico->setNombreRazon('Test System') + ->setNif('12345678Z') + ->setNombreSistemaInformatico('Test Software') + ->setIdSistemaInformatico('TEST001') + ->setVersion('1.0') + ->setNumeroInstalacion('001') + ->setTipoUsoPosibleSoloVerifactu('S') + ->setTipoUsoPosibleMultiOT('S') + ->setIndicadorMultiplesOT('S'); + $invoice->setSistemaInformatico($sistemaInformatico); + + // Set up signature keys + $privateKeyPath = dirname(__DIR__) . '/certs/private.pem'; + $publicKeyPath = dirname(__DIR__) . '/certs/public.pem'; + $certificatePath = dirname(__DIR__) . '/certs/certificate.pem'; + + // Set the keys + $invoice->setPrivateKeyPath($privateKeyPath) + ->setPublicKeyPath($publicKeyPath) + ->setCertificatePath($certificatePath); + + // Generate signed XML + $xml = $invoice->toXml(); + + // Debug output + echo "\nGenerated XML with signature:\n"; + echo $xml; + echo "\n\n"; + + // Load the XML into a DOMDocument for verification + $doc = new \DOMDocument(); + $doc->loadXML($xml); + + // Verify the signature + $this->assertTrue($invoice->verifySignature($doc)); + + // Validate against schema + $this->assertValidatesAgainstXsd($xml, $this->getTestXsdPath()); + + // Clean up test keys + } } \ No newline at end of file diff --git a/tests/Feature/EInvoice/Verifactu/certs/certificate.pem b/tests/Feature/EInvoice/Verifactu/certs/certificate.pem new file mode 100644 index 0000000000..ba17209857 --- /dev/null +++ b/tests/Feature/EInvoice/Verifactu/certs/certificate.pem @@ -0,0 +1,25 @@ +-----BEGIN CERTIFICATE----- +MIIEITCCAwmgAwIBAgIULRklu/Ae+uWUdejwvfO7wKhs0lIwDQYJKoZIhvcNAQEL +BQAwgZ8xCzAJBgNVBAYTAkVTMQ8wDQYDVQQIDAZNYWRyaWQxDzANBgNVBAcMBk1h +ZHJpZDEaMBgGA1UECgwRVGVzdCBPcmdhbml6YXRpb24xFjAUBgNVBAsMDUlUIERl +cGFydG1lbnQxGTAXBgNVBAMMEHRlc3QuZXhhbXBsZS5jb20xHzAdBgkqhkiG9w0B +CQEWEHRlc3RAZXhhbXBsZS5jb20wHhcNMjUwNDI1MDQxMzA2WhcNMjYwNDI1MDQx +MzA2WjCBnzELMAkGA1UEBhMCRVMxDzANBgNVBAgMBk1hZHJpZDEPMA0GA1UEBwwG +TWFkcmlkMRowGAYDVQQKDBFUZXN0IE9yZ2FuaXphdGlvbjEWMBQGA1UECwwNSVQg +RGVwYXJ0bWVudDEZMBcGA1UEAwwQdGVzdC5leGFtcGxlLmNvbTEfMB0GCSqGSIb3 +DQEJARYQdGVzdEBleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBALZExtAsqspmBMot0/D1Ska36SaEEIZurdgh5odE2jcKWubJQegu4tAu +Eq8LbL6bw8qmJZ6txp3fknIK3RjSyT39Rk0KtpGvrVjFPLAD42UNr8fVspPDHUR9 +yUvSpXoDxtcL6y6pn08jNnBFHkSnQK9br5wiNXWhSDkRTKfJ6Jvc269dA1qcJ2jU +S1k5TXULhZwwvjeTFKb9+xwRjdnLlyh4+WSUvwUMDFgXeKn1iQppZ0Oc+G4Xyq8W +oViOwruNMT3iUN83I3wKk3NOkdbx/IxD87UB19mKdmV4X1Nlj+m+/gidDf2DmriM +FtnPp3kPaqwWBB+rV4AggwnKAOwfmUECAwEAAaNTMFEwHQYDVR0OBBYEFLR1jHET +y8qVyFzsD9Ui0466XsLqMB8GA1UdIwQYMBaAFLR1jHETy8qVyFzsD9Ui0466XsLq +MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAIllEMRCi/n6bqfv +3eMuzHBxvUNNjmTIbEfnPyjf81FB78e1V3mXE5cYcVbDOwbvzZKa6qcioG88iW8Y +5b9RnLQjy9os7xhne1BJq3J2B6XKlHv9O1f5x1fb6nZ/8m7XyGmXctIGs3K29/sF +zFqx7UgSujZCtKannnjS7RCMTbCixhxVbgwBRSkTLBxNWssHaJ7q4ktrfKdXSHAm +BhfgrvbILYYYN38i7Hfwru7m6Uoo7I+sy8yfHZzm3DwvswVRpRFIWLtkc6PBbyE4 +xXWjoJrbFGyzSdLg+DX5SpJ4569Jueuj2LiDCsJ3r1whlUO/gDfB3jdrrUrkxrBq +mmW22K8= +-----END CERTIFICATE----- diff --git a/tests/Feature/EInvoice/Verifactu/certs/private.pem b/tests/Feature/EInvoice/Verifactu/certs/private.pem new file mode 100644 index 0000000000..e9123fddea --- /dev/null +++ b/tests/Feature/EInvoice/Verifactu/certs/private.pem @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC2RMbQLKrKZgTK +LdPw9UpGt+kmhBCGbq3YIeaHRNo3ClrmyUHoLuLQLhKvC2y+m8PKpiWercad35Jy +Ct0Y0sk9/UZNCraRr61YxTywA+NlDa/H1bKTwx1EfclL0qV6A8bXC+suqZ9PIzZw +RR5Ep0CvW6+cIjV1oUg5EUynyeib3NuvXQNanCdo1EtZOU11C4WcML43kxSm/fsc +EY3Zy5coePlklL8FDAxYF3ip9YkKaWdDnPhuF8qvFqFYjsK7jTE94lDfNyN8CpNz +TpHW8fyMQ/O1AdfZinZleF9TZY/pvv4InQ39g5q4jBbZz6d5D2qsFgQfq1eAIIMJ +ygDsH5lBAgMBAAECggEAC732iOa3wf48hMHbC4Th2hhy/rY3UlHDBU95yHEZFb6n +CIMiqdCLcBnnvjsgME9cl6uIdOaBCx6iEpK8l6LMsB+m7cOo603D/xoFxNkbRyFQ +l7EepgJF2mm4FBhn7KpdnvD3n7PxvWlpUmZBgu67bhCcCZTymLdhLbv9kjmhsJi3 +qqlygaWUaj91yQFLJ4PeTnTMRd4yVFvP9IDGxgTQzDzlMQz1uAu52dunB+TkLkJI +/MgbhC5/bAWmgqgmbNjhFkQgwdG787jstXtMvLBGnYNhBVzuqr4XHh31pKFhl+z3 +hG1Hi3yg+XzKVSS7wb+qMv38dGVZYSTSa06vs03mWQKBgQD8cbcGu52iFi1h/9xY +ge1EmIiT9SrYIaYhzrPFdzeY7jpaXfXYZ+nPyrTs6nRxW/RsHP1YxM5WUTE23zhL +g9W1NWATHKh1n+QAwHQdYZWJN1LyqCW5q3fl0koAQGuXIMqKpv3PR+4pyhxdGNC9 +MnPTLQmWkIMA7RBAizNN3aXaYwKBgQC41gRDf1Zv/Wg2kc8u3hOX0tbuT9bxium3 +kfXmDEJUQ9fww8rA9ZKo3weKMvUklmrXEbLjMN2Md8hmdBVHQXXhtXyZzoiCHVct +DNW2TvqBxpJCnLA3bHr8fgPSAHs7SGAM8gF5mY9ZhPI4+Drjk6YlOHVcEr82C1nG +jJbCdVYdCwKBgFdO1OglNy150hRUs1aBCRhyULorTrgVWynSHWasBrSDn/blDEPe +HIVbLBvMMp2KGgzSMeTjnCFKT2UU6pljbSQQ47t4a+LSe76u0PngaCFe2vdFpFaE +sSDxC9rubMeF9cbiXmG0FPCrEAg6rubgbiKZLvm93TESzE4mVoYVpGjVAoGBAKrG +0tmqZarDb+47ejnLZj74xy0ZB3fU6Wx6p8ANw5sns/T4cfUm8IKmzsiZnHZZpA1i +hO300D5gzgAbcS2NBeWtUZqqgOX3RfyRx0PSZRJS7gAt1YLf8CIqLE1ztGhpzpUn +ZMV9ZD1J4KNSdtaeLPxm1chkadb9Vc1lSEYTM6VRAoGAIcA1aEllKEOWXeZAUfny +l67WA0ua6ss2dPQWJumflcf73jajE+jZQagsnRIJv34jJaP/O5A5OKU1WnemKlVU +fyBd4R4nr47yCLxk2HNwUQBluG6EIeUbdEM/2P6qAamIG4FzurvTKIZ7h2dgvtME +RaRkhB6Q8tXUwjQ+HfCpw9Q= +-----END PRIVATE KEY----- diff --git a/tests/Feature/EInvoice/Verifactu/certs/public.pem b/tests/Feature/EInvoice/Verifactu/certs/public.pem new file mode 100644 index 0000000000..9aef5f40dc --- /dev/null +++ b/tests/Feature/EInvoice/Verifactu/certs/public.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtkTG0CyqymYEyi3T8PVK +RrfpJoQQhm6t2CHmh0TaNwpa5slB6C7i0C4SrwtsvpvDyqYlnq3Gnd+ScgrdGNLJ +Pf1GTQq2ka+tWMU8sAPjZQ2vx9Wyk8MdRH3JS9KlegPG1wvrLqmfTyM2cEUeRKdA +r1uvnCI1daFIORFMp8nom9zbr10DWpwnaNRLWTlNdQuFnDC+N5MUpv37HBGN2cuX +KHj5ZJS/BQwMWBd4qfWJCmlnQ5z4bhfKrxahWI7Cu40xPeJQ3zcjfAqTc06R1vH8 +jEPztQHX2Yp2ZXhfU2WP6b7+CJ0N/YOauIwW2c+neQ9qrBYEH6tXgCCDCcoA7B+Z +QQIDAQAB +-----END PUBLIC KEY----- diff --git a/tests/Feature/EInvoice/Verifactu/schema/SuministroInformacion.xsd b/tests/Feature/EInvoice/Verifactu/schema/SuministroInformacion.xsd index b6d14b5820..7877089bbc 100644 --- a/tests/Feature/EInvoice/Verifactu/schema/SuministroInformacion.xsd +++ b/tests/Feature/EInvoice/Verifactu/schema/SuministroInformacion.xsd @@ -1,6 +1,6 @@ - + Datos de cabecera diff --git a/tests/Feature/EInvoice/Verifactu/schema/xmldsig/xmldsig-core-schema.xsd b/tests/Feature/EInvoice/Verifactu/schema/xmldsig/xmldsig-core-schema.xsd new file mode 100644 index 0000000000..df126b30e6 --- /dev/null +++ b/tests/Feature/EInvoice/Verifactu/schema/xmldsig/xmldsig-core-schema.xsd @@ -0,0 +1,318 @@ + + + + + + ]> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +