Foros Club Delphi

Foros Club Delphi (https://www.clubdelphi.com/foros/index.php)
-   Errores (relacionados con al AEAT) (https://www.clubdelphi.com/foros/forumdisplay.php?f=78)
-   -   No devuelve nada el envío en PHP (hace unos días si funcionaba) (https://www.clubdelphi.com/foros/showthread.php?t=97200)

mmorenogalbis 31-01-2025 13:29:06

No devuelve nada el envío en PHP (hace unos días si funcionaba)
 
Buenas,

¿Alguien sabe si ha cambiado alguna URL o esquema?
Antes siempre recibía respuesta pero dejé este asunto unos días-semanas y ahora no me muestra respuesta el envío a VERIFACTU.

Este es mi código de prueba en PHP (unicamente he cambiado las urls con h_t_t_p_s):

Código PHP:

<?php

error_reporting
(E_ALL E_NOTICE); // Notificar todos los errores excepto E_NOTICE

echo "<h2>Comienza la prueba de VERIFACTU</h2>";

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





$xml='<SOAP-ENV:Envelope xmlns:SOAP-ENV="h_t_t_p://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="h_t_t_p_s://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroInformacion.xsd" xmlns:ns2="h_t_t_p_s://www2.agenciatributaria.gob.es/static_files/common/internet/dep/aplicaciones/es/aeat/tike/cont/ws/SuministroLR.xsd">
    <SOAP-ENV:Body>
        <ns2:RegFactuSistemaFacturacion>
            <ns1:Cabecera>
                <ns1:ObligadoEmision>
                    <ns1:NombreRazon>NOMBRE_EMISOR_FACTURA</ns1:NombreRazon>
                    <ns1:NIF>NIF_EMISOR_FACTURA</ns1:NIF>
                </ns1:ObligadoEmision>
            </ns1:Cabecera>
            <ns2:RegistroFactura>
                <ns2:RegistroAlta>
                    <ns1:IDVersion>1.0</ns1:IDVersion>
                    <ns1:IDFactura>
                        <ns1:IDEmisorFactura>NIF_EMISOR_FACTURA</ns1:IDEmisorFactura>
                        <ns1:NumSerieFactura>A022</ns1:NumSerieFactura>
                        <ns1:FechaExpedicionFactura>08-09-2024</ns1:FechaExpedicionFactura>
                    </ns1:IDFactura>
                    <ns1:NombreRazonEmisor>NOMBRE_EMISOR_FACTURA</ns1:NombreRazonEmisor>
                    <ns1:TipoFactura>F1</ns1:TipoFactura>
                    <ns1:DescripcionOperacion>VENTA MERCADERIAS</ns1:DescripcionOperacion>
                    <ns1:Destinatarios>
                        <ns1:IDDestinatario>
                            <ns1:NombreRazon>NOMBRE_CLIENTE</ns1:NombreRazon>
                            <ns1:NIF>NIF_CLIENTE</ns1:NIF>
                        </ns1:IDDestinatario>
                    </ns1:Destinatarios>
                    <ns1:Desglose>
                        <ns1:DetalleDesglose>
                            <ns1:ClaveRegimen>01</ns1:ClaveRegimen>
                            <ns1:CalificacionOperacion>S1</ns1:CalificacionOperacion>
                            <ns1:TipoImpositivo>21</ns1:TipoImpositivo>
                            <ns1:BaseImponibleOimporteNoSujeto>100.00</ns1:BaseImponibleOimporteNoSujeto>
                            <ns1:CuotaRepercutida>21.00</ns1:CuotaRepercutida>
                        </ns1:DetalleDesglose>
                    </ns1:Desglose>
                    <ns1:CuotaTotal>21.00</ns1:CuotaTotal>
                    <ns1:ImporteTotal>121.00</ns1:ImporteTotal>
                    <ns1:Encadenamiento>
                        <ns1:PrimerRegistro>S</ns1:PrimerRegistro>
                    </ns1:Encadenamiento>
                    <ns1:SistemaInformatico>
                        <ns1:NombreRazon>NOMBRE_EMPRESA_SOFTWARE</ns1:NombreRazon>
                        <ns1:NIF>NIF_EMPRESA_SOFTWARE</ns1:NIF>
                        <ns1:NombreSistemaInformatico>SOFTWARE TPV DE PRUEBAS</ns1:NombreSistemaInformatico>
                        <ns1:IdSistemaInformatico>7</ns1:IdSistemaInformatico>
                        <ns1:Version>1.0</ns1:Version>
                        <ns1:NumeroInstalacion>38</ns1:NumeroInstalacion>
                        <ns1:TipoUsoPosibleSoloVerifactu>S</ns1:TipoUsoPosibleSoloVerifactu>
                        <ns1:TipoUsoPosibleMultiOT>N</ns1:TipoUsoPosibleMultiOT>
                        <ns1:IndicadorMultiplesOT>N</ns1:IndicadorMultiplesOT>
                    </ns1:SistemaInformatico>
                    <ns1:FechaHoraHusoGenRegistro>2024-10-01T09:05:00+02:00</ns1:FechaHoraHusoGenRegistro>
                    <ns1:TipoHuella>01</ns1:TipoHuella>
                    <ns1:Huella>DD127FF3CEE27D933D330BD718FFBD1D13B2876B14C3C3B2E3B329195</ns1:Huella>
                </ns2:RegistroAlta>
            </ns2:RegistroFactura>
        </ns2:RegFactuSistemaFacturacion>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>'
;

    
$pfxFile 'certificados/nombre_de_mi_certificado.p12'// Ruta al archivo PFX del certificado electronico
$pfxPassword '12345'// Contraseña del PFX del certificado electronico

$ch curl_init($url);
curl_setopt($chCURLOPT_RETURNTRANSFERtrue);
curl_setopt($chCURLOPT_SSL_VERIFYPEERfalse);
curl_setopt($chCURLOPT_TIMEOUT30);
curl_setopt($chCURLOPT_POSTtrue);
curl_setopt($chCURLOPT_POSTFIELDS$xml);
curl_setopt($chCURLOPT_HTTPHEADER, array('Content-Type: application/xml',"SOAPAction: AltaFactuSistemaFacturacion"));
curl_setopt($chCURLOPT_SSLCERTTYPE'P12');
curl_setopt($chCURLOPT_SSLCERT$pfxFile);
curl_setopt($chCURLOPT_SSLCERTPASSWD$pfxPassword);
$response curl_exec($ch);
if (
curl_errno($ch)) {
    echo 
'Error: ' curl_error($ch);
} else {
    
print_r("<pre>".htmlentities($response)."<pre>");
echo 
"TERMINADO";
}
curl_close($ch);

?>


dec 07-02-2025 11:20:07

Hola a todos,

En la línea en que usas la función "error_reporting", sitúa el siguiente código, a ver si puedes ver algún error:

Código PHP:


ini_set
('display_errors'1);
ini_set('display_startup_errors'1);
error_reporting(E_ALL); 


mmorenogalbis 07-02-2025 11:52:46

era el certificado
 
Gracias dec, no daba ningún error.

Simplemente era el certificado que estaba caducado. Es curioso que la respuesta era [VACIA], pero nada devolvía error...

Gracias.

ermendalenda 07-02-2025 12:12:02

Cita:

Empezado por mmorenogalbis (Mensaje 561825)
Gracias dec, no daba ningún error.

Simplemente era el certificado que estaba caducado. Es curioso que la respuesta era [VACIA], pero nada devolvía error...

Gracias.

Tienes que tener de alguna forma información de que es error de certificado(error 401), lo que ocurre es que la respuesta no es igual que para las respuestas de los registros correcto/incorrecto/parcialmente correcto
Cita:


No autorizado. Se ha producido un error al verificar el certificado presentado. Las causas m&aacute;s probables de este error son:</p><ul><li>El certificado no ha sido firmado por una autoridad reconocida.</li><li>El tipo de certificado no es v&aacute;lido para el servicio al que se quiere acceder.</li><li>El certificado ha expirado.</li></ul>

Joseg012 23-02-2025 17:24:26

SoapClient
 
Creo que más práctico usar: $client = new \SoapClient($url, array('trace' => TRUE)); , luego poder pasarle al WSDL, el valor como por ejemplo: $client->AltaFactuSistemaFacturacion(array('xml' => ($xmlDocument)));

La Repuesta viene data por $client->SOAPFault->faultstring, aún no llegado ha esta parte creo puede ser así.

Saludos;

Joseg012 27-02-2025 21:45:25

Clases Programa SoapService
 
Luego de varias pruebas en diferentes escenarios, me pude dar cuenta que las incidencias puede ser de varios tipos. Pueden de tipo remoto o local; sobre todo cuando es SoapFaul.

Al final mi clase para el proceso quedo definida así:
Esto es para php 8.1.
Código PHP:

<?php 
/**
 * Permite la gestión de los archivos xml, en el procesamiento WSDL mediante SOAP.
 * Date: 27/02/2025
 * Copyright:2025 
 * Author: José Hernández 
 */

namespace ElectronicInvoice\Content\SoapService;

class 
ServiceVerifactu
{
    public 
$Parameters;
    private 
$response;
    private 
$type_incedent;
    private 
$correcto false;

    public function 
__construct()
    {
        
$this->Parameters = (object) [
            
'http_service'=>'',
            
'http_port_test'=>'',
            
'http_port_prod'=>'',
            
'certificate'=>'',
            
'passw_cert'=>'',
            
'type_process'=>'',
            
'content_invoice'=>''
        
];
    }
    
/**
     * @return bool Si fue correcta la validación de la factura 
     */
    
public function getCorrect():bool{
        return 
$this->correcto;
    }
    
/***
     * @return string Devuelve el tipo de incidencia
     */
    
public function getTypeIncedent()
    {
        return 
$this->type_incedent;
    }
    
/***
     * Description: Permite definir los parametros
     * Date: 27/02/2025
     * Copyright: 2025
     * Author: Ing. José Hernández
     * @return object Retorna el objecto Parameter
     */
    
public function parameters()
    {
        return 
$this->Parameters;
    }
    
/**
     * Se emplea la conexión mediante SOAP, para los datos envueltos 
     * @return string return $response Devuelve la repuesta del servicio 
     */
    
public function getResponseService():string 
    
{
        
$this->response ""// declaración de la variable
        
try {
            
// Cargar el XML de la factura
            // El valor de pase debe ser de tipo string, previamente con file_get_contents()
            
$factura = new \SimpleXMLElement($this->Parameters->content_invoice);
            
$client = new \SoapClient($this->Parameters->http_service$this->ClientOptions());

            
// Llamada al servicio SOAP
            
$this->correcto false;
            
$this->response $client->__doRequest($factura->asXml(), $this->getPortWork(),'AltaFactuSistemaFacturacion'SOAP_1_1);

            
$this->processIncident();

        } catch (\
SoapFault $e) {
            
// Manejo de errores
            
$this->response "Error en la llamada SOAPFaul(Local): " $e->getMessage();
        } catch (\
Exception $e) {
            
// Manejo de errores
            
$this->response "Error System: " $e->getMessage();
        }
        return 
$this->response;
    }
    
/**
     * Permite determinar que incidencia se produjo en el proceso 
     * estan pueden : html, SoapFaul o Data Center errores de transcripción 
     * @return string Retorna el $response en forma esquematica 
     */
    
private function processIncident()
    {
        
$this->callMethodIncident();
    }
    private function 
callMethodIncident()
    {
        switch (
true)
        {
            case 
str_contains($this->response'env:Fault'):
                
// Incidencia remota retorna SoapFaul
                
$this->IncidentRemoteSoapFaul();
                
$this->type_incedent "SoapFaul";
                break;
            case 
str_contains($this->response'DOCTYPE html'):
                
// Incidencia remota retorna Sitio Web con error
                
$this->IncidentRemoteHtml();
                
$this->type_incedent "HtmlDoc";
                break;
            case 
str_contains($this->response'tikR:RespuestaLinea'):
                
// Incidencia de transcripción de data, reporte factura.  
                
$this->IncidentRemoteAnswerOnline();
                
$this->type_incedent "RespLin";
                break;
            default:
                
// Proceso inesperado no se determino la incidencia
                
$this->response "Error no determinado:".$this->response;
                
$this->type_incedent "NoDet";
        }
    }
    
/**
     * Client Options 
     * Description: Permite obtener las configuraciones para
     * procesar el servicio SOAP.
     * Date: 27/02/2025
     * Nota: El certificado debe estar en extensión pem
     * para convertir : openssl pkcs12 -in cert.pfx -out cert.pem -nodes
     * @return array return parameters options.
     */
    
private function ClientOptions():array 
    {
        return [
            
'soap_version' => SOAP_1_1,
            
'trace' => 1// Habilita el seguimiento para depuración
            
'exceptions' => false,
            
'cache_wsdl' => WSDL_CACHE_NONE
            
'local_cert' => $this->Parameters->certificate,
            
'passphrase' => $this->Parameters->passw_cert,
            
'stream_context' => stream_context_create(
                        [
                        
'ssl' => [
                            
'verify_peer'       => false,
                            
'verify_peer_name'  => false,
                            
'allow_self_signed' => true,
                          ]
                    ]
                )
            ];
    }
    
/**
     * @return string Retorna el puerto a emplear 
     */
    
public function getPortWork():string 
    
{
        return (
$this->Parameters->type_process=='prodXml') ? $this->Parameters->http_port_test $this->Parameters->http_port_prod;
    }
    private function 
IncidentRemoteSoapFaul()
    {
        
$xml_resp simplexml_load_string($this->response);
        
$this->response "Response: \n" .$xml_resp->xpath('//env:Fault')[0]->faultcode ."\n\n"$xml_resp->xpath('//env:Fault')[0]->faultstring;
    }
    private function 
IncidentRemoteHtml()
    {
        
// Tomar los datos mediante un sistema de expresión regular decidir modo de repuesta.
        
$html_data $this->response;
        
$this->response preg_replace('/(<body\s[\s\S\d\D\w\W]+<\/body>)/','$1',$html_data);
    }
    private function 
IncidentRemoteAnswerOnline()
    {
        
// Cargar el XML en un objeto SimpleXML
        
$xml simplexml_load_string($this->response);

        
// Registrar los namespaces para poder acceder a los elementos
        
$namespaces $xml->getNamespaces(true);

        
// Registrar el namespace 'env' y 'tikR'
        
$xml->registerXPathNamespace('env'$namespaces['env']);
        
$xml->registerXPathNamespace('tikR'$namespaces['tikR']);

        
// Extraer los datos que necesitas
        
$estadoEnvio $xml->xpath('//env:Body/tikR:RespuestaRegFactuSistemaFacturacion/tikR:EstadoEnvio')[0];
        
$estadoRegistro $xml->xpath('//env:Body/tikR:RespuestaRegFactuSistemaFacturacion/tikR:RespuestaLinea/tikR:EstadoRegistro')[0];
        if (
$estadoEnvio!="Correcto" && $estadoRegistro!="Correcto" ){
            
$codigoErrorRegistro $xml->xpath('//env:Body/tikR:RespuestaRegFactuSistemaFacturacion/tikR:RespuestaLinea/tikR:CodigoErrorRegistro')[0];
            
$descripcionErrorRegistro $xml->xpath('//env:Body/tikR:RespuestaRegFactuSistemaFacturacion/tikR:RespuestaLinea/tikR:DescripcionErrorRegistro')[0];
        } else {
            
$codigoErrorRegistro "";
            
$descripcionErrorRegistro="Procesado Correctamente !!! ";
            
$this->correcto True;
        }

        
// Concatenar los datos en una variable
        
$resultado  "Estado del Envio: " . (string)$estadoEnvio "\n" .
                      
"Estado del Registro: " . (string)$estadoRegistro "\n" .
                      
"Código Registro: " . (string)$codigoErrorRegistro "\n" .
                      
"Descripción: " . (string)$descripcionErrorRegistro;

        
// Datos devueltos en cadena de string. 
        
$this->response $resultado;
    }
    
}
?>

Este un ejemplo de cómo cargar los datos en los parámetros:
Código PHP:

<?php 
$root_file_signature 
$root_file "/files/signxml/" $name_file "_sign.xml";
// Cargar el XML de la factura
$factura file_get_contents($root_file_signature);

$this->soap_service->parameters()->content_invoice $factura;
$this->soap_service->parameters()->http_service $this->http_service;
$this->soap_service->parameters()->http_port_test $this->http_test;
$this->soap_service->parameters()->http_port_prod $this->http_prod;
$this->soap_service->parameters()->type_process $this->http_ambiente;
$this->soap_service->parameters()->certificate $certificate;
$this->soap_service->parameters()->passw_cert $password;
?>

:cool: Si quieren algo así para su lenguaje de desarrollo pueden, hacer la conversión de la clase mediante IA.


La franja horaria es GMT +2. Ahora son las 08:22:28.

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