Ver Mensaje Individual
  #1320  
Antiguo 17-02-2024
ermendalenda ermendalenda is offline
Miembro
 
Registrado: ago 2021
Posts: 2.766
Reputación: 8
ermendalenda Va por buen camino
codigo ejemplo php encapsulamiento y envio

Aquí te dejo un encapsulamiento y envio, totalmente funcional para face y faceb2b en entornos de prueba y producción, si aprendes como funciona lo puedes modificar para cualquier definición que hagan teniendo en cuenta que yo le paso el path donde tengo el fichero y los datos por curl de esta forma:
Código:
curl --connect-timeout 10 --insecure -A "FacturaePHP/1.7.1" -H "SOAPAction: https://webservice.face.gob.es#enviarFactura" --data "@C:\xml\Facturare_FICTICIO_fichero.xml" --cert-type P12 --cert c:\....\fichero_certificado.pfx:clave -H "Content-Type: text/xml" https://se-face-webservice.redsara.es/facturasspp2 -o C:\respuestas\Respuesta_face_o.html -D C:\respuestas\Respuesta_face_D.html
Código PHP:
 <?php 
//ASIGNA EL ENTORNO QUE VAMOS A USAR  $endpoint como variable de uso GLOBALS
//************************************
//*************  FACE   **************
//************************************
//  ENTORNO DE PRODUCCION
//$endpoint= "https://webservice.face.gob.es/facturasspp2" :
      
//  ENTORNO DE PRUEBAS
$endpoint= "https://se-face-webservice.redsara.es/facturasspp2";      
//************************************


//************************************
//************  FACEB2B   ************
//************************************
//  ENTORNO DE PRODUCCION
//$endpoint= "https://ws.faceb2b.gob.es/sv1/invoice" :

//  ENTORNO DE PRUEBAS
//$endpoint= "https://se-ws-faceb2b.redsara.es/sv1/invoice";
//************************************





//**Asigna a $datos la variable de la llamada curl (fichero) que contiene 2 variables separadas por signo "="
$datos = $_GET['fichero'];

//***Asigno $datos a un array ya que contiene 2 campos 
$varias=explode("=",$datos);
//***El primer campo del array contiene el nombre del fichero del certificado

// **EJEMPLO PFX $dir_cert = ($_SERVER['DOCUMENT_ROOT'].'/certs/Certificado.pfx' );
//variable de uso GLOBALS
$dir_cert = ($_SERVER['DOCUMENT_ROOT'].'/certs/'.$varias[1] );

//***El segundo campo del array contiene el password del certificado
//variable de uso GLOBALS
$clave=$varias[2];

//variable de uso GLOBALS
$filename=$varias[3];
$NombreFichero=$varias[3];

//variable de uso GLOBALS
$xml2=file_get_contents("php://input");


//variable de uso GLOBALS
$version = "1.7.1";

// Cargamos la factura en una instancia de FacturaeFile

//$invoice2 = new InvoiceTest();

//$invoice2->EnviarFactura($xml2);
//*****************************
//****** ENVIAR POR FACE ******
//*****************************
    
$invoice = new FacturaeFile();
    
$invoice=$GLOBALS["xml2"];


    
    
$nulo=null;
    
$face = new FaceTrait();

    
$res =$face->sendInvoice("[email protected]", $invoice);


//*****************************
//***** ENVIAR POR FACEB2B ****
//*****************************

//    $invoice = new FacturaeFile();
//    $invoice=$GLOBALS["xml2"];
//    $nulo=null;
//    $face = new FaceB2BTrait();

//    $res =$face->sendInvoice("[email protected]", $invoice);




/**
 * Gets both public and private keys from a PKCS#12 certificate store or a PEM
 * file (X.509 certificate).
 */
class KeyPairReader {
  private 
$publicChain = [];
  private 
$privateKey = null;


  
/**
   * Get public certificates chain from child to top CA
   * @return string[] Array of PEM strings
   */
  
public function getPublicChain() {
    return 
$this->publicChain;
  }


  
/**
   * Get public key
   * @return string|null Certificate for the Public Key in PEM format
   */
  
public function getPublicKey() {
    return empty(
$this->publicChain) ? null : $this->publicChain[0];
  }


  
/**
   * Get private key
   * @return \OpenSSLAsymmetricKey|resource|null Decrypted Private Key
   */
  
public function getPrivateKey() {
    return 
$this->privateKey;
  }


  
/**
   * KeyPairReader constructor
   *
   * @param string      $publicPath  Path to public key in PEM or PKCS#12 file
   * @param string|null $privatePath Path to private key (null for PKCS#12)
   * @param string      $passphrase  Private key passphrase
   */
  
public function __construct($publicPath, $privatePath=null, $passphrase="") {
    if (
is_null($privatePath)) {
      
$this->readPkcs12($GLOBALS["dir_cert"], $passphrase);
    } else {
      
$this->readX509($GLOBALS["dir_cert"], $privatePath, $passphrase);
    }
  }


  
/**
   * Read a X.509 certificate and PEM encoded private key
   *
   * @param string $publicPath  Path to public key PEM file
   * @param string $privatePath Path to private key PEM file
   * @param string $passphrase  Private key passphrase
   */
  
private function readX509($publicPath, $privatePath, $passphrase) {
    if (!
is_file($GLOBALS["dir_cert"]) || !is_file($privatePath)) return;

    
// Validate and normalize public key
    
$publicKey = openssl_x509_read(file_get_contents($GLOBALS["dir_cert"]));
    if (empty(
$publicKey)) return;
    
openssl_x509_export($publicKey, $publicKeyPem);
    
$this->publicChain = array($publicKeyPem);
    
    
// Decrypt private key
    
$this->privateKey = openssl_pkey_get_private(file_get_contents($privatePath), $passphrase);
  }


  
/**
   * Read a PKCS#12 Certificate Store
   *
   * @param string $certPath   The certificate store file name
   * @param string $passphrase Password for unlocking the PKCS#12 file
   */
  
private function readPkcs12($certPath, $passphrase) {
    if (!
is_file($certPath)) return;
    if (
openssl_pkcs12_read(file_get_contents($certPath), $store, $passphrase)) {
      
$this->publicChain = array($store['cert']);
      if (!empty(
$store['extracerts'])) {
        
$this->publicChain = array_merge($this->publicChain, $store['extracerts']);
      }
      
$this->privateKey = openssl_pkey_get_private($store['pkey']);
      unset(
$store);
    }
  }

}
class 
XmlTools {

  
/**
   * Escape XML value
   * @param  string $value Input value
   * @return string        Escaped input
   */
  
public function escape($value) {
    return 
htmlspecialchars($value, ENT_XML1, 'UTF-8');
  }


  
/**
   * Generate random ID
   *
   * This method is used for generating random IDs required when signing the
   * document.
   *
   * @return int Random number
   */
  
public function randomId() {
    if (
function_exists('random_int')) return random_int(0x10000000, 0x7FFFFFFF);
    return 
rand(100000, 999999);
  }


  
/**
   * Inject namespaces
   * @param  string          $xml   Input XML
   * @param  string|string[] $newNs Namespaces
   * @return string                 Canonicalized XML with new namespaces
   */
  
public function injectNamespaces($xml, $newNs) {
    if (!
is_array($newNs)) $newNs = array($newNs);
    
$xml = explode(">", $xml, 2);
    
$oldNs = explode(" ", $xml[0]);
    
$elementName = array_shift($oldNs);

    
// Combine and sort namespaces
    
$xmlns = array();
    
$attributes = array();
    foreach (
array_merge($oldNs, $newNs) as $name) {
      if (
strpos($name, 'xmlns:') === 0) {
        
$xmlns[] = $name;
      } else {
        
$attributes[] = $name;
      }
    }
    
sort($xmlns);
    
sort($attributes);
    
$ns = array_merge($xmlns, $attributes);

    
// Generate new XML element
    
$xml = $elementName . " " . implode(' ', $ns) . ">" . $xml[1];
    return 
$xml;
  }


  
/**
   * To Base64
   * @param  string  $bytes  Input
   * @param  boolean $pretty Pretty Base64 response
   * @return string          Base64 response
   */
  
public function toBase64($bytes, $pretty=false) {
    
$res = base64_encode($bytes);
    return 
$pretty ? $this->prettify($res) : $res;
  }


  
/**
   * Prettify
   * @param  string $input Input string
   * @return string        Multi-line resposne
   */
  
private function prettify($input) {
    return 
chunk_split($input, 76, "\n");
  }


  
/**
   * Get digest in SHA-512
   * @param  string  $input  Input string
   * @param  boolean $pretty Pretty Base64 response
   * @return string          Digest
   */
  
public function getDigest($input, $pretty=false) {
    return 
$this->toBase64(hash("sha512", $input, true), $pretty);
  }


  
/**
   * Get certificate
   * @param  string  $pem    Certificate for the public key in PEM format
   * @param  boolean $pretty Pretty Base64 response
   * @return string          Base64 Certificate
   */
  
public function getCert($pem, $pretty=true) {
    
$pem = str_replace("-----BEGIN CERTIFICATE-----", "", $pem);
    
$pem = str_replace("-----END CERTIFICATE-----", "", $pem);
    
$pem = str_replace("\n", "", str_replace("\r", "", $pem));
    if (
$pretty) $pem = $this->prettify($pem);
    return 
$pem;
  }




  
/**
   * Get signature in SHA-1
   * @param  string  $payload    Data to sign
   * @param  string  $privateKey Private Key
   * @param  boolean $pretty     Pretty Base64 response
   * @return string              Base64 Signature
   */
  
public function getSignature($payload, $privateKey, $pretty=true) {
    
openssl_sign($payload, $signature, $privateKey);
    return 
$this->toBase64($signature, $pretty);
  }


}

Class 
Faceb2bTrait {
  
/**
   * Get web namespace
   * @return string Web namespace
   */
  
protected function getWebNamespace() {
    return 
"https://webservice.faceb2b.gob.es";
  }


  
/**
   * Send invoice
   * @param  FacturaeFile      $invoice    Invoice
   * @param  FacturaeFile|null $attachment Optional attachment
   * @return SimpleXMLElement              Response
   */
  
public function sendInvoice($invoice, $attachment=null) {
    
$tools = new XmlTools();
    
$req = '<web:SendInvoice><request>';

    
$req .= '<invoiceFile>' .
        
'<content>' . $tools->toBase64($GLOBALS["xml2"]) . '</content>' .
        
'<name>' . $GLOBALS["NombreFichero"] . '</name>' .
        
'<mime>text/xml</mime>' . // Mandatory MIME type
      
'</invoiceFile>';

    if (!
is_null($attachment)) {
      
$req .= '<attachmentFile>' .
          
'<content>' . $tools->toBase64($attachment->getData()) . '</content>' .
          
'<name>' . $attachment->getFilename() . '</name>' .
          
'<mime>' . $attachment->getMimeType() . '</mime>' .
        
'</attachmentFile>';
    }

    
$req .= '</request></web:SendInvoice>';
    
//$soap=new SoapClient1();
$soap = new FacturaeFile();
      
    return 
$soap->request($req);
  }


  
/**
   * Get invoice details
   * @param  string           $regId Registry number
   * @return SimpleXMLElement        Response
   */
  
public function getInvoiceDetails($regId) {
    return 
$this->request('<web:GetInvoiceDetails><request>' .
      
'<registryNumber>' . $regId . '</registryNumber>' .
      
'</request></web:GetInvoiceDetails>');
  }


  
/**
   * Request invoice cancellation
   * @param  string           $regId   Registry number
   * @param  string           $reason  Reason code
   * @param  string|null      $comment Optional comments
   * @return SimpleXMLElement          Response
   */
  
public function requestInvoiceCancellation($regId, $reason, $comment=null) {
    
$req = '<web:RequestInvoiceCancellation><request>';
    
$req .= '<registryNumber>' . $regId . '</registryNumber>';
    
$req .= '<reason>' . $reason . '</reason>';
    if (!
is_null($comment)) {
      
$req .= '<comment>' . $comment . '</comment>';
    }
    
$req .= '</request></web:RequestInvoiceCancellation>';

    return 
$this->request($req);
  }


  
/**
   * Get registered invoices
   * @param  string|null      $receivingUnit Receiving unit code
   * @return SimpleXMLElement                Response
   */
  
public function getRegisteredInvoices($receivingUnit=null) {
    
$req = '<web:GetRegisteredInvoices><request>';
    if (
is_null($receivingUnit)) {
      
$req .= '<receivingUnit>' . $receivingUnit . '</receivingUnit>';
    }
    
$req .= '</request></web:GetRegisteredInvoices>';
    return 
$this->request($req);
  }


  
/**
   * Get invoice cancellations
   * @return SimpleXMLElement Response
   */
  
public function getInvoiceCancellations() {
    return 
$this->request('<web:GetInvoiceCancellations><request>' .
      
'</request></web:GetInvoiceCancellations>');
  }


  
/**
   * Download invoice
   * @param  string           $regId    Registry number
   * @param  boolean          $validate Validate invoice
   * @return SimpleXMLElement           Response
   */
  
public function downloadInvoice($regId, $validate=true) {
    
$req = '<web:DownloadInvoice><request>';
    
$req .= '<registryNumber>' . $regId . '</registryNumber>';
    if (
$validate) {
      
$req .= '<signatureValidationMode>validate</signatureValidationMode>';
    }
    
$req .= '</request></web:DownloadInvoice>';
    return 
$this->request($req);
  }


  
/**
   * Confirm invoice download
   * @param  string           $regId   Registry number
   * @return SimpleXMLElement          Response
   */
  
public function confirmInvoiceDownload($regId) {
    return 
$this->request('<web:ConfirmInvoiceDownload><request>' .
      
'<registryNumber>' . $regId . '</registryNumber>' .
      
'</request></web:ConfirmInvoiceDownload>');
  }


  
/**
   * Reject invoice
   * @param  string           $regId   Registry number
   * @param  string           $reason  Reason code
   * @param  string|null      $comment Optional comments
   * @return SimpleXMLElement          Response
   */
  
public function rejectInvoice($regId, $reason, $comment=null) {
    
$req = '<web:RejectInvoice><request>';
    
$req .= '<registryNumber>' . $regId . '</registryNumber>';
    
$req .= '<reason>' . $reason . '</reason>';
    if (!
is_null($comment)) {
      
$req .= '<comment>' . $comment . '</comment>';
    }
    
$req .= '</request></web:RejectInvoice>';
    return 
$this->request($req);
  }


  
/**
   * Mark invoice as paid
   * @param  string           $regId   Registry number
   * @return SimpleXMLElement          Response
   */
  
public function markInvoiceAsPaid($regId) {
    return 
$this->request('<web:MarkInvoiceAsPaid><request>' .
      
'<registryNumber>' . $regId . '</registryNumber>' .
      
'</request></web:MarkInvoiceAsPaid>');
  }


  
/**
   * Accept invoice cancellation
   * @param  string           $regId   Registry number
   * @return SimpleXMLElement          Response
   */
  
public function acceptInvoiceCancellation($regId) {
    return 
$this->request('<web:AcceptInvoiceCancellation><request>' .
      
'<registryNumber>' . $regId . '</registryNumber>' .
      
'</request></web:AcceptInvoiceCancellation>');
  }


  
/**
   * Reject invoice cancellation
   * @param  string           $regId   Registry number
   * @param  string           $comment Comment
   * @return SimpleXMLElement          Response
   */
  
public function rejectInvoiceCancellation($regId, $comment) {
    return 
$this->request('<web:RejectInvoiceCancellation><request>' .
      
'<registryNumber>' . $regId . '</registryNumber>' .
      
'<comment>' . $comment . '</comment>' .
      
'</request></web:RejectInvoiceCancellation>');
  }



  
/**
   * Get codes
   * @param  string           $type Code type
   * @return SimpleXMLElement       Response
   */
  
public function getCodes($type="") {
    return 
$this->request('<web:GetCodes><request>' .
      
'<codeType>' . $type . '</codeType>' .
      
'</request></web:GetCodes>');
  }
}
//trait FaceTrait {
class FaceTrait {
  
/**
   * Get web namespace
   * @return string Web namespace
   */
  
protected function getWebNamespace() {
    return 
"https://webservice.face.gob.es";
  }
  
/**
   * Send invoice
   * @param  string           $email       Email address
   * @param  FacturaeFile     $invoice     Invoice
   * @param  FacturaeFile[]   $attachments Attachments
   * @return SimpleXMLElement              Response
   */
  
public function sendInvoice($email, $invoice, $attachments=array()) {
    
$tools = new XmlTools();
    
$req = '<web:enviarFactura><request>';
    
$req .= '<correo>' . $email . '</correo>';
    
$req .= '<factura>' .
        
'<factura>' . $tools->toBase64($GLOBALS["xml2"]) . '</factura>' .
        
'<nombre>' . $GLOBALS["NombreFichero"] . '</nombre>' .
        
'<mime>application/xml</mime>' . // Mandatory MIME type
      
'</factura>';
    
$req .= '<anexos>';
    foreach (
$attachments as $file) {
      
$req .= '<anexo>' .
          
'<anexo>' . $tools->toBase64($file->getData()) . '</anexo>' .
          
'<nombre>' . $file->getFilename() . '</nombre>' .
          
'<mime>' . $file->getMimeType() . '</mime>' .
        
'</anexo>';
    }
    
$req .= '</anexos>';
    
$req .= '</request></web:enviarFactura>';

//echo "*** Esta es la variable de envio req: ";
//echo $req;
//echo " *** FIN VARIABLE REQ *** ";


    //$soap=new SoapClient1();
    
$soap = new FacturaeFile();
      
    
$ok=$soap->request($req);
  }


  
/**
   * Cancel invoice 
   * @param  string           $regId  Invoice register ID
   * @param  string           $reason Cancelation reason
   * @return SimpleXMLElement         Response
   */
  
public function cancelInvoice($regId, $reason) {
    return 
$this->request('<web:anularFactura>' .
      
'<numeroRegistro>' . $regId . '</numeroRegistro>' .
      
'<motivo>' . $reason . '</motivo>' .
      
'</web:anularFactura>');
  }

  
/**
   * Get invoice status codes
   * @return SimpleXMLElement Response
   */
  
public function getStatus() {
    return 
$this->request('<web:consultarEstados></web:consultarEstados>');
  }


  
/**
   * Get administrations
   * @param  boolean          $onlyTopLevel Get only top level administrations
   * @return SimpleXMLElement               Response
   */
  
public function getAdministrations($onlyTopLevel=true) {
    
$tag = "consultarAdministraciones";
    if (!
$onlyTopLevel) $tag .= "Repositorio";
    return 
$this->request("<web:$tag></web:$tag>");
  }


  
/**
   * Get units
   * @param  string|null      $code Administration code
   * @return SimpleXMLElement       Response
   */
  
public function getUnits($code=null) {
    if (
is_null($code)) return $this->request('<web:consultarUnidades></web:consultarUnidades>');
    return 
$this->request('<web:consultarUnidadesPorAdministracion>' .
      
'<codigoDir>' . $code . '</codigoDir>' .
      
'</web:consultarUnidadesPorAdministracion>');
  }


  
/**
   * Get NIFs
   * @param  string|null      $code Administration code
   * @return SimpleXMLElement       Response
   */
  
public function getNifs($code=null) {
    if (
is_null($code)) return $this->request('<web:consultarNIFs></web:consultarNIFs>');
    return 
$this->request('<web:consultarNIFsPorAdministracion>' .
      
'<codigoDir>' . $code . '</codigoDir>' .
      
'</web:consultarNIFsPorAdministracion>');
  }


  
/**
   * Get invoice
   * @param  string|string[]  $regId Invoice register ID(s)
   * @return SimpleXMLElement        Response
   */
  
public function getInvoices($regId) {
    if (
is_string($regId)) {
      return 
$this->request('<web:consultarFactura>' .
        
'<numeroRegistro>' . $regId . '</numeroRegistro>' .
        
'</web:consultarFactura>');
    }
    
$req = '<web:consultarListadoFacturas><request>';
    foreach (
$regId as $id) $req .= '<numeroRegistro>' . $id . '</numeroRegistro>';
    
$req .= '</request></web:consultarListadoFacturas>';
    return 
$this->request($req);
  }



}
trait 
StageableTrait {
  private 
$production = true;

  
/**
   * Set production environment
   * @param boolean $production Is production
   */
  
public function setProduction($production) {
    
$this->production = $production;
  }


  
/**
   * Is production
   * @return boolean Is production
   */
  
public function isProduction() {
    return 
$this->production;
  }
}
/**
 * Facturae File
 *
 * Represents a file that can be used as an attachment for an invoice or to
 * send information to a Web Service.
 */
class FacturaeFile {
  const 
REQUEST_EXPIRATION = 60; // In seconds

  
private $publicKey;
  private 
$privateKey;

  private 
$filename;
  private 
$data;
  private 
$mime;

/**
   * Get web namespace
   * @return string Web namespace
   */
  
protected function getWebNamespace() {
    return 
"https://webservice.face.gob.es";
  }


public function 
request($body) {

$tools = new XmlTools();
    

$nulo=null;


//DATOS SIGN

$policy= array(
    
"name" => "Politica de Firma FacturaE v3.1",
    
"url" => "http://www.facturae.es/politica_de_firma_formato_facturae/politica_de_firma_formato_facturae_v3_1.pdf",
    
"digest" => "Ohixl6upD6av8N7pEvDABhEL6hM=");
    
// Generate random IDs
    
    // Load public and private keys
    
$reader = new KeyPairReader($GLOBALS["dir_cert"], $nulo, $GLOBALS["clave"]);
    
$publicChain2 = $reader->getPublicChain();
    
$privateKey2 = $reader->getPrivateKey();
    
$signPolicy2 = $policy;
    unset(
$reader);


    
//No usado Comprueba si hay key publica y privada y devueleve 1=hay 0=no hay
    //return (!empty($publicChain2) && !empty($privateKey2));


    
$reader = new KeyPairReader($GLOBALS["dir_cert"], null, $GLOBALS["clave"]);
    
//$publicChain1 = $reader->getPublicChain();
    
$publicKey1 = $publicChain2;
     
$publicChain1 = $publicChain2;

    
$privateKey1 = $reader->getPrivateKey();
    
$signPolicy1 = $signPolicy2;
    unset(
$reader);

    
$tools = new XmlTools();

    
// Generate random IDs for this request
    
$bodyId = "BodyId-" . $tools->randomId();
    
$certId = "CertId-" . $tools->randomId();
    
$keyId = "KeyId-" . $tools->randomId();
    
$strId = "SecTokId-" . $tools->randomId();
    
$timestampId = "TimestampId-" . $tools->randomId();
    
$sigId = "SignatureId-" . $tools->randomId();

    
// Define namespaces array
    
$ns = array(
      
"soapenv" => 'xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"',
      
"web" => 'xmlns:web="' . $this->getWebNamespace() . '"',
      
"ds" => 'xmlns:ds="http://www.w3.org/2000/09/xmldsig#"',
      
"wsu" => 'xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"',
      
"wsse" => 'xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"'
    
);

    
// Generate request body
    
$reqBody = '<soapenv:Body wsu:Id="' . $bodyId . '">' . $body . '</soapenv:Body>';
    
$bodyDigest = $tools->getDigest($tools->injectNamespaces($reqBody, $ns));

    
// Generate timestamp
    
$timeCreated = time();
    
$timeExpires = $timeCreated + self::REQUEST_EXPIRATION;
    
$reqTimestamp = '<wsu:Timestamp wsu:Id="' . $timestampId . '">' .
        
'<wsu:Created>' . date('c', $timeCreated) . '</wsu:Created>' .
        
'<wsu:Expires>' . date('c', $timeExpires) . '</wsu:Expires>' .
      
'</wsu:Timestamp>';
    
$timestampDigest = $tools->getDigest(
      
$tools->injectNamespaces($reqTimestamp, $ns)
    );

    
// Generate request header
    
$reqHeader = '<soapenv:Header>';
    
$reqHeader .= '<wsse:Security soapenv:mustUnderstand="1">' ;
    
$reqHeader .= '<wsse:BinarySecurityToken ' .
      
'EncodingType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary" ' .
      
'wsu:Id="' . $certId . '" ' .
      
'ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3">' .
        
$tools->getCert($publicKey1[0], false) .
      
'</wsse:BinarySecurityToken>';

    
// Generate signed info
    
$signedInfo = '<ds:SignedInfo>' .
        
'<ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315">' .
        
'</ds:CanonicalizationMethod>' .
        
'<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"></ds:SignatureMethod>' .
        
'<ds:Reference URI="#' . $timestampId . '">' .
          
'<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></ds:DigestMethod>' .
          
'<ds:DigestValue>' . $timestampDigest . '</ds:DigestValue>' .
        
'</ds:Reference>' .
        
'<ds:Reference URI="#' . $bodyId . '">' .
          
'<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha512"></ds:DigestMethod>' .
          
'<ds:DigestValue>' . $bodyDigest . '</ds:DigestValue>' .
        
'</ds:Reference>' .
      
'</ds:SignedInfo>';
    
$signedInfoPayload = $tools->injectNamespaces($signedInfo, $ns);

    
// Add signature and KeyInfo to header
    
$reqHeader .= '<ds:Signature Id="' . $sigId . '">' .
      
$signedInfo .
      
'<ds:SignatureValue>' .
        
$tools->getSignature($signedInfoPayload, $privateKey1, false) .
      
'</ds:SignatureValue>';
    
$reqHeader .= '<ds:KeyInfo Id="' . $keyId . '">' .
      
'<wsse:SecurityTokenReference wsu:Id="' . $strId . '">' .
        
'<wsse:Reference URI="#' . $certId . '" ' .
        
'ValueType="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3">' .
        
'</wsse:Reference>' .
      
'</wsse:SecurityTokenReference>' .
      
'</ds:KeyInfo>';
    
$reqHeader .= '</ds:Signature>';

    
// Add timestamp and close header
    
$reqHeader .= $reqTimestamp;
    
$reqHeader .= '</wsse:Security>';
    
$reqHeader .= '</soapenv:Header>';

    
// Generate final request
    
$req = '<soapenv:Envelope>' . $reqHeader . $reqBody . '</soapenv:Envelope>';
    
$req = $tools->injectNamespaces($req, $ns);
    
$req = '<?xml version="1.0" encoding="UTF-8"?>' . $req;

    
// Extract SOAP action from "<web:ACTION></web:ACTION>"
    
$soapAction = substr($body, 5, strpos($body, '>')-5);
    
$soapAction = $this->getWebNamespace() . "#$soapAction";

    
$ch = curl_init();
    
curl_setopt_array($ch, array(
      
CURLOPT_URL => $GLOBALS['endpoint'],
      
CURLOPT_RETURNTRANSFER => 1,
      
CURLOPT_SSL_VERIFYPEER => 0,
      
CURLOPT_TIMEOUT => 30,
      
CURLOPT_POST => 1,
      
CURLOPT_POSTFIELDS => $req,
      
CURLOPT_HTTPHEADER => array(
        
"Content-Type: text/xml",
        
"SOAPAction: " . $soapAction
      
),
      
CURLOPT_USERAGENT => "FacturaePHP/" . $GLOBALS["version"]
    ));
    
$res = curl_exec($ch);
    
curl_close($ch);
    unset(
$ch);


    
//// Parse response
    
    
$xml = new \DOMDocument();
    
$xml->loadXML($res);
    
$xml = $xml->getElementsByTagName('Body')->item(0)->getElementsByTagName('*')->item(0);
$child = $xml->getElementsByTagName('codigo');
 
foreach (
$child as $chi) 
{
    echo 
$chi->nodeName . "=" . $chi->nodeValue . "\r\n" ;
 }
    
$child = $xml->getElementsByTagName('descripcion');

foreach (
$child as $chi) 
{
    echo 
$chi->nodeName . "=" . $chi->nodeValue . "\r\n" ;
 }
    
$child = $xml->getElementsByTagName('codigo Seguimiento');

foreach (
$child as $chi) 
{
    echo 
$chi->nodeName . "=" . $chi->nodeValue . "\r\n" ;
 }
    
$child = $xml->getElementsByTagName('codigoSeguimiento');

foreach (
$child as $chi) 
{
    echo 
$chi->nodeName . "=" . $chi->nodeValue . "\r\n" ;
 }


//echo  "---- FIN RESULTADO ----"  . "\r\n" . "---- RESPUESTA COMPLETA  ----"  . "\r\n" . $res . "\r\n" . "---- FIN RESPUESTA COMPLETA ----" .  $req;

//if ($res->resultado->codigo == 0) {
//  // La factura ha sido aceptada
//  echo "Número de registro => " . $res->factura->numeroRegistro;
//} else {
//  // FACe ha rechazado la factura
//}

//return $req;  
}



  
/**
   * Get data
   * @return string Data
   */
  
public function getData() {
    return 
$this->data;
  }


  
/**
   * Get filename
   * @return string Filename
   */
  
public function getFilename() {
    return 
$this->filename;
  }


  
/**
   * Get MIME type
   * @return string MIME type
   */
  
public function getMimeType() {
    return 
$this->mime;
  }
}




?>
[/php]