diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index bdda11c..8e27dee 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -12,18 +12,19 @@ jobs:
runs-on: ubuntu-latest
continue-on-error: ${{ matrix.experimental || false }}
strategy:
+ fail-fast: false
matrix:
- php-version: ['5.6', '7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.1']
+ php-version: ['5.6', '7.0', '7.1', '7.2', '7.3', '7.4', '8.0', '8.1', '8.2']
include:
- - php-version: '8.2'
+ - php-version: '8.3'
test-ws: true
send-coverage: true
- - php-version: '8.3'
+ - php-version: '8.4'
experimental: true
steps:
# Download code from repository
- name: Checkout code
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
# Setup PHP and Composer
- name: Setup PHP
diff --git a/README.md b/README.md
index d6bb6ca..4778656 100644
--- a/README.md
+++ b/README.md
@@ -65,5 +65,13 @@ file_put_contents(__DIR__ . "/factura.xsig", $signedXml);
- Sellado de tiempo según el [RFC3161](https://www.ietf.org/rfc/rfc3161.txt)
- Envío automatizado de facturas a **FACe y FACeB2B** 🔥
+## Usan Facturae-PHP
+Estas son algunas de las organizaciones y soluciones software que usan Facturae-PHP o mantienen un fork interno basado en el código de la librería:
+
+
+
+
+
+
## Licencia
Facturae-PHP se encuentra bajo [licencia MIT](LICENSE). Eso implica que puedes utilizar este paquete en cualquier proyecto (incluso con fines comerciales), siempre y cuando hagas referencia al uso y autoría de la misma.
diff --git a/doc/envio-y-recepcion/face.md b/doc/envio-y-recepcion/face.md
index d4876c1..20add0e 100644
--- a/doc/envio-y-recepcion/face.md
+++ b/doc/envio-y-recepcion/face.md
@@ -50,6 +50,17 @@ $endpointUrl = "https://w390w.gipuzkoa.net/WAS/HACI/HFAServiciosProveedoresWEB/s
$face = new CustomFaceClient($endpointUrl, "certificado.pfx", null, "passphrase");
```
+Si al intentar conectarte al punto de recepción obtienes un error similar a "A bad canonicalization algorithm was specified", es posible que el servidor de destino no soporte canonicalización de XML exclusiva ([EXC-C14N](https://www.w3.org/TR/xml-exc-c14n/)).
+Para usar C14N en vez de EXC-C14N, utiliza este método del cliente:
+```php
+$face->setExclusiveC14n(false);
+```
+
+Otro error típico es "Document element namespace mismatch expected". En ese caso, el punto de entrada necesita usar un namespace personalizado, que puedes especificar usando este método:
+```php
+$face->setWebNamespace('https://webservice.efact.es/sspp'); // Ejemplo para e-FACT
+```
+
---
## Listado de métodos
diff --git a/src/Common/FacturaeSigner.php b/src/Common/FacturaeSigner.php
index 9b69cde..b279e31 100644
--- a/src/Common/FacturaeSigner.php
+++ b/src/Common/FacturaeSigner.php
@@ -175,14 +175,18 @@ public function sign($xml) {
$signingTime = ($this->signingTime === null) ? time() : $this->signingTime;
$certData = openssl_x509_parse($this->publicChain[0]);
$certIssuer = [];
- foreach ($certData['issuer'] as $item=>$rawValues) {
- if (!array_key_exists($item, self::ALLOWED_OID_TYPES)) {
- continue;
- }
- $item = self::ALLOWED_OID_TYPES[$item];
+ foreach ($certData['issuer'] as $rawType=>$rawValues) {
$values = is_array($rawValues) ? $rawValues : [$rawValues];
foreach ($values as $value) {
- $certIssuer[] = "$item=$value";
+ if ($rawType === "UNDEF" && preg_match('/^VAT[A-Z]{2}-/', $value) === 1) {
+ $type = "OID.2.5.4.97"; // Fix for OpenSSL <3.0.0
+ } else {
+ if (!array_key_exists($rawType, self::ALLOWED_OID_TYPES)) {
+ continue; // Skip unknown OID types
+ }
+ $type = self::ALLOWED_OID_TYPES[$rawType];
+ }
+ $certIssuer[] = "$type=$value";
}
}
$certIssuer = implode(', ', array_reverse($certIssuer));
diff --git a/src/Extensions/FacturaeExtension.php b/src/Extensions/FacturaeExtension.php
index 73ab121..29f1c7b 100644
--- a/src/Extensions/FacturaeExtension.php
+++ b/src/Extensions/FacturaeExtension.php
@@ -4,6 +4,7 @@
use josemmo\Facturae\Facturae;
abstract class FacturaeExtension {
+ /** @var Facturae */
private $fac;
/**
diff --git a/src/Extensions/Fb2bExtension.php b/src/Extensions/Fb2bExtension.php
index d4331d6..864485e 100644
--- a/src/Extensions/Fb2bExtension.php
+++ b/src/Extensions/Fb2bExtension.php
@@ -4,9 +4,13 @@
use josemmo\Facturae\FacturaeCentre;
class Fb2bExtension extends FacturaeExtension {
+ /** @var array */
private $publicSectorInfo = array();
+ /** @var FacturaeCentre|null */
private $receiver = null;
+ /** @var FacturaeCentre[] */
private $sellerCentres = array();
+ /** @var FacturaeCentre[] */
private $buyerCentres = array();
/**
diff --git a/src/Face/CustomFaceClient.php b/src/Face/CustomFaceClient.php
index 310e2af..6500c9c 100644
--- a/src/Face/CustomFaceClient.php
+++ b/src/Face/CustomFaceClient.php
@@ -5,6 +5,7 @@
class CustomFaceClient extends SoapClient {
private $endpointUrl;
+ private $webNamespace = "https://webservice.face.gob.es";
use FaceTrait;
@@ -22,6 +23,23 @@ public function __construct($endpointUrl, $publicPath, $privatePath=null, $passp
}
+ /**
+ * Set custom web service namespace
+ * @param string $webNamespace Web service namespace to override the default one
+ */
+ public function setWebNamespace($webNamespace) {
+ $this->webNamespace = $webNamespace;
+ }
+
+
+ /**
+ * @inheritdoc
+ */
+ protected function getWebNamespace() {
+ return $this->webNamespace;
+ }
+
+
/**
* Get endpoint URL
* @return string Endpoint URL
diff --git a/src/Face/SoapClient.php b/src/Face/SoapClient.php
index 368f7e2..f68ca1a 100644
--- a/src/Face/SoapClient.php
+++ b/src/Face/SoapClient.php
@@ -8,6 +8,7 @@
abstract class SoapClient {
const REQUEST_EXPIRATION = 60; // In seconds
+ private $useExcC14n = true;
use KeyPairReaderTrait;
@@ -28,6 +29,15 @@ public function __construct($storeOrCertificate, $privateKey=null, $passphrase='
}
+ /**
+ * Set exclusive canonicalization mode
+ * @param boolean $enabled Whether to use EXC-C14N (`true`) or C14N (`false`)
+ */
+ public function setExclusiveC14n($enabled) {
+ $this->useExcC14n = $enabled;
+ }
+
+
/**
* Get endpoint URL
* @return string Endpoint URL
@@ -91,8 +101,10 @@ protected function request($body) {
'';
// Generate signed info
+ $c14nNamespace = $this->useExcC14n ? "http://www.w3.org/2001/10/xml-exc-c14n#" : "http://www.w3.org/TR/2001/REC-xml-c14n-20010315";
+ $signedInfoNs = $this->useExcC14n ? ['xmlns:ds' => $ns['xmlns:ds']] : $ns;
$signedInfo = '' .
- '' .
+ '' .
'' .
'' .
'' .
@@ -104,7 +116,7 @@ protected function request($body) {
'' . $bodyDigest . '' .
'' .
'';
- $signedInfoPayload = XmlTools::injectNamespaces($signedInfo, $ns);
+ $signedInfoPayload = XmlTools::injectNamespaces($signedInfo, $signedInfoNs);
// Add signature and KeyInfo to header
$reqHeader .= '' .
diff --git a/src/Facturae.php b/src/Facturae.php
index 10ec12d..e3401cb 100644
--- a/src/Facturae.php
+++ b/src/Facturae.php
@@ -10,7 +10,7 @@
* Class for creating electronic invoices that comply with the Spanish FacturaE format.
*/
class Facturae {
- const VERSION = "1.7.9";
+ const VERSION = "1.8.0";
const USER_AGENT = "FacturaePHP/" . self::VERSION;
const SCHEMA_3_2 = "3.2";
diff --git a/src/FacturaeCentre.php b/src/FacturaeCentre.php
index d9e7a67..a2f2ec0 100644
--- a/src/FacturaeCentre.php
+++ b/src/FacturaeCentre.php
@@ -25,18 +25,29 @@ class FacturaeCentre {
const ROLE_B2B_COLLECTION_RECEIVER = "Collection receiver";
const ROLE_B2B_ISSUER = "Issuer";
+ /** @var string|null */
public $code = null;
+ /** @var string|null */
public $role = null;
+ /** @var string|null */
public $name = null;
+ /** @var string|null */
public $firstSurname = null;
+ /** @var string|null */
public $lastSurname = null;
+ /** @var string|null */
public $description = null;
+ /** @var string|null */
public $address = null;
+ /** @var string|null */
public $postCode = null;
+ /** @var string|null */
public $town = null;
+ /** @var string|null */
public $province = null;
+ /** @var string */
public $countryCode = "ESP";
diff --git a/src/FacturaeItem.php b/src/FacturaeItem.php
index 18077c1..5e8b252 100644
--- a/src/FacturaeItem.php
+++ b/src/FacturaeItem.php
@@ -12,32 +12,54 @@ class FacturaeItem {
/** Non-subject operation */
const SPECIAL_TAXABLE_EVENT_NON_SUBJECT = "02";
+ /** @var int|string|null */
private $articleCode = null;
+ /** @var string|null */
private $name = null;
+ /** @var string|null */
private $description = null;
+ /** @var int|float */
private $quantity = 1;
+ /** @var string */
private $unitOfMeasure = Facturae::UNIT_DEFAULT;
+ /** @var int|float|null */
private $unitPrice = null;
+ /** @var int|float|null */
private $unitPriceWithoutTax = null;
private $discounts = array();
private $charges = array();
private $taxesOutputs = array();
private $taxesWithheld = array();
+ /** @var string|null */
private $specialTaxableEventCode = null;
+ /** @var string|null */
private $specialTaxableEventReason = null;
+ /** @var string|null */
private $issuerContractReference = null;
+ /** @var string|null */
private $issuerContractDate = null;
+ /** @var string|null */
private $issuerTransactionReference = null;
+ /** @var string|null */
private $issuerTransactionDate = null;
+ /** @var string|null */
private $receiverContractReference = null;
+ /** @var string|null */
private $receiverContractDate = null;
+ /** @var string|null */
private $receiverTransactionReference = null;
+ /** @var string|null */
private $receiverTransactionDate = null;
+ /** @var string|null */
private $fileReference = null;
+ /** @var string|null */
private $fileDate = null;
+ /** @var string|null */
private $sequenceNumber = null;
+ /** @var string|null */
private $periodStart = null;
+ /** @var string|null */
private $periodEnd = null;
diff --git a/src/FacturaeParty.php b/src/FacturaeParty.php
index 863f12d..2f3fc16 100644
--- a/src/FacturaeParty.php
+++ b/src/FacturaeParty.php
@@ -16,38 +16,87 @@ class FacturaeParty {
'IRL', 'ITA', 'LTU', 'LUX', 'LVA', 'MLT', 'NLD', 'POL', 'PRT', 'ROU', 'SVK', 'SVN', 'SWE'
];
+ /** @var boolean */
public $isLegalEntity = true; // By default is a company and not a person
+ /** @var string|null */
public $taxNumber = null;
+ /** @var string|null */
public $name = null;
- // This block is only used for legal entities
- public $book = null; // "Libro"
- public $registerOfCompaniesLocation = null; // "Registro mercantil"
- public $sheet = null; // "Hoja"
- public $folio = null; // "Folio"
- public $section = null; // "Sección"
- public $volume = null; // "Tomo"
+ /**
+ * Libro (only for legal entities)
+ * @var string|null
+ */
+ public $book = null;
+ /**
+ * Registro mercantil (only for legal entities)
+ * @var string|null
+ */
+ public $registerOfCompaniesLocation = null;
+ /**
+ * Hoja (only for legal entities)
+ * @var string|null
+ */
+ public $sheet = null;
+ /**
+ * Folio (only for legal entities)
+ * @var string|null
+ */
+ public $folio = null;
+ /**
+ * Sección (only for legal entities)
+ * @var string|null
+ */
+ public $section = null;
+ /**
+ * Tomo (only for legal entities)
+ * @var string|null
+ */
+ public $volume = null;
- // This block is only required for individuals
+ /**
+ * First surname (required for individuals)
+ * @var string|null
+ */
public $firstSurname = null;
+ /**
+ * Last surname (required for individuals)
+ * @var string|null
+ */
public $lastSurname = null;
+ /** @var string|null */
public $address = null;
+ /** @var string|null */
public $postCode = null;
+ /** @var string|null */
public $town = null;
+ /** @var string|null */
public $province = null;
+ /** @var string */
public $countryCode = "ESP";
- /** @var boolean|null */
- public $isEuropeanUnionResident = null; // By default is calculated based on the country code
+ /**
+ * NOTE: By default (when `null`) is calculated based on the country code
+ * @var boolean|null
+ */
+ public $isEuropeanUnionResident = null;
+ /** @var string|null */
public $email = null;
+ /** @var string|null */
public $phone = null;
+ /** @var string|null */
public $fax = null;
+ /** @var string|null */
public $website = null;
+ /** @var string|null */
public $contactPeople = null;
+ /** @var string|null */
public $cnoCnae = null;
+ /** @var string|null */
public $ineTownCode = null;
+ /** @var FacturaeCentre[] */
public $centres = array();
diff --git a/src/FacturaeTraits/UtilsTrait.php b/src/FacturaeTraits/UtilsTrait.php
index 02dea57..b94551d 100644
--- a/src/FacturaeTraits/UtilsTrait.php
+++ b/src/FacturaeTraits/UtilsTrait.php
@@ -10,6 +10,7 @@
* @var Facturae $this
*/
trait UtilsTrait {
+ /** @var FacturaeExtension[] */
protected $extensions = array();
/**
diff --git a/tests/AbstractTest.php b/tests/AbstractTest.php
index 570054f..6c6aa65 100644
--- a/tests/AbstractTest.php
+++ b/tests/AbstractTest.php
@@ -10,7 +10,6 @@ abstract class AbstractTest extends TestCase {
const OUTPUT_DIR = __DIR__ . "/output";
const CERTS_DIR = __DIR__ . "/certs";
const FACTURAE_CERT_PASS = "1234";
- const WEBSERVICES_CERT_PASS = "IZProd2021";
const NOTIFICATIONS_EMAIL = "josemmo@pm.me";
const COOKIES_PATH = self::OUTPUT_DIR . "/cookies.txt";
diff --git a/tests/WebservicesTest.php b/tests/WebservicesTest.php
index 8dd1396..0d9dd98 100644
--- a/tests/WebservicesTest.php
+++ b/tests/WebservicesTest.php
@@ -5,6 +5,7 @@
use josemmo\Facturae\FacturaeParty;
use josemmo\Facturae\FacturaeCentre;
use josemmo\Facturae\FacturaeFile;
+use josemmo\Facturae\Face\CustomFaceClient;
use josemmo\Facturae\Face\FaceClient;
use josemmo\Facturae\Face\Faceb2bClient;
@@ -50,7 +51,7 @@ private function getWsBaseInvoice() {
public function testFace() {
$this->checkEnv();
- $face = new FaceClient(self::CERTS_DIR . "/webservices.p12", null, self::WEBSERVICES_CERT_PASS);
+ $face = new FaceClient(self::CERTS_DIR . "/facturae.p12", null, self::FACTURAE_CERT_PASS);
$face->setProduction(false);
// Test misc. methods
@@ -59,6 +60,11 @@ public function testFace() {
$this->assertNotEmpty($face->getUnits('E04921501')->relaciones);
$this->assertNotEmpty($face->getNifs('E04921501')->nifs);
+ // Test C14N (non-exclusive)
+ $face->setExclusiveC14n(false);
+ $this->assertNotEmpty($face->getStatus()->estados);
+ $face->setExclusiveC14n(true);
+
// Generate invoice
$fac = $this->getWsBaseInvoice();
$fac->setBuyer(new FacturaeParty([
@@ -86,7 +92,7 @@ public function testFace() {
])
]
]));
- $fac->sign(self::CERTS_DIR . "/webservices.p12", null, self::WEBSERVICES_CERT_PASS);
+ $fac->sign(self::CERTS_DIR . "/facturae.p12", null, self::FACTURAE_CERT_PASS);
// Send invoice
$invoiceFile = new FacturaeFile();
@@ -107,13 +113,25 @@ public function testFace() {
}
+ public function testCustomFace() {
+ $this->checkEnv();
+
+ $endpointUrl = "https://efact-pre.aoc.cat/bustia/services/EFactWebServiceProxyService";
+ $customFace = new CustomFaceClient($endpointUrl, self::CERTS_DIR . "/facturae.p12", null, self::FACTURAE_CERT_PASS);
+ $customFace->setWebNamespace('https://webservice.efact.es/sspp');
+
+ // Test misc. methods
+ $this->assertNotEmpty($customFace->getStatus()->estados);
+ }
+
+
/**
* Test FACeB2B
*/
public function testFaceb2b() {
$this->checkEnv();
- $faceb2b = new Faceb2bClient(self::CERTS_DIR . "/webservices.p12", null, self::WEBSERVICES_CERT_PASS);
+ $faceb2b = new Faceb2bClient(self::CERTS_DIR . "/facturae.p12", null, self::FACTURAE_CERT_PASS);
$faceb2b->setProduction(false);
// Test misc. methods
@@ -121,6 +139,11 @@ public function testFaceb2b() {
$this->assertEquals(intval($faceb2b->getRegisteredInvoices()->resultStatus->code), 0);
$this->assertEquals(intval($faceb2b->getInvoiceCancellations()->resultStatus->code), 0);
+ // Test C14N (non-exclusive)
+ $faceb2b->setExclusiveC14n(false);
+ $this->assertNotEmpty($faceb2b->getCodes()->codes);
+ $faceb2b->setExclusiveC14n(true);
+
// Generate invoice
$fac = $this->getWsBaseInvoice();
$fac->setBuyer(new FacturaeParty([
@@ -135,7 +158,7 @@ public function testFaceb2b() {
"code" => "ESA789231250000",
"name" => "Centro administrativo receptor"
]));
- $fac->sign(self::CERTS_DIR . "/webservices.p12", null, self::WEBSERVICES_CERT_PASS);
+ $fac->sign(self::CERTS_DIR . "/facturae.p12", null, self::FACTURAE_CERT_PASS);
// Send invoice
$invoiceFile = new FacturaeFile();
diff --git a/tests/certs/facturae-private.pem b/tests/certs/facturae-private.pem
index 2b03bdb..41e9cea 100644
--- a/tests/certs/facturae-private.pem
+++ b/tests/certs/facturae-private.pem
@@ -1,30 +1,30 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
-MIIFHDBOBgkqhkiG9w0BBQ0wQTApBgkqhkiG9w0BBQwwHAQIG4Kky+Gm/VICAggA
-MAwGCCqGSIb3DQIJBQAwFAYIKoZIhvcNAwcECLtOZxo5MtoqBIIEyGblv9lcZ4gh
-9UMmH4DvngSuZQ8V4hboG3Eqd0YVpLs8r+/CXXh11J7f6EJKwaGKIxb9s7YI6t2u
-KdtNhfv8KzBpl9yQx2LPtuyb4QUcrPuOMQ6I6AneKLyvDXWFUCRw3XM/1oqlzxjP
-9G4RxEsxiLJuPaTPxhSAbi7hdm7RREXmL/4+gs1fxjk98bvDAmZDpjnxgEiXeZE1
-qOlvWm6MHL/Qo3kpbk6DKk3uqpcX12T6/LwDX35zktzu6BzrukvS5tvmZH0gFJYv
-+YXllJPE5OAaxLBGPVhVOZGMs5Fe9adcr0dcTzJGc5hC25pnHIl39/aiiLQ0sKUQ
-MkV/rV8ChoRiA++rlC6Pcm0ucco1pYnejCPaTPfvEL2GPWp5O4CU+UQL+X+hdmd2
-vAprvikEIxT3XjrKe+iQTRdZkwIcRjpFo6qvxN2lHhIQjAJE4U3gfnHE1tCAvrA/
-fP/ThSRGI268zw0MFx2hGELwUSbt5WXV6ajXg+tWP3FUgSy/amcxeezUUqiq4KMe
-OPzKFSvekkfEQjVi5R/HxeLkCbJrhDXmxhtrLKRE47kdfNB8MSaufuhJJrstvCJ+
-7UWRWl6tvWpVB0oJPD8NXq+eV8Hv9onsuie/Vg2c13Sj2Gu21QHkIOAElvktFTi0
-kV/gj2DCPWZEi71fejpHKdQzBykbMaibzNcDqiVb0tElEo0QjZhjxc69tYgSSAY2
-zLMHybeA9ea/8S4MTC2A655X/5cgwQWRcAz8Iks4bBQLknjoIC9va8/K6JfScfxi
-BTH6qYwP2YbkcgmP9s9QeqZqEdV+YKfuNOWHafRlvDBAagH4pmdN1OSl12ivkiF/
-cjt1KYTl3QO+3ABrHVNK59Np6O6KlMGgBbkqs6rmSZQ9QfracSIqgJF91iACBiZJ
-uCU8q6HJJRj6If+XjGd+8VTQ+lORLjhgNXxb9KXpPNVayNUrwNKWHshgXvepXFEJ
-fegQwzugFEyf0cRXWWc6zp/BOUdHk+g11QqwXXuRd1C7jLJd9N3DSfCRcBNDF1M0
-Mzk+1WuPewBoH18LNty6O/jxoxe7zpLT87A8FMeRMi+x8jBVN1sUxSPSPp2wyAuF
-iOgEPjlsSHi3/Hxd8li5a7J/I9my8Y85Qyq13O4OiVohlXZA3UKaRBJS+ETs3BYY
-j1aqS207Hzu7kCLlSFjW7s8QXpjUuJDkSIKLiKgn2Akge6Ay8FRFENfnd28OwYDP
-QRAGXFjkqfPyTiL/CRO1fnC2tCN6HbddX02UDekg7RQCCkGHv14dz8TphovvgICf
-MU+5zULnHSc7YNBsvKo8laFEZ7HZfdw8caxCxcdKptbLIPDAQIpVTwiRxOXL/dbh
-/jB7nA5+aC1b0EYFd6XWoyxw1fglMrypKqOt6qSvpfl4nqGbrx102izjQCnUxNPd
-yMSSrnhemoGMyYoC5DdlSyiTFFMpKkqb3rVNT+eyMtCPQDVIni7g9RKWlXyPe9Gi
-vj+cvdzwnFa+LWAKJDDcCdsF9hN7I/xrHdYMzzMsIR1W0leQKmWvGbjS1LMDch/h
-R2VJniGFkbYFkqUw3FfoTgSadvYQD30sG+R2rnBMuADpfLwmUpA90bLSYDdEI4kL
-CQ7aDxgZUOrFC+FxxMcuKw==
+MIIFLTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIBvzP/2jJehkCAggA
+MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBANEgcqWh4Z4q+YhIKCUaaABIIE
+0MNoYmNFQCtXs4cFvGSS30slLW1to3y3emUm6Qgf7CVHWEHbWLMiXaD7Ak/XyUty
+WXbAr41Ew4hM/gKYQGXh+imdzMG+kOYgcqf38JRhPvBkiwrCZnmCxJyC+RSbLU4w
+wNmF+JwfDtw9269X1ewWwnKaceqhA71TDLfzJFKDydZGKXAgFeilZhhCq8K32VZo
+qtZD58beHeldBvnazc1vTI6dP6gFbnDxE0S79h5lNsEeq8Qd2zCJ3OfuVviVGbpF
+YF52BDG8T+clFuFIxg9zZQiVh4tUmuaFeAzuGbMM9FSlKkExn/9dx2xSS7rnFmTK
+ordGD67XT51C7Z4pYFZ644niXQiK3YeFziSfITzSlyYCk0e8vcArlKu8+oUh2pU0
+o6h7Ue+I9mlXswY5bBzH+MXJyUK23EOVBftBnAGThoMFq/XCEX+We52X8zoLmeIF
+gLdi9Pdk/LpsiLTsvHCKVdxWg4yCHGwACx8pdvcAwbDdXmYTbg/nUso1aFSNt1Oj
+Mg2WtfQJ30SidmxfluWaxmuHbYUNLRsviizDyrvukv21DCK8FNlPzJrDrjn7ohyv
++gYqaguU0rPE+fCGkRDNfzJ3hy4Rhq8uYKW2E/CJUHexpk1nsvuNGtPUyA1qlbLm
+pyZmGbhsZZOVNhFEtbxx1oHT/lrTcRnXo4Wl62Y8x+KBneBaQUTKSmD/5HChqSFU
+UzL8iAWHDw1WQSeks1aEzIWzmdS8uxiKrCIWLlyJQE6gWnSwxj1MYSjrKSPCY57+
+E3yDWZFkRgRrFBaJsjo7iisuHKNMGIH1WUO5qIchBJaRlE9o3JJd16Cdst/ILgqj
+AKqcOZ0hiN83f2KXsMv7rKi8IxpIHPtrEdvrMY0AUtVBRNj5hpv6w9TFH6ELfLES
+Q6RLQ+3jkhzmi8Z1xeF94Kv/r5+ZhBrMuauqrbPsdT0VK/Y9eqnZCSaflP58eIN+
+i28a09xzp5N+c4pproaKHLT/iL1FOHNJb1xJ7aYIkLcXmbdNZLcAPlWPtkatfwZ7
+MfUijJCWCu0EFl9+zhmOyDmC2RCMnL4MxeOD7LtqEE2DfECWmk7rVIIQ20Orvr22
+6WH8JyMRlq/2o0TWE6/j1PPN3RmdMEJwSsLmAcQy0Px69GMC2w8HidkdDwCKRLvo
+thy+gfIShXTCCwY0N2IwxUNv17XIFV+uIatR83evDEOqMypybhGA9sXHrGKJz9/e
+AquF82caXsULkvdbxtd+PPn/GAP9+kY4HfWfrlWkgXk7PMQtHJT+JSfMza8qKtnV
+p+cruUAvrS0DgApLWxQt2TDPPXdGxlWAJmeu2cNVUNGfbTfWx4w8sSQy0zQrG26e
+X3otyVIf8fs7cvQxox9EBLcLxFwQiMeqz96QVs3ymFh1kpUthBW6894pb1RT3V66
+DtIpATu8F1Vf1Umas/TgMzJRnA0CUzPXY6xjIPGMc08kKYK8wnhEmHqT8929PGm/
+0l1D+F7vGKsbZy2vn/bdR+EdKpS7WQbysRtGuOXyWfjzfxqXvEg+dO7sNjmCBz7W
+tdhZSfdBQ8knPIScdX4bkABZhSat3pGbk/HcZ4k1AbamPh63/AHT6XIRYqPxyLUU
+O6kkEbFOeT76+wGstBGYPQICGUjd3EqBlJLSBEkIiWY8
-----END ENCRYPTED PRIVATE KEY-----
\ No newline at end of file
diff --git a/tests/certs/facturae-public.pem b/tests/certs/facturae-public.pem
index 22dd397..8938204 100644
--- a/tests/certs/facturae-public.pem
+++ b/tests/certs/facturae-public.pem
@@ -1,43 +1,43 @@
-----BEGIN CERTIFICATE-----
-MIIHhjCCBm6gAwIBAgIQSOSlyjvRFUlfo/hUFNAvqDANBgkqhkiG9w0BAQsFADBL
+MIIHoDCCBoigAwIBAgIQc/g6LlAaVg9l7w+qq1iLnzANBgkqhkiG9w0BAQsFADBL
MQswCQYDVQQGEwJFUzERMA8GA1UECgwIRk5NVC1SQ00xDjAMBgNVBAsMBUNlcmVz
-MRkwFwYDVQQDDBBBQyBGTk1UIFVzdWFyaW9zMB4XDTIwMTEwNTEzMDQyMFoXDTI0
-MTEwNTEzMDQyMFowgYUxCzAJBgNVBAYTAkVTMRgwFgYDVQQFEw9JRENFUy05OTk5
-OTk5OVIxEDAOBgNVBCoMB1BSVUVCQVMxGjAYBgNVBAQMEUVJREFTIENFUlRJRklD
+MRkwFwYDVQQDDBBBQyBGTk1UIFVzdWFyaW9zMB4XDTI0MDMxMTE0MDUzMFoXDTI4
+MDMxMTE0MDUzMFowgYUxCzAJBgNVBAYTAkVTMRgwFgYDVQQFEw9JRENFUy05OTk5
+OTk3MkMxEDAOBgNVBCoMB1BSVUVCQVMxGjAYBgNVBAQMEUVJREFTIENFUlRJRklD
QURPMS4wLAYDVQQDDCVFSURBUyBDRVJUSUZJQ0FETyBQUlVFQkFTIC0gOTk5OTk5
-OTlSMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAujAnB2L5X2Bm42S5
-f/axKFu1QsAcZGJAeYELZZJ04jriBu3E8V3Rus3tUxfQ+ylqBm0bNWgHfP+gekos
-HaYoJNQmAVBuwpd183uHksTRUtbeOAFS2xd7v29stM7ARkec+WVV+SK8G6HECIB0
-VIAMoB2tVs0y6XRVRcjE4I7kH1h3ZbMIzvW43B4hxruYtXcvozGwvZpxQKVrjEY8
-IXH5+aXHM8WLCba4I06FyhvI+2/9WUPN2YvDoml7lQM4edgepTEZifq2ZPHGpCC5
-NhSXj2ab5FtnGTMgUaWH6tCljT0kOdfJBOHnIWOw4dBdgkik2CuxwGyMrq/P5VqQ
-IC2hXQIDAQABo4IEKTCCBCUwgZIGA1UdEQSBijCBh4Edc29wb3J0ZV90ZWNuaWNv
-X2NlcmVzQGZubXQuZXOkZjBkMRgwFgYJKwYBBAGsZgEEDAk5OTk5OTk5OVIxGjAY
-BgkrBgEEAaxmAQMMC0NFUlRJRklDQURPMRQwEgYJKwYBBAGsZgECDAVFSURBUzEW
-MBQGCSsGAQQBrGYBAQwHUFJVRUJBUzAMBgNVHRMBAf8EAjAAMA4GA1UdDwEB/wQE
-AwIF4DAdBgNVHSUEFjAUBggrBgEFBQcDBAYIKwYBBQUHAwIwHQYDVR0OBBYEFE5a
-HiQQRwVYJzmmkfG/i5MxmMNdMB8GA1UdIwQYMBaAFLHUT8QjefpEBQnG6znP6DWw
-uCBkMIGCBggrBgEFBQcBAQR2MHQwPQYIKwYBBQUHMAGGMWh0dHA6Ly9vY3NwdXN1
-LmNlcnQuZm5tdC5lcy9vY3NwdXN1L09jc3BSZXNwb25kZXIwMwYIKwYBBQUHMAKG
-J2h0dHA6Ly93d3cuY2VydC5mbm10LmVzL2NlcnRzL0FDVVNVLmNydDCCARUGA1Ud
-IASCAQwwggEIMIH6BgorBgEEAaxmAwoBMIHrMCkGCCsGAQUFBwIBFh1odHRwOi8v
-d3d3LmNlcnQuZm5tdC5lcy9kcGNzLzCBvQYIKwYBBQUHAgIwgbAMga1DZXJ0aWZp
-Y2FkbyBjdWFsaWZpY2FkbyBkZSBmaXJtYSBlbGVjdHLDs25pY2EuIFN1amV0byBh
-IGxhcyBjb25kaWNpb25lcyBkZSB1c28gZXhwdWVzdGFzIGVuIGxhIERQQyBkZSBs
-YSBGTk1ULVJDTSBjb24gTklGOiBRMjgyNjAwNC1KIChDL0pvcmdlIEp1YW4gMTA2
-LTI4MDA5LU1hZHJpZC1Fc3Bhw7FhKTAJBgcEAIvsQAEAMIG6BggrBgEFBQcBAwSB
-rTCBqjAIBgYEAI5GAQEwCwYGBACORgEDAgEPMBMGBgQAjkYBBjAJBgcEAI5GAQYB
-MHwGBgQAjkYBBTByMDcWMWh0dHBzOi8vd3d3LmNlcnQuZm5tdC5lcy9wZHMvUERT
-QUNVc3Vhcmlvc19lcy5wZGYTAmVzMDcWMWh0dHBzOi8vd3d3LmNlcnQuZm5tdC5l
-cy9wZHMvUERTQUNVc3Vhcmlvc19lbi5wZGYTAmVuMIG1BgNVHR8Ega0wgaowgaeg
-gaSggaGGgZ5sZGFwOi8vbGRhcHVzdS5jZXJ0LmZubXQuZXMvY249Q1JMMzc0OCxj
-bj1BQyUyMEZOTVQlMjBVc3VhcmlvcyxvdT1DRVJFUyxvPUZOTVQtUkNNLGM9RVM/
-Y2VydGlmaWNhdGVSZXZvY2F0aW9uTGlzdDtiaW5hcnk/YmFzZT9vYmplY3RjbGFz
-cz1jUkxEaXN0cmlidXRpb25Qb2ludDANBgkqhkiG9w0BAQsFAAOCAQEAH4t5/v/S
-Lsm/dXRDw4QblCmTX+5pgXJ+4G1Lb3KTSPtDJ0UbQiAMUx+iqDDOoMHU5H7po/HZ
-LJXgNwvKLoiLbl5/q6Mqasif87fa6awNkuz/Y6dvXw0UOJh+Ud/Wrk0EyaP9ZtrL
-VsraUOobNyS6g+lOrCxRrNxGRK2yAeotO6LEo1y3b7CB+Amd2jDq8lY3AtCYlrhu
-CaTf0AD9IBYYmigHzFD/VH5a8uG95l6J85FQG7tMsG6UQHFM2EmNhpbrYH+ihetz
-3UhzcC5Fd/P1X7pGBymQgbCyBjCRf/HEVzyoHL72uMp2I4JXX4v8HABZT8xtlDY4
-LE0am9keJhaNcg==
+NzJDMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArX6zxub5QjNrWl5a
+sMPracpf+dxfRFA49u3+18Ul0EdeRVyqYyeeGfoO5dL2U4PoyEkGSCVl0pf3KXtB
+FCarYimaLe1GujFBwUMM3ywsJpV04vik/LRYKSTgLIXV2iiDsyThZ9vkD/nf5CJh
+kxJxRtEZ6znJjCoZOrKzcSEbHvKXVe0AtJnADuUyThw/6a0kdKKPB3RdhpjYxlKe
+pe9UOWewvTVYyxmoqNeQ1zj/dsYUP4gIdOo1NbjQ+OP1rw30yZSCx9IziW7LKmJN
+paehNl0zSXyFRiZEOT7F/26lBsx+lRucYi8B7p7oek58Ewno2RXvR78ZVihzlhqr
+FtWNjQIDAQABo4IEQzCCBD8wcQYDVR0RBGowaKRmMGQxGDAWBgkrBgEEAaxmAQQM
+CTk5OTk5OTcyQzEaMBgGCSsGAQQBrGYBAwwLQ0VSVElGSUNBRE8xFDASBgkrBgEE
+AaxmAQIMBUVJREFTMRYwFAYJKwYBBAGsZgEBDAdQUlVFQkFTMAwGA1UdEwEB/wQC
+MAAwDgYDVR0PAQH/BAQDAgXgMCoGA1UdJQQjMCEGCCsGAQUFBwMCBgorBgEEAYI3
+CgMMBgkqhkiG9y8BAQUwHQYDVR0OBBYEFFB7rD8U2UiFZ+YXsxDmRtmFq2O4MB8G
+A1UdIwQYMBaAFLHUT8QjefpEBQnG6znP6DWwuCBkMIGCBggrBgEFBQcBAQR2MHQw
+PQYIKwYBBQUHMAGGMWh0dHA6Ly9vY3NwdXN1LmNlcnQuZm5tdC5lcy9vY3NwdXN1
+L09jc3BSZXNwb25kZXIwMwYIKwYBBQUHMAKGJ2h0dHA6Ly93d3cuY2VydC5mbm10
+LmVzL2NlcnRzL0FDVVNVLmNydDCCARUGA1UdIASCAQwwggEIMIH6BgorBgEEAaxm
+AwoBMIHrMCkGCCsGAQUFBwIBFh1odHRwOi8vd3d3LmNlcnQuZm5tdC5lcy9kcGNz
+LzCBvQYIKwYBBQUHAgIwgbAMga1DZXJ0aWZpY2FkbyBjdWFsaWZpY2FkbyBkZSBm
+aXJtYSBlbGVjdHLDs25pY2EuIFN1amV0byBhIGxhcyBjb25kaWNpb25lcyBkZSB1
+c28gZXhwdWVzdGFzIGVuIGxhIERQQyBkZSBsYSBGTk1ULVJDTSBjb24gTklGOiBR
+MjgyNjAwNC1KIChDL0pvcmdlIEp1YW4gMTA2LTI4MDA5LU1hZHJpZC1Fc3Bhw7Fh
+KTAJBgcEAIvsQAEAMIG6BggrBgEFBQcBAwSBrTCBqjAIBgYEAI5GAQEwCwYGBACO
+RgEDAgEPMBMGBgQAjkYBBjAJBgcEAI5GAQYBMHwGBgQAjkYBBTByMDcWMWh0dHBz
+Oi8vd3d3LmNlcnQuZm5tdC5lcy9wZHMvUERTQUNVc3Vhcmlvc19lcy5wZGYTAmVz
+MDcWMWh0dHBzOi8vd3d3LmNlcnQuZm5tdC5lcy9wZHMvUERTQUNVc3Vhcmlvc19l
+bi5wZGYTAmVuMIHkBgNVHR8EgdwwgdkwgdaggdOggdCGgZ5sZGFwOi8vbGRhcHVz
+dS5jZXJ0LmZubXQuZXMvY249Q1JMVTg2OSxjbj1BQyUyMEZOTVQlMjBVc3Vhcmlv
+cyxvdT1DRVJFUyxvPUZOTVQtUkNNLGM9RVM/Y2VydGlmaWNhdGVSZXZvY2F0aW9u
+TGlzdDtiaW5hcnk/YmFzZT9vYmplY3RjbGFzcz1jUkxEaXN0cmlidXRpb25Qb2lu
+dIYtaHR0cDovL3d3dy5jZXJ0LmZubXQuZXMvY3Jsc2FjdXN1L0NSTFU4NjkuY3Js
+MA0GCSqGSIb3DQEBCwUAA4IBAQA+YKUUF+kIW32y+vbAmSr+8aYswNC7NPV/utEq
+uxX3VBGL3xLDUgkQ+5iiW7G194p93Ynnl639g070Q0SmuOPlQhgqYIMrYZbm8L1e
+AlbvqLsSIeRVhPaSbHzwbmUG5hQ7hZQ4ClG2xZcOC2auPb6UOLMEpSCs2TtuYZmn
+Ivh+t9YRrfFAUSLB315GHycdtrjOeEz68Dz+2UdvSmla3OO3X7XgDKqSSlul2vce
+Znp+cy07trzwiwyN+lq3icNT3bgu8eXeu05kvLklADjbNq509n3KSnAWRC44i1Xg
+5zXzm95KmcidynauRr6XqU0ENKHRLEWn4CltdYFkb6VtvuLC
-----END CERTIFICATE-----
\ No newline at end of file
diff --git a/tests/certs/facturae.p12 b/tests/certs/facturae.p12
index 5867c10..dd9b424 100644
Binary files a/tests/certs/facturae.p12 and b/tests/certs/facturae.p12 differ
diff --git a/tests/certs/webservices.p12 b/tests/certs/webservices.p12
deleted file mode 100644
index 53c7051..0000000
Binary files a/tests/certs/webservices.p12 and /dev/null differ