Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Envío de registros y sus respuestas (https://www.clubdelphi.com/foros/forumdisplay.php?f=66)
-   -   Funcionamiento de SOAP y error con wsdl (https://www.clubdelphi.com/foros/showthread.php?t=97388)

Jariverom 07-04-2025 11:35:06

Funcionamiento de SOAP y error con wsdl
 
Buenas, estoy desarrollando una aplicación en php para verifactu y tengo algunos xml que he conseguido probar desde el entorno de pruebas que proporciona la AEAT pero ahora estoy intentando hacer el soap desde mi aplicación y me da un error al intentar cargar entidades del mismo.
El error que me da es el siguiente:
Código:

"SOAP-ERROR: Parsing WSDL: Couldn't load from 'h_t_t_p_s://prewww2.aeat.es/static_files/common/internet/dep/aplicaciones/es/aeat/tikeV1.0/cont/ws/SistemaFacturacion.wsdl' : failed to load external entity "h_t_t_p_s://prewww2.aeat.es/static_files/common/internet/dep/aplicaciones/es/aeat/tikeV1.0/cont/ws/SistemaFacturacion.wsdl"
"


y mi código esta así ahora mismo:
Código:

   
$xml = cargarHistorico($anio, $mes);

    // Definiciones proporcionadas
    $wsdlUrl = 'h_t_t_p_s://prewww2.aeat.es/static_files/common/internet/dep/aplicaciones/es/aeat/tikeV1.0/cont/ws/SistemaFacturacion.wsdl';

    $certificatePath = RUTA_CERTIFICADO_VERIFACTU;
    $certificatePassword = RUTA_PASSWD_CERTIFICADO_VERIFACTU;

    // Verificar si el WSDL es accesible
    $wsdlContent = @file_get_contents($wsdlUrl);
    if ($wsdlContent === false) {
        echo "No se puede acceder al WSDL. Verifica la URL y la configuración del servidor.";
        return;
    } else {
        echo "WSDL cargado correctamente.\n";
    }

    try {
        // Crear el cliente SOAP con las configuraciones proporcionadas
        $client = new SoapClient($wsdlUrl, [  // Nombre del servicio
            'local_cert' => $certificatePath,
            'passphrase' => $certificatePassword,
            'trace' => 1,
            'exceptions' => 1
        ]);
    } catch (SoapFault $th) {
        $mensajeError = $th->getMessage();
        echo "Error al crear el cliente SOAP: " . $th->getMessage();
        return;
    }

    try {
        // Llamada al método SOAP
        $response = $client->__soapCall('EnviarFactura', [['facturaXML' => $xml]]);
        return $response;
    } catch (SoapFault $fault) {
        echo "Error en la llamada SOAP: " . $fault->getMessage();
        return;
    }

El error me da al crear el cliente de soap

delphiGar 07-04-2025 13:25:28

la url que coges es para la configuracion del servicio.

La url de pruebas es:

Código:

https://prewww1.aeat.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/VerifactuSOAP

Jariverom 07-04-2025 13:53:44

Cita:

Empezado por delphiGar (Mensaje 563495)
la url que coges es para la configuracion del servicio.

La url de pruebas es:

Código:

h_t_t_p_s://prewww1.aeat.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/VerifactuSOAP


Usando ese endpoint me da el siguiente error:
"SOAP-ERROR: Parsing WSDL: Couldn't load from 'h_t_t_p_s://prewww1.aeat.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/VerifactuSOAP' : EndTag: '</' not found"

delphiGar 07-04-2025 14:02:16

Cita:

Empezado por Jariverom (Mensaje 563490)
Buenas, estoy desarrollando una aplicación en php para verifactu y tengo algunos xml que he conseguido probar desde el entorno de pruebas que proporciona la AEAT pero ahora estoy intentando hacer el soap desde mi aplicación y me da un error al intentar cargar entidades del mismo.
El error que me da es el siguiente:
Código:

"SOAP-ERROR: Parsing WSDL: Couldn't load from 'h_t_t_p_s://prewww2.aeat.es/static_files/common/internet/dep/aplicaciones/es/aeat/tikeV1.0/cont/ws/SistemaFacturacion.wsdl' : failed to load external entity "h_t_t_p_s://prewww2.aeat.es/static_files/common/internet/dep/aplicaciones/es/aeat/tikeV1.0/cont/ws/SistemaFacturacion.wsdl"
"


y mi código esta así ahora mismo:
Código:

   
$xml = cargarHistorico($anio, $mes);

    // Definiciones proporcionadas
    $wsdlUrl = 'h_t_t_p_s://prewww2.aeat.es/static_files/common/internet/dep/aplicaciones/es/aeat/tikeV1.0/cont/ws/SistemaFacturacion.wsdl';

    $certificatePath = RUTA_CERTIFICADO_VERIFACTU;
    $certificatePassword = RUTA_PASSWD_CERTIFICADO_VERIFACTU;

    // Verificar si el WSDL es accesible
    $wsdlContent = @file_get_contents($wsdlUrl);
    if ($wsdlContent === false) {
        echo "No se puede acceder al WSDL. Verifica la URL y la configuración del servidor.";
        return;
    } else {
        echo "WSDL cargado correctamente.\n";
    }

    try {
        // Crear el cliente SOAP con las configuraciones proporcionadas
        $client = new SoapClient($wsdlUrl, [  // Nombre del servicio
            'local_cert' => $certificatePath,
            'passphrase' => $certificatePassword,
            'trace' => 1,
            'exceptions' => 1
        ]);
    } catch (SoapFault $th) {
        $mensajeError = $th->getMessage();
        echo "Error al crear el cliente SOAP: " . $th->getMessage();
        return;
    }

    try {
        // Llamada al método SOAP
        $response = $client->__soapCall('EnviarFactura', [['facturaXML' => $xml]]);
        return $response;
    } catch (SoapFault $fault) {
        echo "Error en la llamada SOAP: " . $fault->getMessage();
        return;
    }

El error me da al crear el cliente de soap

Por lo que veo el fichero XML no lo cargas, primero, estas cargando la URL, supongo que sera el archivo xml lo que quieres cargar y no la URL:

Código PHP:

 $wsdlContent = @file_get_contents($wsdlUrl); 

Segundo, no envias nada, la variable $xml no existe, creo que aqui querias poner $wsdlContent:

Código PHP:

$response $client->__soapCall('EnviarFactura', [['facturaXML' => $xml]]); 


Jariverom 07-04-2025 14:15:45

la variable $xml la cargo con el metodo cargarHistorico($anio, $mes), al cual le paso el año y el mes ya que he visto que para hacer la solicitud al endpoint me pide esos dos campos obligatorios.
esa variable lo que tiene tras la carga es lo siguiente (cambio los datos del emisor).

Código:

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="h_t_t_p://schemas.xmlsoap.org/soap/envelope/" xmlns:con="h_t_t_p_s://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/ConsultaLR.xsd" xmlns:sum="h_t_t_p_s://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd">
  <soapenv:Header/>
  <soapenv:Body>
    <con:ConsultaFactuSistemaFacturacion>
      <con:Cabecera>
        <sum:IDVersion>1.0</sum:IDVersion>
        <sum:ObligadoEmision>
          <sum:NombreRazon>NombreEmisor</sum:NombreRazon>
          <sum:NIF>00000000X</sum:NIF>
        </sum:ObligadoEmision>
      </con:Cabecera>
      <con:FiltroConsulta>
        <con:PeriodoImputacion>
          <sum:Ejercicio>2025</sum:Ejercicio>
          <sum:Periodo>01</sum:Periodo>
        </con:PeriodoImputacion>
      </con:FiltroConsulta>
    </con:ConsultaFactuSistemaFacturacion>
  </soapenv:Body>
</soapenv:Envelope>


Es posible que dentro del soap tenga que usar un archivo pem y no un p12? estoy intentando eso pero me sigue dando errores

delphiGar 07-04-2025 14:22:37

Que quieres hacer, enviar o consultar, el ultimo xml que has puesto es de consulta?

Jariverom 07-04-2025 14:27:21

la verdad que cualquiera me vale pero de momento la parte que estoy haciendo es la de consulta, en el entorno de prueba de la AEAT donde entras con certificado electrónico he probado ese xml y si me devuelve el xml con las facturas que tengo enviadas pero ahora al intentar hacerlo desde la aplicación de php no consigo que funcione correctamente el soap (es la primera vez que toco SOAP y llevo con php 2 meses)

delphiGar 07-04-2025 15:37:41

Cita:

Empezado por Jariverom (Mensaje 563501)
la variable $xml la cargo con el metodo cargarHistorico($anio, $mes), al cual le paso el año y el mes ya que he visto que para hacer la solicitud al endpoint me pide esos dos campos obligatorios.
esa variable lo que tiene tras la carga es lo siguiente (cambio los datos del emisor).

Código:

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="h_t_t_p://schemas.xmlsoap.org/soap/envelope/" xmlns:con="h_t_t_p_s://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/ConsultaLR.xsd" xmlns:sum="h_t_t_p_s://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd">
  <soapenv:Header/>
  <soapenv:Body>
    <con:ConsultaFactuSistemaFacturacion>
      <con:Cabecera>
        <sum:IDVersion>1.0</sum:IDVersion>
        <sum:ObligadoEmision>
          <sum:NombreRazon>NombreEmisor</sum:NombreRazon>
          <sum:NIF>00000000X</sum:NIF>
        </sum:ObligadoEmision>
      </con:Cabecera>
      <con:FiltroConsulta>
        <con:PeriodoImputacion>
          <sum:Ejercicio>2025</sum:Ejercicio>
          <sum:Periodo>01</sum:Periodo>
        </con:PeriodoImputacion>
      </con:FiltroConsulta>
    </con:ConsultaFactuSistemaFacturacion>
  </soapenv:Body>
</soapenv:Envelope>


Es posible que dentro del soap tenga que usar un archivo pem y no un p12? estoy intentando eso pero me sigue dando errores

Para esta utiliza lo siguiente:

Código PHP:

$response $client->__soapCall'ConsultaFactuSistemaFacturacion', ['facturaXML' => $xml]); 

Código PHP:

$response $client->ConsultaFactuSistemaFacturacion( ['facturaXML' => $xml]); 


alexrodral 09-04-2025 10:12:09

Buenas, me esta pasando un error parecido en el proyecto que tengo en php para el tema de verifactu, inteno crear el cliente SOAP y todo el rato me sale este error:
Error:
Código:

message =
"SOAP-ERROR: Parsing WSDL: Couldn't load from 'h_t_t_p_s://prewww1.aeat.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/VerifactuSOAP' : failed to load external entity "h_t_t_p_s://prewww1.aeat.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/VerifactuSOAP"
"
*Exception*string =
""
code =
0
file =
"C:\xampp\htdocs\factu\funciones\funciones_verifactu.php"
line =
489
*Exception*trace =
array(2)
*Exception*previous =
null
faultstring =
"SOAP-ERROR: Parsing WSDL: Couldn't load from 'h_t_t_p_s://prewww1.aeat.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/VerifactuSOAP' : failed to load external entity "h_t_t_p_s://prewww1.aeat.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/VerifactuSOAP"
"
faultcode =
"WSDL"

Este es el codigo para generar el SOAP client:
Código PHP:

$client = new SoapClient((string) $wsdlUrl, [ 
            
'local_cert' => $filename,
            
'passphrase' => $certificatePassword,
            
'cache_wsdl' => WSDL_CACHE_NONE,
            
'soap_version' => SOAP_1_1,
            
'style' => SOAP_DOCUMENT,
            
'use' => SOAP_LITERAL,
            
'trace' => 1,
            
'exceptions' => 1,

        ]); 

La ruta que estoy usando para acceder a la AEAT es esta :
Código PHP:

$wsdlUrl 'h_t_t_p_s://prewww1.aeat.es/wlpl/TIKE-CONT/ws/SistemaFacturacion/VerifactuSOAP'

Y estoy usando los certificados de pruebas que da la AEAT, el XML que estoy intentando subir es este:
Código:

<soapenv:Envelope xmlns:soapenv="h_t_t_p://schemas.xmlsoap.org/soap/envelope/" xmlns:con="h_t_t_p_s://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/ConsultaLR.xsd" xmlns:sum="h_t_t_p_s://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd">
<soapenv:Header/>
<soapenv:Body>
<con:ConsultaFactuSistemaFacturacion>
<con:Cabecera>
<sum:IDVersion>1.0</sum:IDVersion>
<sum:ObligadoEmision>
<sum:NombreRazon>Certificado Pruebas</sum:NombreRazon>
<sum:NIF>A39200019</sum:NIF>
</sum:ObligadoEmision>
</con:Cabecera>
<con:FiltroConsulta>
<con:PeriodoImputacion>
<sum:Ejercicio>2025</sum:Ejercicio>
<sum:Periodo>04</sum:Periodo>
</con:PeriodoImputacion>
</con:FiltroConsulta>
</con:ConsultaFactuSistemaFacturacion>
</soapenv:Body>
</soapenv:Envelope>


Jariverom 09-04-2025 12:47:54

Estoy en ese mismo punto, pero he conseguido extraer mas sobre el error y me especifica que falta la Cabecera en el XML y en la variable donde tengo el XML si está ese nodo.

morta71 10-04-2025 11:04:21

Cita:

Empezado por Jariverom (Mensaje 563582)
Estoy en ese mismo punto, pero he conseguido extraer mas sobre el error y me especifica que falta la Cabecera en el XML y en la variable donde tengo el XML si está ese nodo.

Yo lo tengo desarrollado en PHP (Symfony) y a grandes rasgos esta es mi implementación:

Tengo las clases según las especificaciones del WSDL de la AEAT (CabeceraType, Destinatarios, DatosPresentacionType, etc...), pero se pueden obviar dichas clases y en lugar de pasar objetos al cliente SOAP se pueden pasar los datos en un array asociativo con la misma estructura, o el XML directo.

Para realizar los envíos SOAP tengo una clase SoapVerifactu.class:

Código PHP:

class SoapVerifactu extends \SoapClient
{
    public function 
__construct(
        
CertificadoService $certificado,
        
#[Autowire('%sif_verifactu_wsdl%')] string $wdsl,
        #[Autowire('%sif_verifactu_endpoint%')] string $location,
        
array $options = [],
        
bool $ssl_verifypeer true)
    {
        if (!
$certificado->valid()) {
            throw new \
RuntimeException('Certificado no válido. Compruebe que el certificado digital sea válido.');
        }

        
$options += [
            
'trace' => true,
            
'cache_wsdl' => WSDL_CACHE_NONE,
            
'classmap' => [       // Omitir si se prefiere no mapear con clases propias
                
'CabeceraConsultaSf' => CabeceraConsulta::class,
                
'CabeceraType' => Cabecera::class,
                
'Destinatarios' => Destinatarios::class,
                
'DatosPresentacionType' => DatosPresentacionType::class,
                
'DatosPresentacion2Type' => DatosPresentacion2Type::class,
                
'DesgloseRectificacionType' => DesgloseRectificacionType::class,
                
'DesgloseType' => DesgloseType::class,
                
'DetalleType' => DetalleType::class,
                
'EncadenamientoFacturaAnteriorType' => EncadenamientoFacturaAnteriorType::class,
                
'EstadoRegFactuType' => EstadoRegFactuType::class,
                
'FacturasRectificadas' => FacturasRectificadas::class,
                
'FacturasSustituidas' => FacturasSustituidas::class,
                
'FiltroConsulta' => FiltroConsulta::class,
                
'IDFacturaARType' => IDFacturaARType::class,
                
'IDFacturaExpedidaType' => IDFacturaExpedidaType::class,
                
'IDOtroType' => IDOtroType::class,
                
'ObligadoEmisionConsultaType' => ObligadoEmisionConsultaType::class,
                
'OperacionType' => OperacionType::class,
                
'PeriodoImputacion' => PeriodoImputacionType::class,
                
'PersonaFisicaJuridicaESType' => PersonaFisicaJuridicaESType::class,
                
'PersonaFisicaJuridicaType' => PersonaFisicaJuridicaType::class,
                
'RegistroDuplicadoType' => RegistroDuplicadoType::class,
                
'RegistroRespuestaConsultaRegFacturacionType' => RegistroRespuestaConsultaRegFacturacionType::class,
                
'RemisionVoluntaria' => RemisionVoluntaria::class,
                
'RespuestaBaseType' => RespuestaRegFactuSistemaFacturacion::class,
                
'RespuestaConsultaFactuSistemaFacturacionType' => RespuestaConsultaFactuSistemaFacturacionType::class,
                
'RespuestaDatosRegistroFacturacionType' => RespuestaDatosRegistroFacturacionType::class,
                
'RespuestaExpedidaType' => RespuestaExpedidaType::class,
            ],
            
'local_cert' => $certificado->pem(),   // path al certificado PEM
            
'passphrase' => $certificado->key(),   // pin/clave
            
'location' => $location,
            
'stream_context' => [
                
'http' => [
                    
'user_agent' => 'PHPSoapClient',
                ],
                
'ssl' => [
                    
'ciphers' => 'DEFAULT@SECLEVEL=1',
                ],
            ],
        ];

        
$options['stream_context'] = $this->stream_context($options['stream_context'], $ssl_verifypeer);

        
parent::__construct($wdsl$options);
    }

    public function 
consultaFacturacion(array $parametros): mixed
    
{
        return 
$this->__soapCall('ConsultaFactuSistemaFacturacion', [$parametros]);
    }

    public function 
registroFacturacion(array $parametros): mixed
    
{
        
// Para envío de string XML
        // return $this->__doRequest($xml, $this->location, '', 1);

        
return $this->__soapCall('RegFactuSistemaFacturacion', [$parametros]);
    }

    protected function 
stream_context($options = [], $ssl_verifypeer true)
    {
        switch (
gettype($options)) {
            case 
'array':
                
$context stream_context_create($options);
                break;
            case 
'resource':
                
$context $options;
        }

        if (!
$ssl_verifypeer) {
            
stream_context_set_option($context, [
                
'ssl' => [
                    
'verify_peer' => false,
                    
'verify_peer_name' => false,
                    
'allow_self_signed' => true,
                ],
            ]);
        }

        return 
$context;
    }


La llamada al servicio SOAP la realizo de la siguiente manera:

Código PHP:

public function __construct(
    ...,
    private 
SoapVerifactu $soap,
) {
}
 ...
 
public function 
execute() {
    ....
    
    
/** @var RespuestaRegFactuSistemaFacturacion $respuesta */
    
$respuesta $this->soap->registroFacturacion([
        
'Cabecera' => $cabecera,        // objeto CabeceraType o un array asociativo
        
'RegistroFactura' => $registrosFactura,    // objeto con los registros de factura o array asociativo
    
]);
    ...


La respuesta es un objeto RespuestaRegFactuSistemaFacturacion según se describe en el WSDL/XSD de la AEAT.

Hasta ahora he realizado envíos y consultas sin problemas.

Espero que te sirva de ayuda.

alexrodral 10-04-2025 14:33:18

Yo por mi parte ya he solucionado el problema que tenía, el problema que tenía era que yo estaba intentando mandar un fichero .xml de todas las formas que se me pasaban por la cabeza, finalmente he creado un objeto generico stdClass en PHP donde he simulado la estructura del xml y eso por algún motivo me lo ha aceptado.
Muchas gracias a los que habéis intentado ayudarme, es mi primera vez usando SOAP y no tenía ni idea de como usarlo.


La franja horaria es GMT +2. Ahora son las 09:56:53.

Powered by vBulletin® Version 3.6.8
Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
Traducción al castellano por el equipo de moderadores del Club Delphi