Fixes for verifactu WS
This commit is contained in:
parent
1c5c568251
commit
a431dd43d4
|
|
@ -6267,16 +6267,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "league/csv",
|
"name": "league/csv",
|
||||||
"version": "9.24.0",
|
"version": "9.24.1",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/thephpleague/csv.git",
|
"url": "https://github.com/thephpleague/csv.git",
|
||||||
"reference": "3f11e12ccd7d6dc25729056e7ce386e4084089ea"
|
"reference": "e0221a3f16aa2a823047d59fab5809d552e29bc8"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/thephpleague/csv/zipball/3f11e12ccd7d6dc25729056e7ce386e4084089ea",
|
"url": "https://api.github.com/repos/thephpleague/csv/zipball/e0221a3f16aa2a823047d59fab5809d552e29bc8",
|
||||||
"reference": "3f11e12ccd7d6dc25729056e7ce386e4084089ea",
|
"reference": "e0221a3f16aa2a823047d59fab5809d552e29bc8",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
|
@ -6354,20 +6354,20 @@
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2025-06-24T19:54:25+00:00"
|
"time": "2025-06-25T14:53:51+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "league/flysystem",
|
"name": "league/flysystem",
|
||||||
"version": "3.29.1",
|
"version": "3.30.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/thephpleague/flysystem.git",
|
"url": "https://github.com/thephpleague/flysystem.git",
|
||||||
"reference": "edc1bb7c86fab0776c3287dbd19b5fa278347319"
|
"reference": "2203e3151755d874bb2943649dae1eb8533ac93e"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/edc1bb7c86fab0776c3287dbd19b5fa278347319",
|
"url": "https://api.github.com/repos/thephpleague/flysystem/zipball/2203e3151755d874bb2943649dae1eb8533ac93e",
|
||||||
"reference": "edc1bb7c86fab0776c3287dbd19b5fa278347319",
|
"reference": "2203e3151755d874bb2943649dae1eb8533ac93e",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
|
@ -6391,13 +6391,13 @@
|
||||||
"composer/semver": "^3.0",
|
"composer/semver": "^3.0",
|
||||||
"ext-fileinfo": "*",
|
"ext-fileinfo": "*",
|
||||||
"ext-ftp": "*",
|
"ext-ftp": "*",
|
||||||
"ext-mongodb": "^1.3",
|
"ext-mongodb": "^1.3|^2",
|
||||||
"ext-zip": "*",
|
"ext-zip": "*",
|
||||||
"friendsofphp/php-cs-fixer": "^3.5",
|
"friendsofphp/php-cs-fixer": "^3.5",
|
||||||
"google/cloud-storage": "^1.23",
|
"google/cloud-storage": "^1.23",
|
||||||
"guzzlehttp/psr7": "^2.6",
|
"guzzlehttp/psr7": "^2.6",
|
||||||
"microsoft/azure-storage-blob": "^1.1",
|
"microsoft/azure-storage-blob": "^1.1",
|
||||||
"mongodb/mongodb": "^1.2",
|
"mongodb/mongodb": "^1.2|^2",
|
||||||
"phpseclib/phpseclib": "^3.0.36",
|
"phpseclib/phpseclib": "^3.0.36",
|
||||||
"phpstan/phpstan": "^1.10",
|
"phpstan/phpstan": "^1.10",
|
||||||
"phpunit/phpunit": "^9.5.11|^10.0",
|
"phpunit/phpunit": "^9.5.11|^10.0",
|
||||||
|
|
@ -6435,9 +6435,9 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/thephpleague/flysystem/issues",
|
"issues": "https://github.com/thephpleague/flysystem/issues",
|
||||||
"source": "https://github.com/thephpleague/flysystem/tree/3.29.1"
|
"source": "https://github.com/thephpleague/flysystem/tree/3.30.0"
|
||||||
},
|
},
|
||||||
"time": "2024-10-08T08:58:34+00:00"
|
"time": "2025-06-25T13:29:59+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "league/flysystem-aws-s3-v3",
|
"name": "league/flysystem-aws-s3-v3",
|
||||||
|
|
@ -6496,16 +6496,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "league/flysystem-local",
|
"name": "league/flysystem-local",
|
||||||
"version": "3.29.0",
|
"version": "3.30.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/thephpleague/flysystem-local.git",
|
"url": "https://github.com/thephpleague/flysystem-local.git",
|
||||||
"reference": "e0e8d52ce4b2ed154148453d321e97c8e931bd27"
|
"reference": "6691915f77c7fb69adfb87dcd550052dc184ee10"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/e0e8d52ce4b2ed154148453d321e97c8e931bd27",
|
"url": "https://api.github.com/repos/thephpleague/flysystem-local/zipball/6691915f77c7fb69adfb87dcd550052dc184ee10",
|
||||||
"reference": "e0e8d52ce4b2ed154148453d321e97c8e931bd27",
|
"reference": "6691915f77c7fb69adfb87dcd550052dc184ee10",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
|
@ -6539,9 +6539,9 @@
|
||||||
"local"
|
"local"
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"source": "https://github.com/thephpleague/flysystem-local/tree/3.29.0"
|
"source": "https://github.com/thephpleague/flysystem-local/tree/3.30.0"
|
||||||
},
|
},
|
||||||
"time": "2024-08-09T21:24:39+00:00"
|
"time": "2025-05-21T10:34:19+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "league/fractal",
|
"name": "league/fractal",
|
||||||
|
|
@ -8603,16 +8603,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "open-telemetry/api",
|
"name": "open-telemetry/api",
|
||||||
"version": "1.3.0",
|
"version": "1.4.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/opentelemetry-php/api.git",
|
"url": "https://github.com/opentelemetry-php/api.git",
|
||||||
"reference": "4e3bb38e069876fb73c2ce85c89583bf2b28cd86"
|
"reference": "b3a9286f9c1c8247c83493c5b1fa475cd0cec7f7"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/opentelemetry-php/api/zipball/4e3bb38e069876fb73c2ce85c89583bf2b28cd86",
|
"url": "https://api.github.com/repos/opentelemetry-php/api/zipball/b3a9286f9c1c8247c83493c5b1fa475cd0cec7f7",
|
||||||
"reference": "4e3bb38e069876fb73c2ce85c89583bf2b28cd86",
|
"reference": "b3a9286f9c1c8247c83493c5b1fa475cd0cec7f7",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
|
@ -8632,7 +8632,7 @@
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"branch-alias": {
|
"branch-alias": {
|
||||||
"dev-main": "1.1.x-dev"
|
"dev-main": "1.4.x-dev"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"autoload": {
|
"autoload": {
|
||||||
|
|
@ -8669,7 +8669,7 @@
|
||||||
"issues": "https://github.com/open-telemetry/opentelemetry-php/issues",
|
"issues": "https://github.com/open-telemetry/opentelemetry-php/issues",
|
||||||
"source": "https://github.com/open-telemetry/opentelemetry-php"
|
"source": "https://github.com/open-telemetry/opentelemetry-php"
|
||||||
},
|
},
|
||||||
"time": "2025-05-07T12:32:21+00:00"
|
"time": "2025-06-19T23:36:51+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "open-telemetry/context",
|
"name": "open-telemetry/context",
|
||||||
|
|
@ -10785,21 +10785,20 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "ramsey/uuid",
|
"name": "ramsey/uuid",
|
||||||
"version": "4.8.1",
|
"version": "4.9.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/ramsey/uuid.git",
|
"url": "https://github.com/ramsey/uuid.git",
|
||||||
"reference": "fdf4dd4e2ff1813111bd0ad58d7a1ddbb5b56c28"
|
"reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/ramsey/uuid/zipball/fdf4dd4e2ff1813111bd0ad58d7a1ddbb5b56c28",
|
"url": "https://api.github.com/repos/ramsey/uuid/zipball/4e0e23cc785f0724a0e838279a9eb03f28b092a0",
|
||||||
"reference": "fdf4dd4e2ff1813111bd0ad58d7a1ddbb5b56c28",
|
"reference": "4e0e23cc785f0724a0e838279a9eb03f28b092a0",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13",
|
"brick/math": "^0.8.8 || ^0.9 || ^0.10 || ^0.11 || ^0.12 || ^0.13",
|
||||||
"ext-json": "*",
|
|
||||||
"php": "^8.0",
|
"php": "^8.0",
|
||||||
"ramsey/collection": "^1.2 || ^2.0"
|
"ramsey/collection": "^1.2 || ^2.0"
|
||||||
},
|
},
|
||||||
|
|
@ -10858,9 +10857,9 @@
|
||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/ramsey/uuid/issues",
|
"issues": "https://github.com/ramsey/uuid/issues",
|
||||||
"source": "https://github.com/ramsey/uuid/tree/4.8.1"
|
"source": "https://github.com/ramsey/uuid/tree/4.9.0"
|
||||||
},
|
},
|
||||||
"time": "2025-06-01T06:28:46+00:00"
|
"time": "2025-06-25T14:20:11+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "razorpay/razorpay",
|
"name": "razorpay/razorpay",
|
||||||
|
|
@ -18360,16 +18359,16 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "nunomaduro/collision",
|
"name": "nunomaduro/collision",
|
||||||
"version": "v8.8.1",
|
"version": "v8.8.2",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/nunomaduro/collision.git",
|
"url": "https://github.com/nunomaduro/collision.git",
|
||||||
"reference": "44ccb82e3e21efb5446748d2a3c81a030ac22bd5"
|
"reference": "60207965f9b7b7a4ce15a0f75d57f9dadb105bdb"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/nunomaduro/collision/zipball/44ccb82e3e21efb5446748d2a3c81a030ac22bd5",
|
"url": "https://api.github.com/repos/nunomaduro/collision/zipball/60207965f9b7b7a4ce15a0f75d57f9dadb105bdb",
|
||||||
"reference": "44ccb82e3e21efb5446748d2a3c81a030ac22bd5",
|
"reference": "60207965f9b7b7a4ce15a0f75d57f9dadb105bdb",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
|
|
@ -18455,7 +18454,7 @@
|
||||||
"type": "patreon"
|
"type": "patreon"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2025-06-11T01:04:21+00:00"
|
"time": "2025-06-25T02:12:12+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "phar-io/manifest",
|
"name": "phar-io/manifest",
|
||||||
|
|
|
||||||
|
|
@ -13,10 +13,9 @@ class WSTest extends TestCase
|
||||||
|
|
||||||
public function test_send_aeat_example_to_verifactu()
|
public function test_send_aeat_example_to_verifactu()
|
||||||
{
|
{
|
||||||
|
// Generate current timestamp in the correct format
|
||||||
|
$currentTimestamp = date('Y-m-d\TH:i:sP');
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
$soapXml = <<<XML
|
$soapXml = <<<XML
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
|
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
|
||||||
|
|
@ -26,26 +25,29 @@ class WSTest extends TestCase
|
||||||
<soapenv:Body>
|
<soapenv:Body>
|
||||||
<sum:RegFactuSistemaFacturacion>
|
<sum:RegFactuSistemaFacturacion>
|
||||||
<sum:Cabecera>
|
<sum:Cabecera>
|
||||||
|
<!-- ObligadoEmision: The computer system submitting on behalf of the invoice issuer -->
|
||||||
<sum1:ObligadoEmision>
|
<sum1:ObligadoEmision>
|
||||||
<sum1:NombreRazon>Certificate One Telematics</sum1:NombreRazon>
|
<sum1:NombreRazon>CERTIFICADO FISICA PRUEBAS</sum1:NombreRazon>
|
||||||
<sum1:NIF>99999910G</sum1:NIF>
|
<sum1:NIF>99999910G</sum1:NIF>
|
||||||
</sum1:ObligadoEmision>
|
</sum1:ObligadoEmision>
|
||||||
</sum:Cabecera>
|
</sum:Cabecera>
|
||||||
<sum:RegistroFactura>
|
<sum:RegistroFactura>
|
||||||
<sum1:RegistroAlta>
|
<sum1:RegistroAlta>
|
||||||
<sum1:IDVersion>1.0</sum1:IDVersion>
|
<sum1:IDVersion>1.0</sum1:IDVersion>
|
||||||
|
<!-- IDFactura: The actual invoice issuer (using same test NIF) -->
|
||||||
<sum1:IDFactura>
|
<sum1:IDFactura>
|
||||||
<sum1:IDEmisorFactura>99999910G</sum1:IDEmisorFactura>
|
<sum1:IDEmisorFactura>99999910G</sum1:IDEmisorFactura>
|
||||||
<sum1:NumSerieFactura>TEST-001</sum1:NumSerieFactura>
|
<sum1:NumSerieFactura>TEST-003</sum1:NumSerieFactura>
|
||||||
<sum1:FechaExpedicionFactura>24-06-2025</sum1:FechaExpedicionFactura>
|
<sum1:FechaExpedicionFactura>24-06-2025</sum1:FechaExpedicionFactura>
|
||||||
</sum1:IDFactura>
|
</sum1:IDFactura>
|
||||||
<sum1:NombreRazonEmisor>Certificate One Telematics</sum1:NombreRazonEmisor>
|
<!-- NombreRazonEmisor: The actual business that issued the invoice -->
|
||||||
|
<sum1:NombreRazonEmisor>CERTIFICADO FISICA PRUEBAS</sum1:NombreRazonEmisor>
|
||||||
<sum1:TipoFactura>F1</sum1:TipoFactura>
|
<sum1:TipoFactura>F1</sum1:TipoFactura>
|
||||||
<sum1:DescripcionOperacion>Test invoice</sum1:DescripcionOperacion>
|
<sum1:DescripcionOperacion>Test invoice submitted by computer system on behalf of business</sum1:DescripcionOperacion>
|
||||||
<sum1:Destinatarios>
|
<sum1:Destinatarios>
|
||||||
<sum1:IDDestinatario>
|
<sum1:IDDestinatario>
|
||||||
<sum1:NombreRazon>Test Recipient</sum1:NombreRazon>
|
<sum1:NombreRazon>Test Recipient Company</sum1:NombreRazon>
|
||||||
<sum1:NIF>99999999A</sum1:NIF>
|
<sum1:NIF>B12345674</sum1:NIF>
|
||||||
</sum1:IDDestinatario>
|
</sum1:IDDestinatario>
|
||||||
</sum1:Destinatarios>
|
</sum1:Destinatarios>
|
||||||
<sum1:Desglose>
|
<sum1:Desglose>
|
||||||
|
|
@ -59,10 +61,15 @@ class WSTest extends TestCase
|
||||||
</sum1:Desglose>
|
</sum1:Desglose>
|
||||||
<sum1:CuotaTotal>21.00</sum1:CuotaTotal>
|
<sum1:CuotaTotal>21.00</sum1:CuotaTotal>
|
||||||
<sum1:ImporteTotal>121.00</sum1:ImporteTotal>
|
<sum1:ImporteTotal>121.00</sum1:ImporteTotal>
|
||||||
|
<!-- Encadenamiento: Required chaining information -->
|
||||||
|
<sum1:Encadenamiento>
|
||||||
|
<sum1:PrimerRegistro>S</sum1:PrimerRegistro>
|
||||||
|
</sum1:Encadenamiento>
|
||||||
|
<!-- SistemaInformatico: The computer system details (same as ObligadoEmision) -->
|
||||||
<sum1:SistemaInformatico>
|
<sum1:SistemaInformatico>
|
||||||
<sum1:NombreRazon>SSSS</sum1:NombreRazon>
|
<sum1:NombreRazon>CERTIFICADO FISICA PRUEBAS</sum1:NombreRazon>
|
||||||
<sum1:NIF>99999910G</sum1:NIF>
|
<sum1:NIF>99999910G</sum1:NIF>
|
||||||
<sum1:NombreSistemaInformatico>NombreSistemaInformatico</sum1:NombreSistemaInformatico>
|
<sum1:NombreSistemaInformatico>InvoiceNinja</sum1:NombreSistemaInformatico>
|
||||||
<sum1:IdSistemaInformatico>77</sum1:IdSistemaInformatico>
|
<sum1:IdSistemaInformatico>77</sum1:IdSistemaInformatico>
|
||||||
<sum1:Version>1.0.03</sum1:Version>
|
<sum1:Version>1.0.03</sum1:Version>
|
||||||
<sum1:NumeroInstalacion>383</sum1:NumeroInstalacion>
|
<sum1:NumeroInstalacion>383</sum1:NumeroInstalacion>
|
||||||
|
|
@ -70,7 +77,7 @@ class WSTest extends TestCase
|
||||||
<sum1:TipoUsoPosibleMultiOT>S</sum1:TipoUsoPosibleMultiOT>
|
<sum1:TipoUsoPosibleMultiOT>S</sum1:TipoUsoPosibleMultiOT>
|
||||||
<sum1:IndicadorMultiplesOT>S</sum1:IndicadorMultiplesOT>
|
<sum1:IndicadorMultiplesOT>S</sum1:IndicadorMultiplesOT>
|
||||||
</sum1:SistemaInformatico>
|
</sum1:SistemaInformatico>
|
||||||
<sum1:FechaHoraHusoGenRegistro>2025-06-24T22:34:00+01:00</sum1:FechaHoraHusoGenRegistro>
|
<sum1:FechaHoraHusoGenRegistro>{$currentTimestamp}</sum1:FechaHoraHusoGenRegistro>
|
||||||
<sum1:TipoHuella>01</sum1:TipoHuella>
|
<sum1:TipoHuella>01</sum1:TipoHuella>
|
||||||
<sum1:Huella>PLACEHOLDER_HUELLA</sum1:Huella>
|
<sum1:Huella>PLACEHOLDER_HUELLA</sum1:Huella>
|
||||||
</sum1:RegistroAlta>
|
</sum1:RegistroAlta>
|
||||||
|
|
@ -80,9 +87,17 @@ class WSTest extends TestCase
|
||||||
</soapenv:Envelope>
|
</soapenv:Envelope>
|
||||||
XML;
|
XML;
|
||||||
|
|
||||||
// Calculate the correct hash for the XML content (excluding the signature)
|
// Calculate the correct hash using AEAT's specified format
|
||||||
$xmlForHash = $this->getXmlForHashCalculation($soapXml);
|
$correctHash = $this->calculateVerifactuHash(
|
||||||
$correctHash = strtoupper(hash('sha256', $xmlForHash));
|
'99999910G', // IDEmisorFactura
|
||||||
|
'TEST-003', // NumSerieFactura
|
||||||
|
'24-06-2025', // FechaExpedicionFactura
|
||||||
|
'F1', // TipoFactura
|
||||||
|
'21.00', // CuotaTotal
|
||||||
|
'121.00', // ImporteTotal
|
||||||
|
'', // Huella (empty for first calculation)
|
||||||
|
$currentTimestamp // FechaHoraHusoGenRegistro (current time)
|
||||||
|
);
|
||||||
|
|
||||||
// Replace the placeholder with the correct hash
|
// Replace the placeholder with the correct hash
|
||||||
$soapXml = str_replace('PLACEHOLDER_HUELLA', $correctHash, $soapXml);
|
$soapXml = str_replace('PLACEHOLDER_HUELLA', $correctHash, $soapXml);
|
||||||
|
|
@ -106,7 +121,8 @@ XML;
|
||||||
'verify' => false,
|
'verify' => false,
|
||||||
'timeout' => 30,
|
'timeout' => 30,
|
||||||
])
|
])
|
||||||
->post('https://prewww1.aeat.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/VerifactuSOAP', $soapXml);
|
->withBody($soapXml, 'text/xml')
|
||||||
|
->post('https://prewww1.aeat.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/VerifactuSOAP');
|
||||||
|
|
||||||
\Log::info('Request with AEAT official test data:');
|
\Log::info('Request with AEAT official test data:');
|
||||||
\Log::info($soapXml);
|
\Log::info($soapXml);
|
||||||
|
|
@ -124,787 +140,33 @@ XML;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract the XML content that should be used for hash calculation
|
* Calculate Verifactu hash using AEAT's specified format
|
||||||
* This excludes the signature and focuses on the business data
|
* Based on AEAT response showing the exact format they use
|
||||||
*/
|
*/
|
||||||
private function getXmlForHashCalculation(string $fullXml): string
|
private function calculateVerifactuHash(
|
||||||
{
|
string $idEmisorFactura,
|
||||||
$doc = new \DOMDocument();
|
string $numSerieFactura,
|
||||||
$doc->loadXML($fullXml);
|
string $fechaExpedicionFactura,
|
||||||
|
string $tipoFactura,
|
||||||
|
string $cuotaTotal,
|
||||||
|
string $importeTotal,
|
||||||
|
string $huella,
|
||||||
|
string $fechaHoraHusoGenRegistro
|
||||||
|
): string {
|
||||||
|
// Build the hash input string exactly as AEAT expects it
|
||||||
|
$hashInput = "IDEmisorFactura={$idEmisorFactura}&" .
|
||||||
|
"NumSerieFactura={$numSerieFactura}&" .
|
||||||
|
"FechaExpedicionFactura={$fechaExpedicionFactura}&" .
|
||||||
|
"TipoFactura={$tipoFactura}&" .
|
||||||
|
"CuotaTotal={$cuotaTotal}&" .
|
||||||
|
"ImporteTotal={$importeTotal}&" .
|
||||||
|
"Huella={$huella}&" .
|
||||||
|
"FechaHoraHusoGenRegistro={$fechaHoraHusoGenRegistro}";
|
||||||
|
|
||||||
// Find the RegistroAlta element
|
\Log::info('Hash input string: ' . $hashInput);
|
||||||
$registroAlta = $doc->getElementsByTagNameNS('https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd', 'RegistroAlta')->item(0);
|
|
||||||
|
|
||||||
if (!$registroAlta) {
|
// Calculate SHA256 hash and return in uppercase
|
||||||
throw new \Exception('RegistroAlta element not found');
|
return strtoupper(hash('sha256', $hashInput));
|
||||||
}
|
|
||||||
|
|
||||||
// Create a new document with just the RegistroAlta content
|
|
||||||
$hashDoc = new \DOMDocument('1.0', 'UTF-8');
|
|
||||||
$hashDoc->preserveWhiteSpace = false;
|
|
||||||
$hashDoc->formatOutput = false;
|
|
||||||
|
|
||||||
// Import the RegistroAlta node
|
|
||||||
$importedNode = $hashDoc->importNode($registroAlta, true);
|
|
||||||
$hashDoc->appendChild($importedNode);
|
|
||||||
|
|
||||||
// Return the XML string for hash calculation
|
|
||||||
return $hashDoc->saveXML();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_send_brand_new_invoice_to_verifactu()
|
|
||||||
{
|
|
||||||
// Create a completely new invoice with fresh data
|
|
||||||
$soapXml = <<<XML
|
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
|
|
||||||
xmlns:sum="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroLR.xsd"
|
|
||||||
xmlns:sum1="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd">
|
|
||||||
<soapenv:Header/>
|
|
||||||
<soapenv:Body>
|
|
||||||
<sum:RegFactuSistemaFacturacion>
|
|
||||||
<sum:Cabecera>
|
|
||||||
<sum1:ObligadoEmision>
|
|
||||||
<sum1:NombreRazon>NUEVA EMPRESA TEST SL</sum1:NombreRazon>
|
|
||||||
<sum1:NIF>99999910G</sum1:NIF>
|
|
||||||
</sum1:ObligadoEmision>
|
|
||||||
</sum:Cabecera>
|
|
||||||
<sum:RegistroFactura>
|
|
||||||
<sum1:RegistroAlta>
|
|
||||||
<sum1:IDVersion>1.0</sum1:IDVersion>
|
|
||||||
<sum1:IDFactura>
|
|
||||||
<sum1:IDEmisorFactura>99999910G</sum1:IDEmisorFactura>
|
|
||||||
<sum1:NumSerieFactura>FAC-2025-001</sum1:NumSerieFactura>
|
|
||||||
<sum1:FechaExpedicionFactura>24-06-2025</sum1:FechaExpedicionFactura>
|
|
||||||
</sum1:IDFactura>
|
|
||||||
<sum1:NombreRazonEmisor>NUEVA EMPRESA TEST SL</sum1:NombreRazonEmisor>
|
|
||||||
<sum1:TipoFactura>F1</sum1:TipoFactura>
|
|
||||||
<sum1:DescripcionOperacion>Venta de servicios informáticos</sum1:DescripcionOperacion>
|
|
||||||
<sum1:Destinatarios>
|
|
||||||
<sum1:IDDestinatario>
|
|
||||||
<sum1:NombreRazon>CLIENTE TEST SA</sum1:NombreRazon>
|
|
||||||
<sum1:NIF>B12345678</sum1:NIF>
|
|
||||||
</sum1:IDDestinatario>
|
|
||||||
</sum1:Destinatarios>
|
|
||||||
<sum1:Desglose>
|
|
||||||
<sum1:DetalleDesglose>
|
|
||||||
<sum1:ClaveRegimen>01</sum1:ClaveRegimen>
|
|
||||||
<sum1:CalificacionOperacion>S1</sum1:CalificacionOperacion>
|
|
||||||
<sum1:TipoImpositivo>21</sum1:TipoImpositivo>
|
|
||||||
<sum1:BaseImponibleOimporteNoSujeto>500.00</sum1:BaseImponibleOimporteNoSujeto>
|
|
||||||
<sum1:CuotaRepercutida>105.00</sum1:CuotaRepercutida>
|
|
||||||
</sum1:DetalleDesglose>
|
|
||||||
</sum1:Desglose>
|
|
||||||
<sum1:CuotaTotal>105.00</sum1:CuotaTotal>
|
|
||||||
<sum1:ImporteTotal>605.00</sum1:ImporteTotal>
|
|
||||||
<sum1:SistemaInformatico>
|
|
||||||
<sum1:NombreRazon>INVOICE NINJA</sum1:NombreRazon>
|
|
||||||
<sum1:NIF>99999910G</sum1:NIF>
|
|
||||||
<sum1:NombreSistemaInformatico>InvoiceNinja</sum1:NombreSistemaInformatico>
|
|
||||||
<sum1:IdSistemaInformatico>001</sum1:IdSistemaInformatico>
|
|
||||||
<sum1:Version>5.0</sum1:Version>
|
|
||||||
<sum1:NumeroInstalacion>001</sum1:NumeroInstalacion>
|
|
||||||
<sum1:TipoUsoPosibleSoloVerifactu>S</sum1:TipoUsoPosibleSoloVerifactu>
|
|
||||||
<sum1:TipoUsoPosibleMultiOT>N</sum1:TipoUsoPosibleMultiOT>
|
|
||||||
<sum1:IndicadorMultiplesOT>N</sum1:IndicadorMultiplesOT>
|
|
||||||
</sum1:SistemaInformatico>
|
|
||||||
<sum1:FechaHoraHusoGenRegistro>2025-06-24T22:40:00+01:00</sum1:FechaHoraHusoGenRegistro>
|
|
||||||
<sum1:TipoHuella>01</sum1:TipoHuella>
|
|
||||||
<sum1:Huella>PLACEHOLDER_HUELLA</sum1:Huella>
|
|
||||||
</sum1:RegistroAlta>
|
|
||||||
</sum:RegistroFactura>
|
|
||||||
</sum:RegFactuSistemaFacturacion>
|
|
||||||
</soapenv:Body>
|
|
||||||
</soapenv:Envelope>
|
|
||||||
XML;
|
|
||||||
|
|
||||||
// Calculate the correct hash for the XML content
|
|
||||||
$xmlForHash = $this->getXmlForHashCalculation($soapXml);
|
|
||||||
$correctHash = strtoupper(hash('sha256', $xmlForHash));
|
|
||||||
|
|
||||||
// Replace the placeholder with the correct hash
|
|
||||||
$soapXml = str_replace('PLACEHOLDER_HUELLA', $correctHash, $soapXml);
|
|
||||||
|
|
||||||
\Log::info('Brand new invoice - Calculated hash: ' . $correctHash);
|
|
||||||
|
|
||||||
// Try the Sello endpoint for certificate-based access
|
|
||||||
$endpoint = 'https://prewww10.aeat.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/VerifactuSOAP';
|
|
||||||
$certPath = storage_path('aeat-cert2.pem');
|
|
||||||
$keyPath = storage_path('aeat-key2.pem');
|
|
||||||
|
|
||||||
// Sign the XML
|
|
||||||
$signingService = new \App\Services\EDocument\Standards\Verifactu\Signing\SigningService($soapXml, file_get_contents($keyPath), file_get_contents($certPath));
|
|
||||||
$soapXml = $signingService->sign();
|
|
||||||
|
|
||||||
// Send the request
|
|
||||||
$response = Http::withHeaders([
|
|
||||||
'Content-Type' => 'text/xml; charset=utf-8',
|
|
||||||
'SOAPAction' => '',
|
|
||||||
])
|
|
||||||
->withOptions([
|
|
||||||
'cert' => $certPath,
|
|
||||||
'ssl_key' => $keyPath,
|
|
||||||
'verify' => false,
|
|
||||||
'timeout' => 30,
|
|
||||||
])
|
|
||||||
->post($endpoint, $soapXml);
|
|
||||||
|
|
||||||
\Log::info('Brand new invoice request:');
|
|
||||||
\Log::info($soapXml);
|
|
||||||
\Log::info('Brand new invoice response:');
|
|
||||||
\Log::info('Response Status: ' . $response->status());
|
|
||||||
\Log::info('Response Headers: ' . json_encode($response->headers()));
|
|
||||||
\Log::info('Response Body: ' . $response->body());
|
|
||||||
|
|
||||||
// Check if the response contains an error
|
|
||||||
$responseBody = $response->body();
|
|
||||||
if (strpos($responseBody, 'Error interno en el servidor') !== false) {
|
|
||||||
\Log::error('SOAP response contains server error: ' . $responseBody);
|
|
||||||
$this->fail('SOAP response contains server error: ' . $responseBody);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the response contains a success message
|
|
||||||
if (strpos($responseBody, 'RegFactuSistemaFacturacionResponse') !== false) {
|
|
||||||
\Log::info('SOAP response contains success message');
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->assertTrue($response->successful());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_send_official_example_to_verifactu()
|
|
||||||
{
|
|
||||||
// Use the exact structure from the official AEAT example
|
|
||||||
$soapXml = <<<XML
|
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
|
|
||||||
xmlns:sum="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroLR.xsd"
|
|
||||||
xmlns:sum1="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd">
|
|
||||||
<soapenv:Header/>
|
|
||||||
<soapenv:Body>
|
|
||||||
<sum:RegFactuSistemaFacturacion>
|
|
||||||
<sum:Cabecera>
|
|
||||||
<sum1:ObligadoEmision>
|
|
||||||
<sum1:NombreRazon>99999910G</sum1:NombreRazon>
|
|
||||||
<sum1:NIF>99999910G</sum1:NIF>
|
|
||||||
</sum1:ObligadoEmision>
|
|
||||||
</sum:Cabecera>
|
|
||||||
<sum:RegistroFactura>
|
|
||||||
<sum1:RegistroAlta>
|
|
||||||
<sum1:IDVersion>1.0</sum1:IDVersion>
|
|
||||||
<sum1:IDFactura>
|
|
||||||
<sum1:IDEmisorFactura>99999910G</sum1:IDEmisorFactura>
|
|
||||||
<sum1:NumSerieFactura>12345</sum1:NumSerieFactura>
|
|
||||||
<sum1:FechaExpedicionFactura>24-06-2025</sum1:FechaExpedicionFactura>
|
|
||||||
</sum1:IDFactura>
|
|
||||||
<sum1:NombreRazonEmisor>99999910G</sum1:NombreRazonEmisor>
|
|
||||||
<sum1:TipoFactura>F1</sum1:TipoFactura>
|
|
||||||
<sum1:DescripcionOperacion>Test invoice following official example</sum1:DescripcionOperacion>
|
|
||||||
<sum1:Destinatarios>
|
|
||||||
<sum1:IDDestinatario>
|
|
||||||
<sum1:NombreRazon>B12345678</sum1:NombreRazon>
|
|
||||||
<sum1:NIF>B12345678</sum1:NIF>
|
|
||||||
</sum1:IDDestinatario>
|
|
||||||
</sum1:Destinatarios>
|
|
||||||
<sum1:Desglose>
|
|
||||||
<sum1:DetalleDesglose>
|
|
||||||
<sum1:ClaveRegimen>01</sum1:ClaveRegimen>
|
|
||||||
<sum1:CalificacionOperacion>S1</sum1:CalificacionOperacion>
|
|
||||||
<sum1:TipoImpositivo>4</sum1:TipoImpositivo>
|
|
||||||
<sum1:BaseImponibleOimporteNoSujeto>10.00</sum1:BaseImponibleOimporteNoSujeto>
|
|
||||||
<sum1:CuotaRepercutida>0.40</sum1:CuotaRepercutida>
|
|
||||||
</sum1:DetalleDesglose>
|
|
||||||
<sum1:DetalleDesglose>
|
|
||||||
<sum1:ClaveRegimen>01</sum1:ClaveRegimen>
|
|
||||||
<sum1:CalificacionOperacion>S1</sum1:CalificacionOperacion>
|
|
||||||
<sum1:TipoImpositivo>21</sum1:TipoImpositivo>
|
|
||||||
<sum1:BaseImponibleOimporteNoSujeto>100.00</sum1:BaseImponibleOimporteNoSujeto>
|
|
||||||
<sum1:CuotaRepercutida>21.00</sum1:CuotaRepercutida>
|
|
||||||
</sum1:DetalleDesglose>
|
|
||||||
</sum1:Desglose>
|
|
||||||
<sum1:CuotaTotal>21.40</sum1:CuotaTotal>
|
|
||||||
<sum1:ImporteTotal>131.40</sum1:ImporteTotal>
|
|
||||||
<sum1:Encadenamiento>
|
|
||||||
<sum1:RegistroAnterior>
|
|
||||||
<sum1:IDEmisorFactura>99999910G</sum1:IDEmisorFactura>
|
|
||||||
<sum1:NumSerieFactura>44</sum1:NumSerieFactura>
|
|
||||||
<sum1:FechaExpedicionFactura>24-06-2025</sum1:FechaExpedicionFactura>
|
|
||||||
<sum1:Huella>HuellaRegistroAnterior</sum1:Huella>
|
|
||||||
</sum1:RegistroAnterior>
|
|
||||||
</sum1:Encadenamiento>
|
|
||||||
<sum1:SistemaInformatico>
|
|
||||||
<sum1:NombreRazon>99999910G</sum1:NombreRazon>
|
|
||||||
<sum1:NIF>99999910G</sum1:NIF>
|
|
||||||
<sum1:NombreSistemaInformatico>InvoiceNinja</sum1:NombreSistemaInformatico>
|
|
||||||
<sum1:IdSistemaInformatico>77</sum1:IdSistemaInformatico>
|
|
||||||
<sum1:Version>1.0.03</sum1:Version>
|
|
||||||
<sum1:NumeroInstalacion>383</sum1:NumeroInstalacion>
|
|
||||||
<sum1:TipoUsoPosibleSoloVerifactu>N</sum1:TipoUsoPosibleSoloVerifactu>
|
|
||||||
<sum1:TipoUsoPosibleMultiOT>S</sum1:TipoUsoPosibleMultiOT>
|
|
||||||
<sum1:IndicadorMultiplesOT>S</sum1:IndicadorMultiplesOT>
|
|
||||||
</sum1:SistemaInformatico>
|
|
||||||
<sum1:FechaHoraHusoGenRegistro>2025-06-24T22:40:00+01:00</sum1:FechaHoraHusoGenRegistro>
|
|
||||||
<sum1:TipoHuella>01</sum1:TipoHuella>
|
|
||||||
<sum1:Huella>PLACEHOLDER_HUELLA</sum1:Huella>
|
|
||||||
</sum1:RegistroAlta>
|
|
||||||
</sum:RegistroFactura>
|
|
||||||
</sum:RegFactuSistemaFacturacion>
|
|
||||||
</soapenv:Body>
|
|
||||||
</soapenv:Envelope>
|
|
||||||
XML;
|
|
||||||
|
|
||||||
// Calculate the correct hash for the XML content
|
|
||||||
$xmlForHash = $this->getXmlForHashCalculation($soapXml);
|
|
||||||
$correctHash = strtoupper(hash('sha256', $xmlForHash));
|
|
||||||
|
|
||||||
// Replace the placeholder with the correct hash
|
|
||||||
$soapXml = str_replace('PLACEHOLDER_HUELLA', $correctHash, $soapXml);
|
|
||||||
|
|
||||||
\Log::info('Official example - Calculated hash: ' . $correctHash);
|
|
||||||
|
|
||||||
// Try the standard Verifactu endpoint
|
|
||||||
$endpoint = 'https://prewww1.aeat.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/VerifactuSOAP';
|
|
||||||
$certPath = storage_path('aeat-cert2.pem');
|
|
||||||
$keyPath = storage_path('aeat-key2.pem');
|
|
||||||
|
|
||||||
// Sign the XML
|
|
||||||
$signingService = new \App\Services\EDocument\Standards\Verifactu\Signing\SigningService($soapXml, file_get_contents($keyPath), file_get_contents($certPath));
|
|
||||||
$soapXml = $signingService->sign();
|
|
||||||
|
|
||||||
// Send the request
|
|
||||||
$response = Http::withHeaders([
|
|
||||||
'Content-Type' => 'text/xml; charset=utf-8',
|
|
||||||
'SOAPAction' => '',
|
|
||||||
])
|
|
||||||
->withOptions([
|
|
||||||
'cert' => $certPath,
|
|
||||||
'ssl_key' => $keyPath,
|
|
||||||
'verify' => false,
|
|
||||||
'timeout' => 30,
|
|
||||||
])
|
|
||||||
->post($endpoint, $soapXml);
|
|
||||||
|
|
||||||
\Log::info('Official example request:');
|
|
||||||
\Log::info($soapXml);
|
|
||||||
\Log::info('Official example response:');
|
|
||||||
\Log::info('Response Status: ' . $response->status());
|
|
||||||
\Log::info('Response Headers: ' . json_encode($response->headers()));
|
|
||||||
\Log::info('Response Body: ' . $response->body());
|
|
||||||
|
|
||||||
// Check if the response contains an error
|
|
||||||
$responseBody = $response->body();
|
|
||||||
if (strpos($responseBody, 'Error interno en el servidor') !== false) {
|
|
||||||
\Log::error('SOAP response contains server error: ' . $responseBody);
|
|
||||||
$this->fail('SOAP response contains server error: ' . $responseBody);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the response contains a success message
|
|
||||||
if (strpos($responseBody, 'RegFactuSistemaFacturacionResponse') !== false) {
|
|
||||||
\Log::info('SOAP response contains success message');
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->assertTrue($response->successful());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_send_new_invoice_without_chaining()
|
|
||||||
{
|
|
||||||
// Create a completely new invoice without any chaining
|
|
||||||
$soapXml = <<<XML
|
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
|
|
||||||
xmlns:sum="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroLR.xsd"
|
|
||||||
xmlns:sum1="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd">
|
|
||||||
<soapenv:Header/>
|
|
||||||
<soapenv:Body>
|
|
||||||
<sum:RegFactuSistemaFacturacion>
|
|
||||||
<sum:Cabecera>
|
|
||||||
<sum1:ObligadoEmision>
|
|
||||||
<sum1:IDEmisorFactura>
|
|
||||||
<sum1:NIF>99999910G</sum1:NIF>
|
|
||||||
<sum1:ApellidosNombreRazonSocial>Empresa Ejemplo SL</sum1:ApellidosNombreRazonSocial>
|
|
||||||
</sum1:IDEmisorFactura>
|
|
||||||
</sum1:ObligadoEmision>
|
|
||||||
<sum1:SistemaInformatico>
|
|
||||||
<sum1:NIFDesarrollador>99999910G</sum1:NIFDesarrollador>
|
|
||||||
<sum1:Nombre>Mi Sistema</sum1:Nombre>
|
|
||||||
<sum1:Version>1.0</sum1:Version>
|
|
||||||
</sum1:SistemaInformatico>
|
|
||||||
<sum1:PeriodoImpositivo>
|
|
||||||
<sum1:Ejercicio>2025</sum1:Ejercicio>
|
|
||||||
<sum1:Periodo>06</sum1:Periodo>
|
|
||||||
</sum1:PeriodoImpositivo>
|
|
||||||
</sum:Cabecera>
|
|
||||||
<sum:Factura>
|
|
||||||
<sum1:IDFactura>
|
|
||||||
<sum1:IDEmisorFactura>
|
|
||||||
<sum1:NIF>99999910G</sum1:NIF>
|
|
||||||
<sum1:ApellidosNombreRazonSocial>Empresa Ejemplo SL</sum1:ApellidosNombreRazonSocial>
|
|
||||||
</sum1:IDEmisorFactura>
|
|
||||||
<sum1:NumSerieFacturaEmisor>FAC001</sum1:NumSerieFacturaEmisor>
|
|
||||||
<sum1:FechaExpedicionFacturaEmisor>25-06-2025</sum1:FechaExpedicionFacturaEmisor>
|
|
||||||
</sum1:IDFactura>
|
|
||||||
<sum1:FacturaExpedida>
|
|
||||||
<sum1:TipoFactura>F1</sum1:TipoFactura>
|
|
||||||
<sum1:FechaOperacion>25-06-2025</sum1:FechaOperacion>
|
|
||||||
<sum1:Cliente>
|
|
||||||
<sum1:IDOtro>
|
|
||||||
<sum1:CodigoPais>ES</sum1:CodigoPais>
|
|
||||||
<sum1:IDType>02</sum1:IDType>
|
|
||||||
<sum1:ID>12345678A</sum1:ID>
|
|
||||||
</sum1:IDOtro>
|
|
||||||
<sum1:ApellidosNombreRazonSocial>Cliente Ejemplo</sum1:ApellidosNombreRazonSocial>
|
|
||||||
<sum1:NIFRepresentante>99999910G</sum1:NIFRepresentante>
|
|
||||||
</sum1:Cliente>
|
|
||||||
<sum1:DetalleFactura>
|
|
||||||
<sum1:DescripcionOperacion>Venta de productos</sum1:DescripcionOperacion>
|
|
||||||
<sum1:DetalleNoSujeta>
|
|
||||||
<sum1:Causa>RL</sum1:Causa>
|
|
||||||
<sum1:ImportePorArticulos7_14_Otros>100.00</sum1:ImportePorArticulos7_14_Otros>
|
|
||||||
</sum1:DetalleNoSujeta>
|
|
||||||
<sum1:ImporteTotal>100.00</sum1:ImporteTotal>
|
|
||||||
<sum1:BaseImponibleACoste>100.00</sum1:BaseImponibleACoste>
|
|
||||||
</sum1:DetalleFactura>
|
|
||||||
<sum1:Contraparte>
|
|
||||||
<sum1:IDOtro>
|
|
||||||
<sum1:CodigoPais>ES</sum1:CodigoPais>
|
|
||||||
<sum1:IDType>02</sum1:IDType>
|
|
||||||
<sum1:ID>12345678A</sum1:ID>
|
|
||||||
</sum1:IDOtro>
|
|
||||||
<sum1:ApellidosNombreRazonSocial>Cliente Ejemplo</sum1:ApellidosNombreRazonSocial>
|
|
||||||
</sum1:Contraparte>
|
|
||||||
</sum1:FacturaExpedida>
|
|
||||||
</sum:Factura>
|
|
||||||
</sum:RegFactuSistemaFacturacion>
|
|
||||||
</soapenv:Body>
|
|
||||||
</soapenv:Envelope>
|
|
||||||
XML;
|
|
||||||
|
|
||||||
// Calculate the correct hash for this XML
|
|
||||||
$xmlForHash = str_replace('PLACEHOLDER_HUELLA', '', $soapXml);
|
|
||||||
$xmlForHash = preg_replace('/<sum1:HuellaDigital>.*?<\/sum1:HuellaDigital>/s', '', $xmlForHash);
|
|
||||||
$correctHash = strtoupper(hash('sha256', $xmlForHash));
|
|
||||||
|
|
||||||
// Replace the placeholder with the correct hash
|
|
||||||
$soapXml = str_replace('PLACEHOLDER_HUELLA', $correctHash, $soapXml);
|
|
||||||
|
|
||||||
\Log::info('New invoice without chaining - Calculated hash: ' . $correctHash);
|
|
||||||
|
|
||||||
// Sign the XML before sending
|
|
||||||
$certPath = storage_path('aeat-cert2.pem');
|
|
||||||
$keyPath = storage_path('aeat-key2.pem');
|
|
||||||
$signingService = new \App\Services\EDocument\Standards\Verifactu\Signing\SigningService($soapXml, file_get_contents($keyPath), file_get_contents($certPath));
|
|
||||||
$soapXml = $signingService->sign();
|
|
||||||
|
|
||||||
// Send the request
|
|
||||||
$response = Http::withHeaders([
|
|
||||||
'Content-Type' => 'text/xml; charset=utf-8',
|
|
||||||
'SOAPAction' => '',
|
|
||||||
])
|
|
||||||
->withOptions([
|
|
||||||
'cert' => storage_path('aeat-cert2.pem'),
|
|
||||||
'ssl_key' => storage_path('aeat-key2.pem'),
|
|
||||||
'verify' => false,
|
|
||||||
'timeout' => 30,
|
|
||||||
])
|
|
||||||
->post('https://prewww1.aeat.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/VerifactuSOAP', $soapXml);
|
|
||||||
|
|
||||||
\Log::info('New invoice without chaining response:');
|
|
||||||
\Log::info('Response Status: ' . $response->status());
|
|
||||||
\Log::info('Response Headers: ' . json_encode($response->headers()));
|
|
||||||
\Log::info('Response Body: ' . $response->body());
|
|
||||||
|
|
||||||
if (!$response->successful()) {
|
|
||||||
\Log::error('Request failed with status: ' . $response->status());
|
|
||||||
\Log::error('Response body: ' . $response->body());
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->assertTrue($response->successful());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_send_official_structure_with_real_data()
|
|
||||||
{
|
|
||||||
// Use the exact structure from the official example but with real data
|
|
||||||
$soapXml = <<<XML
|
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
|
|
||||||
xmlns:sum="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroLR.xsd"
|
|
||||||
xmlns:sum1="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd">
|
|
||||||
<soapenv:Header/>
|
|
||||||
<soapenv:Body>
|
|
||||||
<sum:RegFactuSistemaFacturacion>
|
|
||||||
<sum:Cabecera>
|
|
||||||
<sum1:ObligadoEmision>
|
|
||||||
<sum1:NombreRazon>Empresa Test SL</sum1:NombreRazon>
|
|
||||||
<sum1:NIF>99999910G</sum1:NIF>
|
|
||||||
</sum1:ObligadoEmision>
|
|
||||||
</sum:Cabecera>
|
|
||||||
<sum:RegistroFactura>
|
|
||||||
<sum1:RegistroAlta>
|
|
||||||
<sum1:IDVersion>1.0</sum1:IDVersion>
|
|
||||||
<sum1:IDFactura>
|
|
||||||
<sum1:IDEmisorFactura>99999910G</sum1:IDEmisorFactura>
|
|
||||||
<sum1:NumSerieFactura>TEST-001</sum1:NumSerieFactura>
|
|
||||||
<sum1:FechaExpedicionFactura>24-06-2025</sum1:FechaExpedicionFactura>
|
|
||||||
</sum1:IDFactura>
|
|
||||||
<sum1:NombreRazonEmisor>Empresa Test SL</sum1:NombreRazonEmisor>
|
|
||||||
<sum1:TipoFactura>F1</sum1:TipoFactura>
|
|
||||||
<sum1:DescripcionOperacion>Venta de productos de prueba</sum1:DescripcionOperacion>
|
|
||||||
<sum1:Destinatarios>
|
|
||||||
<sum1:IDDestinatario>
|
|
||||||
<sum1:NombreRazon>Cliente Test SL</sum1:NombreRazon>
|
|
||||||
<sum1:NIF>B12345678</sum1:NIF>
|
|
||||||
</sum1:IDDestinatario>
|
|
||||||
</sum1:Destinatarios>
|
|
||||||
<sum1:DetalleFactura>
|
|
||||||
<sum1:DescripcionOperacion>Venta de productos de prueba</sum1:DescripcionOperacion>
|
|
||||||
<sum1:DetalleNoSujeta>
|
|
||||||
<sum1:Causa>RL</sum1:Causa>
|
|
||||||
<sum1:ImportePorArticulos7_14_Otros>100.00</sum1:ImportePorArticulos7_14_Otros>
|
|
||||||
</sum1:DetalleNoSujeta>
|
|
||||||
<sum1:ImporteTotal>100.00</sum1:ImporteTotal>
|
|
||||||
<sum1:BaseImponibleACoste>100.00</sum1:BaseImponibleACoste>
|
|
||||||
</sum1:DetalleFactura>
|
|
||||||
<sum1:Contraparte>
|
|
||||||
<sum1:IDOtro>
|
|
||||||
<sum1:CodigoPais>ES</sum1:CodigoPais>
|
|
||||||
<sum1:IDType>02</sum1:IDType>
|
|
||||||
<sum1:ID>B12345678</sum1:ID>
|
|
||||||
</sum1:IDOtro>
|
|
||||||
<sum1:ApellidosNombreRazonSocial>Cliente Test SL</sum1:ApellidosNombreRazonSocial>
|
|
||||||
</sum1:Contraparte>
|
|
||||||
<sum1:Encadenamiento>
|
|
||||||
<sum1:PrimerRegistro>S</sum1:PrimerRegistro>
|
|
||||||
</sum1:Encadenamiento>
|
|
||||||
<sum1:FechaHoraHusoGenRegistro>2025-06-24T22:34:00+01:00</sum1:FechaHoraHusoGenRegistro>
|
|
||||||
<sum1:TipoHuella>01</sum1:TipoHuella>
|
|
||||||
<sum1:Huella>PLACEHOLDER_HUELLA</sum1:Huella>
|
|
||||||
</sum1:RegistroAlta>
|
|
||||||
</sum:RegistroFactura>
|
|
||||||
</sum:RegFactuSistemaFacturacion>
|
|
||||||
</soapenv:Body>
|
|
||||||
</soapenv:Envelope>
|
|
||||||
XML;
|
|
||||||
|
|
||||||
// Calculate the correct hash for this XML
|
|
||||||
$xmlForHash = $this->getXmlForHashCalculation($soapXml);
|
|
||||||
$correctHash = strtoupper(hash('sha256', $xmlForHash));
|
|
||||||
|
|
||||||
// Replace the placeholder with the correct hash
|
|
||||||
$soapXml = str_replace('PLACEHOLDER_HUELLA', $correctHash, $soapXml);
|
|
||||||
|
|
||||||
\Log::info('Official structure with real data - Calculated hash: ' . $correctHash);
|
|
||||||
|
|
||||||
// Sign the XML before sending
|
|
||||||
$certPath = storage_path('aeat-cert2.pem');
|
|
||||||
$keyPath = storage_path('aeat-key2.pem');
|
|
||||||
$signingService = new \App\Services\EDocument\Standards\Verifactu\Signing\SigningService($soapXml, file_get_contents($keyPath), file_get_contents($certPath));
|
|
||||||
$soapXml = $signingService->sign();
|
|
||||||
|
|
||||||
// Send the request
|
|
||||||
$response = Http::withHeaders([
|
|
||||||
'Content-Type' => 'text/xml; charset=utf-8',
|
|
||||||
'SOAPAction' => '',
|
|
||||||
])
|
|
||||||
->withOptions([
|
|
||||||
'cert' => storage_path('aeat-cert2.pem'),
|
|
||||||
'ssl_key' => storage_path('aeat-key2.pem'),
|
|
||||||
'verify' => false,
|
|
||||||
'timeout' => 30,
|
|
||||||
])
|
|
||||||
->post('https://prewww1.aeat.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/VerifactuSOAP', $soapXml);
|
|
||||||
|
|
||||||
\Log::info('Official structure with real data response:');
|
|
||||||
\Log::info('Response Status: ' . $response->status());
|
|
||||||
\Log::info('Response Headers: ' . json_encode($response->headers()));
|
|
||||||
\Log::info('Response Body: ' . $response->body());
|
|
||||||
|
|
||||||
if (!$response->successful()) {
|
|
||||||
\Log::error('Request failed with status: ' . $response->status());
|
|
||||||
\Log::error('Response body: ' . $response->body());
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->assertTrue($response->successful());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_send_minimal_invoice_to_aeat()
|
|
||||||
{
|
|
||||||
// Create the most minimal invoice possible with only required fields
|
|
||||||
$soapXml = <<<XML
|
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
|
|
||||||
xmlns:sum="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroLR.xsd"
|
|
||||||
xmlns:sum1="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd">
|
|
||||||
<soapenv:Header/>
|
|
||||||
<soapenv:Body>
|
|
||||||
<sum:RegFactuSistemaFacturacion>
|
|
||||||
<sum:Cabecera>
|
|
||||||
<sum1:ObligadoEmision>
|
|
||||||
<sum1:NombreRazon>Test Company</sum1:NombreRazon>
|
|
||||||
<sum1:NIF>99999910G</sum1:NIF>
|
|
||||||
</sum1:ObligadoEmision>
|
|
||||||
</sum:Cabecera>
|
|
||||||
<sum:RegistroFactura>
|
|
||||||
<sum1:RegistroAlta>
|
|
||||||
<sum1:IDVersion>1.0</sum1:IDVersion>
|
|
||||||
<sum1:IDFactura>
|
|
||||||
<sum1:IDEmisorFactura>99999910G</sum1:IDEmisorFactura>
|
|
||||||
<sum1:NumSerieFactura>001</sum1:NumSerieFactura>
|
|
||||||
<sum1:FechaExpedicionFactura>01-01-2024</sum1:FechaExpedicionFactura>
|
|
||||||
</sum1:IDFactura>
|
|
||||||
<sum1:NombreRazonEmisor>Test Company</sum1:NombreRazonEmisor>
|
|
||||||
<sum1:TipoFactura>F1</sum1:TipoFactura>
|
|
||||||
<sum1:DescripcionOperacion>Test</sum1:DescripcionOperacion>
|
|
||||||
<sum1:DetalleFactura>
|
|
||||||
<sum1:DescripcionOperacion>Test</sum1:DescripcionOperacion>
|
|
||||||
<sum1:DetalleNoSujeta>
|
|
||||||
<sum1:Causa>RL</sum1:Causa>
|
|
||||||
<sum1:ImportePorArticulos7_14_Otros>10.00</sum1:ImportePorArticulos7_14_Otros>
|
|
||||||
</sum1:DetalleNoSujeta>
|
|
||||||
<sum1:ImporteTotal>10.00</sum1:ImporteTotal>
|
|
||||||
</sum1:DetalleFactura>
|
|
||||||
<sum1:Encadenamiento>
|
|
||||||
<sum1:PrimerRegistro>S</sum1:PrimerRegistro>
|
|
||||||
</sum1:Encadenamiento>
|
|
||||||
<sum1:FechaHoraHusoGenRegistro>2024-01-01T12:00:00+01:00</sum1:FechaHoraHusoGenRegistro>
|
|
||||||
<sum1:TipoHuella>01</sum1:TipoHuella>
|
|
||||||
<sum1:Huella>PLACEHOLDER_HUELLA</sum1:Huella>
|
|
||||||
</sum1:RegistroAlta>
|
|
||||||
</sum:RegistroFactura>
|
|
||||||
</sum:RegFactuSistemaFacturacion>
|
|
||||||
</soapenv:Body>
|
|
||||||
</soapenv:Envelope>
|
|
||||||
XML;
|
|
||||||
|
|
||||||
// Calculate the correct hash for this XML
|
|
||||||
$xmlForHash = $this->getXmlForHashCalculation($soapXml);
|
|
||||||
$correctHash = strtoupper(hash('sha256', $xmlForHash));
|
|
||||||
|
|
||||||
// Replace the placeholder with the correct hash
|
|
||||||
$soapXml = str_replace('PLACEHOLDER_HUELLA', $correctHash, $soapXml);
|
|
||||||
|
|
||||||
\Log::info('Minimal invoice - Calculated hash: ' . $correctHash);
|
|
||||||
|
|
||||||
// Sign the XML before sending
|
|
||||||
$certPath = storage_path('aeat-cert2.pem');
|
|
||||||
$keyPath = storage_path('aeat-key2.pem');
|
|
||||||
$signingService = new \App\Services\EDocument\Standards\Verifactu\Signing\SigningService($soapXml, file_get_contents($keyPath), file_get_contents($certPath));
|
|
||||||
$soapXml = $signingService->sign();
|
|
||||||
|
|
||||||
// Send the request
|
|
||||||
$response = Http::withHeaders([
|
|
||||||
'Content-Type' => 'text/xml; charset=utf-8',
|
|
||||||
'SOAPAction' => '',
|
|
||||||
])
|
|
||||||
->withOptions([
|
|
||||||
'cert' => storage_path('aeat-cert2.pem'),
|
|
||||||
'ssl_key' => storage_path('aeat-key2.pem'),
|
|
||||||
'verify' => false,
|
|
||||||
'timeout' => 30,
|
|
||||||
])
|
|
||||||
->post('https://prewww1.aeat.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/VerifactuSOAP', $soapXml);
|
|
||||||
|
|
||||||
\Log::info('Minimal invoice response:');
|
|
||||||
\Log::info('Response Status: ' . $response->status());
|
|
||||||
\Log::info('Response Headers: ' . json_encode($response->headers()));
|
|
||||||
\Log::info('Response Body: ' . $response->body());
|
|
||||||
|
|
||||||
// Check if we got a different response
|
|
||||||
$responseBody = $response->body();
|
|
||||||
if (strpos($responseBody, 'Error interno en el servidor') === false) {
|
|
||||||
\Log::info('SUCCESS: No server error found in response!');
|
|
||||||
} else {
|
|
||||||
\Log::error('Still getting server error with minimal invoice');
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->assertTrue($response->successful());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_send_ultra_minimal_invoice_to_aeat()
|
|
||||||
{
|
|
||||||
// Ultra minimal invoice with current date and no optional fields
|
|
||||||
$currentDate = date('d-m-Y');
|
|
||||||
$currentDateTime = date('Y-m-d\TH:i:sP');
|
|
||||||
|
|
||||||
$soapXml = <<<XML
|
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
|
|
||||||
xmlns:sum="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroLR.xsd"
|
|
||||||
xmlns:sum1="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd">
|
|
||||||
<soapenv:Header/>
|
|
||||||
<soapenv:Body>
|
|
||||||
<sum:RegFactuSistemaFacturacion>
|
|
||||||
<sum:Cabecera>
|
|
||||||
<sum1:ObligadoEmision>
|
|
||||||
<sum1:NombreRazon>Test</sum1:NombreRazon>
|
|
||||||
<sum1:NIF>99999910G</sum1:NIF>
|
|
||||||
</sum1:ObligadoEmision>
|
|
||||||
</sum:Cabecera>
|
|
||||||
<sum:RegistroFactura>
|
|
||||||
<sum1:RegistroAlta>
|
|
||||||
<sum1:IDVersion>1.0</sum1:IDVersion>
|
|
||||||
<sum1:IDFactura>
|
|
||||||
<sum1:IDEmisorFactura>99999910G</sum1:IDEmisorFactura>
|
|
||||||
<sum1:NumSerieFactura>1</sum1:NumSerieFactura>
|
|
||||||
<sum1:FechaExpedicionFactura>$currentDate</sum1:FechaExpedicionFactura>
|
|
||||||
</sum1:IDFactura>
|
|
||||||
<sum1:NombreRazonEmisor>Test</sum1:NombreRazonEmisor>
|
|
||||||
<sum1:TipoFactura>F1</sum1:TipoFactura>
|
|
||||||
<sum1:DescripcionOperacion>Test</sum1:DescripcionOperacion>
|
|
||||||
<sum1:DetalleFactura>
|
|
||||||
<sum1:DescripcionOperacion>Test</sum1:DescripcionOperacion>
|
|
||||||
<sum1:DetalleNoSujeta>
|
|
||||||
<sum1:Causa>RL</sum1:Causa>
|
|
||||||
<sum1:ImportePorArticulos7_14_Otros>1.00</sum1:ImportePorArticulos7_14_Otros>
|
|
||||||
</sum1:DetalleNoSujeta>
|
|
||||||
<sum1:ImporteTotal>1.00</sum1:ImporteTotal>
|
|
||||||
</sum1:DetalleFactura>
|
|
||||||
<sum1:Encadenamiento>
|
|
||||||
<sum1:PrimerRegistro>S</sum1:PrimerRegistro>
|
|
||||||
</sum1:Encadenamiento>
|
|
||||||
<sum1:FechaHoraHusoGenRegistro>$currentDateTime</sum1:FechaHoraHusoGenRegistro>
|
|
||||||
<sum1:TipoHuella>01</sum1:TipoHuella>
|
|
||||||
<sum1:Huella>PLACEHOLDER_HUELLA</sum1:Huella>
|
|
||||||
</sum1:RegistroAlta>
|
|
||||||
</sum:RegistroFactura>
|
|
||||||
</sum:RegFactuSistemaFacturacion>
|
|
||||||
</soapenv:Body>
|
|
||||||
</soapenv:Envelope>
|
|
||||||
XML;
|
|
||||||
|
|
||||||
// Calculate the correct hash for this XML
|
|
||||||
$xmlForHash = $this->getXmlForHashCalculation($soapXml);
|
|
||||||
$correctHash = strtoupper(hash('sha256', $xmlForHash));
|
|
||||||
|
|
||||||
// Replace the placeholder with the correct hash
|
|
||||||
$soapXml = str_replace('PLACEHOLDER_HUELLA', $correctHash, $soapXml);
|
|
||||||
|
|
||||||
\Log::info('Ultra minimal invoice - Calculated hash: ' . $correctHash);
|
|
||||||
\Log::info('Using date: ' . $currentDate . ' and datetime: ' . $currentDateTime);
|
|
||||||
|
|
||||||
// Sign the XML before sending
|
|
||||||
$certPath = storage_path('aeat-cert2.pem');
|
|
||||||
$keyPath = storage_path('aeat-key2.pem');
|
|
||||||
$signingService = new \App\Services\EDocument\Standards\Verifactu\Signing\SigningService($soapXml, file_get_contents($keyPath), file_get_contents($certPath));
|
|
||||||
$soapXml = $signingService->sign();
|
|
||||||
|
|
||||||
// Send the request
|
|
||||||
$response = Http::withHeaders([
|
|
||||||
'Content-Type' => 'text/xml; charset=utf-8',
|
|
||||||
'SOAPAction' => '',
|
|
||||||
])
|
|
||||||
->withOptions([
|
|
||||||
'cert' => storage_path('aeat-cert2.pem'),
|
|
||||||
'ssl_key' => storage_path('aeat-key2.pem'),
|
|
||||||
'verify' => false,
|
|
||||||
'timeout' => 30,
|
|
||||||
])
|
|
||||||
->post('https://prewww1.aeat.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/VerifactuSOAP', $soapXml);
|
|
||||||
|
|
||||||
\Log::info('Ultra minimal invoice response:');
|
|
||||||
\Log::info('Response Status: ' . $response->status());
|
|
||||||
\Log::info('Response Headers: ' . json_encode($response->headers()));
|
|
||||||
\Log::info('Response Body: ' . $response->body());
|
|
||||||
|
|
||||||
// Check if we got a different response
|
|
||||||
$responseBody = $response->body();
|
|
||||||
if (strpos($responseBody, 'Error interno en el servidor') === false) {
|
|
||||||
\Log::info('SUCCESS: No server error found in ultra minimal response!');
|
|
||||||
} else {
|
|
||||||
\Log::error('Still getting server error with ultra minimal invoice');
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->assertTrue($response->successful());
|
|
||||||
}
|
|
||||||
|
|
||||||
public function test_send_invoice_without_encadenamiento()
|
|
||||||
{
|
|
||||||
// Try without Encadenamiento section
|
|
||||||
$currentDate = date('d-m-Y');
|
|
||||||
$currentDateTime = date('Y-m-d\TH:i:sP');
|
|
||||||
|
|
||||||
$soapXml = <<<XML
|
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
|
|
||||||
xmlns:sum="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroLR.xsd"
|
|
||||||
xmlns:sum1="https://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd">
|
|
||||||
<soapenv:Header/>
|
|
||||||
<soapenv:Body>
|
|
||||||
<sum:RegFactuSistemaFacturacion>
|
|
||||||
<sum:Cabecera>
|
|
||||||
<sum1:ObligadoEmision>
|
|
||||||
<sum1:NombreRazon>Test</sum1:NombreRazon>
|
|
||||||
<sum1:NIF>99999910G</sum1:NIF>
|
|
||||||
</sum1:ObligadoEmision>
|
|
||||||
</sum:Cabecera>
|
|
||||||
<sum:RegistroFactura>
|
|
||||||
<sum1:RegistroAlta>
|
|
||||||
<sum1:IDVersion>1.0</sum1:IDVersion>
|
|
||||||
<sum1:IDFactura>
|
|
||||||
<sum1:IDEmisorFactura>99999910G</sum1:IDEmisorFactura>
|
|
||||||
<sum1:NumSerieFactura>1</sum1:NumSerieFactura>
|
|
||||||
<sum1:FechaExpedicionFactura>$currentDate</sum1:FechaExpedicionFactura>
|
|
||||||
</sum1:IDFactura>
|
|
||||||
<sum1:NombreRazonEmisor>Test</sum1:NombreRazonEmisor>
|
|
||||||
<sum1:TipoFactura>F1</sum1:TipoFactura>
|
|
||||||
<sum1:DescripcionOperacion>Test</sum1:DescripcionOperacion>
|
|
||||||
<sum1:DetalleFactura>
|
|
||||||
<sum1:DescripcionOperacion>Test</sum1:DescripcionOperacion>
|
|
||||||
<sum1:DetalleNoSujeta>
|
|
||||||
<sum1:Causa>RL</sum1:Causa>
|
|
||||||
<sum1:ImportePorArticulos7_14_Otros>1.00</sum1:ImportePorArticulos7_14_Otros>
|
|
||||||
</sum1:DetalleNoSujeta>
|
|
||||||
<sum1:ImporteTotal>1.00</sum1:ImporteTotal>
|
|
||||||
</sum1:DetalleFactura>
|
|
||||||
<sum1:FechaHoraHusoGenRegistro>$currentDateTime</sum1:FechaHoraHusoGenRegistro>
|
|
||||||
<sum1:TipoHuella>01</sum1:TipoHuella>
|
|
||||||
<sum1:Huella>PLACEHOLDER_HUELLA</sum1:Huella>
|
|
||||||
</sum1:RegistroAlta>
|
|
||||||
</sum:RegistroFactura>
|
|
||||||
</sum:RegFactuSistemaFacturacion>
|
|
||||||
</soapenv:Body>
|
|
||||||
</soapenv:Envelope>
|
|
||||||
XML;
|
|
||||||
|
|
||||||
// Calculate the correct hash for this XML
|
|
||||||
$xmlForHash = $this->getXmlForHashCalculation($soapXml);
|
|
||||||
$correctHash = strtoupper(hash('sha256', $xmlForHash));
|
|
||||||
|
|
||||||
// Replace the placeholder with the correct hash
|
|
||||||
$soapXml = str_replace('PLACEHOLDER_HUELLA', $correctHash, $soapXml);
|
|
||||||
|
|
||||||
\Log::info('Invoice without Encadenamiento - Calculated hash: ' . $correctHash);
|
|
||||||
|
|
||||||
// Sign the XML before sending
|
|
||||||
$certPath = storage_path('aeat-cert2.pem');
|
|
||||||
$keyPath = storage_path('aeat-key2.pem');
|
|
||||||
$signingService = new \App\Services\EDocument\Standards\Verifactu\Signing\SigningService($soapXml, file_get_contents($keyPath), file_get_contents($certPath));
|
|
||||||
$soapXml = $signingService->sign();
|
|
||||||
|
|
||||||
// Send the request
|
|
||||||
$response = Http::withHeaders([
|
|
||||||
'Content-Type' => 'text/xml; charset=utf-8',
|
|
||||||
'SOAPAction' => '',
|
|
||||||
])
|
|
||||||
->withOptions([
|
|
||||||
'cert' => storage_path('aeat-cert2.pem'),
|
|
||||||
'ssl_key' => storage_path('aeat-key2.pem'),
|
|
||||||
'verify' => false,
|
|
||||||
'timeout' => 30,
|
|
||||||
])
|
|
||||||
->post('https://prewww1.aeat.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/VerifactuSOAP', $soapXml);
|
|
||||||
|
|
||||||
\Log::info('Invoice without Encadenamiento response:');
|
|
||||||
\Log::info('Response Status: ' . $response->status());
|
|
||||||
\Log::info('Response Headers: ' . json_encode($response->headers()));
|
|
||||||
\Log::info('Response Body: ' . $response->body());
|
|
||||||
|
|
||||||
// Check if we got a different response
|
|
||||||
$responseBody = $response->body();
|
|
||||||
if (strpos($responseBody, 'Error interno en el servidor') === false) {
|
|
||||||
\Log::info('SUCCESS: No server error found without Encadenamiento!');
|
|
||||||
} else {
|
|
||||||
\Log::error('Still getting server error without Encadenamiento');
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->assertTrue($response->successful());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue