Club Delphi  
    FTP   CCD     Buscar   Trucos   Trabajo   Foros

Retroceder   Foros Club Delphi > Principal > Internet
Registrarse FAQ Miembros Calendario Guía de estilo Temas de Hoy

Grupo de Teaming del ClubDelphi

Respuesta
 
Herramientas Buscar en Tema Desplegado
  #521  
Antiguo 27-04-2021
joselugrk joselugrk is offline
Miembro
 
Registrado: abr 2021
Posts: 28
Poder: 0
joselugrk Va por buen camino
Question Anular Factura

Cita:
Empezado por Neftali [Germán.Estévez] Ver Mensaje
Ojo, que a lo mejor estás confundiendo la anulación con la rectificación.
La primera simplemente envías el XML de anulación y la factura se elimina del sistema. En el segundo caso, creas la nueva factura rectificativa (en negativo como comentas) se genera el nuevo XML y se envía (no como anulacion sino como factura nueva).
Hola Neftali - Keys, primero muchas gracias. No lo tengo claro. He leído en una Web (no sé si se puede poner el link en el foro)

" ------------------------------------------------------------------------------------------------------------------------------------------
Cuándo puedo anular una factura
Los motivos por los que una empresa puede realizar una cancelación de invoice o e factura, pueden ser, entre otros:
  • No se ha recibido el producto, aunque se haya pagado.
  • No se ha llevado a cabo un servicio, aunque se haya abonado
  • Directamente cuando un producto ha sido devuelto
Solo es factible anular una factura si ha sido recién enviada y no está incluida en un trimestre contable ya cerrado. Es decir: no tienen el IVA liquidado. En este caso, cancelamos la factura y emitimos una nota de crédito.

En el caso de que el invoice, o la factura electrónica, tenga un error (faltan datos, se han calculado mal los impuestos) y el trimestre contable/administrativo ya esté cerrado y liquidado, se debe emitir una factura rectificativa, de la que hablaremos en otro artículo.

Una de las preguntas comunes es: ¿puedo anular una factura aunque ya haya pasado el periodo impositivo en el que se registró? La AEAT dice que sí.

¿Qué es una nota de crédito?

La nota de crédito es un documento legal en el cual queda constancia del reembolso, al incluir todos los importes finales en negativo de la factura a cancelar. De esta manera, a efectos prácticos, es como si se anulara la otra factura. Y Hacienda tiene constancia de que ese dinero ya no está en la caja de la empresa.

La nota de crédito se realiza como una factura normal, pero que se identifique claramente que es una nota de crédito. El cliente debe recibir la nota junto a copia de la factura original, y que se vea la anulación del importe económico.

Volviendo a Hacienda, debes mantener las facturas antiguas, aunque estén “anuladas” con una nota de crédito, por si acaso en algún momento tuvieras una inspección de la Agencia Tributaria. Así podrás demostrar que la nota de crédito se realizó en base a una factura real.

Cómo anular una factura.
Teniendo en cuenta todo lo anterior, para anular una factura, es necesario:

Hacer la factura nueva, donde quede claro que es una “nota de crédito”.
Asignarle un número correlativo al de la serie de facturas que ya se han emitido. No debe ser correlativo al de la factura que estás anulando, si no que si en el período contable corriente llevas 22 invoice o facturas, la nota sería la 23.
Incluir el número de referencia de la factura anulada.
Y no te olvides de incluir las cantidades que se han anulado, en negativo.

Conclusión
Los errores en las facturas pueden ocurrir, como humanos que somos, por diferentes motivos. Y es importante saber gestionar esas facturas erróneas, por ejemplo para hacer anulaciones.

------------------------------------------------------------------------------------------------------------------------------------------ "
Por esto, si anulo una factura, TicketBAI me dice que tengo que crear un XML de Anulación con los dato indicados en el esquema del XML de Anulación. Pero arriba indica que se debería crear, también, una nota de crédito (factura negativa en la misma serie) e enviársela al cliente para que tenga constancia de la anulación de la factura. ¿A está factura negativa le debo crear su XML de Alta? Ya que si no creo el XML de Alta me va a quedar un número de factura que no le he enviado a Diputación.

Y para las Facturas rectificativas se crea un XML de Alta indicando la factura que se rectifica. Pero se crea otra factura en una serie diferente con los datos rectificados.


Igual tienes razón y me estoy liando yo solo.

¿Cómo debería realizarlo?

Saludos y muchas gracias,
Joselu
Responder Con Cita
  #522  
Antiguo 27-04-2021
joselugrk joselugrk is offline
Miembro
 
Registrado: abr 2021
Posts: 28
Poder: 0
joselugrk Va por buen camino
Thumbs up Envío de Ficheros con ChilKat

Cita:
Empezado por aar1 Ver Mensaje
Hola joselugrk.

Para realizar la conexión del envío debes utilizar ChilkatSocket, este es un ejemplo en VB6 (Bizkaia):

Código Delphi [-]
Dim socket As New ChilkatSocket
success = socket.SetSslClientCertPfx("tu certificado.fpx", "contraseña del certificado")
success = socket.Connect("pruesarrerak.bizkaia.eus", 443, True, 5000)

Para comprimir y realizar el envío utiliza ChilkatRest y ChilkatStringBuilder:

Código Delphi [-]
Dim rest As New ChilkatRest
success = rest.UseConnection(socket, True)

rest.AddHeader "Accept-Encoding", "gzip"
rest.AddHeader "Content-Encoding", "gzip"
rest.AddHeader "Content-Length", filesize
rest.AddHeader "Content-Type", "application/octet-stream"
rest.AddHeader "eus-bizkaia-n3-version", "1.0"
rest.AddHeader "eus-bizkaia-n3-content-type", "application/xml"
rest.AddHeader "eus-bizkaia-n3-data", cadenajson

Dim dat As String

Dim stringBuilder As New ChilkatStringBuilder
success = stringBuilder.LoadFile("archivo XML a enviar", "utf-8")

dat = stringBuilder.GetAsString()

success = rest.SendReqStringBody("POST", "/N3B4000M/aurkezpena", dat)

No hace falta comprimir el archivo antes de enviarlo, ChilkatRest lo comprime y lo envía.

Para leer la cabecera de la respuesta:

Código Delphi [-]
success = rest.ReadResponseHeader()
dat = rest.ResponseHeader()

Para leer el archivo comprimido incluido en el body de la respuesta:

Código Delphi [-]
Dim aux_bin As New ChilkatBinData

aux_bin.AppendBinary (rest.ReadRespBodyBinary())
aux_bin.WriteFile "nombre del archivo comprimido donde se guarda la respuesta"
Dim gzip As New ChilkatGzip
success = gzip.UncompressFile("nombre del archivo comprimido donde se guarda la respuesta", "nombre del archivo XML descomprimido")

No he incluido las verificaciones de la variable success para no extender el código.

Espero que te sirva de ayuda.

Un saludo.
Lo reviso, y sobre todo muchas gracias por todo.
Joselu
Responder Con Cita
  #523  
Antiguo 28-04-2021
Elyas Elyas is offline
Registrado
 
Registrado: mar 2021
Posts: 6
Poder: 0
Elyas Va por buen camino
Hola chic@s, tengo algunas dudas y me gustaría saber cómo afrontáis vosotros estas situaciones.

Pj. qué valores han de ir al registrar la primera factura en ticketbai en el encadenamiento?

- NumFacturaAnterior ?
- FechaExpedicionFacturaAnterior ?
- SignatureValueFirmaFacturaAnterior ?

Si no ponemos nada devuelve error y no registra la factura. En la primera si van los datos de la anterior factura y ésta no está registrada en ticketbai, entonces nos dará un warning? es eso aceptable o hay que hacerlo de otra forma?

--------

Y por otro lado vosotros cómo lo organizáis si tenéis un cuadro como este:

Factura Nº 003 vamos a registrar esta factura
Factura Nº 002 ya generada, NO registrada en ticketbai (pj vuestro servidor caído o similar)
Factura Nº 001 ya generada, registrada en ticketbai

al registrar la factura Nº 003, nos dará error en el encadenamiento al referirse a la 002 por tanto cómo lo resolvéis? si al registrar la 003 tomamos la 001 como la anterior entonces rompemos el orden en el que se generaron las facturas en nuestro sistema o eso da igual? siempre que una vez resuelto el problema de la factura 002, al registrarla cogeríamos la última factura aquella que esté registrada arriba aunque por orden de fechas no se cumple el encadenamiento? ¿Cómo hacéis vosotros en caso de que una factura no se ha registrado por alguna razón y queréis tramitar las demás? cómo actuáis?

Un saludo, y muchas gracias de antelación lo que compartan su experiencia!
Responder Con Cita
  #524  
Antiguo 28-04-2021
Avatar de Neftali [Germán.Estévez]
Neftali [Germán.Estévez] Neftali [Germán.Estévez] is offline
[becario]
 
Registrado: jul 2004
Ubicación: Barcelona - España
Posts: 18.286
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 Elyas Ver Mensaje
Pj. qué valores han de ir al registrar la primera factura en ticketbai en el encadenamiento?
- NumFacturaAnterior ?
- FechaExpedicionFacturaAnterior ?
- SignatureValueFirmaFacturaAnterior ?

Si no ponemos nada devuelve error y no registra la factura. En la primera si van los datos de la anterior factura y ésta no está registrada en ticketbai, entonces nos dará un warning? es eso aceptable o hay que hacerlo de otra forma?

En el caso de la primera factura no debes rellenar la información de la factura anterior. El bloque:
Cita:
<EncadenamientoFacturaAnterior>
...
</EncadenamientoFacturaAnterior>
No debe estar presente (los TAGs tampoco).

Debería devolverte una respuesta con un "Warning/aviso", pero te la aceptan igualmente. Algo como esto.





Esto que te he explicado es para la primera. Por ejemplo la primera de una serie.
__________________
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
  #525  
Antiguo 28-04-2021
Avatar de Neftali [Germán.Estévez]
Neftali [Germán.Estévez] Neftali [Germán.Estévez] is offline
[becario]
 
Registrado: jul 2004
Ubicación: Barcelona - España
Posts: 18.286
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 Elyas Ver Mensaje
Y por otro lado vosotros cómo lo organizáis si tenéis un cuadro como este:

Factura Nº 003 vamos a registrar esta factura
Factura Nº 002 ya generada, NO registrada en ticketbai (pj vuestro servidor caído o similar)
Factura Nº 001 ya generada, registrada en ticketbai

al registrar la factura Nº 003, nos dará error en el encadenamiento al referirse a la 002 por tanto cómo lo resolvéis?
Las facturas que subas a ticketBAI siempre deben encadenarse con la última subida, así que en tu caso la 3 tiene que ir encadenada con la 1. Cualquier otra cosa te dará error.
Otro problema es que tal vez luego tengas que justificar qué ha pasado con la NUMFACT=2.

Nosotros, lo que hacemos es asignar el NUMDOC al subir la factura, por lo tanto siempre son correlativas.

Cita:
Empezado por Elyas Ver Mensaje
¿Cómo hacéis vosotros en caso de que una factura no se ha registrado por alguna razón y queréis tramitar las demás? cómo actuáis?

En nuestro caso no queda registrada como factura, sino como documento temporal.
Cuando se "finaliza" ese documento se le asigna fecha, numero y se sube a TBAI.
__________________
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
  #526  
Antiguo 28-04-2021
Elyas Elyas is offline
Registrado
 
Registrado: mar 2021
Posts: 6
Poder: 0
Elyas Va por buen camino
¡¡¡Muchísimas gracias por las respuestas!!!
Responder Con Cita
  #527  
Antiguo 29-04-2021
Sistel Sistel is offline
Miembro
 
Registrado: nov 2019
Ubicación: Bilbao
Posts: 372
Poder: 5
Sistel Va por buen camino
Cita:
Empezado por bilbur Ver Mensaje
Cuando vaya avanzando un poco más, si a alguien le interesa, subiré lo que estoy desarrolando en PHP (sin dependencia de terceros ni para generar el xml, firmar ni enviar)
Hola Bilbur.

Yo también trabajo en PHP y me interesa controlar todo el proceso (generación, firma y envío) dependiendo lo menos posible de librerías ajenas.
Cualquier información sobre el tema, me interesa.

Gracias
Responder Con Cita
  #528  
Antiguo 29-04-2021
bilbur bilbur is offline
Miembro
 
Registrado: dic 2019
Posts: 60
Poder: 5
bilbur Va por buen camino
Cita:
Empezado por Sistel Ver Mensaje
Hola Bilbur.

Yo también trabajo en PHP y me interesa controlar todo el proceso (generación, firma y envío) dependiendo lo menos posible de librerías ajenas.
Cualquier información sobre el tema, me interesa.

Gracias

Hasta hoy tengo completo TBAI GIP


Genero xml
Firmo xml
Envio xml firmado a Gipuzkoa (curl php)

Todo OK

Ahora estoy con TBAI BIZ

Genero y firmo igual que TBAI GIP es lógico
No consigo envia a BIZ con curl php (llevo días sin avanzar nada)


Si te interesa, vamos por partes y probando lo que finciona
Responder Con Cita
  #529  
Antiguo 29-04-2021
Sistel Sistel is offline
Miembro
 
Registrado: nov 2019
Ubicación: Bilbao
Posts: 372
Poder: 5
Sistel Va por buen camino
Cita:
Empezado por bilbur Ver Mensaje
...
Ahora estoy con TBAI BIZ
Genero y firmo igual que TBAI GIP es lógico
No consigo envia a BIZ con curl php (llevo días sin avanzar nada)
...
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
Responder Con Cita
  #530  
Antiguo 29-04-2021
Avatar de Neftali [Germán.Estévez]
Neftali [Germán.Estévez] Neftali [Germán.Estévez] is offline
[becario]
 
Registrado: jul 2004
Ubicación: Barcelona - España
Posts: 18.286
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 bilbur Ver Mensaje
Ahora estoy con TBAI BIZ
Genero y firmo igual que TBAI GIP es lógico
No consigo envia a BIZ con curl php (llevo días sin avanzar nada)
Yo os diría que realizarais pruebas con Postman, por ejemplo, para probar las diferentes partes del proceso.
Por ejemplo, se pueden configurar los parámetros de cabecera, el certificado de envío y enviar uno de los ficheros de ejemplo de BATUZ; Con eso ya podréis obtener una respuesta como esta:



Que ya son códigos de error y respuesta de BATUZ.
A partir de ahí se trata de ir centrándose en otros aspectos.

Modificar el fichero de ejemplo, por uno de los vuestros, y de ahí en adelante.

En el caso del ejemplo el problema del certificado (Interesado no identificado), porque la gente de BATUZ no admite certificados de prueba como si se hare en TicketBAI (**NOTA**, por si alguien lo está haciendo así que lo tenga en cuenta...)

Para la parte del JSON, también podéis utilizar uno de ejemplo que se puede descargar de aquí y modificarlo lévemente.
__________________
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
  #531  
Antiguo 29-04-2021
Sistel Sistel is offline
Miembro
 
Registrado: nov 2019
Ubicación: Bilbao
Posts: 372
Poder: 5
Sistel Va por buen camino
Cita:
Empezado por Neftali [Germán.Estévez] Ver Mensaje
Yo os diría que realizarais pruebas con Postman, por ejemplo, para probar las diferentes partes del proceso.
Por ejemplo, se pueden configurar los parámetros de cabecera, el certificado de envío y enviar uno de los ficheros de ejemplo de BATUZ; Con eso ya podréis obtener una respuesta como esta:



Que ya son códigos de error y respuesta de BATUZ.
A partir de ahí se trata de ir centrándose en otros aspectos.

Modificar el fichero de ejemplo, por uno de los vuestros, y de ahí en adelante.

En el caso del ejemplo el problema del certificado (Interesado no identificado), porque la gente de BATUZ no admite certificados de prueba como si se hare en TicketBAI (**NOTA**, por si alguien lo está haciendo así que lo tenga en cuenta...)

Para la parte del JSON, también podéis utilizar uno de ejemplo que se puede descargar de aquí y modificarlo lévemente.
Hola Neftali.

Magnífica idea probar los envíos con Postman o Imsomnia.

¡Muchas gracias!
Responder Con Cita
  #532  
Antiguo 30-04-2021
bilbur bilbur is offline
Miembro
 
Registrado: dic 2019
Posts: 60
Poder: 5
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
  #533  
Antiguo 01-05-2021
Sistel Sistel is offline
Miembro
 
Registrado: nov 2019
Ubicación: Bilbao
Posts: 372
Poder: 5
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
  #534  
Antiguo 01-05-2021
bilbur bilbur is offline
Miembro
 
Registrado: dic 2019
Posts: 60
Poder: 5
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
  #535  
Antiguo 01-05-2021
Galaxian Galaxian is offline
Miembro
 
Registrado: mar 2021
Posts: 52
Poder: 4
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
  #536  
Antiguo 02-05-2021
bilbur bilbur is offline
Miembro
 
Registrado: dic 2019
Posts: 60
Poder: 5
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
  #537  
Antiguo 02-05-2021
bilbur bilbur is offline
Miembro
 
Registrado: dic 2019
Posts: 60
Poder: 5
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
  #538  
Antiguo 02-05-2021
bilbur bilbur is offline
Miembro
 
Registrado: dic 2019
Posts: 60
Poder: 5
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
  #539  
Antiguo 02-05-2021
Sistel Sistel is offline
Miembro
 
Registrado: nov 2019
Ubicación: Bilbao
Posts: 372
Poder: 5
Sistel Va por buen camino
Cita:
Empezado por bilbur Ver Mensaje
Nos hemos quedado aquí:
...
¡¡¡¡ Genial, Bilbur !!!!

Estaba atrancado con el tema de la firma.
No conseguía firmar el XML sin errores.
Seguro que con tu clase Firmador, salgo, por fín, del atolladero.

¡¡¡ Muchísimas Gracias !!!
Apunta que te debo alguna, para cuando acabe la pandemia.

Un cordial saludo

Última edición por Sistel fecha: 02-05-2021 a las 19:15:30.
Responder Con Cita
  #540  
Antiguo 03-05-2021
Avatar de Neftali [Germán.Estévez]
Neftali [Germán.Estévez] Neftali [Germán.Estévez] is offline
[becario]
 
Registrado: jul 2004
Ubicación: Barcelona - España
Posts: 18.286
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
Respuesta



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 3557 Hace 6 Días 17:42:47
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 21:33:52.


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