Skip to content

Commit

Permalink
Arreglado C14N en webservices
Browse files Browse the repository at this point in the history
- Actualizada clase SoapClient para usar EXC-C14N por defecto
- Actualizada clase CustomFaceClient
- Actualizados tests unitarios
- Actualizada documentación

> Fixes #152
  • Loading branch information
josemmo committed Apr 28, 2024
1 parent 312ad85 commit 02cde07
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 2 deletions.
11 changes: 11 additions & 0 deletions doc/envio-y-recepcion/face.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
18 changes: 18 additions & 0 deletions src/Face/CustomFaceClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

class CustomFaceClient extends SoapClient {
private $endpointUrl;
private $webNamespace = "https://webservice.face.gob.es";

use FaceTrait;

Expand All @@ -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
Expand Down
16 changes: 14 additions & 2 deletions src/Face/SoapClient.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
abstract class SoapClient {

const REQUEST_EXPIRATION = 60; // In seconds
private $useExcC14n = true;

use KeyPairReaderTrait;

Expand All @@ -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
Expand Down Expand Up @@ -91,8 +101,10 @@ protected function request($body) {
'</wsse:BinarySecurityToken>';

// 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 = '<ds:SignedInfo>' .
'<ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315">' .
'<ds:CanonicalizationMethod Algorithm="' . $c14nNamespace . '">' .
'</ds:CanonicalizationMethod>' .
'<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"></ds:SignatureMethod>' .
'<ds:Reference URI="#' . $timestampId . '">' .
Expand All @@ -104,7 +116,7 @@ protected function request($body) {
'<ds:DigestValue>' . $bodyDigest . '</ds:DigestValue>' .
'</ds:Reference>' .
'</ds:SignedInfo>';
$signedInfoPayload = XmlTools::injectNamespaces($signedInfo, $ns);
$signedInfoPayload = XmlTools::injectNamespaces($signedInfo, $signedInfoNs);

// Add signature and KeyInfo to header
$reqHeader .= '<ds:Signature Id="' . $sigId . '">' .
Expand Down
23 changes: 23 additions & 0 deletions tests/WebservicesTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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([
Expand Down Expand Up @@ -107,6 +113,18 @@ 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
*/
Expand All @@ -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([
Expand Down

0 comments on commit 02cde07

Please sign in to comment.