invoiceninja/app/Services/EDocument/Standards/Verifactu/VerifactuClient.php

188 lines
7.6 KiB
PHP

<?php
/**
* VerifactuClient.php
*
* SOAP client for sending invoices (facturas) to AEAT Verifactu service.
* Supports production and test endpoints via a mode switch.
*/
namespace App\Services\EDocument\Standards\Verifactu;
use App\Services\EDocument\Standards\Verifactu\Types\Cabecera;
use App\Services\EDocument\Standards\Verifactu\Types\Desglose;
use App\Services\EDocument\Standards\Verifactu\Types\DesgloseRectificacion;
use App\Services\EDocument\Standards\Verifactu\Types\Destinatarios;
use App\Services\EDocument\Standards\Verifactu\Types\Detalle;
use App\Services\EDocument\Standards\Verifactu\Types\DetalleDesglose;
use App\Services\EDocument\Standards\Verifactu\Types\Encadenamiento;
use App\Services\EDocument\Standards\Verifactu\Types\IDDestinatario;
use App\Services\EDocument\Standards\Verifactu\Types\IDFactura;
use App\Services\EDocument\Standards\Verifactu\Types\IDFacturaAR;
use App\Services\EDocument\Standards\Verifactu\Types\IDFacturaExpedida;
use App\Services\EDocument\Standards\Verifactu\Types\IDOtro;
use App\Services\EDocument\Standards\Verifactu\Types\ImporteSgn14_2;
use App\Services\EDocument\Standards\Verifactu\Types\Incidencia;
use App\Services\EDocument\Standards\Verifactu\Types\ObligadoEmision;
use App\Services\EDocument\Standards\Verifactu\Types\OperacionExenta;
use App\Services\EDocument\Standards\Verifactu\Types\PersonaFisicaJuridica;
use App\Services\EDocument\Standards\Verifactu\Types\PersonaFisicaJuridicaES;
use App\Services\EDocument\Standards\Verifactu\Types\RechazoPrevio;
use App\Services\EDocument\Standards\Verifactu\Types\RegFactuSistemaFacturacion;
use App\Services\EDocument\Standards\Verifactu\Types\RegistroAlta;
use App\Services\EDocument\Standards\Verifactu\Types\RegistroAnterior;
use App\Services\EDocument\Standards\Verifactu\Types\RegistroFactura;
use App\Services\EDocument\Standards\Verifactu\Types\RegistroFacturacionAlta;
use App\Services\EDocument\Standards\Verifactu\Types\RegistroFacturacionAnulacion;
use App\Services\EDocument\Standards\Verifactu\Types\SistemaInformatico;
use App\Services\EDocument\Standards\Verifactu\Types\Subsanacion;
class VerifactuClient
{
const MODE_PROD = 'prod';
const MODE_TEST = 'test';
/**
* @var array<string,string>
*/
private static array $endpoints = [
self::MODE_PROD => 'https://www1.agenciatributaria.gob.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/VerifactuSOAP',
self::MODE_TEST => 'https://prewww1.aeat.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/VerifactuSOAP',
];
private \SoapClient $client;
private string $mode;
/**
* @param string $mode One of VerifactuClient::MODE_PROD or MODE_TEST
* @param string|null $wsdl Path to the WSDL file; defaults to xsd/SistemaFacturacion.wsdl
* @param array $options Additional SoapClient options
*
* @throws \InvalidArgumentException
* @throws \SoapFault
*/
public function __construct(string $mode = self::MODE_TEST, string $wsdl = null, array $options = [])
{
if (!isset(self::$endpoints[$mode])) {
throw new \InvalidArgumentException("Invalid mode '{$mode}', must be 'prod' or 'test'.");
}
$this->mode = $mode;
$endpoint = self::$endpoints[$mode];
$wsdlPath = $wsdl ?: __DIR__ . '/xsd/SistemaFacturacion.wsdl';
// Default SOAP client options with classmap for generated s
$defaultOpts = [
'trace' => true,
'exceptions' => true,
'cache_wsdl' => WSDL_CACHE_NONE,
'location' => $endpoint,
'soap_version' => SOAP_1_1,
'classmap' => [
'Cabecera' => Cabecera::class,
'Desglose' => Desglose::class,
'DesgloseRectificacion' => DesgloseRectificacion::class,
'Destinatarios' => Destinatarios::class,
'Detalle' => Detalle::class,
'DetalleDesglose' => DetalleDesglose::class,
'Encadenamiento' => Encadenamiento::class,
'IDDestinatario' => IDDestinatario::class,
'IDFactura' => IDFactura::class,
'IDFacturaAR' => IDFacturaAR::class,
'IDFacturaExpedida' => IDFacturaExpedida::class,
'IDOtro' => IDOtro::class,
'ImporteSgn14_2' => ImporteSgn14_2::class,
'Incidencia' => Incidencia::class,
'ObligadoEmision' => ObligadoEmision::class,
'OperacionExenta' => OperacionExenta::class,
'PersonaFisicaJuridica' => PersonaFisicaJuridica::class,
'PersonaFisicaJuridicaES' => PersonaFisicaJuridicaES::class,
'RechazoPrevio' => RechazoPrevio::class,
'RegFactuSistemaFacturacion' => RegFactuSistemaFacturacion::class,
'RegistroAlta' => RegistroAlta::class,
'RegistroAnterior' => RegistroAnterior::class,
'RegistroFactura' => RegistroFactura::class,
'RegistroFacturacionAlta' => RegistroFacturacionAlta::class,
'RegistroFacturacionAnulacion' => RegistroFacturacionAnulacion::class,
'RegistroFacturacionSubsanacion' => Subsanacion::class,
'SistemaInformatico' => SistemaInformatico::class,
],
];
$opts = array_merge($defaultOpts, $options);
$this->client = new \SoapClient($wsdlPath, $opts);
}
/**
* Send an invoice registration (alta) request
*
* @param RegistroAlta $registro
* @return mixed The SOAP response
* @throws \SoapFault
*/
public function sendRegistroAlta(RegistroAlta $registro)
{
$factura = new RegistroFactura();
$factura->setRegistroAlta($registro);
$wrapper = new RegFactuSistemaFacturacion();
$wrapper->addToRegistroFactura($factura);
return $this->sendRegistroFactura($wrapper);
}
/**
* Send an invoice cancellation (anulación) request
*
* @param RegistroFacturacionAnulacion $registro
* @return mixed The SOAP response
* @throws \SoapFault
*/
public function sendRegistroAnulacion(RegistroFacturacionAnulacion $registro)
{
$factura = new RegistroFactura();
$factura->setRegistroAnulacion($registro);
$wrapper = new RegFactuSistemaFacturacion();
$wrapper->addToRegistroFactura($factura);
return $this->sendRegistroFactura($wrapper);
}
/**
* Low-level send: SoapClient marshals the object per classmap
*
* @param RegFactuSistemaFacturacion $wrapper
* @return mixed The SOAP response
* @throws \SoapFault
*/
public function sendRegistroFactura(RegFactuSistemaFacturacion $wrapper)
{
return $this->client->__soapCall(
'RegFactuSistemaFacturacion',
['RegFactuSistemaFacturacion' => $wrapper]
);
}
/**
* Get the last raw request XML
*
* @return string
*/
public function getLastRequest(): string
{
return $this->client->__getLastRequest();
}
/**
* Get the last raw response XML
*
* @return string
*/
public function getLastResponse(): string
{
return $this->client->__getLastResponse();
}
}