Consultar Registro de Compras y Ventas en Chile

58 views
Skip to first unread message

José Santos Arias Vega

unread,
Mar 23, 2024, 10:51:04 PM3/23/24
to oo...@googlegroups.com, harbourm...@googlegroups.com
Saludos al Grupo.

En una ocasión me consultaron si se podía automatizar la descarga del registro de compras y ventas desde la página del Servicio de Impuestos Internos de Chile.

Me costó bastante hacerlo pero al fin lo logré y adjunto código para que lo prueben los colegas de Chile y si les sirve también como guía para los demás países.

El único inconveniente es que se necesita si o si un certificado digital del usuario autorizado para la empresa que se consultará, esta parte de firma no lo pude hacer en harbour y tuve que hacer una rutina en C# para resolverlo, si alguien sabe como hacerlo con Harbour puede brindar la ayuda necesaria para no depender de otros lenguajes, en fin ahí va el codigo.

Está probado en Harbor puro y duro, se debe compilar así:

hbmk2 rcvhb xhb.hbc hbwin.hbc

#ifndef __XHARBOUR__
   #xcommand TRY              => BEGIN SEQUENCE WITH {|o| break(o)}
   #xcommand CATCH [<!oErr!>] => RECOVER [USING <oErr>] <-oErr->
   #xcommand FINALLY          => ALWAYS
#endif


function ConsultaRCV
local cCompras,oHttp,cUrl,cJson,cTokencResponse,cAut,oRestapi,cHtml,cArchivo,cHash
cToken:=Token()

//Creamos cadena Json para enviar //
cCompras:='{"metaData":{ "namespace":"cl.sii.sdi.lob.diii.consdcv.data.api.interfaces.FacadeService/getDetalleCompraExport","conversationId":"'+cToken+'","transactionId":"0","page":null},"data":{ "rutEmisor":"77254882","dvEmisor":"6","ptributario":"202401","operacion":"COMPRA","estadoContab":"REGISTRO","codTipoDoc":"0"}}'
///////////////////////////////////
BEGIN SEQUENCE WITH {|o| break(o)}
oHttp := Win_OleCreateObject( "MSXML2.ServerXMLHTTP" )
RECOVER USING oErr
    alert( oErr:Description, "Error Microsoft XML Core Services (MSXML)" )
    oHttp:=""
END SEQUENCE
oHttp:Open( "POST", cUrl, .F.)
oHttp:setRequestHeader("Cookie" , "TOKEN="+cToken) //+ CRLF)   // Cookie con Token solicitado en autenticacion
oHttp:setRequestHeader("Content-Type" ," application/json; charset=utf-8" ) //+ CRLF)
oHttp:setRequestHeader("Content-Lenght" , 1000) //+ CRLF)
oHttp:Send(cCompras)   //  Se envia cadena JSON
cResponse:=oHttp:responseText
hb_memowrit("Respuesta.json",cResponse)
oHttp:=Nil
Return (cResponse)

function Token
local cEnvioDTE,sArchivo,oRestApi,CHtml,cUrl,cHeader,cXml,cSemilla,cDatoSemilla,cArmatoken,cFirma,cToken,cEnviaAlSii,cResultado

******************* Solicitar Token al SII **************

cXml:=ComunicacionSII("Semilla")
hb_memowrit("Semilla.xml",cXml)
cSemilla:=hb_memoread("Semilla.xml")
cDatoSemilla:=ProcesoXml(cSemilla,"SEMILLA")
cArmatoKen:=ArmaToken(cDatoSemilla)
hb_memowrit("SolicitaToken.xml",cArmaToken)
cFirma:=FirmaDTE("SolicitaToken.xml",'""')
hb_Memowrit('Token.xml',cFirma)
cXml:=ComunicacionSII("Token",'Token.xml')
cToken:=ProcesoXml(hb_Memoread("TokenSii.xml"),"TOKEN")
Return(cToken)

************************************************************

Function ProcesoXml( c_XmlRecibido, cadena )
local   oXMLDoc         := ''
local   oXMLNode        := ''
local aDatosxml:={}
local cDatosxm1
*=================================================
oXmlDoc         := TXmlDocument():new()
oXMlDoc:read( c_XmlRecibido )

oXmlNode        := oXmlDoc:findFirst(cadena)
cDatosxm1        := oXmlNode:cData

return(cDatosxm1)


function ArmaToken(DatoSemilla)
local cXmlToken
cXmlToken:=''
cXmlToken +='<?xml version="1.0" encoding="UTF-8"?>'
cXmlToken +='<getToken>'
cXmlToken +='<item>'
cXmlToken +='<Semilla>'+DatoSemilla+'</Semilla>'
cXmlToken +='</item>'
cXmlToken +='</getToken>'
return(cXmlToken)

************************************************
function ComunicacionSII(cDato,cArchivoFirmado)

Local  cTx, signed, txt, sig , cComando,cArchivoFirma,oSoapClient,cXml2
Local cDominio_WebServices := "https://palena.sii.cl/DTEWS/CrSeed.jws?WSDL"  
Local cDominio_WebServicesToken:= "https://palena.sii.cl/DTEWS/GetTokenFromSeed.jws?WSDL"

cArchivoFirma:=alltrim(Hb_Memoread(cArchivoFirmado))
if cDato="Semilla"

       // Obtener una semilla --------------------------------------------------

       oSoapClient  := CreateObject( "MSSOAP.SoapClient30" )  // Conecta a SOAP 3.0
       oSoapClient:msSoapInit( cDominio_WebServices )
 
       try
          cXml2 := oSoapClient:getSeed()
*          alert( cXml )
*          hb_MemoWrit( 'Prueba_res.xml', cXml )
       catch
          cValue := oSOAPClient:faultString + Hb_OsNewLine() + oSOAPClient:detail
          alert( "error:"+cValue )
       end
       oSoapClient := NIL

  elseif cDato="Token"

       oSoapClient  := CreateObject( "MSSOAP.SoapClient30" )  // Conecta a SOAP 3.0
       oSoapClient:msSoapInit( cDominio_WebServicesToken )

       try
          cXml2:= oSoapClient:getToken(cArchivoFirma)
          hb_MemoWrit( 'Tokensii.xml', cXml2 )
       catch
          cValue := oSOAPClient:faultString + Hb_OsNewLine() + oSOAPClient:detail
          alert( "Estamos en Token error:"+cValue )
       end
       oSoapClient := NIL

endif

return (cXml2)


************************************************  Esta Rutina esta creada en C# ******************
********   Aqui necesitamos la ayuda de alguien que sepa como firmar con Harbour *****************
*function FirmaDTE(cRuta,sUri,cRuta2,cRuta3,sUri2)
function FirmaDTE(cRuta,sUri)
Local  cTx, signed, txt, sig , privatekey,cComando,cArchivoFirmado

cComando := 'c:\boletael\javsistemas\firma.exe ' + cRuta +'  '+sUri

cOutErr1    := ''                                                                                                                                                                                                                                                                                                                                               
cOutErr2    := ''                                                                                                                                                                                                                                                                                                                                               
nErrorLevel := HB_PROCESSRUN(cComando,,@cOutErr1,@cOutErr2,.T.) // EL .T. HACE QUE NO DISPLAYE EN CONSOLA                                                                                                                                                                                                                                                       
DO CASE                                                                                                                                                                                                                                                                                                                                                         

   CASE nErrorLevel==0

        // TODO PERFECTO                                                                                                                                                                                                                                                                                                                                               

   CASE nErrorLevel==-1                                                                                                                                                                                                                                                                                                                                         

        alert('ERROR al ejecutar FirmaDTE'+CRLF+STR(nErrorLevel,5)+' '+ERRORTXT(nErrorLevel))
        RETURN(1)                                                                                                                                                                                                                                                                                                                                               

   CASE nErrorLevel==2                                                                                                                                                                                                                                                                                                                                          

        alert('ERROR al ejecutar FirmaDTE'+CRLF+STR(nErrorLevel,5)+' '+ERRORTXT(nErrorLevel))

        RETURN(1)                                                                                                                                                                                                                                                                                                                                               

   OTHERWISE                                                                                                                                                                                                                                                                                                                                                    

        alert('ERROR al ejecutar FirmaDTE'+CRLF+STR(nErrorLevel,5)+' '+cOutErr2)

        RETURN(1)                                                                                                                                                                                                                                                                                                                                               

ENDCASE               
return trim(cOutErr1)

function ERRORTXT(nError)
return(str(nError))

La rutina en C# para firmar es la siguiente:

using System;
using System.IO;
using System.Xml;
using System.Xml.Linq;
using System.Text;
using System.Security.Cryptography;
using System.Security.Cryptography.Xml;
using System.Security.Cryptography.X509Certificates;


namespace firmax509
{
        class MainClass
        {
                public static void Main (string[] args)
                {
                   string cArchivo = args[0];
                   string uri = args[1];



                   Firma firma = new Firma(@"C:\boletael\javsistemas\certificado.pfx", "08122000");

                      // xml no firmado
                


            string xDocument =File.ReadAllText(@cArchivo, Encoding.Default);

            System.Text.StringBuilder sbEx = new System.Text.StringBuilder();
            sbEx.Append(xDocument); //+ dia.ToShortDateString() + ".");


             string unsignedXml = sbEx.ToString();
                 const string fic = @"c:\boletael\ArchivoFirmado.xml";

                   string signedXml = firma.Firmar(unsignedXml, referenceUri: uri, addTransform: true);
                   System.IO.StreamWriter sw = new System.IO.StreamWriter(fic,false, Encoding.Default);
                   sw.WriteLine(signedXml);
                   sw.Close();
           
               Console.WriteLine (signedXml);

                }
        }
}







public class Firma
{
    private X509Certificate2 certificado { get; set; }
 
    public Firma(string certificatePath, string password)
    {
        certificado = new X509Certificate2(certificatePath, password);
    }
 
    ///
    /// Se le pasa un xml en string y lo devuelve firmado
    /// 
 
    /// xml no firmado
    /// si se quiere firmar una parte del xml se debe poner el #id, si no: ""                         
    /// para firmar semilla se requiere, para firmar envioDTE y DTE no se requiere
    public string Firmar(string xml, string referenceUri, bool addTransform)
    {
        XmlDocument xmlDocument = new XmlDocument();
        xmlDocument.PreserveWhitespace = true;
        xmlDocument.LoadXml(xml);
 
        SignedXml signedXml = new SignedXml(xmlDocument);
        signedXml.SigningKey = certificado.PrivateKey;
 
        KeyInfo keyInfo = new KeyInfo();
        keyInfo.AddClause(new RSAKeyValue((RSA)certificado.PrivateKey));
        keyInfo.AddClause(new KeyInfoX509Data(certificado));
 
        Reference reference = new Reference();
        reference.Uri = referenceUri;
 
        if (addTransform)
        {
            reference.AddTransform(new XmlDsigEnvelopedSignatureTransform());
        }
 
        Signature signature = signedXml.Signature;
        signature.SignedInfo.AddReference(reference);
        signature.KeyInfo = keyInfo;
 
        // Generar firma
        signedXml.ComputeSignature();
 
        // Insertar la firma en xmlDocument
        xmlDocument.DocumentElement.AppendChild(xmlDocument.ImportNode(signedXml.GetXml(), true));
 
        return xmlDocument.InnerXml;
    }
}

Saludos.


José Arias Vega
Reply all
Reply to author
Forward
0 new messages