Refactor for additional test cases
This commit is contained in:
parent
dd60eb3b58
commit
fdf7d2d3cf
|
|
@ -13,7 +13,8 @@ abstract class BaseXmlModel
|
|||
{
|
||||
$element = $doc->createElementNS(self::XML_NAMESPACE, self::XML_NAMESPACE_PREFIX . ':' . $name);
|
||||
if ($value !== null) {
|
||||
$element->nodeValue = $value;
|
||||
$textNode = $doc->createTextNode($value);
|
||||
$element->appendChild($textNode);
|
||||
}
|
||||
foreach ($attributes as $attrName => $attrValue) {
|
||||
$element->setAttribute($attrName, $attrValue);
|
||||
|
|
@ -25,7 +26,8 @@ abstract class BaseXmlModel
|
|||
{
|
||||
$element = $doc->createElementNS(self::XML_DS_NAMESPACE, self::XML_DS_NAMESPACE_PREFIX . ':' . $name);
|
||||
if ($value !== null) {
|
||||
$element->nodeValue = $value;
|
||||
$textNode = $doc->createTextNode($value);
|
||||
$element->appendChild($textNode);
|
||||
}
|
||||
foreach ($attributes as $attrName => $attrValue) {
|
||||
$element->setAttribute($attrName, $attrValue);
|
||||
|
|
@ -37,12 +39,12 @@ abstract class BaseXmlModel
|
|||
{
|
||||
$elements = $parent->getElementsByTagNameNS($namespace, $name);
|
||||
if ($elements->length > 0) {
|
||||
return $elements->item(0)->nodeValue;
|
||||
return $elements->item(0)->textContent;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
abstract public function toXml(): string;
|
||||
abstract public function toXml(\DOMDocument $doc): \DOMElement;
|
||||
|
||||
public static function fromXml($xml): self
|
||||
{
|
||||
|
|
@ -55,6 +57,8 @@ abstract class BaseXmlModel
|
|||
}
|
||||
|
||||
$doc = new \DOMDocument();
|
||||
$doc->formatOutput = true;
|
||||
$doc->preserveWhiteSpace = false;
|
||||
if (!$doc->loadXML($xml)) {
|
||||
throw new \DOMException('Failed to load XML: Invalid XML format');
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,13 +9,9 @@ class Cupon extends BaseXmlModel
|
|||
protected float $importeCupon;
|
||||
protected ?string $descripcionCupon = null;
|
||||
|
||||
public function toXml(): string
|
||||
public function toXml(\DOMDocument $doc): \DOMElement
|
||||
{
|
||||
$doc = new \DOMDocument('1.0', 'UTF-8');
|
||||
$doc->formatOutput = true;
|
||||
|
||||
$root = $this->createElement($doc, 'Cupon');
|
||||
$doc->appendChild($root);
|
||||
|
||||
// Add required elements
|
||||
$root->appendChild($this->createElement($doc, 'IDCupon', $this->idCupon));
|
||||
|
|
@ -27,7 +23,7 @@ class Cupon extends BaseXmlModel
|
|||
$root->appendChild($this->createElement($doc, 'DescripcionCupon', $this->descripcionCupon));
|
||||
}
|
||||
|
||||
return $doc->saveXML();
|
||||
return $root;
|
||||
}
|
||||
|
||||
public static function fromDOMElement(\DOMElement $element): self
|
||||
|
|
|
|||
|
|
@ -11,13 +11,9 @@ class Desglose extends BaseXmlModel
|
|||
protected ?array $desgloseIRPF = null;
|
||||
protected ?array $desgloseIS = null;
|
||||
|
||||
public function toXml(): string
|
||||
public function toXml(\DOMDocument $doc): \DOMElement
|
||||
{
|
||||
$doc = new \DOMDocument('1.0', 'UTF-8');
|
||||
$doc->formatOutput = true;
|
||||
|
||||
$root = $this->createElement($doc, 'Desglose');
|
||||
$doc->appendChild($root);
|
||||
|
||||
// Create DetalleDesglose element
|
||||
$detalleDesglose = $this->createElement($doc, 'DetalleDesglose');
|
||||
|
|
@ -49,8 +45,10 @@ class Desglose extends BaseXmlModel
|
|||
}
|
||||
|
||||
// Add BaseImponibleOimporteNoSujeto (required)
|
||||
if (isset($this->desgloseFactura['BaseImponibleOimporteNoSujeto'])) {
|
||||
$detalleDesglose->appendChild($this->createElement($doc, 'BaseImponibleOimporteNoSujeto',
|
||||
number_format($this->desgloseFactura['BaseImponible'], 2, '.', '')));
|
||||
number_format($this->desgloseFactura['BaseImponibleOimporteNoSujeto'], 2, '.', '')));
|
||||
}
|
||||
|
||||
// Add BaseImponibleACoste if present
|
||||
if (isset($this->desgloseFactura['BaseImponibleACoste'])) {
|
||||
|
|
@ -59,9 +57,9 @@ class Desglose extends BaseXmlModel
|
|||
}
|
||||
|
||||
// Add CuotaRepercutida if present
|
||||
if (isset($this->desgloseFactura['Cuota'])) {
|
||||
if (isset($this->desgloseFactura['CuotaRepercutida'])) {
|
||||
$detalleDesglose->appendChild($this->createElement($doc, 'CuotaRepercutida',
|
||||
number_format($this->desgloseFactura['Cuota'], 2, '.', '')));
|
||||
number_format($this->desgloseFactura['CuotaRepercutida'], 2, '.', '')));
|
||||
}
|
||||
|
||||
// Add TipoRecargoEquivalencia if present
|
||||
|
|
@ -95,13 +93,15 @@ class Desglose extends BaseXmlModel
|
|||
}
|
||||
|
||||
// Add BaseImponibleOimporteNoSujeto (required)
|
||||
if (isset($this->desgloseIVA['BaseImponibleOimporteNoSujeto'])) {
|
||||
$detalleDesglose->appendChild($this->createElement($doc, 'BaseImponibleOimporteNoSujeto',
|
||||
number_format($this->desgloseIVA['BaseImponible'], 2, '.', '')));
|
||||
number_format($this->desgloseIVA['BaseImponibleOimporteNoSujeto'], 2, '.', '')));
|
||||
}
|
||||
|
||||
// Add CuotaRepercutida if present
|
||||
if (isset($this->desgloseIVA['Cuota'])) {
|
||||
if (isset($this->desgloseIVA['CuotaRepercutida'])) {
|
||||
$detalleDesglose->appendChild($this->createElement($doc, 'CuotaRepercutida',
|
||||
number_format($this->desgloseIVA['Cuota'], 2, '.', '')));
|
||||
number_format($this->desgloseIVA['CuotaRepercutida'], 2, '.', '')));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -110,7 +110,7 @@ class Desglose extends BaseXmlModel
|
|||
$root->appendChild($detalleDesglose);
|
||||
}
|
||||
|
||||
return $doc->saveXML();
|
||||
return $root;
|
||||
}
|
||||
|
||||
public static function fromDOMElement(\DOMElement $element): self
|
||||
|
|
|
|||
|
|
@ -8,37 +8,23 @@ class Encadenamiento extends BaseXmlModel
|
|||
protected ?EncadenamientoFacturaAnterior $registroAnterior = null;
|
||||
protected ?EncadenamientoFacturaAnterior $registroPosterior = null;
|
||||
|
||||
public function toXml(): string
|
||||
public function toXml(\DOMDocument $doc): \DOMElement
|
||||
{
|
||||
$doc = new \DOMDocument('1.0', 'UTF-8');
|
||||
$root = $this->createElement($doc, 'Encadenamiento');
|
||||
$doc->appendChild($root);
|
||||
|
||||
if ($this->primerRegistro !== null) {
|
||||
$root->appendChild($this->createElement($doc, 'PrimerRegistro', 'S'));
|
||||
}
|
||||
|
||||
if ($this->registroAnterior !== null) {
|
||||
$registroAnteriorXml = $this->registroAnterior->toXml();
|
||||
$registroAnteriorDoc = new \DOMDocument();
|
||||
$registroAnteriorDoc->loadXML($registroAnteriorXml);
|
||||
$registroAnteriorNode = $doc->importNode($registroAnteriorDoc->documentElement, true);
|
||||
$root->appendChild($registroAnteriorNode);
|
||||
$root->appendChild($this->registroAnterior->toXml($doc));
|
||||
}
|
||||
|
||||
if ($this->registroPosterior !== null) {
|
||||
$registroPosteriorXml = $this->registroPosterior->toXml();
|
||||
$registroPosteriorDoc = new \DOMDocument();
|
||||
$registroPosteriorDoc->loadXML($registroPosteriorXml);
|
||||
$registroPosteriorNode = $doc->importNode($registroPosteriorDoc->documentElement, true);
|
||||
$root->appendChild($registroPosteriorNode);
|
||||
$root->appendChild($this->registroPosterior->toXml($doc));
|
||||
}
|
||||
|
||||
// Add namespace declaration to the root element
|
||||
$root->setAttribute('xmlns:sf', self::XML_NAMESPACE);
|
||||
$root->setAttribute('xmlns:ds', self::XML_DS_NAMESPACE);
|
||||
|
||||
return $doc->saveXML();
|
||||
return $root;
|
||||
}
|
||||
|
||||
public static function fromXml($xml): BaseXmlModel
|
||||
|
|
@ -140,20 +126,16 @@ class EncadenamientoFacturaAnterior extends BaseXmlModel
|
|||
protected string $fechaExpedicionFactura;
|
||||
protected string $huella;
|
||||
|
||||
public function toXml(): string
|
||||
public function toXml(\DOMDocument $doc): \DOMElement
|
||||
{
|
||||
$doc = new \DOMDocument('1.0', 'UTF-8');
|
||||
$doc->formatOutput = true;
|
||||
|
||||
$root = $this->createElement($doc, 'RegistroAnterior');
|
||||
$doc->appendChild($root);
|
||||
|
||||
$root->appendChild($this->createElement($doc, 'IDEmisorFactura', $this->idEmisorFactura));
|
||||
$root->appendChild($this->createElement($doc, 'NumSerieFactura', $this->numSerieFactura));
|
||||
$root->appendChild($this->createElement($doc, 'FechaExpedicionFactura', $this->fechaExpedicionFactura));
|
||||
$root->appendChild($this->createElement($doc, 'Huella', $this->huella));
|
||||
|
||||
return $doc->saveXML();
|
||||
return $root;
|
||||
}
|
||||
|
||||
public static function fromDOMElement(\DOMElement $element): self
|
||||
|
|
|
|||
|
|
@ -436,61 +436,66 @@ class Invoice extends BaseXmlModel
|
|||
return $this;
|
||||
}
|
||||
|
||||
protected function signXml(\DOMDocument $doc): void
|
||||
public function signXml(\DOMDocument $doc): void
|
||||
{
|
||||
if (!$this->privateKeyPath || !file_exists($this->privateKeyPath)) {
|
||||
throw new \RuntimeException('Private key not found or not set');
|
||||
if (!file_exists($this->certificatePath)) {
|
||||
throw new \RuntimeException("Certificate file not found at: " . $this->certificatePath);
|
||||
}
|
||||
|
||||
if (!$this->certificatePath || !file_exists($this->certificatePath)) {
|
||||
throw new \RuntimeException('Certificate not found or not set');
|
||||
if (!file_exists($this->privateKeyPath)) {
|
||||
throw new \RuntimeException("Private key file not found at: " . $this->privateKeyPath);
|
||||
}
|
||||
|
||||
Log::info('Starting XML signing process');
|
||||
Log::debug('XML before signing: ' . $doc->saveXML());
|
||||
|
||||
try {
|
||||
// Create a new Security object
|
||||
Log::debug('Creating XMLSecurityDSig object');
|
||||
$objDSig = new XMLSecurityDSig();
|
||||
$xmlContent = $doc->saveXML();
|
||||
Log::debug("XML before signing:", ['xml' => $xmlContent]);
|
||||
|
||||
// Set canonicalization method
|
||||
Log::debug('Setting canonicalization method');
|
||||
$objDSig = new XMLSecurityDSig();
|
||||
$objDSig->setCanonicalMethod(XMLSecurityDSig::EXC_C14N);
|
||||
|
||||
// Create a new Security key
|
||||
Log::debug('Creating XMLSecurityKey object');
|
||||
$objKey = new XMLSecurityKey(XMLSecurityKey::RSA_SHA256, ['type' => 'private']);
|
||||
// Create a new security key
|
||||
$objKey = new XMLSecurityKey(XMLSecurityKey::RSA_SHA256, array('type' => 'private'));
|
||||
|
||||
// Load the private key
|
||||
Log::debug('Loading private key from: ' . $this->privateKeyPath);
|
||||
$objKey->loadKey($this->privateKeyPath, true);
|
||||
|
||||
// Add reference
|
||||
Log::debug('Adding reference to document');
|
||||
// Add the reference
|
||||
$objDSig->addReference(
|
||||
$doc,
|
||||
XMLSecurityDSig::SHA256,
|
||||
['http://www.w3.org/2000/09/xmldsig#enveloped-signature']
|
||||
[
|
||||
'http://www.w3.org/2000/09/xmldsig#enveloped-signature',
|
||||
'http://www.w3.org/2001/10/xml-exc-c14n#'
|
||||
],
|
||||
['force_uri' => true]
|
||||
);
|
||||
Log::debug('Added reference to document');
|
||||
|
||||
// Add the certificate
|
||||
Log::debug('Adding certificate');
|
||||
// Add the certificate to the security object
|
||||
$objDSig->add509Cert(file_get_contents($this->certificatePath));
|
||||
|
||||
// Sign the XML document
|
||||
Log::debug('Signing document');
|
||||
// Append the signature
|
||||
$objDSig->sign($objKey);
|
||||
|
||||
// Append the signature to the XML
|
||||
Log::debug('Appending signature');
|
||||
$objDSig->appendSignature($doc->documentElement);
|
||||
|
||||
Log::debug('XML after signing: ' . $doc->saveXML());
|
||||
// Verify the signature
|
||||
$objKey = new XMLSecurityKey(XMLSecurityKey::RSA_SHA256, array('type' => 'public'));
|
||||
$objKey->loadKey($this->publicKeyPath, true, true);
|
||||
|
||||
if ($objDSig->verify($objKey) === 1) {
|
||||
Log::debug("Signature verification successful");
|
||||
} else {
|
||||
Log::error("Signature verification failed");
|
||||
}
|
||||
|
||||
$xmlContent = $doc->saveXML();
|
||||
Log::debug("XML after signing:", ['xml' => $xmlContent]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Error during XML signing: ' . $e->getMessage());
|
||||
Log::error('Stack trace: ' . $e->getTraceAsString());
|
||||
Log::error("Error during XML signing: " . $e->getMessage(), [
|
||||
'exception' => $e,
|
||||
'trace' => $e->getTraceAsString()
|
||||
]);
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
|
@ -501,65 +506,65 @@ class Invoice extends BaseXmlModel
|
|||
throw new \RuntimeException('Public key not found or not set');
|
||||
}
|
||||
|
||||
try {
|
||||
Log::info('Starting signature verification');
|
||||
Log::debug('XML to verify: ' . $doc->saveXML());
|
||||
|
||||
// Get the signature node
|
||||
$objXMLSecDSig = new XMLSecurityDSig();
|
||||
|
||||
// Locate the signature
|
||||
Log::debug('Locating signature');
|
||||
$objDSig = $objXMLSecDSig->locateSignature($doc);
|
||||
if (!$objDSig) {
|
||||
throw new \RuntimeException('Signature not found in document');
|
||||
}
|
||||
|
||||
// Canonicalize the signed info
|
||||
Log::debug('Canonicalizing SignedInfo');
|
||||
$objXMLSecDSig->canonicalizeSignedInfo();
|
||||
|
||||
// Validate references
|
||||
Log::debug('Validating references');
|
||||
try {
|
||||
$objXMLSecDSig->validateReference();
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Reference validation failed: ' . $e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
|
||||
// Get the key from the certificate
|
||||
Log::debug('Locating key');
|
||||
$objKey = $objXMLSecDSig->locateKey();
|
||||
if (!$objKey) {
|
||||
throw new \RuntimeException('Key not found in signature');
|
||||
}
|
||||
|
||||
// Load the public key
|
||||
Log::debug('Loading public key from: ' . $this->publicKeyPath);
|
||||
$objKey->loadKey($this->publicKeyPath, false, true);
|
||||
|
||||
// Verify the signature
|
||||
return $objXMLSecDSig->verify($objKey) === 1;
|
||||
Log::debug('Verifying signature');
|
||||
$result = $objXMLSecDSig->verify($objKey) === 1;
|
||||
|
||||
Log::info('Signature verification ' . ($result ? 'succeeded' : 'failed'));
|
||||
return $result;
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Error during signature verification: ' . $e->getMessage());
|
||||
Log::error('Stack trace: ' . $e->getTraceAsString());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
public function toXml(): string
|
||||
public function toXml(\DOMDocument $doc): \DOMElement
|
||||
{
|
||||
// Validate required fields first, outside of try-catch
|
||||
$requiredFields = [
|
||||
'idVersion' => 'IDVersion',
|
||||
'idFactura' => 'NumSerieFactura',
|
||||
'nombreRazonEmisor' => 'NombreRazonEmisor',
|
||||
'tipoFactura' => 'TipoFactura',
|
||||
'descripcionOperacion' => 'DescripcionOperacion',
|
||||
'cuotaTotal' => 'CuotaTotal',
|
||||
'importeTotal' => 'ImporteTotal',
|
||||
'fechaHoraHusoGenRegistro' => 'FechaHoraHusoGenRegistro',
|
||||
'tipoHuella' => 'TipoHuella',
|
||||
'huella' => 'Huella'
|
||||
];
|
||||
|
||||
foreach ($requiredFields as $property => $fieldName) {
|
||||
if (!isset($this->$property)) {
|
||||
throw new \InvalidArgumentException("Missing required field: $fieldName");
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$doc = new \DOMDocument('1.0', 'UTF-8');
|
||||
$doc->preserveWhiteSpace = false;
|
||||
$doc->formatOutput = true;
|
||||
|
||||
// Create root element with proper namespaces
|
||||
$root = $doc->createElementNS(parent::XML_NAMESPACE, parent::XML_NAMESPACE_PREFIX . ':RegistroAlta');
|
||||
$root->setAttribute('xmlns:ds', parent::XML_DS_NAMESPACE);
|
||||
$doc->appendChild($root);
|
||||
|
||||
// Add namespaces
|
||||
$root->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:' . parent::XML_NAMESPACE_PREFIX, parent::XML_NAMESPACE);
|
||||
$root->setAttributeNS('http://www.w3.org/2000/xmlns/', 'xmlns:' . parent::XML_DS_NAMESPACE_PREFIX, parent::XML_DS_NAMESPACE);
|
||||
|
||||
// Add required elements in exact order according to schema
|
||||
$root->appendChild($this->createElement($doc, 'IDVersion', $this->idVersion));
|
||||
|
|
@ -597,18 +602,14 @@ class Invoice extends BaseXmlModel
|
|||
$root->appendChild($facturasRectificadas);
|
||||
|
||||
// Add ImporteRectificacion
|
||||
$importeRectificacion = $this->createElement($doc, 'ImporteRectificacion');
|
||||
$importeRectificacion->appendChild($this->createElement($doc, 'BaseRectificada',
|
||||
number_format($this->facturaRectificativa->getBaseRectificada(), 2, '.', '')));
|
||||
$importeRectificacion->appendChild($this->createElement($doc, 'CuotaRectificada',
|
||||
number_format($this->facturaRectificativa->getCuotaRectificada(), 2, '.', '')));
|
||||
|
||||
if ($this->facturaRectificativa->getCuotaRecargoRectificado() !== null) {
|
||||
$importeRectificacion->appendChild($this->createElement($doc, 'CuotaRecargoRectificado',
|
||||
number_format($this->facturaRectificativa->getCuotaRecargoRectificado(), 2, '.', '')));
|
||||
if ($this->importeRectificacion !== null) {
|
||||
$root->appendChild($this->createElement($doc, 'ImporteRectificacion', (string)$this->importeRectificacion));
|
||||
}
|
||||
}
|
||||
|
||||
$root->appendChild($importeRectificacion);
|
||||
// Add other optional elements
|
||||
if ($this->fechaOperacion !== null) {
|
||||
$root->appendChild($this->createElement($doc, 'FechaOperacion', $this->fechaOperacion));
|
||||
}
|
||||
|
||||
$root->appendChild($this->createElement($doc, 'DescripcionOperacion', $this->descripcionOperacion));
|
||||
|
|
@ -629,7 +630,7 @@ class Invoice extends BaseXmlModel
|
|||
$root->appendChild($this->createElement($doc, 'EmitidaPorTerceroODestinatario', $this->emitidaPorTerceroODestinatario));
|
||||
}
|
||||
|
||||
// Add tercero if present
|
||||
// Add Tercero if set
|
||||
if ($this->tercero !== null) {
|
||||
$terceroElement = $this->createElement($doc, 'Tercero');
|
||||
$terceroElement->appendChild($this->createElement($doc, 'NombreRazon', $this->tercero->getRazonSocial()));
|
||||
|
|
@ -637,21 +638,21 @@ class Invoice extends BaseXmlModel
|
|||
$root->appendChild($terceroElement);
|
||||
}
|
||||
|
||||
// Add destinatarios if present
|
||||
// Add Destinatarios if set
|
||||
if ($this->destinatarios !== null && count($this->destinatarios) > 0) {
|
||||
$destinatariosElement = $this->createElement($doc, 'Destinatarios');
|
||||
foreach ($this->destinatarios as $destinatario) {
|
||||
$idDestinatarioElement = $this->createElement($doc, 'IDDestinatario');
|
||||
$idDestinatarioElement->appendChild($this->createElement($doc, 'NombreRazon', $destinatario->getNombreRazon() ?? $destinatario->getRazonSocial()));
|
||||
|
||||
// Handle either NIF or IDOtro
|
||||
// Add NombreRazon
|
||||
$idDestinatarioElement->appendChild($this->createElement($doc, 'NombreRazon', $destinatario->getNombreRazon()));
|
||||
|
||||
// Add either NIF or IDOtro
|
||||
if ($destinatario->getNif() !== null) {
|
||||
$idDestinatarioElement->appendChild($this->createElement($doc, 'NIF', $destinatario->getNif()));
|
||||
} else {
|
||||
$idOtroElement = $this->createElement($doc, 'IDOtro');
|
||||
if ($destinatario->getPais() !== null) {
|
||||
$idOtroElement->appendChild($this->createElement($doc, 'CodigoPais', $destinatario->getPais()));
|
||||
}
|
||||
$idOtroElement->appendChild($this->createElement($doc, 'IDType', $destinatario->getTipoIdentificacion()));
|
||||
$idOtroElement->appendChild($this->createElement($doc, 'ID', $destinatario->getIdOtro()));
|
||||
$idDestinatarioElement->appendChild($idOtroElement);
|
||||
|
|
@ -662,92 +663,86 @@ class Invoice extends BaseXmlModel
|
|||
$root->appendChild($destinatariosElement);
|
||||
}
|
||||
|
||||
// Add desglose
|
||||
try {
|
||||
$desgloseXml = $this->desglose->toXml();
|
||||
$desgloseDoc = new \DOMDocument();
|
||||
if (!$desgloseDoc->loadXML($desgloseXml)) {
|
||||
error_log("Failed to load desglose XML");
|
||||
throw new \DOMException('Failed to load desglose XML');
|
||||
}
|
||||
$desgloseNode = $doc->importNode($desgloseDoc->documentElement, true);
|
||||
// Remove any existing namespace declarations
|
||||
foreach (['xmlns:sf', 'xmlns:ds'] as $attr) {
|
||||
if ($desgloseNode->hasAttribute($attr)) {
|
||||
$desgloseNode->removeAttribute($attr);
|
||||
}
|
||||
}
|
||||
$root->appendChild($desgloseNode);
|
||||
} catch (\Exception $e) {
|
||||
error_log("Error in desglose: " . $e->getMessage());
|
||||
throw $e;
|
||||
// Add Desglose
|
||||
if ($this->desglose !== null) {
|
||||
$root->appendChild($this->desglose->toXml($doc));
|
||||
}
|
||||
|
||||
// Add CuotaTotal and ImporteTotal
|
||||
$root->appendChild($this->createElement($doc, 'CuotaTotal', number_format($this->cuotaTotal, 2, '.', '')));
|
||||
$root->appendChild($this->createElement($doc, 'ImporteTotal', number_format($this->importeTotal, 2, '.', '')));
|
||||
$root->appendChild($this->createElement($doc, 'CuotaTotal', (string)$this->cuotaTotal));
|
||||
$root->appendChild($this->createElement($doc, 'ImporteTotal', (string)$this->importeTotal));
|
||||
|
||||
// Add encadenamiento
|
||||
try {
|
||||
$encadenamientoXml = $this->encadenamiento->toXml();
|
||||
$encadenamientoDoc = new \DOMDocument();
|
||||
if (!$encadenamientoDoc->loadXML($encadenamientoXml)) {
|
||||
error_log("Failed to load encadenamiento XML");
|
||||
throw new \DOMException('Failed to load encadenamiento XML');
|
||||
}
|
||||
$encadenamientoNode = $doc->importNode($encadenamientoDoc->documentElement, true);
|
||||
$root->appendChild($encadenamientoNode);
|
||||
} catch (\Exception $e) {
|
||||
error_log("Error in encadenamiento: " . $e->getMessage());
|
||||
throw $e;
|
||||
// Add Encadenamiento
|
||||
if ($this->encadenamiento !== null) {
|
||||
$root->appendChild($this->encadenamiento->toXml($doc));
|
||||
}
|
||||
|
||||
// Add sistema informatico
|
||||
$sistemaElement = $this->createElement($doc, 'SistemaInformatico');
|
||||
$sistemaElement->appendChild($this->createElement($doc, 'NombreRazon', $this->sistemaInformatico->getNombreRazon()));
|
||||
$sistemaElement->appendChild($this->createElement($doc, 'NIF', $this->sistemaInformatico->getNif()));
|
||||
$sistemaElement->appendChild($this->createElement($doc, 'NombreSistemaInformatico', $this->sistemaInformatico->getNombreSistemaInformatico()));
|
||||
$sistemaElement->appendChild($this->createElement($doc, 'IdSistemaInformatico', $this->sistemaInformatico->getIdSistemaInformatico()));
|
||||
$sistemaElement->appendChild($this->createElement($doc, 'Version', $this->sistemaInformatico->getVersion()));
|
||||
$sistemaElement->appendChild($this->createElement($doc, 'NumeroInstalacion', $this->sistemaInformatico->getNumeroInstalacion()));
|
||||
$sistemaElement->appendChild($this->createElement($doc, 'TipoUsoPosibleSoloVerifactu', $this->sistemaInformatico->getTipoUsoPosibleSoloVerifactu()));
|
||||
$sistemaElement->appendChild($this->createElement($doc, 'TipoUsoPosibleMultiOT', $this->sistemaInformatico->getTipoUsoPosibleMultiOT()));
|
||||
$sistemaElement->appendChild($this->createElement($doc, 'IndicadorMultiplesOT', $this->sistemaInformatico->getIndicadorMultiplesOT()));
|
||||
$root->appendChild($sistemaElement);
|
||||
// Add SistemaInformatico
|
||||
if ($this->sistemaInformatico !== null) {
|
||||
$root->appendChild($this->sistemaInformatico->toXml($doc));
|
||||
}
|
||||
|
||||
// Add remaining required fields
|
||||
// Add FechaHoraHusoGenRegistro
|
||||
$root->appendChild($this->createElement($doc, 'FechaHoraHusoGenRegistro', $this->fechaHoraHusoGenRegistro));
|
||||
|
||||
// Add NumRegistroAcuerdoFacturacion
|
||||
if ($this->numRegistroAcuerdoFacturacion !== null) {
|
||||
$root->appendChild($this->createElement($doc, 'NumRegistroAcuerdoFacturacion', $this->numRegistroAcuerdoFacturacion));
|
||||
}
|
||||
|
||||
// Add IdAcuerdoSistemaInformatico
|
||||
if ($this->idAcuerdoSistemaInformatico !== null) {
|
||||
$root->appendChild($this->createElement($doc, 'IDAcuerdoSistemaInformatico', $this->idAcuerdoSistemaInformatico));
|
||||
$root->appendChild($this->createElement($doc, 'IdAcuerdoSistemaInformatico', $this->idAcuerdoSistemaInformatico));
|
||||
}
|
||||
|
||||
// Add TipoHuella and Huella
|
||||
$root->appendChild($this->createElement($doc, 'TipoHuella', $this->tipoHuella));
|
||||
$root->appendChild($this->createElement($doc, 'Huella', $this->huella));
|
||||
|
||||
// Add signature if present
|
||||
if ($this->signature !== null) {
|
||||
$signatureElement = $this->createDsElement($doc, 'Signature', $this->signature);
|
||||
$root->appendChild($signatureElement);
|
||||
return $root;
|
||||
}
|
||||
|
||||
// Sign the document if private key is set
|
||||
if ($this->privateKeyPath !== null) {
|
||||
try {
|
||||
$this->signXml($doc);
|
||||
} catch (\Exception $e) {
|
||||
throw new \RuntimeException('Failed to sign XML: ' . $e->getMessage());
|
||||
public function toXmlString(): string
|
||||
{
|
||||
// Validate required fields first, outside of try-catch
|
||||
$requiredFields = [
|
||||
'idVersion' => 'IDVersion',
|
||||
'idFactura' => 'NumSerieFactura',
|
||||
'nombreRazonEmisor' => 'NombreRazonEmisor',
|
||||
'tipoFactura' => 'TipoFactura',
|
||||
'descripcionOperacion' => 'DescripcionOperacion',
|
||||
'cuotaTotal' => 'CuotaTotal',
|
||||
'importeTotal' => 'ImporteTotal',
|
||||
'fechaHoraHusoGenRegistro' => 'FechaHoraHusoGenRegistro',
|
||||
'tipoHuella' => 'TipoHuella',
|
||||
'huella' => 'Huella'
|
||||
];
|
||||
|
||||
foreach ($requiredFields as $property => $fieldName) {
|
||||
if (!isset($this->$property)) {
|
||||
throw new \InvalidArgumentException("Missing required field: $fieldName");
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
$doc = new \DOMDocument('1.0', 'UTF-8');
|
||||
$doc->preserveWhiteSpace = false;
|
||||
$doc->formatOutput = true;
|
||||
|
||||
// Create root element using toXml method
|
||||
$root = $this->toXml($doc);
|
||||
$doc->appendChild($root);
|
||||
|
||||
// Sign the document if certificates are set
|
||||
if ($this->privateKeyPath && $this->certificatePath) {
|
||||
$this->signXml($doc);
|
||||
}
|
||||
|
||||
return $doc->saveXML();
|
||||
} catch (\Exception $e) {
|
||||
error_log("Error in toXml: " . $e->getMessage());
|
||||
throw new \RuntimeException("Error in toXml: " . $e->getMessage(), 0, $e);
|
||||
Log::error('Error generating XML: ' . $e->getMessage());
|
||||
Log::error('Stack trace: ' . $e->getTraceAsString());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -103,13 +103,9 @@ class PersonaFisicaJuridica extends BaseXmlModel
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function toXml(): string
|
||||
public function toXml(\DOMDocument $doc): \DOMElement
|
||||
{
|
||||
$doc = new \DOMDocument('1.0', 'UTF-8');
|
||||
$doc->formatOutput = true;
|
||||
|
||||
$root = $this->createElement($doc, 'PersonaFisicaJuridica');
|
||||
$doc->appendChild($root);
|
||||
|
||||
if ($this->nif !== null) {
|
||||
$root->appendChild($this->createElement($doc, 'NIF', $this->nif));
|
||||
|
|
@ -143,7 +139,7 @@ class PersonaFisicaJuridica extends BaseXmlModel
|
|||
$root->appendChild($this->createElement($doc, 'Pais', $this->pais));
|
||||
}
|
||||
|
||||
return $doc->saveXML($root);
|
||||
return $root;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -15,13 +15,9 @@ class SistemaInformatico extends BaseXmlModel
|
|||
protected string $tipoUsoPosibleMultiOT = 'S';
|
||||
protected string $indicadorMultiplesOT = 'S';
|
||||
|
||||
public function toXml(): string
|
||||
public function toXml(\DOMDocument $doc): \DOMElement
|
||||
{
|
||||
$doc = new \DOMDocument('1.0', 'UTF-8');
|
||||
$doc->formatOutput = true;
|
||||
|
||||
$root = $this->createElement($doc, 'SistemaInformatico');
|
||||
$doc->appendChild($root);
|
||||
|
||||
// Add nombreRazon
|
||||
$root->appendChild($this->createElement($doc, 'NombreRazon', $this->nombreRazon));
|
||||
|
|
@ -42,7 +38,7 @@ class SistemaInformatico extends BaseXmlModel
|
|||
$root->appendChild($this->createElement($doc, 'TipoUsoPosibleMultiOT', $this->tipoUsoPosibleMultiOT));
|
||||
$root->appendChild($this->createElement($doc, 'IndicadorMultiplesOT', $this->indicadorMultiplesOT));
|
||||
|
||||
return $doc->saveXML();
|
||||
return $root;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ use App\Services\EDocument\Standards\Verifactu\Models\SistemaInformatico;
|
|||
use App\Services\EDocument\Standards\Verifactu\Models\PrimerRegistroCadena;
|
||||
use App\Services\EDocument\Standards\Verifactu\Models\PersonaFisicaJuridica;
|
||||
use App\Services\EDocument\Standards\Verifactu\Models\FacturaRectificativa;
|
||||
use App\Services\EDocument\Standards\Verifactu\Models\DetalleDesglose;
|
||||
|
||||
class InvoiceTest extends BaseModelTest
|
||||
{
|
||||
|
|
@ -43,9 +44,9 @@ class InvoiceTest extends BaseModelTest
|
|||
'Impuesto' => '01',
|
||||
'ClaveRegimen' => '01',
|
||||
'CalificacionOperacion' => 'S1',
|
||||
'BaseImponible' => 1000.00,
|
||||
'BaseImponibleOimporteNoSujeto' => 1000.00,
|
||||
'TipoImpositivo' => 21,
|
||||
'Cuota' => 210.00
|
||||
'CuotaRepercutida' => 210.00
|
||||
]);
|
||||
$invoice->setDesglose($desglose);
|
||||
|
||||
|
|
@ -74,7 +75,7 @@ class InvoiceTest extends BaseModelTest
|
|||
->setDescripcionCupon('Descuento promocional');
|
||||
// $invoice->setCupon($cupon);
|
||||
|
||||
$xml = $invoice->toXml();
|
||||
$xml = $invoice->toXmlString();
|
||||
|
||||
// Debug output
|
||||
echo "\nGenerated XML:\n";
|
||||
|
|
@ -131,9 +132,9 @@ class InvoiceTest extends BaseModelTest
|
|||
'Impuesto' => '01',
|
||||
'ClaveRegimen' => '02',
|
||||
'CalificacionOperacion' => 'S2',
|
||||
'BaseImponible' => 100.00,
|
||||
'BaseImponibleOimporteNoSujeto' => 100.00,
|
||||
'TipoImpositivo' => 21,
|
||||
'Cuota' => 21.00
|
||||
'CuotaRepercutida' => 21.00
|
||||
]);
|
||||
$invoice->setDesglose($desglose);
|
||||
|
||||
|
|
@ -156,7 +157,7 @@ class InvoiceTest extends BaseModelTest
|
|||
$encadenamiento->setPrimerRegistro('S');
|
||||
$invoice->setEncadenamiento($encadenamiento);
|
||||
|
||||
$xml = $invoice->toXml();
|
||||
$xml = $invoice->toXmlString();
|
||||
|
||||
// Debug output
|
||||
echo "\nGenerated XML:\n";
|
||||
|
|
@ -211,31 +212,31 @@ class InvoiceTest extends BaseModelTest
|
|||
'Impuesto' => '01',
|
||||
'ClaveRegimen' => '02',
|
||||
'CalificacionOperacion' => 'S2',
|
||||
'BaseImponible' => -100.00,
|
||||
'BaseImponibleOimporteNoSujeto' => -100.00,
|
||||
'TipoImpositivo' => 21,
|
||||
'Cuota' => -21.00
|
||||
'CuotaRepercutida' => -21.00
|
||||
]);
|
||||
$invoice->setDesglose($desglose);
|
||||
|
||||
// Add FacturaRectificativa
|
||||
$facturaRectificativa = new FacturaRectificativa(
|
||||
'I', // TipoRectificativa
|
||||
-100.00, // BaseRectificada
|
||||
-21.00 // CuotaRectificada
|
||||
);
|
||||
$facturaRectificativa->addFacturaRectificada(
|
||||
'B12345678', // NIF
|
||||
'FAC-2023-001', // NumSerieFactura
|
||||
'01-01-2023' // FechaExpedicionFactura
|
||||
);
|
||||
$invoice->setFacturaRectificativa($facturaRectificativa);
|
||||
|
||||
// Add encadenamiento with PrimerRegistro
|
||||
// Add encadenamiento
|
||||
$encadenamiento = new Encadenamiento();
|
||||
$encadenamiento->setPrimerRegistro('S');
|
||||
$invoice->setEncadenamiento($encadenamiento);
|
||||
|
||||
$xml = $invoice->toXml();
|
||||
// Add rectified invoice
|
||||
$facturaRectificativa = new FacturaRectificativa(
|
||||
'I', // tipoRectificativa
|
||||
-100.00, // baseRectificada
|
||||
-21.00 // cuotaRectificada
|
||||
);
|
||||
$facturaRectificativa->addFacturaRectificada(
|
||||
'B12345678', // nif
|
||||
'FAC-2023-001', // numSerie
|
||||
'01-01-2023' // fecha
|
||||
);
|
||||
$invoice->setFacturaRectificativa($facturaRectificativa);
|
||||
|
||||
$xml = $invoice->toXmlString();
|
||||
|
||||
// Debug output
|
||||
echo "\nGenerated XML:\n";
|
||||
|
|
@ -281,12 +282,15 @@ class InvoiceTest extends BaseModelTest
|
|||
->setNumeroInstalacion('INST-001');
|
||||
$invoice->setSistemaInformatico($sistema);
|
||||
|
||||
// Add desglose
|
||||
// Add desglose with correct key names
|
||||
$desglose = new Desglose();
|
||||
$desglose->setDesgloseIVA([
|
||||
'BaseImponible' => 100.00,
|
||||
'TipoImpositivo' => 21,
|
||||
'Cuota' => 21.00
|
||||
'Impuesto' => '01',
|
||||
'ClaveRegimen' => '02',
|
||||
'CalificacionOperacion' => 'S2',
|
||||
'BaseImponibleOimporteNoSujeto' => 100.00,
|
||||
'TipoImpositivo' => 21.00,
|
||||
'CuotaRepercutida' => 21.00
|
||||
]);
|
||||
$invoice->setDesglose($desglose);
|
||||
|
||||
|
|
@ -295,7 +299,12 @@ class InvoiceTest extends BaseModelTest
|
|||
$encadenamiento->setPrimerRegistro('S');
|
||||
$invoice->setEncadenamiento($encadenamiento);
|
||||
|
||||
$xml = $invoice->toXml();
|
||||
$xml = $invoice->toXmlString();
|
||||
|
||||
// Debug output
|
||||
echo "\nGenerated XML:\n";
|
||||
echo $xml;
|
||||
echo "\n\n";
|
||||
|
||||
// Validate against XSD
|
||||
$this->assertValidatesAgainstXsd($xml, $this->getTestXsdPath());
|
||||
|
|
@ -307,6 +316,8 @@ class InvoiceTest extends BaseModelTest
|
|||
$this->assertEquals($invoice->getNombreRazonEmisor(), $deserialized->getNombreRazonEmisor());
|
||||
$this->assertEquals($invoice->getTipoFactura(), $deserialized->getTipoFactura());
|
||||
$this->assertEquals($invoice->getFacturaSinIdentifDestinatarioArt61d(), $deserialized->getFacturaSinIdentifDestinatarioArt61d());
|
||||
$this->assertEquals($invoice->getCuotaTotal(), $deserialized->getCuotaTotal());
|
||||
$this->assertEquals($invoice->getImporteTotal(), $deserialized->getImporteTotal());
|
||||
}
|
||||
|
||||
public function testInvalidXmlThrowsException(): void
|
||||
|
|
@ -324,7 +335,7 @@ class InvoiceTest extends BaseModelTest
|
|||
$this->expectException(\InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('Missing required field: IDVersion');
|
||||
|
||||
$invoice->toXml();
|
||||
$invoice->toXmlString();
|
||||
}
|
||||
|
||||
public function test_create_and_serialize_rectification_invoice()
|
||||
|
|
@ -342,9 +353,12 @@ class InvoiceTest extends BaseModelTest
|
|||
->setRazonSocial('Empresa Ejemplo SL'))
|
||||
->setDesglose((new Desglose())
|
||||
->setDesgloseIVA([
|
||||
'BaseImponible' => 1000.00,
|
||||
'TipoImpositivo' => 21,
|
||||
'Cuota' => 210.00
|
||||
'Impuesto' => '01',
|
||||
'ClaveRegimen' => '01',
|
||||
'CalificacionOperacion' => 'S1',
|
||||
'BaseImponibleOimporteNoSujeto' => 1000.00,
|
||||
'TipoImpositivo' => 21.00,
|
||||
'CuotaRepercutida' => 210.00
|
||||
]))
|
||||
->setCuotaTotal(210)
|
||||
->setImporteTotal(1000)
|
||||
|
|
@ -380,7 +394,7 @@ class InvoiceTest extends BaseModelTest
|
|||
|
||||
$invoice->setFacturaRectificativa($facturaRectificativa);
|
||||
|
||||
$xml = $invoice->toXml();
|
||||
$xml = $invoice->toXmlString();
|
||||
|
||||
// Debug output
|
||||
echo "\nGenerated XML:\n";
|
||||
|
|
@ -388,6 +402,15 @@ class InvoiceTest extends BaseModelTest
|
|||
echo "\n\n";
|
||||
|
||||
$this->assertValidatesAgainstXsd($xml, $this->getTestXsdPath());
|
||||
|
||||
// Test deserialization
|
||||
$deserialized = Invoice::fromXml($xml);
|
||||
$this->assertEquals($invoice->getIdVersion(), $deserialized->getIdVersion());
|
||||
$this->assertEquals($invoice->getIdFactura(), $deserialized->getIdFactura());
|
||||
$this->assertEquals($invoice->getTipoFactura(), $deserialized->getTipoFactura());
|
||||
$this->assertEquals($invoice->getTipoRectificativa(), $deserialized->getTipoRectificativa());
|
||||
$this->assertEquals($invoice->getCuotaTotal(), $deserialized->getCuotaTotal());
|
||||
$this->assertEquals($invoice->getImporteTotal(), $deserialized->getImporteTotal());
|
||||
}
|
||||
|
||||
public function testCreateAndSerializeInvoiceWithMultipleRecipients(): void
|
||||
|
|
@ -410,28 +433,28 @@ class InvoiceTest extends BaseModelTest
|
|||
$destinatario1 = new PersonaFisicaJuridica();
|
||||
$destinatario1
|
||||
->setNif('B87654321')
|
||||
->setNombreRazon('Cliente 1 SL'); // Changed from setRazonSocial to setNombreRazon
|
||||
->setNombreRazon('Cliente 1 SL');
|
||||
$destinatarios[] = $destinatario1;
|
||||
|
||||
$destinatario2 = new PersonaFisicaJuridica();
|
||||
$destinatario2
|
||||
->setPais('FR') // French company
|
||||
->setTipoIdentificacion('02') // NIF-IVA (VAT number)
|
||||
->setIdOtro('FR12345678901') // French VAT number
|
||||
->setNombreRazon('Client 2 SARL'); // French company name
|
||||
->setPais('FR')
|
||||
->setTipoIdentificacion('02')
|
||||
->setIdOtro('FR12345678901')
|
||||
->setNombreRazon('Client 2 SARL');
|
||||
$destinatarios[] = $destinatario2;
|
||||
|
||||
$invoice->setDestinatarios($destinatarios);
|
||||
|
||||
// Add desglose with proper structure
|
||||
// Add desglose with proper structure and correct key names
|
||||
$desglose = new Desglose();
|
||||
$desglose->setDesgloseIVA([
|
||||
'Impuesto' => '01',
|
||||
'ClaveRegimen' => '01',
|
||||
'CalificacionOperacion' => 'S1',
|
||||
'BaseImponible' => 200.00,
|
||||
'BaseImponibleOimporteNoSujeto' => 200.00,
|
||||
'TipoImpositivo' => 21.00,
|
||||
'Cuota' => 42.00
|
||||
'CuotaRepercutida' => 42.00
|
||||
]);
|
||||
$invoice->setDesglose($desglose);
|
||||
|
||||
|
|
@ -454,7 +477,15 @@ class InvoiceTest extends BaseModelTest
|
|||
->setIndicadorMultiplesOT('S');
|
||||
$invoice->setSistemaInformatico($sistema);
|
||||
|
||||
$xml = $invoice->toXml();
|
||||
// Generate XML string
|
||||
$xml = $invoice->toXmlString();
|
||||
|
||||
// Debug output
|
||||
echo "\nGenerated XML:\n";
|
||||
echo $xml;
|
||||
echo "\n\n";
|
||||
|
||||
// Validate against XSD
|
||||
$this->assertValidatesAgainstXsd($xml, $this->getTestXsdPath());
|
||||
|
||||
// Test deserialization
|
||||
|
|
@ -493,10 +524,10 @@ class InvoiceTest extends BaseModelTest
|
|||
$desglose->setDesgloseIVA([
|
||||
'Impuesto' => '01',
|
||||
'ClaveRegimen' => '01',
|
||||
'OperacionExenta' => 'E1',
|
||||
'BaseImponible' => 100.00,
|
||||
'CalificacionOperacion' => 'N1',
|
||||
'BaseImponibleOimporteNoSujeto' => 100.00,
|
||||
'TipoImpositivo' => 0,
|
||||
'Cuota' => 0.00
|
||||
'CuotaRepercutida' => 0.00
|
||||
]);
|
||||
$invoice->setDesglose($desglose);
|
||||
|
||||
|
|
@ -519,7 +550,14 @@ class InvoiceTest extends BaseModelTest
|
|||
->setIndicadorMultiplesOT('S');
|
||||
$invoice->setSistemaInformatico($sistema);
|
||||
|
||||
$xml = $invoice->toXml();
|
||||
// Generate XML string
|
||||
$xml = $invoice->toXmlString();
|
||||
|
||||
// Debug output
|
||||
echo "\nGenerated XML:\n";
|
||||
echo $xml;
|
||||
echo "\n\n";
|
||||
|
||||
$this->assertValidatesAgainstXsd($xml, $this->getTestXsdPath());
|
||||
|
||||
// Test deserialization
|
||||
|
|
@ -582,7 +620,14 @@ class InvoiceTest extends BaseModelTest
|
|||
->setIndicadorMultiplesOT('S');
|
||||
$invoice->setSistemaInformatico($sistema);
|
||||
|
||||
$xml = $invoice->toXml();
|
||||
// Generate XML string
|
||||
$xml = $invoice->toXmlString();
|
||||
|
||||
// Debug output
|
||||
echo "\nGenerated XML:\n";
|
||||
echo $xml;
|
||||
echo "\n\n";
|
||||
|
||||
$this->assertValidatesAgainstXsd($xml, $this->getTestXsdPath());
|
||||
|
||||
// Test deserialization
|
||||
|
|
@ -637,7 +682,8 @@ class InvoiceTest extends BaseModelTest
|
|||
->setIndicadorMultiplesOT('S');
|
||||
$invoice->setSistemaInformatico($sistema);
|
||||
|
||||
$xml = $invoice->toXml();
|
||||
// Generate XML string
|
||||
$xml = $invoice->toXmlString();
|
||||
|
||||
// Debug output
|
||||
echo "\nGenerated XML:\n";
|
||||
|
|
@ -659,15 +705,22 @@ class InvoiceTest extends BaseModelTest
|
|||
->setIdFactura('FAC-2023-009')
|
||||
->setNombreRazonEmisor('Empresa Ejemplo SL')
|
||||
->setTipoFactura('F1')
|
||||
->setEmitidaPorTerceroODestinatario('T')
|
||||
->setDescripcionOperacion('Factura emitida por tercero')
|
||||
->setEmitidaPorTerceroODestinatario('T')
|
||||
->setCuotaTotal(21.00)
|
||||
->setImporteTotal(100.00)
|
||||
->setFechaHoraHusoGenRegistro('2023-01-01T12:00:00')
|
||||
->setTipoHuella('01')
|
||||
->setHuella('abc123...');
|
||||
|
||||
// Add desglose with proper structure
|
||||
// Set up the third party issuer
|
||||
$tercero = new PersonaFisicaJuridica();
|
||||
$tercero
|
||||
->setNif('B98765432')
|
||||
->setRazonSocial('Tercero Emisor SL');
|
||||
$invoice->setTercero($tercero);
|
||||
|
||||
// Add desglose
|
||||
$desglose = new Desglose();
|
||||
$desglose->setDesgloseIVA([
|
||||
'Impuesto' => '01',
|
||||
|
|
@ -679,13 +732,6 @@ class InvoiceTest extends BaseModelTest
|
|||
]);
|
||||
$invoice->setDesglose($desglose);
|
||||
|
||||
// Add third party with proper structure
|
||||
$tercero = new PersonaFisicaJuridica();
|
||||
$tercero
|
||||
->setRazonSocial('Tercero Emisor SL')
|
||||
->setNif('B98765432');
|
||||
$invoice->setTercero($tercero);
|
||||
|
||||
// Add encadenamiento (required)
|
||||
$encadenamiento = new Encadenamiento();
|
||||
$encadenamiento->setPrimerRegistro('S');
|
||||
|
|
@ -705,7 +751,8 @@ class InvoiceTest extends BaseModelTest
|
|||
->setIndicadorMultiplesOT('S');
|
||||
$invoice->setSistemaInformatico($sistema);
|
||||
|
||||
$xml = $invoice->toXml();
|
||||
// Generate XML string
|
||||
$xml = $invoice->toXmlString();
|
||||
|
||||
// Debug output
|
||||
echo "\nGenerated XML:\n";
|
||||
|
|
@ -768,7 +815,14 @@ class InvoiceTest extends BaseModelTest
|
|||
$encadenamiento->setPrimerRegistro('S');
|
||||
$invoice->setEncadenamiento($encadenamiento);
|
||||
|
||||
$xml = $invoice->toXml();
|
||||
// Generate XML string
|
||||
$xml = $invoice->toXmlString();
|
||||
|
||||
// Debug output
|
||||
echo "\nGenerated XML:\n";
|
||||
echo $xml;
|
||||
echo "\n\n";
|
||||
|
||||
$this->assertValidatesAgainstXsd($xml, $this->getTestXsdPath());
|
||||
|
||||
// Test deserialization
|
||||
|
|
@ -776,26 +830,43 @@ class InvoiceTest extends BaseModelTest
|
|||
$this->assertEquals('S', $deserialized->getMacrodato());
|
||||
}
|
||||
|
||||
public function testCreateAndSerializeInvoiceWithDigitalSignature(): void
|
||||
public function testCreateAndSerializeInvoiceWithAgreementData(): void
|
||||
{
|
||||
$invoice = new Invoice();
|
||||
$invoice
|
||||
->setIdVersion('1.0')
|
||||
->setIdFactura('FAC-2023-011')
|
||||
$invoice->setIdVersion('1.0')
|
||||
->setIdFactura('FAC-2023-012')
|
||||
->setNombreRazonEmisor('Empresa Ejemplo SL')
|
||||
->setTipoFactura('F1')
|
||||
->setDescripcionOperacion('Factura con firma digital')
|
||||
->setCuotaTotal(21.00)
|
||||
->setImporteTotal(100.00)
|
||||
->setDescripcionOperacion('Factura con datos de acuerdo')
|
||||
->setCuotaTotal(21)
|
||||
->setImporteTotal(100)
|
||||
->setFechaHoraHusoGenRegistro('2023-01-01T12:00:00')
|
||||
->setTipoHuella('01')
|
||||
->setHuella('abc123...');
|
||||
->setHuella('abc123...')
|
||||
->setNumRegistroAcuerdoFacturacion('REG-001')
|
||||
->setIdAcuerdoSistemaInformatico('AGR-001');
|
||||
|
||||
// Add sistema informatico with all required fields
|
||||
// Set up Desglose
|
||||
$desglose = new Desglose();
|
||||
$desglose->setDesgloseIVA([
|
||||
'Impuesto' => '01',
|
||||
'ClaveRegimen' => '02',
|
||||
'CalificacionOperacion' => 'S2',
|
||||
'BaseImponible' => 100.00,
|
||||
'TipoImpositivo' => 21.00,
|
||||
'Cuota' => 21.00
|
||||
]);
|
||||
$invoice->setDesglose($desglose);
|
||||
|
||||
// Set up Encadenamiento
|
||||
$encadenamiento = new Encadenamiento();
|
||||
$encadenamiento->setPrimerRegistro('S');
|
||||
$invoice->setEncadenamiento($encadenamiento);
|
||||
|
||||
// Set up SistemaInformatico
|
||||
$sistema = new SistemaInformatico();
|
||||
$sistema
|
||||
->setNombreRazon('Sistema de Facturación')
|
||||
->setNif('B12345678')
|
||||
$sistema->setNombreRazon('Sistema de Facturación')
|
||||
->setNIF('B12345678')
|
||||
->setNombreSistemaInformatico('SistemaFacturacion')
|
||||
->setIdSistemaInformatico('01')
|
||||
->setVersion('1.0')
|
||||
|
|
@ -805,142 +876,69 @@ class InvoiceTest extends BaseModelTest
|
|||
->setIndicadorMultiplesOT('S');
|
||||
$invoice->setSistemaInformatico($sistema);
|
||||
|
||||
// Add desglose with proper structure
|
||||
$desglose = new Desglose();
|
||||
$desglose->setDesgloseIVA([
|
||||
'Impuesto' => '01',
|
||||
'ClaveRegimen' => '02',
|
||||
'CalificacionOperacion' => 'S2',
|
||||
'BaseImponible' => 100.00,
|
||||
'TipoImpositivo' => 21,
|
||||
'Cuota' => 21.00
|
||||
]);
|
||||
$invoice->setDesglose($desglose);
|
||||
|
||||
// Add encadenamiento (required)
|
||||
$encadenamiento = new Encadenamiento();
|
||||
$encadenamiento->setPrimerRegistro('S');
|
||||
$invoice->setEncadenamiento($encadenamiento);
|
||||
|
||||
// Set up paths for certificates
|
||||
$certsPath = dirname(__DIR__) . '/certs/';
|
||||
$privateKeyPath = $certsPath . 'private.pem';
|
||||
$publicKeyPath = $certsPath . 'public.pem';
|
||||
$certificatePath = $certsPath . 'certificate.pem';
|
||||
|
||||
// Check if certificate files exist and are readable
|
||||
foreach (['private.pem', 'public.pem', 'certificate.pem'] as $file) {
|
||||
$path = $certsPath . $file;
|
||||
echo "\n$file: ";
|
||||
if (!file_exists($path)) {
|
||||
echo "MISSING";
|
||||
throw new \RuntimeException("Certificate file $file does not exist at $path");
|
||||
}
|
||||
if (!is_readable($path)) {
|
||||
echo "NOT READABLE";
|
||||
throw new \RuntimeException("Certificate file $file is not readable at $path");
|
||||
}
|
||||
echo "OK (size: " . filesize($path) . " bytes)";
|
||||
}
|
||||
|
||||
// Set the keys
|
||||
$invoice->setPrivateKeyPath($privateKeyPath)
|
||||
->setPublicKeyPath($publicKeyPath)
|
||||
->setCertificatePath($certificatePath);
|
||||
|
||||
// Generate signed XML
|
||||
$xml = $invoice->toXml();
|
||||
|
||||
// Debug output
|
||||
echo "\nGenerated XML with signature:\n";
|
||||
echo $xml;
|
||||
echo "\n\n";
|
||||
|
||||
// Load the XML into a DOMDocument for verification
|
||||
$doc = new \DOMDocument();
|
||||
$doc->loadXML($xml);
|
||||
|
||||
// Verify the signature
|
||||
$this->assertTrue($invoice->verifySignature($doc));
|
||||
|
||||
// Skip schema validation for signed XML since the signature schema is not part of the core invoice schema
|
||||
// Instead, validate the XML before signing
|
||||
$unsignedXml = $invoice->toXml(false);
|
||||
$this->assertValidatesAgainstXsd($unsignedXml, $this->getTestXsdPath());
|
||||
}
|
||||
|
||||
public function testCreateAndSerializeInvoiceWithAgreementData(): void
|
||||
{
|
||||
$invoice = new Invoice();
|
||||
$invoice
|
||||
->setIdVersion('1.0')
|
||||
->setIdFactura('FAC-2023-012')
|
||||
->setNombreRazonEmisor('Empresa Ejemplo SL')
|
||||
->setTipoFactura('F1')
|
||||
->setDescripcionOperacion('Factura con datos de acuerdo')
|
||||
->setCuotaTotal(21.00)
|
||||
->setImporteTotal(100.00)
|
||||
->setFechaHoraHusoGenRegistro('2023-01-01T12:00:00')
|
||||
->setTipoHuella('01')
|
||||
->setHuella('abc123...')
|
||||
->setNumRegistroAcuerdoFacturacion('REG-001')
|
||||
->setIdAcuerdoSistemaInformatico('AGR-001');
|
||||
|
||||
// Add sistema informatico
|
||||
$sistema = new SistemaInformatico();
|
||||
$sistema
|
||||
->setNombreRazon('Sistema de Facturación')
|
||||
->setNif('B12345678')
|
||||
->setNombreSistemaInformatico('SistemaFacturacion')
|
||||
->setIdSistemaInformatico('01')
|
||||
->setVersion('1.0')
|
||||
->setNumeroInstalacion('INST-001');
|
||||
$invoice->setSistemaInformatico($sistema);
|
||||
|
||||
$xml = $invoice->toXml();
|
||||
$xml = $invoice->toXmlString();
|
||||
$this->assertValidatesAgainstXsd($xml, $this->getTestXsdPath());
|
||||
|
||||
// Test deserialization
|
||||
$deserialized = Invoice::fromXml($xml);
|
||||
$this->assertEquals('REG-001', $deserialized->getNumRegistroAcuerdoFacturacion());
|
||||
$this->assertEquals('AGR-001', $deserialized->getIdAcuerdoSistemaInformatico());
|
||||
// Deserialize and verify
|
||||
$deserializedInvoice = Invoice::fromXml($xml);
|
||||
$this->assertEquals('REG-001', $deserializedInvoice->getNumRegistroAcuerdoFacturacion());
|
||||
$this->assertEquals('AGR-001', $deserializedInvoice->getIdAcuerdoSistemaInformatico());
|
||||
}
|
||||
|
||||
public function testCreateAndSerializeInvoiceWithRejectionAndCorrection(): void
|
||||
public function testCreateAndSerializeInvoiceWithRejectionAndCorrection()
|
||||
{
|
||||
$invoice = new Invoice();
|
||||
$invoice
|
||||
->setIdVersion('1.0')
|
||||
$invoice->setIdVersion('1.0')
|
||||
->setIdFactura('FAC-2023-013')
|
||||
->setNombreRazonEmisor('Empresa Ejemplo SL')
|
||||
->setTipoFactura('F1')
|
||||
->setRechazoPrevio('S')
|
||||
->setSubsanacion('S')
|
||||
->setRechazoPrevio('S')
|
||||
->setTipoFactura('F1')
|
||||
->setDescripcionOperacion('Factura con rechazo y subsanación')
|
||||
->setCuotaTotal(21.00)
|
||||
->setImporteTotal(100.00)
|
||||
->setCuotaTotal(21)
|
||||
->setImporteTotal(100)
|
||||
->setFechaHoraHusoGenRegistro('2023-01-01T12:00:00')
|
||||
->setTipoHuella('01')
|
||||
->setHuella('abc123...');
|
||||
|
||||
// Add sistema informatico
|
||||
$sistema = new SistemaInformatico();
|
||||
$sistema
|
||||
->setNombreRazon('Sistema de Facturación')
|
||||
// Add proper Desglose
|
||||
$desglose = new Desglose();
|
||||
$desglose->setDesgloseFactura([
|
||||
'Impuesto' => '01',
|
||||
'ClaveRegimen' => '02',
|
||||
'CalificacionOperacion' => 'S2',
|
||||
'TipoImpositivo' => 21.00,
|
||||
'BaseImponible' => 100.00,
|
||||
'Cuota' => 21.00
|
||||
]);
|
||||
$invoice->setDesglose($desglose);
|
||||
|
||||
// Add proper Encadenamiento
|
||||
$encadenamiento = new Encadenamiento();
|
||||
$encadenamiento->setPrimerRegistro('S');
|
||||
$invoice->setEncadenamiento($encadenamiento);
|
||||
|
||||
// Add SistemaInformatico
|
||||
$sistemaInformatico = new SistemaInformatico();
|
||||
$sistemaInformatico->setNombreRazon('Sistema de Facturación')
|
||||
->setNif('B12345678')
|
||||
->setNombreSistemaInformatico('SistemaFacturacion')
|
||||
->setIdSistemaInformatico('01')
|
||||
->setVersion('1.0')
|
||||
->setNumeroInstalacion('INST-001');
|
||||
$invoice->setSistemaInformatico($sistema);
|
||||
->setNumeroInstalacion('INST-001')
|
||||
->setTipoUsoPosibleSoloVerifactu('S')
|
||||
->setTipoUsoPosibleMultiOT('S')
|
||||
->setIndicadorMultiplesOT('S');
|
||||
$invoice->setSistemaInformatico($sistemaInformatico);
|
||||
|
||||
$xml = $invoice->toXml();
|
||||
$xml = $invoice->toXmlString();
|
||||
$this->assertNotEmpty($xml);
|
||||
$this->assertValidatesAgainstXsd($xml, $this->getTestXsdPath());
|
||||
|
||||
// Test deserialization
|
||||
$deserialized = Invoice::fromXml($xml);
|
||||
$this->assertEquals('S', $deserialized->getRechazoPrevio());
|
||||
$this->assertEquals('S', $deserialized->getSubsanacion());
|
||||
$deserializedInvoice = Invoice::fromXml($xml);
|
||||
$this->assertEquals('S', $deserializedInvoice->getSubsanacion());
|
||||
$this->assertEquals('S', $deserializedInvoice->getRechazoPrevio());
|
||||
}
|
||||
|
||||
public function testCreateAndSerializeInvoiceWithOperationDate(): void
|
||||
|
|
@ -970,7 +968,31 @@ class InvoiceTest extends BaseModelTest
|
|||
->setNumeroInstalacion('INST-001');
|
||||
$invoice->setSistemaInformatico($sistema);
|
||||
|
||||
$xml = $invoice->toXml();
|
||||
// Add Desglose
|
||||
$desglose = new Desglose();
|
||||
$desglose->setDesgloseFactura([
|
||||
'Impuesto' => '01',
|
||||
'ClaveRegimen' => '02',
|
||||
'CalificacionOperacion' => 'S2',
|
||||
'TipoImpositivo' => '21.00',
|
||||
'BaseImponibleOimporteNoSujeto' => '100.00',
|
||||
'CuotaRepercutida' => '21.00'
|
||||
]);
|
||||
$invoice->setDesglose($desglose);
|
||||
|
||||
// Add Encadenamiento
|
||||
$encadenamiento = new Encadenamiento();
|
||||
$encadenamiento->setPrimerRegistro('S');
|
||||
$invoice->setEncadenamiento($encadenamiento);
|
||||
|
||||
// Generate XML string
|
||||
$xml = $invoice->toXmlString();
|
||||
|
||||
// Debug output
|
||||
echo "\nGenerated XML:\n";
|
||||
echo $xml;
|
||||
echo "\n\n";
|
||||
|
||||
$this->assertValidatesAgainstXsd($xml, $this->getTestXsdPath());
|
||||
|
||||
// Test deserialization
|
||||
|
|
@ -1013,7 +1035,14 @@ class InvoiceTest extends BaseModelTest
|
|||
->setNumeroInstalacion('INST-001');
|
||||
$invoice->setSistemaInformatico($sistema);
|
||||
|
||||
$xml = $invoice->toXml();
|
||||
// Generate XML string
|
||||
$xml = $invoice->toXmlString();
|
||||
|
||||
// Debug output
|
||||
echo "\nGenerated XML:\n";
|
||||
echo $xml;
|
||||
echo "\n\n";
|
||||
|
||||
$this->assertValidatesAgainstXsd($xml, $this->getTestXsdPath());
|
||||
|
||||
// Test deserialization
|
||||
|
|
@ -1050,7 +1079,7 @@ class InvoiceTest extends BaseModelTest
|
|||
->setNumeroInstalacion('INST-001');
|
||||
$invoice->setSistemaInformatico($sistema);
|
||||
|
||||
$invoice->toXml();
|
||||
$invoice->toXmlString();
|
||||
}
|
||||
|
||||
public function testInvalidTipoRectificativaThrowsException(): void
|
||||
|
|
@ -1082,7 +1111,7 @@ class InvoiceTest extends BaseModelTest
|
|||
->setNumeroInstalacion('INST-001');
|
||||
$invoice->setSistemaInformatico($sistema);
|
||||
|
||||
$invoice->toXml();
|
||||
$invoice->toXmlString();
|
||||
}
|
||||
|
||||
public function testInvalidDateFormatThrowsException(): void
|
||||
|
|
@ -1113,7 +1142,7 @@ class InvoiceTest extends BaseModelTest
|
|||
->setNumeroInstalacion('INST-001');
|
||||
$invoice->setSistemaInformatico($sistema);
|
||||
|
||||
$invoice->toXml();
|
||||
$invoice->toXmlString();
|
||||
}
|
||||
|
||||
public function testInvalidNIFFormatThrowsException(): void
|
||||
|
|
@ -1151,7 +1180,7 @@ class InvoiceTest extends BaseModelTest
|
|||
->setNumeroInstalacion('INST-001');
|
||||
$invoice->setSistemaInformatico($sistema);
|
||||
|
||||
$invoice->toXml();
|
||||
$invoice->toXmlString();
|
||||
}
|
||||
|
||||
public function testInvalidAmountFormatThrowsException(): void
|
||||
|
|
@ -1182,7 +1211,7 @@ class InvoiceTest extends BaseModelTest
|
|||
->setNumeroInstalacion('INST-001');
|
||||
$invoice->setSistemaInformatico($sistema);
|
||||
|
||||
$invoice->toXml();
|
||||
$invoice->toXmlString();
|
||||
}
|
||||
|
||||
public function testInvalidSchemaThrowsException(): void
|
||||
|
|
@ -1253,7 +1282,7 @@ class InvoiceTest extends BaseModelTest
|
|||
->setCertificatePath($certificatePath);
|
||||
|
||||
// Generate signed XML
|
||||
$xml = $invoice->toXml();
|
||||
$xml = $invoice->toXmlString();
|
||||
|
||||
// Debug output
|
||||
echo "\nGenerated XML with signature:\n";
|
||||
|
|
|
|||
|
|
@ -1,25 +1,24 @@
|
|||
-----BEGIN CERTIFICATE-----
|
||||
MIIEITCCAwmgAwIBAgIULRklu/Ae+uWUdejwvfO7wKhs0lIwDQYJKoZIhvcNAQEL
|
||||
BQAwgZ8xCzAJBgNVBAYTAkVTMQ8wDQYDVQQIDAZNYWRyaWQxDzANBgNVBAcMBk1h
|
||||
ZHJpZDEaMBgGA1UECgwRVGVzdCBPcmdhbml6YXRpb24xFjAUBgNVBAsMDUlUIERl
|
||||
cGFydG1lbnQxGTAXBgNVBAMMEHRlc3QuZXhhbXBsZS5jb20xHzAdBgkqhkiG9w0B
|
||||
CQEWEHRlc3RAZXhhbXBsZS5jb20wHhcNMjUwNDI1MDQxMzA2WhcNMjYwNDI1MDQx
|
||||
MzA2WjCBnzELMAkGA1UEBhMCRVMxDzANBgNVBAgMBk1hZHJpZDEPMA0GA1UEBwwG
|
||||
TWFkcmlkMRowGAYDVQQKDBFUZXN0IE9yZ2FuaXphdGlvbjEWMBQGA1UECwwNSVQg
|
||||
RGVwYXJ0bWVudDEZMBcGA1UEAwwQdGVzdC5leGFtcGxlLmNvbTEfMB0GCSqGSIb3
|
||||
DQEJARYQdGVzdEBleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
|
||||
AQoCggEBALZExtAsqspmBMot0/D1Ska36SaEEIZurdgh5odE2jcKWubJQegu4tAu
|
||||
Eq8LbL6bw8qmJZ6txp3fknIK3RjSyT39Rk0KtpGvrVjFPLAD42UNr8fVspPDHUR9
|
||||
yUvSpXoDxtcL6y6pn08jNnBFHkSnQK9br5wiNXWhSDkRTKfJ6Jvc269dA1qcJ2jU
|
||||
S1k5TXULhZwwvjeTFKb9+xwRjdnLlyh4+WSUvwUMDFgXeKn1iQppZ0Oc+G4Xyq8W
|
||||
oViOwruNMT3iUN83I3wKk3NOkdbx/IxD87UB19mKdmV4X1Nlj+m+/gidDf2DmriM
|
||||
FtnPp3kPaqwWBB+rV4AggwnKAOwfmUECAwEAAaNTMFEwHQYDVR0OBBYEFLR1jHET
|
||||
y8qVyFzsD9Ui0466XsLqMB8GA1UdIwQYMBaAFLR1jHETy8qVyFzsD9Ui0466XsLq
|
||||
MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAIllEMRCi/n6bqfv
|
||||
3eMuzHBxvUNNjmTIbEfnPyjf81FB78e1V3mXE5cYcVbDOwbvzZKa6qcioG88iW8Y
|
||||
5b9RnLQjy9os7xhne1BJq3J2B6XKlHv9O1f5x1fb6nZ/8m7XyGmXctIGs3K29/sF
|
||||
zFqx7UgSujZCtKannnjS7RCMTbCixhxVbgwBRSkTLBxNWssHaJ7q4ktrfKdXSHAm
|
||||
BhfgrvbILYYYN38i7Hfwru7m6Uoo7I+sy8yfHZzm3DwvswVRpRFIWLtkc6PBbyE4
|
||||
xXWjoJrbFGyzSdLg+DX5SpJ4569Jueuj2LiDCsJ3r1whlUO/gDfB3jdrrUrkxrBq
|
||||
mmW22K8=
|
||||
MIIEATCCAumgAwIBAgIUKnHs6qxWiKTbXcDRrT6GNnbuYt0wDQYJKoZIhvcNAQEL
|
||||
BQAwgY8xCzAJBgNVBAYTAkVTMQ8wDQYDVQQIDAZNYWRyaWQxDzANBgNVBAcMBk1h
|
||||
ZHJpZDEVMBMGA1UECgwMVGVzdCBDb21wYW55MQswCQYDVQQLDAJJVDEZMBcGA1UE
|
||||
AwwQdGVzdC5leGFtcGxlLmNvbTEfMB0GCSqGSIb3DQEJARYQdGVzdEBleGFtcGxl
|
||||
LmNvbTAeFw0yNTA0MjUwNTAwMTNaFw0yNjA0MjUwNTAwMTNaMIGPMQswCQYDVQQG
|
||||
EwJFUzEPMA0GA1UECAwGTWFkcmlkMQ8wDQYDVQQHDAZNYWRyaWQxFTATBgNVBAoM
|
||||
DFRlc3QgQ29tcGFueTELMAkGA1UECwwCSVQxGTAXBgNVBAMMEHRlc3QuZXhhbXBs
|
||||
ZS5jb20xHzAdBgkqhkiG9w0BCQEWEHRlc3RAZXhhbXBsZS5jb20wggEiMA0GCSqG
|
||||
SIb3DQEBAQUAA4IBDwAwggEKAoIBAQC8A3xqN1BhPw42Ja5W40Wi6q5NC0I7WuCz
|
||||
HTXUIKR6oARtRr28in6dA1hDu3V4hZgvNqveZO9vJOh+mWGWvcHK57mRMc6YJ0Nj
|
||||
bgGKBsGLHvfT8ErDOLjw+BrfC0PRiPWGvXqubt6y6whg4AklskPtT8oAgKvLQlo1
|
||||
mZMo1k9Tnl8cXwFHibCnl6JqkGX2eUeBevAVCJ5//ZgIsHX19rg165kfg8KlcB4C
|
||||
Ts+w+4iLwgW0PnzwdINKtpd7h7HXzkfVIvs32sbYlOOoA7e3Ouqm3/72BlSSHMlv
|
||||
iTLDNAe+5ogTIaGvxxKG+meTiWSULIcba1M3+Ou0Kmp1I4BZWBWJAgMBAAGjUzBR
|
||||
MB0GA1UdDgQWBBQ22t7nxuwbRP36CUFxtKlm7fByXjAfBgNVHSMEGDAWgBQ22t7n
|
||||
xuwbRP36CUFxtKlm7fByXjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUA
|
||||
A4IBAQAIOJZkAmpRy+oldLJ3jRg76J8Vt61vPdMtyY+gHYE0rlkdKWqZcrrWoSbO
|
||||
LrhjcocGp13G6yhOwebDVeHlYI0ninLOynmLWOJTm+BVesQXfN5u2U9CilpFLcid
|
||||
8Q5Z0jqWGjPwA+dcaiVi0xeYbaiUD5+vUEjpC4lmx505XQnUvKMk6TG0E5gn8KFO
|
||||
Na1lXww1Pu1yaxspwjQQNaOCYS736LM2T+IBiuoq56QGp2hO1GdeFC+VhmQgaqdI
|
||||
p/sQl/+Mkvmu/Co7V61J5ZHZeQCVKFKjDfHTCwb/+6yO3ZIqyn8YfK2KF71qfGc5
|
||||
wgPkBJiznHo8tQvbndKWN9Gx5r1X
|
||||
-----END CERTIFICATE-----
|
||||
|
|
|
|||
|
|
@ -1,28 +1,28 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC2RMbQLKrKZgTK
|
||||
LdPw9UpGt+kmhBCGbq3YIeaHRNo3ClrmyUHoLuLQLhKvC2y+m8PKpiWercad35Jy
|
||||
Ct0Y0sk9/UZNCraRr61YxTywA+NlDa/H1bKTwx1EfclL0qV6A8bXC+suqZ9PIzZw
|
||||
RR5Ep0CvW6+cIjV1oUg5EUynyeib3NuvXQNanCdo1EtZOU11C4WcML43kxSm/fsc
|
||||
EY3Zy5coePlklL8FDAxYF3ip9YkKaWdDnPhuF8qvFqFYjsK7jTE94lDfNyN8CpNz
|
||||
TpHW8fyMQ/O1AdfZinZleF9TZY/pvv4InQ39g5q4jBbZz6d5D2qsFgQfq1eAIIMJ
|
||||
ygDsH5lBAgMBAAECggEAC732iOa3wf48hMHbC4Th2hhy/rY3UlHDBU95yHEZFb6n
|
||||
CIMiqdCLcBnnvjsgME9cl6uIdOaBCx6iEpK8l6LMsB+m7cOo603D/xoFxNkbRyFQ
|
||||
l7EepgJF2mm4FBhn7KpdnvD3n7PxvWlpUmZBgu67bhCcCZTymLdhLbv9kjmhsJi3
|
||||
qqlygaWUaj91yQFLJ4PeTnTMRd4yVFvP9IDGxgTQzDzlMQz1uAu52dunB+TkLkJI
|
||||
/MgbhC5/bAWmgqgmbNjhFkQgwdG787jstXtMvLBGnYNhBVzuqr4XHh31pKFhl+z3
|
||||
hG1Hi3yg+XzKVSS7wb+qMv38dGVZYSTSa06vs03mWQKBgQD8cbcGu52iFi1h/9xY
|
||||
ge1EmIiT9SrYIaYhzrPFdzeY7jpaXfXYZ+nPyrTs6nRxW/RsHP1YxM5WUTE23zhL
|
||||
g9W1NWATHKh1n+QAwHQdYZWJN1LyqCW5q3fl0koAQGuXIMqKpv3PR+4pyhxdGNC9
|
||||
MnPTLQmWkIMA7RBAizNN3aXaYwKBgQC41gRDf1Zv/Wg2kc8u3hOX0tbuT9bxium3
|
||||
kfXmDEJUQ9fww8rA9ZKo3weKMvUklmrXEbLjMN2Md8hmdBVHQXXhtXyZzoiCHVct
|
||||
DNW2TvqBxpJCnLA3bHr8fgPSAHs7SGAM8gF5mY9ZhPI4+Drjk6YlOHVcEr82C1nG
|
||||
jJbCdVYdCwKBgFdO1OglNy150hRUs1aBCRhyULorTrgVWynSHWasBrSDn/blDEPe
|
||||
HIVbLBvMMp2KGgzSMeTjnCFKT2UU6pljbSQQ47t4a+LSe76u0PngaCFe2vdFpFaE
|
||||
sSDxC9rubMeF9cbiXmG0FPCrEAg6rubgbiKZLvm93TESzE4mVoYVpGjVAoGBAKrG
|
||||
0tmqZarDb+47ejnLZj74xy0ZB3fU6Wx6p8ANw5sns/T4cfUm8IKmzsiZnHZZpA1i
|
||||
hO300D5gzgAbcS2NBeWtUZqqgOX3RfyRx0PSZRJS7gAt1YLf8CIqLE1ztGhpzpUn
|
||||
ZMV9ZD1J4KNSdtaeLPxm1chkadb9Vc1lSEYTM6VRAoGAIcA1aEllKEOWXeZAUfny
|
||||
l67WA0ua6ss2dPQWJumflcf73jajE+jZQagsnRIJv34jJaP/O5A5OKU1WnemKlVU
|
||||
fyBd4R4nr47yCLxk2HNwUQBluG6EIeUbdEM/2P6qAamIG4FzurvTKIZ7h2dgvtME
|
||||
RaRkhB6Q8tXUwjQ+HfCpw9Q=
|
||||
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC8A3xqN1BhPw42
|
||||
Ja5W40Wi6q5NC0I7WuCzHTXUIKR6oARtRr28in6dA1hDu3V4hZgvNqveZO9vJOh+
|
||||
mWGWvcHK57mRMc6YJ0NjbgGKBsGLHvfT8ErDOLjw+BrfC0PRiPWGvXqubt6y6whg
|
||||
4AklskPtT8oAgKvLQlo1mZMo1k9Tnl8cXwFHibCnl6JqkGX2eUeBevAVCJ5//ZgI
|
||||
sHX19rg165kfg8KlcB4CTs+w+4iLwgW0PnzwdINKtpd7h7HXzkfVIvs32sbYlOOo
|
||||
A7e3Ouqm3/72BlSSHMlviTLDNAe+5ogTIaGvxxKG+meTiWSULIcba1M3+Ou0Kmp1
|
||||
I4BZWBWJAgMBAAECggEAPA8FBE0NITNqT8OzsE/DY+qBqVzKWHmMQ8s18cBHqmBJ
|
||||
abrqSzsWfQOfg2tzd/99AL//IPivP5rTpfVLJkMRj8Z8EbSOYS5RqYZveW71DoLi
|
||||
+dHNoBUThu5KbyuDC4ErpVILyYLjItZPg9qtMVyDtGADkCuKNVDJzktzpdzn1Da7
|
||||
0hIvSDDn4FbZR4rk0Zran/Bg2LPjB0+5by++bTOVycn9ipj5htry1nmDjQ29PkEh
|
||||
vxvo/Cvcuj4RUqsnLyDvpIWaWccw95KZCrKG1SODLypBPdmfEEw0trd2tC4JZG1B
|
||||
pMwwcj7f/tvQilYoO+xMGREJODS50+0m6sC/TYcjjQKBgQDdDYLl1noJ0mSzUADN
|
||||
UpXLE0T6d9NvC9Z6wm/gM6K7mlwqg+B74A6cqO2lTOpV+HXd4BNGYudSEBS5xpA+
|
||||
HV0eFl+JH/R8axZSXCrWELW2eTGSjkaUJ8U+nN6dpVe3ZeZe3E+XjaNqzh+8i7DX
|
||||
pmyb8ecYd8J1wFZDgoJnN3Y9VwKBgQDZvM7DA5dDU/LsYmAx4STxBJUcU/adxnb0
|
||||
yZtWDNjrxuNQeF1wX7VxunfZTERuPMorIKMAKQx9i3OpF3W/fdZFCH9MfJGZuBSN
|
||||
fVpMsGiosaG//A5LkyHiXc9u+xlAufacHF9lY1Y3YjslCwRIxFzOybr8BD1VKQvw
|
||||
jq3W412YHwKBgFDseVcaxWKDG0ppp2GDMkM9sqre4xBsDQCz3bD+NjznrYYC8VNo
|
||||
GvFPBnDVT5RZeQzY0zWZT4XDTVMVVgjnRweUuTTB9gb2TR2pkCT9yQgcJgG5BC2I
|
||||
wbTCSNz9grcpHsTrclfRClo3S3cCzKKl0mzkie1zKkVhPDNKu+J9c1PRAoGAdi+y
|
||||
902arHQD1uVrG4f+2khHe7Hjmi5g4WwRQfsFP62cM1CyP03kGhBB/5QTTNb4UcKM
|
||||
V4jwe7DXHgBa7I8sYBpFpsmy6oJtvYM+IYRbur14c9IoHEOHz+xlrPLk+So3vvLz
|
||||
oP/zi2ppeijpsWnMin4bXbIbbH80OOXpXEvbwLcCgYADsQN3wSh7+7NGq1h9C5s0
|
||||
OJFUZjwTSKkMyw/r8JnMfj39+UB2VHtTl/FYBiVkeuazEm5zcroPNC2lFVvit4rr
|
||||
EzSXmSJ5FrnirfDuRO/xakHYr3tdF05xD+1lq9iSqsUSz2Iq5LcKFHRmiFDD748l
|
||||
+xTBYVY8fTd1+jO8VruQNg==
|
||||
-----END PRIVATE KEY-----
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
-----BEGIN PUBLIC KEY-----
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtkTG0CyqymYEyi3T8PVK
|
||||
RrfpJoQQhm6t2CHmh0TaNwpa5slB6C7i0C4SrwtsvpvDyqYlnq3Gnd+ScgrdGNLJ
|
||||
Pf1GTQq2ka+tWMU8sAPjZQ2vx9Wyk8MdRH3JS9KlegPG1wvrLqmfTyM2cEUeRKdA
|
||||
r1uvnCI1daFIORFMp8nom9zbr10DWpwnaNRLWTlNdQuFnDC+N5MUpv37HBGN2cuX
|
||||
KHj5ZJS/BQwMWBd4qfWJCmlnQ5z4bhfKrxahWI7Cu40xPeJQ3zcjfAqTc06R1vH8
|
||||
jEPztQHX2Yp2ZXhfU2WP6b7+CJ0N/YOauIwW2c+neQ9qrBYEH6tXgCCDCcoA7B+Z
|
||||
QQIDAQAB
|
||||
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvAN8ajdQYT8ONiWuVuNF
|
||||
ouquTQtCO1rgsx011CCkeqAEbUa9vIp+nQNYQ7t1eIWYLzar3mTvbyTofplhlr3B
|
||||
yue5kTHOmCdDY24BigbBix730/BKwzi48Pga3wtD0Yj1hr16rm7esusIYOAJJbJD
|
||||
7U/KAICry0JaNZmTKNZPU55fHF8BR4mwp5eiapBl9nlHgXrwFQief/2YCLB19fa4
|
||||
NeuZH4PCpXAeAk7PsPuIi8IFtD588HSDSraXe4ex185H1SL7N9rG2JTjqAO3tzrq
|
||||
pt/+9gZUkhzJb4kywzQHvuaIEyGhr8cShvpnk4lklCyHG2tTN/jrtCpqdSOAWVgV
|
||||
iQIDAQAB
|
||||
-----END PUBLIC KEY-----
|
||||
|
|
|
|||
Loading…
Reference in New Issue