Club Delphi  
    Paypal   FTP   CCD     Buscar   Trucos   Trabajo   Foros

Retroceder   Foros Club Delphi > Principal > Internet
Registrarse FAQ Miembros Calendario Guía de estilo Buscar Temas de Hoy Marcar Foros Como Leídos

Colaboración Paypal con ClubDelphi

Respuesta
 
Herramientas Buscar en Tema Desplegado
  #1  
Antiguo 30-04-2021
bilbur bilbur is offline
Miembro
 
Registrado: dic 2019
Posts: 62
Poder: 7
bilbur Va por buen camino
Cita:
Empezado por Sistel Ver Mensaje
Hola Bilbur

¿Obtienes alguna respuesta al php-curl que lanzas?

Cuando, en su día, desarrollé el módulo de SII, tuve problemas con el php-curl.
Sin embargo me iba bien si lo lanzaba mediante shell_exec("curl --connect-timeout ...")
Así que lo dejé de esa manera.

Saludos

Esto es para chinarse.
Al final consigo envío y respuesta TBAI BIZ


Correcciones previas:


Error en el json pues yo ponía ejerc 2022 y debo poner 2021
Error en el json que si el nif no coincide con el del obligado tributario
etc


Error de certificado, resulta que el de izenpe (que me dieron para pruebas NO vale)
Error de certificado, resulta que el de FNMT de representante tampoco vale
He cambiado todo a mi certificado personal de FNMT y ya funciona


Recibo respuesta, todos los regitros erróneos
Descomprimo 2 veces la respuesta y me sale el motivo del error (y esto si que es para chinarse)


<SituacionRegistro>
<EstadoRegistro>Incorrecto</EstadoRegistro>
<CodigoErrorRegistro>B4_2000019</CodigoErrorRegistro>
<DescripcionErrorRegistro>
BaseRectificada: La Base Rectificada solo puede venir informada si Tipo (de rectificativa) es S.
</DescripcionErrorRegistro>
</SituacionRegistro>


Eso ya lo sabía, pero en TBAI GIP me daba la misma factura como correcta con aviso
011 Aviso: Error validación de negocio: FacturaRectificativa.

Comento a TBAI GIP y contestan:

Egun on, Buenos días
El motivo del error es que siendo una factura rectificativa, no se indica cual es la factura rectificada.
En TicketBAI siempre es obligatorio informar la factura rectificada correspondiente a la rectificativa.
Un saludo,


Ahora informo de las bases rectificadas en GIP pero no en BIZ y además tb hay que informar de la factura rectificada, tanto GIP como BIZ aunque sea dato no obligatorio (por lo memos hasta el otro día)


Lo importante es esto:
eus-bizkaia-n3-codigo-respuesta:

eus-bizkaia-n3-numero-registro:

eus-bizkaia-n3-tipo-respuesta: Correcto

Content-Type: application/xml;charset=UTF-8

Ordenaré el código PHP ya funcionando y con permiso de los administradores y a petición de Sistel iré subiendo las partes de código que me solicite.

Gracias a todos por todo, me estáis ayudando bastante más de lo que parece
Responder Con Cita
  #2  
Antiguo 01-05-2021
Sistel Sistel is offline
Miembro
 
Registrado: nov 2019
Ubicación: Bilbao
Posts: 484
Poder: 7
Sistel Va por buen camino
Cita:
Empezado por bilbur Ver Mensaje
...
Eso ya lo sabía, pero en TBAI GIP me daba la misma factura como correcta con aviso
011 Aviso: Error validación de negocio: FacturaRectificativa.

Comento a TBAI GIP y contestan:
Egun on, Buenos días
El motivo del error es que siendo una factura rectificativa, no se indica cual es la factura rectificada.
En TicketBAI siempre es obligatorio informar la factura rectificada correspondiente a la rectificativa.
Un saludo,

Ahora informo de las bases rectificadas en GIP pero no en BIZ y además tb hay que informar de la factura rectificada, tanto GIP como BIZ aunque sea dato no obligatorio (por lo memos hasta el otro día)
...
Bueno, en Gipuzkoa, no sé como será, pero en Bizkaia, en el caso de factura rectificativa, sí es obligatorio el número de factura que rectifica.
Boletín Oficial de Bizkaia nº 175 del 11 de septiembre de 2020
Artículo 3, apartado g
Y también aparece en rojo (obligatorio) en la estructura (página 11)

Cita:
Empezado por bilbur Ver Mensaje
...
Ordenaré el código PHP ya funcionando y con permiso de los administradores y a petición de Sistel iré subiendo las partes de código que me solicite.
...
Muchísimas gracias, Bilbur, por tu ayuda.
Seguro que también les será muy útil a otros colegas que leen este foro, aunque no participen.

Un saludo
Responder Con Cita
  #3  
Antiguo 01-05-2021
bilbur bilbur is offline
Miembro
 
Registrado: dic 2019
Posts: 62
Poder: 7
bilbur Va por buen camino
Cita:
Empezado por Sistel Ver Mensaje
Bueno, en Gipuzkoa, no sé como será, pero en Bizkaia, en el caso de factura rectificativa, sí es obligatorio el número de factura que rectifica.
Boletín Oficial de Bizkaia nº 175 del 11 de septiembre de 2020
Artículo 3, apartado g
Y también aparece en rojo (obligatorio) en la estructura (página 11)

Cierto, me lié con el SII puesto que en el xml que genero copié lo que tenía del SII y lo tenía como no obligatorio.


Errores de importancia relativa que se van corrigiendo conforme a los resultados de las pruebas.


Por otro lado, cuando queras empezamos con PHP
Responder Con Cita
  #4  
Antiguo 01-05-2021
Galaxian Galaxian is offline
Miembro
 
Registrado: mar 2021
Posts: 52
Poder: 6
Galaxian Va por buen camino
¿Alguien sabe si el encadenamiento con la factura anterior es relativo o absoluto? es decir, si se encadena con la última factura de su serie o con la última generada sea de la serie que sea?
Responder Con Cita
  #5  
Antiguo 02-05-2021
bilbur bilbur is offline
Miembro
 
Registrado: dic 2019
Posts: 62
Poder: 7
bilbur Va por buen camino
Cita:
Empezado por Galaxian Ver Mensaje
¿Alguien sabe si el encadenamiento con la factura anterior es relativo o absoluto? es decir, si se encadena con la última factura de su serie o con la última generada sea de la serie que sea?

Se encadena con la última generada sea de la serie que sea
Responder Con Cita
  #6  
Antiguo 02-05-2021
bilbur bilbur is offline
Miembro
 
Registrado: dic 2019
Posts: 62
Poder: 7
bilbur Va por buen camino
PHP Generación del xml

Generación previa a su firma
En mi caso sólo vendo bienes, no servicios

La intención es ver la estructura del xml

Código PHP:
 $xml  = "<?xml version='1.0' encoding='UTF-8'?>";     
    $xml .= "<T:TicketBai xmlns:T='urn:ticketbai:emision' xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance' 
    xsi:schemaLocation='http://www.w3.org/TR/xmldsig-core/xmldsig-core-schema.xsd'>";
    $xml .= "<Cabecera>";
    $xml .= "<IDVersionTBAI>".$versionTB."</IDVersionTBAI>";
     
    $xml .= "</Cabecera>";  
    $xml .= "<Sujetos>";
    $xml .= "<Emisor>";
    $xml .= "<NIF>".$cif_emisor."</NIF>";    
    $xml .= "<ApellidosNombreRazonSocial>".$nomb_emisor."</ApellidosNombreRazonSocial>";
    $xml .= "</Emisor>";   

    if ($ano_cli == 'S') $cif_cli = $nif_simplificado;  
    // $ano_cli si es fra simplificada  $nif_simplificado = 00000000T
    $xml .= "<Destinatarios>";
    $xml .= "<IDDestinatario>";    // $pai_cli = país del cliente
    if($pai_cli != 'ES' && $es_exportacion == 'SI')    
    {
    $xml .= "<IDOtro>";
    $xml .= "<CodigoPais>".$pai_cli."</CodigoPais>";
    $xml .= "<IDType>06</IDType>";    
    $xml .= "<ID>".$cif_cli."</ID>";
    $xml .= "</IDOtro>";    
    } elseif($es_intracomunitaria == 'SI')    
    {
    $xml .= "<IDOtro>";
    $xml .= "<IDType>02</IDType>";    
    $xml .= "<ID>".$cif_cli."</ID>";
    $xml .= "</IDOtro>";        
    }
    else    
    $xml .= "<NIF>".$cif_cli."</NIF>";
    $xml .= "<ApellidosNombreRazonSocial>".$nomb_cli."</ApellidosNombreRazonSocial>";
    $xml .= "<CodigoPostal>".$cpo_cli."</CodigoPostal>";  
    $ClientAdresse = elimina_acentos($ClientAdresse);      
    $xml .= "<Direccion>".$ClientAdresse."</Direccion>";     
    $xml .= "</IDDestinatario>";
    $xml .= "</Destinatarios>";
    $xml .= "<EmitidaPorTercerosODestinatario>N</EmitidaPorTercerosODestinatario>";  
    $xml .= "</Sujetos>";
$hora = time();
$hora = (date("H:i:s"));
  
    $xml .= "<Factura>";
    $xml .= "<CabeceraFactura>";  
    $xml .= "<SerieFactura>".$seriefactura."</SerieFactura>";  
    $xml .= "<NumFactura>".$numero_factura."</NumFactura>";
    $xml .= "<FechaExpedicionFactura>".$fecha_factura_TBAI."</FechaExpedicionFactura>";
    $xml .= "<HoraExpedicionFactura>".$hora."</HoraExpedicionFactura>";
    if($ano_cli == 'S')
    $xml .= "<FacturaSimplificada>".$ano_cli."</FacturaSimplificada>"; 
    $xml .= "<FacturaEmitidaSustitucionSimplificada>N</FacturaEmitidaSustitucionSimplificada>";
    if($FacturaRectificativa == 'S')
    {
    $base_ivaR = number_format($bimp_alb*(-1),2,".","");      
    $imp_ivaR  = number_format($iiva_alb*(-1),2,".","");    
    $imp_reqR  = number_format($ire_alb*(-1), 2,".",""); 
    
    $xml .= "<FacturaRectificativa>";  
    if($ano_cli == 'S')
    $xml .= "<Codigo>R5</Codigo>"; 
    else
    $xml .= "<Codigo>R1</Codigo>";   
    $xml .= "<Tipo>I</Tipo>";   
    
    if ($hacienda_foral == 'GIP')    
    {    
    $xml .= "<ImporteRectificacionSustitutiva>";      
    $xml .= "<BaseRectificada>".$base_ivaR."</BaseRectificada>";    
    $xml .= "<CuotaRectificada>".$imp_ivaR."</CuotaRectificada>";    
    $xml .= "<CuotaRecargoRectificada>".$imp_reqR."</CuotaRecargoRectificada>";    
    $xml .= "</ImporteRectificacionSustitutiva>";  
    }
    
    $xml .= "</FacturaRectificativa>";  


    $xml .= "<FacturasRectificadasSustituidas>";  
    $xml .= "<IDFacturaRectificadaSustituida>";    
    $xml .= "<NumFactura>".$rectifica1."</NumFactura>";
    $xml .= "<FechaExpedicionFactura>".$rectifica2."</FechaExpedicionFactura>";    
    $xml .= "</IDFacturaRectificadaSustituida>";      
    $xml .= "</FacturasRectificadasSustituidas>"; 

    }
    $xml .= "</CabeceraFactura>";     

    $xml .= "<DatosFactura>";   
    
    if($FacturaRectificativa == 'S') 
    {        
    $xml .= "<FechaOperacion>".$rectifica2."</FechaOperacion>"; 
    $xml .= "<DescripcionFactura>FACTURA RECTIFICATIVA</DescripcionFactura>";       
    }    
     else
    {
     $xml .= "<FechaOperacion>".$fecha_factura_TBAI."</FechaOperacion>";  
    $xml .= "<DescripcionFactura>FACTURA DE VENTA</DescripcionFactura>";     
    }

/////////// detalle factura ////////////////////////

    if($enviar_detalle_lineas == 'SI')
    {
    $xml .= "<DetallesFactura>";
    
    if($lineas_factura)
    foreach ($lineas_factura as $clave => $valor)
    {
    $valor_valor = explode("|",$valor);
    $des_art   = $valor_valor[1];
    $neto_lin  = number_format($valor_valor[2],2,".","");  
    $prec_lin  = number_format($valor_valor[3],8,".",""); 
    $impo_lin  = $valor_valor[4];
    $impo_lin  = $impo_lin + $impo_lin*($tipo_iva/100) + $impo_lin*($tipo_req/100); 
    $impo_lin  = number_format($impo_lin,2,".",""); 

    $xml .= "<IDDetalleFactura>";
    $des_art = elimina_acentos($des_art);    
    $des_art = preg_replace("([^ A-Za-z0-9])", "", $des_art);     
    $xml .= "<DescripcionDetalle>".$des_art."</DescripcionDetalle>"; 
    $xml .= "<Cantidad>".$neto_lin."</Cantidad>"; 
    $xml .= "<ImporteUnitario>".$prec_lin."</ImporteUnitario>"; 
    $xml .= "<ImporteTotal>".$impo_lin."</ImporteTotal>";     
    $xml .= "</IDDetalleFactura>";
    }       // fin for
    unset($lineas_factura);
    
    $xml .= "</DetallesFactura>";  
    }
    
#########################   totales    

    $xml .= "<ImporteTotalFactura>".$total_factura."</ImporteTotalFactura>"; 

    if($retencion)
    $xml .= "<RetencionSoportada>".$retencion."</RetencionSoportada>"; 
    
    $xml .= "<Claves>";   
    $xml .= "<IDClave>"; 
    $xml .= "<ClaveRegimenIvaOpTrascendencia>".$ClaveRegimenEspecialOTrascendencia."</ClaveRegimenIvaOpTrascendencia>"; 
    $xml .= "</IDClave>";     
    $xml .= "</Claves>";    
  
    $xml .= "</DatosFactura>";    

    $tipo_iva_m = number_format($tipo_iva,2,".","");
    if($tipo_req!=0 && $ire_alb!=0)
    $tipo_req_m = number_format($tipo_req,2,".","");
 
 
    if($es_exportacion == 'SI')    
    {        
    $xml .= "<TipoDesglose>";
    $xml .= "<DesgloseTipoOperacion>";
    $xml .= "<Entrega>";    
    $xml .= "<Sujeta>";
    $xml .= "<Exenta>";
    $xml .= "<DetalleExenta>";        
    $xml .= "<CausaExencion>E2</CausaExencion>";
    $xml .= "<BaseImponible>".$base_iva."</BaseImponible>";
    $xml .= "</DetalleExenta>";        
    $xml .= "</Exenta>";
    $xml .= "</Sujeta>";
    $xml .= "</Entrega>";    
    $xml .= "</DesgloseTipoOperacion>";
    $xml .= "</TipoDesglose>";
    }
    elseif($es_intracomunitaria == 'SI')     
    {
    $xml .= "<TipoDesglose>";
    $xml .= "<DesgloseTipoOperacion>";
    $xml .= "<Entrega>";    
    $xml .= "<Sujeta>";
    $xml .= "<Exenta>";
    $xml .= "<DetalleExenta>";    
    $xml .= "<CausaExencion>E5</CausaExencion>";
    $xml .= "<BaseImponible>".$base_iva."</BaseImponible>";
    $xml .= "</DetalleExenta>";        
    $xml .= "</Exenta>";
    $xml .= "</Sujeta>";
    $xml .= "</Entrega>";    
    $xml .= "</DesgloseTipoOperacion>";
    $xml .= "</TipoDesglose>";    
    } else {
    $xml .= "<TipoDesglose>";
    $xml .= "<DesgloseFactura>";
    $xml .= "<Sujeta>";
    $xml .= "<NoExenta>";
    $xml .= "<DetalleNoExenta>";    
    $xml .= "<TipoNoExenta>S1</TipoNoExenta>";
    $xml .= "<DesgloseIVA>";        
    $xml .= "<DetalleIVA>";
    $xml .= "<BaseImponible>".$base_iva."</BaseImponible>";
    $xml .= "<TipoImpositivo>".$tipo_iva_m."</TipoImpositivo>";
    $xml .= "<CuotaImpuesto>".$imp_iva."</CuotaImpuesto>";
    if($tipo_req!=0 && $ire_alb!=0)
    {
    $xml .= "<TipoRecargoEquivalencia>".$tipo_req_m."</TipoRecargoEquivalencia>";
    $xml .= "<CuotaRecargoEquivalencia>".$imp_req."</CuotaRecargoEquivalencia>";
    }
    $xml .= "</DetalleIVA>";
    $xml .= "</DesgloseIVA>";
    $xml .= "</DetalleNoExenta>";
    $xml .= "</NoExenta>";    
    $xml .= "</Sujeta>";
    $xml .= "</DesgloseFactura>";        
    $xml .= "</TipoDesglose>";
     } 
    $xml .= "</Factura>";
</div>Tened en cuenta u obviad las variables de PHP que vienen de una consulta sql previa


Seguimos con el encadenamiento dentro del mismo script
Busco la factura firmada anterior



Código PHP:
    $firmada_anterior $directorio_TBAI.$factura_anterior;
    
$file_anterior $firmada_anterior;
    if (
file_exists($file_anterior)) {
    
$xml_anterior simplexml_load_file($firmada_anterior);
    
$SerieFacturaAnterior $xml_anterior->Factura->CabeceraFactura->SerieFactura;    
    
$NumFacturaAnterior $xml_anterior->Factura->CabeceraFactura->NumFactura;    
    
$FechaExpedicionFacturaAnterior =    $xml_anterior->Factura->CabeceraFactura->FechaExpedicionFactura;        
    
$signatureNode $xml_anterior->children('ds'true)->Signature;    
    
$SignatureValueFirmaFacturaAnterior $signatureNode->SignatureValue;    
    } else {
    
$enviada_anterior $directorio_TBAI_envios.$factura_anterior;    
    
$file_enviado $enviada_anterior;    
    if (
file_exists($file_enviado)) {    
    
$xml_anterior simplexml_load_file($enviada_anterior);
    
$SerieFacturaAnterior $xml_anterior->Factura->CabeceraFactura->SerieFactura;        
    
$NumFacturaAnterior $xml_anterior->Factura->CabeceraFactura->NumFactura;    
    
$FechaExpedicionFacturaAnterior $xml_anterior->Factura->CabeceraFactura->FechaExpedicionFactura;        
    
$signatureNode $xml_anterior->children('ds'true)->Signature;    
    
$SignatureValueFirmaFacturaAnterior $signatureNode->SignatureValue;    
    } else {    
    
$NumFacturaAnterior 'Primera factura del ciclo de facturacion';    
    
$SignatureValueFirmaFacturaAnterior 'Primera factura del ciclo de facturacion';        
    }
    }    
    
$SignatureValueFirmaFacturaAnterior substr($SignatureValueFirmaFacturaAnterior,0,100);

    
$xml .= "<HuellaTBAI>";
    
$xml .= "<EncadenamientoFacturaAnterior>";
    
$xml .= "<SerieFacturaAnterior>".$SerieFacturaAnterior."</SerieFacturaAnterior>";
    
$xml .= "<NumFacturaAnterior>".$NumFacturaAnterior."</NumFacturaAnterior>";
    
$xml .= "<FechaExpedicionFacturaAnterior>".$FechaExpedicionFacturaAnterior."</FechaExpedicionFacturaAnterior>";    
    
$xml .= "<SignatureValueFirmaFacturaAnterior>".$SignatureValueFirmaFacturaAnterior."</SignatureValueFirmaFacturaAnterior>";
    
$xml .= "</EncadenamientoFacturaAnterior>";
    
$xml .= "<Software>";
    
$xml .= "<LicenciaTBAI>".$LicenciaTBAI."</LicenciaTBAI>";    
    
$xml .= "<EntidadDesarrolladora>";
    
$xml .= "<NIF>".$nif_fabricante."</NIF>";
    
$xml .= "</EntidadDesarrolladora>";    
    
$xml .= "<Nombre>".$SoftwareNombre."</Nombre>";
    
$xml .= "<Version>".$SoftwareVersion."</Version>";
    
$xml .= "</Software>";
    
$xml .= "<NumSerieDispositivo>".$NumSerieDispositivo_TBAI."</NumSerieDispositivo>";
  
$xml .= "</HuellaTBAI>";

   
$xml .= "</T:TicketBai>"
Fima del xml que detallaré más adelante en otro envío


Código PHP:
$guardar_como $tbai_alb.'.xsig';
    
    require_once(
$_SERVER['DOCUMENT_ROOT'].'/xxxxxx/ticketBAI/src/XmlTools.php' );
    require_once(
$_SERVER['DOCUMENT_ROOT'].'/xxxxxx/ticketBAI/src/firmador.php' );
    
$dir_cert = ($_SERVER['DOCUMENT_ROOT'].'/infoxxxxrmix/_certificandos'.$alias_pdf.'/xxxxxxx.pfx' );    
    
$fac = new Firmador();
    
$xmlF $fac -> firmar($dir_cert,$cal_cert$xml$hacienda_foral);

    
$directorio_ventas $directorio_TBAI.$guardar_como;  
     
file_put_contents($directorio_ventas$xmlF); 
</div>

Queda enviar el desarrollo de la firma donde intervienen XmlTools.php, firmador.php y el certificado de firma y su clave.
Responder Con Cita
  #7  
Antiguo 02-05-2021
bilbur bilbur is offline
Miembro
 
Registrado: dic 2019
Posts: 62
Poder: 7
bilbur Va por buen camino
PHP Firma del xml

Nos hemos quedado aquí:

Código PHP:
$guardar_como $tbai_alb.'.xsig';
    
    require_once(
$_SERVER['DOCUMENT_ROOT'].'/xxx/ticketBAI/src/XmlTools.php' );
    require_once(
$_SERVER['DOCUMENT_ROOT'].'/xxx/ticketBAI/src/firmador.php' );
    
$dir_cert = ($_SERVER['DOCUMENT_ROOT'].'/xxx/_certificandos'.'/xxx.pfx' );    
    
$fac = new Firmador();
    
$xmlF $fac -> firmar($dir_cert,$cal_cert$xml$hacienda_foral);

    
$directorio_ventas $directorio_TBAI.$guardar_como;  
    
file_put_contents($directorio_ventas$xmlF); 
Lo que hago es indicar donde guardarlo, en mi caso con el mismo nombre número de orden de emisión de la factura.

XmlTools.php:
Código PHP:
class XmlTools {
    public function 
randomId() {
    if (
function_exists('random_int')) return random_int(0x100000000x7FFFFFFF);
    return 
rand(100000999999);
    }
    public function 
generateGUID($prefix='pfx')
    {
        
$uuid md5(uniqid(mt_rand(), true));
        
$guid $prefix.substr($uuid08)."-".
                
substr($uuid84)."-".
                
substr($uuid124)."-".
                
substr($uuid164)."-".
                
substr($uuid2012);
        return 
$guid;
    }
 } 
</div>

Este es el importante: firmador.php
Código PHP:
<?php
class Firmador{
    
    const 
POLITICA_FIRMA_BIZ = array(
    
"name" => "Politica de firma TicketBAI 1.0",
    
"url" => "https://www.batuz.eus/fitxategiak/batuz/ticketbai/sinadura_elektronikoaren_zehaztapenak_especificaciones_de_la_firma_electronica_v1_0.pdf"
    
"digest" => "Quzn98x3PMbSHwbUzaj5f5KOpiH0u8bvmwbbbNkO9Es="
    
);

    const 
POLITICA_FIRMA_GIP = array(
    
"name" => "Politica de firma TicketBAI 1.0",
    
"url" => "https://www.gipuzkoa.eus/ticketbai/sinadura",  
    
"digest" => "dTtPpv4fWTcejeVx7+91ILruFX3HysbngBlllJm4i/E="
    
);
    
    private 
$signTime         NULL;
    private 
$signPolicy       NULL;
    private 
$publicKey        NULL;
    private 
$privateKey       NULL;
    private 
$cerROOT          NULL;
    private 
$cerINTERMEDIO    NULL;
    private 
$tipoDoc          '01';

    public function 
retC14DigestSha1($strcadena)
    {
    
$strcadena    str_replace("\r"""str_replace("\n"""$strcadena));
    
$d1p        = new DOMDocument('1.0''UTF-8');
    
$d1p->loadXML($strcadena);
    
$strdata    $d1p->C14N();
    return 
base64_encode(hash('sha256' $strdatatrue ));
    }

    public function 
firmar($certificadop12$clavecertificado$xmlsinfirma$hacienda_foral)
    {
    if (!
$pfx file_get_contents($certificadop12))
    {
    echo 
"Error: No se puede leer el fichero del certificado o no existe en la ruta especificada\n";
    exit;
    }
    if (
openssl_pkcs12_read($pfx$key$clavecertificado))
    {
    
$this->publicKey    $key["cert"];
    
$this->privateKey   $key["pkey"];
    
$complem              openssl_pkey_get_details(openssl_pkey_get_private($this->privateKey));
    
$this->Modulus        base64_encode($complem['rsa']['n']);
    
$this->Exponent       base64_encode($complem['rsa']['e']);
    }
    else
    {
    echo 
"Error: No se puede leer el almacén de certificados o la clave no es la correcta.\n";
    exit;
    }

    if (
$hacienda_foral == "BIZ")    
    
$this->signPolicy             self::POLITICA_FIRMA_BIZ;
    if (
$hacienda_foral == "GIP")    
    
$this->signPolicy             self::POLITICA_FIRMA_GIP;
    
$tools = new XmlTools();
    
$this->signatureID             $tools->generateGUID('xmldsig-');
    
$this->signatureValue         $this->signatureID.'-sigvalue';
    
$this->XadesObjectId          "XadesObjectId-".$this->signatureID;
    
$this->KeyInfoId              "KeyInfoId-".$this->signatureID;
    
$this->Reference0Id           $this->signatureID.'-ref0';
    
$this->Reference1Id           "ReferenceKeyInfo";
    
$this->SignedProperties       $this->signatureID.'-signedprops';
    
$this->qualifyingProperties "QualifyingProperties-".$this->signatureID;
    
    
$xml1                       $xmlsinfirma;
    
$xml1                       $this->insertaFirma($xml1);
    return 
$xml1;
    }

    public function 
insertaFirma($xml){
      

      
    if (
is_null($this->publicKey) || is_null($this->privateKey))
    return 
$xml;

    
$d = new DOMDocument('1.0''UTF-8');
    
$d->loadXML($xml);
    
$canonizadoreal $d->C14N();

    
$xmlns 'xmlns:ds="http://www.w3.org/2000/09/xmldsig#" ' .
             
'xmlns:T="urn:ticketbai:emision" ' .
             
'xmlns:xades="http://uri.etsi.org/01903/v1.3.2#"';
            
    
$xmnls_signeg        'xmlns:T="urn:ticketbai:emision" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"';
    
$xmlns_keyinfo       'xmlns:T="urn:ticketbai:emision" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"';
    
$xmnls_signedprops 'xmlns:T="urn:ticketbai:emision" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" xmlns:xades="http://uri.etsi.org/01903/v1.3.2#" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"';

    
$signTime1 date('Y-m-d\TH:i:s+01:00');
    
$certData   openssl_x509_parse($this->publicKey);
    
$certDigest base64_encode(openssl_x509_fingerprint($this->publicKey"sha256"true));
    
$certIssuer = array();

    foreach (
$certData['issuer'] as $item=>$value)
    {
    
$certIssuer[] = $item '=' $value;
    }
    
$certIssuer implode(', 'array_reverse($certIssuer));

    
$prop '<xades:SignedProperties Id="'.$this->SignedProperties.'">' .
      
'<xades:SignedSignatureProperties>'.
          
'<xades:SigningTime>'.$signTime1.'</xades:SigningTime>' .
          
'<xades:SigningCertificate>'.
              
'<xades:Cert>'.
                  
'<xades:CertDigest>' .
                      
'<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>'.
                      
'<ds:DigestValue>'.$certDigest.'</ds:DigestValue>'.
                  
'</xades:CertDigest>'.
                  
'<xades:IssuerSerial>' .
                      
'<ds:X509IssuerName>'.$certIssuer.'</ds:X509IssuerName>'.
                      
'<ds:X509SerialNumber>'.$certData['serialNumber'].'</ds:X509SerialNumber>' .
                  
'</xades:IssuerSerial>'.
              
'</xades:Cert>'.
          
'</xades:SigningCertificate>' .
          
'<xades:SignaturePolicyIdentifier>'.
              
'<xades:SignaturePolicyId>' .
                  
'<xades:SigPolicyId>'.
                      
'<xades:Identifier>'.$this->signPolicy['url'].'</xades:Identifier>'.
                      
'<xades:Description>'.$this->signPolicy['name'].'</xades:Description>'.
                  
'</xades:SigPolicyId>'.
                  
'<xades:SigPolicyHash>' .
                      
'<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />'.
                      
'<ds:DigestValue>'.$this->signPolicy['digest'].'</ds:DigestValue>'.
                  
'</xades:SigPolicyHash>'.
                  
'<xades:SigPolicyQualifiers>'.
                    
'<xades:SigPolicyQualifier>'.
                      
'<xades:SPURI>'.$this->signPolicy['url'].'</xades:SPURI>'.
                    
'</xades:SigPolicyQualifier>'.
                  
'</xades:SigPolicyQualifiers>'.
              
'</xades:SignaturePolicyId>' .
          
'</xades:SignaturePolicyIdentifier>'.
      
'</xades:SignedSignatureProperties>'.
      
'<xades:SignedDataObjectProperties>'.
          
'<xades:DataObjectFormat ObjectReference="#'.$this->Reference0Id.'">'.
                  
'<xades:ObjectIdentifier>' .
                    
'<xades:Identifier Qualifier="OIDAsURN">urn:oid:1.2.840.10003.5.109.10</xades:Identifier>' .
                  
'</xades:ObjectIdentifier>' .          
              
'<xades:MimeType>text/xml</xades:MimeType>'.
              
'<xades:Encoding>UTF-8</xades:Encoding>'.
          
'</xades:DataObjectFormat>'.
      
'</xades:SignedDataObjectProperties>'.
      
'</xades:SignedProperties>';

    
// Prepare key info
    
$publicPEM "";
    
openssl_x509_export($this->publicKey$publicPEM);
    
$publicPEM str_replace("-----BEGIN CERTIFICATE-----"""$publicPEM);
    
$publicPEM str_replace("-----END CERTIFICATE-----"""$publicPEM);
    
$publicPEM str_replace("\r"""str_replace("\n"""$publicPEM));

    
$kInfo '<ds:KeyInfo Id="'.$this->KeyInfoId.'">' .
                
'<ds:X509Data>'  .
                    
'<ds:X509Certificate>'.$publicPEM.'</ds:X509Certificate>' .
                
'</ds:X509Data>' .
                
'<ds:KeyValue>'.
                
'<ds:RSAKeyValue>'.
                    
'<ds:Modulus>'.$this->Modulus.'</ds:Modulus>'.
                    
'<ds:Exponent>'.$this->Exponent.'</ds:Exponent>'.
                
'</ds:RSAKeyValue>'.
                
'</ds:KeyValue>'.                
             
'</ds:KeyInfo>';

    
$keyinfo_para_hash1 str_replace('<ds:KeyInfo''<ds:KeyInfo ' $xmlns_keyinfo$kInfo);
    
$kInfoDigest $this->retC14DigestSha1($keyinfo_para_hash1);
    
$aconop     str_replace('<xades:SignedProperties''<xades:SignedProperties ' $xmnls_signedprops$prop);
    
$propDigest $this->retC14DigestSha1($aconop);
    
$documentDigest base64_encode(hash('sha256'$canonizadorealtrue));
        
    
// Prepare signed info
    
$sInfo '<ds:SignedInfo>' .
        
'<ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>' .
        
'<ds:SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"/>' .
 
        
'<ds:Reference URI="#'.$this->SignedProperties.'" Type="http://uri.etsi.org/01903#SignedProperties">' .
        
'<ds:Transforms>' .
        
'<ds:Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>' .
        
'</ds:Transforms>' .        
        
'<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>' .
        
'<ds:DigestValue>'.$propDigest.'</ds:DigestValue>' .
        
'</ds:Reference>' .

        
'<ds:Reference Id="'.$this->Reference1Id.'" URI="#'.$this->KeyInfoId.'">' .
        
'<ds:Transforms>' .
        
'<ds:Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>' .
        
'</ds:Transforms>' .        
        
'<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />' .
        
'<ds:DigestValue>'.$kInfoDigest.'</ds:DigestValue>' .
        
'</ds:Reference>' .
        
        
'<ds:Reference Id="'.$this->Reference0Id.'" URI=""
        Type="http://www.w3.org/2000/09/xmldsig#Object">' 
.
        
'<ds:Transforms>' .
        
'<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>' .        
        
'<ds:Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>' .
        
'</ds:Transforms>' .
        
'<ds:DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256"/>' .
        
'<ds:DigestValue>'.$documentDigest.'</ds:DigestValue>' .
        
'</ds:Reference>' .

        
'</ds:SignedInfo>';


    
$signaturePayload str_replace('<ds:SignedInfo''<ds:SignedInfo ' $xmnls_signeg$sInfo);

    
$d1p = new DOMDocument('1.0''UTF-8');
    
$d1p->loadXML($signaturePayload);
    
$signaturePayload $d1p->C14N();
    
    
$signatureResult "";
    
$algo "SHA256";
    
    
openssl_sign($signaturePayload$signatureResult$this->privateKey$algo);
    
$signatureResult base64_encode($signatureResult);
    
$sig '<ds:Signature Id="'.$this->signatureID.'" xmlns:ds="http://www.w3.org/2000/09/xmldsig#">'.
    
$sInfo.'<ds:SignatureValue Id="'.$this->signatureValue.'">'.$signatureResult'
    </ds:SignatureValue>'
.
    
$kInfo .
    
'<ds:Object Id="'.$this->XadesObjectId .'">'.
    
'<xades:QualifyingProperties Id="'.$this->qualifyingProperties.'" 
    xmlns:ds="http://www.w3.org/2000/09/xmldsig#" Target="#'
.$this->signatureID.'" 
    xmlns:xades="http://uri.etsi.org/01903/v1.3.2#">'

    
$prop .
    
'</xades:QualifyingProperties>
    </ds:Object>
    </ds:Signature>'
;

    
$xml str_replace('</T:TicketBai>'$sig '</T:TicketBai>'$xml);
    return 
$xml;
    }
}
?>
Mucha atención al contenido de firmador.php
Es posible que algo sobre pero no me atrevo a tocar nada ya que finciona bien.
Proximamente hablaremos del envío de los xml firmados a GIP y a BIZ, este último tiene su miga.
A vuestra disposición para comentar y aclarar lo que requiráis


Sería bueno poder firmar sólo con los .pem y sin clave de certificado pero quizás no se pueda.


Un saludo
Responder Con Cita
  #8  
Antiguo 03-05-2021
Avatar de Neftali [Germán.Estévez]
Neftali [Germán.Estévez] Neftali [Germán.Estévez] is online now
[becario]
 
Registrado: jul 2004
Ubicación: Barcelona - España
Posts: 19.437
Poder: 10
Neftali [Germán.Estévez] Es un diamante en brutoNeftali [Germán.Estévez] Es un diamante en brutoNeftali [Germán.Estévez] Es un diamante en bruto
Cita:
Empezado por Galaxian Ver Mensaje
¿Alguien sabe si el encadenamiento con la factura anterior es relativo o absoluto? es decir, si se encadena con la última factura de su serie o con la última generada sea de la serie que sea?
Cita:
Empezado por bilbur Ver Mensaje
Se encadena con la última generada sea de la serie que sea

Nosotros encadenamos con la última factura independientemente de la serie.
(gracias por la corrección bilbur)
__________________
Germán Estévez => Web/Blog
Guía de estilo, Guía alternativa
Utiliza TAG's en tus mensajes.
Contactar con el Clubdelphi

P.D: Más tiempo dedicado a la pregunta=Mejores respuestas.

Última edición por Neftali [Germán.Estévez] fecha: 03-05-2021 a las 14:00:08.
Responder Con Cita
  #9  
Antiguo 03-05-2021
bilbur bilbur is offline
Miembro
 
Registrado: dic 2019
Posts: 62
Poder: 7
bilbur Va por buen camino
Cita:
Empezado por Neftali [Germán.Estévez] Ver Mensaje
Nosotros encadenamos con la última factura de la misma serie.

Preguntas frecuentes


¿Cómo se realiza el encadenamiento de la factura anterior?


El sistema TicketBAI implica que cada factura debe identificar la factura anterior (serie,
número, fecha y 100 primeros caracteres de la firma). Aunque en el esquema del
fichero TicketBAI estos campos no son obligatorios, esta circunstancia se debe a que
existen varias situaciones en los que este requisito no es exigible (p. ej. la primera
factura, o en caso de rotura del dispositivo).
Dentro de este encadenamiento, cada factura solo puede tener como máximo una
única factura anterior, es decir cada factura de un obligado tributario solo puede estar
referenciada como factura anterior en una única factura de dicho obligado tributario.
El cumplimiento del requisito de encadenamiento depende de la forma de
funcionamiento del sistema de facturación:
Si un mismo dispositivo o centro de facturación emite facturas de diferentes
series, el requisito de encadenamiento debe identificar la factura anterior
expedida, con independencia de la serie dentro de ese dispositivo o centro de
facturación (por ejemplo, a una factura de una serie ordinaria le puede preceder
una factura rectificativa de otra serie).


Igualmente, si el sistema de facturación es un sistema totalmente centralizado,
el encadenamiento se refiere a la última factura expedida por el sistema en
orden temporal.

En concreto, la aplicación “Haz tu factura” funcionaría como un sistema de facturación
totalmente “centralizado”, por lo que encadena todas las facturas en orden temporal.
Si son necesarios varios procesos de facturación (se podrían asimilar a centros de
facturación) para dar respuesta a varios usuarios concurrentes de una misma empresa
de forma paralela, se debería encadenar en orden temporal dentro de cada proceso o
centro de facturación, con independencia de la serie. En este caso, debe existir un
número acotado de procesos o colas de facturación, asociado al número de usuarios
concurrentes por empresa emisora de facturas
Responder Con Cita
  #10  
Antiguo 03-05-2021
Avatar de Neftali [Germán.Estévez]
Neftali [Germán.Estévez] Neftali [Germán.Estévez] is online now
[becario]
 
Registrado: jul 2004
Ubicación: Barcelona - España
Posts: 19.437
Poder: 10
Neftali [Germán.Estévez] Es un diamante en brutoNeftali [Germán.Estévez] Es un diamante en brutoNeftali [Germán.Estévez] Es un diamante en bruto
Cita:
Empezado por Neftali [Germán.Estévez] Ver Mensaje
Nosotros encadenamos con la última factura de la misma serie.

Tienes razón bilbur.
Estaba pensando en el cálculo del HASh no del encadenamiento...

Borro el mensaje anterior para evitar confusiones.
__________________
Germán Estévez => Web/Blog
Guía de estilo, Guía alternativa
Utiliza TAG's en tus mensajes.
Contactar con el Clubdelphi

P.D: Más tiempo dedicado a la pregunta=Mejores respuestas.
Responder Con Cita
  #11  
Antiguo 04-05-2021
Galaxian Galaxian is offline
Miembro
 
Registrado: mar 2021
Posts: 52
Poder: 6
Galaxian Va por buen camino
Cita:
Empezado por bilbur Ver Mensaje
Se encadena con la última generada sea de la serie que sea
Muchas gracias bilbur.

Aprovecho la ocasión para lanzar otra pregunta -de nota- al que ni las haciendas de Guipúzcoa y Vizcaya han respondido: ¿como se declaran las operaciones hechas a comisión por terminales externos pero que se cobran con el programa (por ejemplo las recargas de móviles)? En estos casos, el programa genera una factura simplificada por importe cero, pues las ventas a comisión no son ventas propias del negocio:

• ¿Hay declarar estas facturas? En su caso ¿debe llevar el QR?
• ¿Qué pasa con la comisión? ¿no habría que declararla en el 140 de batuz?
• Y el más difícil todavía: ¿y si la comisión es variable dependiendo de las ventas (ONCE y loterías) y no se conoce hasta que se recibe la factura?

¿Nadie más se ha encontrado con este caso o alguno similar?

Un saludo
Responder Con Cita
  #12  
Antiguo 04-05-2021
Avatar de keys
keys keys is offline
Miembro
 
Registrado: sep 2003
Ubicación: Bilbao
Posts: 1.229
Poder: 24
keys Va por buen camino
Hola.

Yo entiendo que hablas de un suplido.

En el caso en el que, en una factura, por ejemplo, además de los honorarios de un profesional se documentase un suplido, el importe del suplido no formaría parte de la base imponible del IVA pero estas facturas deben cumplir igualmente los requisitos TicketBAI.

En cambio, si únicamente se quisiera documentar un suplido, se trataría de una operación en la que no se documenta ninguna entrega de bienes ni prestación de servicios realizada por el profesional en el desarrollo de su actividad, ya que en estos casos el empresario actuaría como mero intermediario, satisfaciendo sumas de dinero del cliente a un tercero por mandato expreso. En este caso, no habría obligación de expedir factura de acuerdo con lo dispuesto en el Reglamento de facturación, pudiéndose documentar la operación en un recibo u otro justificante. Por tanto, un documento en el que solo se informe de un suplido quedaría fuera del ámbito de aplicación de TicketBAI.

Luego en el 140 tiene que ir como un ingreso sin factura.
Responder Con Cita
  #13  
Antiguo 04-05-2021
bilbur bilbur is offline
Miembro
 
Registrado: dic 2019
Posts: 62
Poder: 7
bilbur Va por buen camino
Cita:
Empezado por Galaxian Ver Mensaje
Muchas gracias bilbur.

Aprovecho la ocasión para lanzar otra pregunta -de nota- al que ni las haciendas de Guipúzcoa y Vizcaya han respondido: ¿como se declaran las operaciones hechas a comisión por terminales externos pero que se cobran con el programa (por ejemplo las recargas de móviles)? En estos casos, el programa genera una factura simplificada por importe cero, pues las ventas a comisión no son ventas propias del negocio:

• ¿Hay declarar estas facturas? En su caso ¿debe llevar el QR?
• ¿Qué pasa con la comisión? ¿no habría que declararla en el 140 de batuz?
• Y el más difícil todavía: ¿y si la comisión es variable dependiendo de las ventas (ONCE y loterías) y no se conoce hasta que se recibe la factura?

¿Nadie más se ha encontrado con este caso o alguno similar?

Un saludo

Entiendo que si se emite factura, aunque importe sea cero, hay que declararla en TBAI y estar a lo que ponga en la factura emitida.
Yo si que las declaro en SII.
A mi se me da, a veces, el caso de factura emitida total cero por venta de mercancía p.e. 10 y devolución de envases tb de -10, total 0,00.

Así no "pierdo" número de factura ni encadenamientos

Lo mejor, probar ahora en pruebas (BIZ y GIP) y ver el resultado
Si da error, enviar correo a los dos y a esperar
Responder Con Cita
Respuesta


Herramientas Buscar en Tema
Buscar en Tema:

Búsqueda Avanzada
Desplegado

Normas de Publicación
no Puedes crear nuevos temas
no Puedes responder a temas
no Puedes adjuntar archivos
no Puedes editar tus mensajes

El código vB está habilitado
Las caritas están habilitado
Código [IMG] está habilitado
Código HTML está deshabilitado
Saltar a Foro

Temas Similares
Tema Autor Foro Respuestas Último mensaje
SII -Nuevo sistema de la Agencia Tributaria española de envío de datos vía Webservice newtron Internet 3716 19-01-2026 20:01:34
Como utilizar la ayuda del nuevo Sistema Operativo gluglu Humor 3 24-09-2007 09:39:05
Aplicacion Agencia De Viajes ArdiIIa Varios 9 20-01-2007 16:49:53
El Vasco Aguirre Al González La Taberna 5 26-05-2006 09:22:28
Microsoft ha lanzado su nuevo sistema operativo DarkByte Humor 0 25-01-2004 09:21:14


La franja horaria es GMT +2. Ahora son las 15:42:50.


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
Copyright 1996-2007 Club Delphi