Envío de Documentos Tributarios Electrónicos al SII en Java: Explicación y Código

Introducción

En este artículo, aprenderás cómo implementar una solución en Java para enviar Documentos Tributarios Electrónicos (DTE) al Servicio de Impuestos Internos (SII) de Chile. Este proceso incluye construir solicitudes HTTP POST utilizando HttpURLConnection, procesar archivos XML, y manejar la respuesta del servidor para obtener un número de seguimiento o TrackID.

Al final, contarás con una clase reutilizable que puedes integrar fácilmente en tus proyectos

Requisitos

Para implementar esta solución, asegúrate de contar con los siguientes elementos:

  1. Documento Tributario Electrónico (DTE): Debe estar timbrado, firmado, y envuelto en un sobre electrónico firmado.
  2. Token de autenticación: Este será enviado como una cookie en la petición HTTP.
  3. RUT del usuario y de la empresa emisora: Necesarios para completar los datos de la solicitud.

El Código

El siguiente código define una clase UploadSii que realiza todo el proceso:

package setsimulacion;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.StringReader;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;


public class UploadSii{
    
   private final String urlenvironment;
    
    public UploadSii(String urlenvironment){
       this.urlenvironment = urlenvironment;
        
    }
    
    public String uploadSii(String valortoken,String nombredte ,String rutemisor,String rutusuario) throws MalformedURLException, IOException, ParserConfigurationException, SAXException{

         String[] arrayrutemisor = rutemisor.split("-");
         String[] arrayrutusuario = rutusuario.split("-");
         
         
         
         
         
        URL url = new URL("https://"+this.urlenvironment+"/cgi_dte/UPL/DTEUpload");
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setDoOutput(true);
        conn.setRequestMethod("POST"); 
        conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=9022632e1130lc4"); 
        conn.setRequestProperty("User-Agent","Mozilla/4.0 (compatible; PROG 1.0; Windows NT 5.0; YComp 5.0.2.4)"); 
        conn.setRequestProperty("Cookie","TOKEN="+valortoken);
        
        
        String archivo = nombredte +".xml";
        conn.setUseCaches(false);
        String cadena = "";
        String contenido = "";
        
        FileReader f = new FileReader(archivo);
        try (BufferedReader b = new BufferedReader(f)) {
            while((cadena = b.readLine())!=null) {
                contenido = contenido + cadena + "\r\n";
            }
        }
      
           
           /* cuerpo de la peticion request */
   
  String stringRequest = "";
stringRequest =  "--9022632e1130lc4"+"\r\n"+
        "Content-Disposition: form-data;"+" name=" +"\""+"rutSender"+"\""+"\r\n"+
        "\r\n"+arrayrutusuario[0]+"\r\n"+
        "--9022632e1130lc4"+"\r\n"+
        "Content-Disposition: form-data; name="+ "\"" +"dvSender" + "\""+"\r\n"+
        "\r\n"+arrayrutusuario[1]+"\r\n"+
        "--9022632e1130lc4"+"\r\n"+
        "Content-Disposition: form-data; name="+ "\""+ "rutCompany" + "\"" + 
        "\r\n"+ "\r\n"+ arrayrutemisor[0]+"\r\n"+
        "--9022632e1130lc4" + "\r\n"+
        "Content-Disposition: form-data; name=" + "\""+"dvCompany" + "\"" +      
        "\r\n"+ "\r\n" +
        arrayrutemisor[1]+"\r\n"+ 
        "--9022632e1130lc4"+"\r\n"+
        "Content-Disposition: form-data; name=" + "\r\n"+"archivo" + "\""+ ";filename="+"\""+archivo+"\"" + "\r\n"+
         "Content-Type: application/octet-stream"+"\r\n"+
        "Content-Transfer-Encoding: binary"+"\r\n"+
         "\r\n"+
         contenido+"\r\n"+"\r\n"+  "--9022632e1130lc4--";
        

System.out.print(stringRequest);


 OutputStream outputStreamToRequestBody = conn.getOutputStream();
        BufferedWriter httpRequestBodyWriter =
                new BufferedWriter(new OutputStreamWriter(outputStreamToRequestBody));
                httpRequestBodyWriter.write(stringRequest);
                httpRequestBodyWriter.flush();
 


     
String targetString = "";
        Reader in = new BufferedReader(new InputStreamReader(
                conn.getInputStream(), "UTF-8"));
        for (int c = in.read(); c != -1; c = in.read())
            targetString +=   (char) c;
   
     
     
     
return   readTrackId(targetString);
           }
       
  
 public String readTrackId(String targetString) throws ParserConfigurationException, SAXException, IOException{
          DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
          dbf.setNamespaceAware(false);
          DocumentBuilder db = dbf.newDocumentBuilder();
           Document doc = db.parse(new InputSource(new StringReader(targetString)));
                        
          Element nl = (Element) doc.getElementsByTagName("TRACKID").item(0);
        
          String valortrackid = nl.getTextContent();
        
 
     return valortrackid;
 }         
         
      }

   

2. Explicación del flujo del programa

a) Configuración inicial

  • Clase UploadSii:
    • Se configura la URL del entorno a través del constructor.
    • Este diseño permite cambiar entre ambientes (producción o certificación) de manera sencilla.

b) Preparación de los datos

  • Parámetros del método uploadSii:

    • valortoken: Token de autenticación.
    • nombredte: Nombre del archivo DTE a subir.
    • rutemisor y rutusuario: Datos de RUT necesarios para la petición.
  • Lectura del archivo DTE:

FileReader f = new FileReader(archivo);
try (BufferedReader b = new BufferedReader(f)) {
    while ((cadena = b.readLine()) != null) {
        contenido = contenido + cadena + "\r\n";
    }
}

Este fragmento lee el contenido del archivo XML línea por línea y lo almacena en un String.

c) Construcción del cuerpo de la petición

  • Uso de la estructura multipart/form-data para incluir múltiples partes en una sola solicitud:
stringRequest =  "--9022632e1130lc4" + "\r\n" +
                 "Content-Disposition: form-data; name=\"rutSender\"\r\n\r\n" +
                 arrayrutusuario[0] + "\r\n" +
                 // Más secciones aquí
                 "--9022632e1130lc4--";


  • Cada parte tiene un encabezado y su respectivo contenido.
  • El archivo XML se incluye como una sección binaria con

  • "Content-Type: application/octet-stream"
    


    d) Envío de la petición

    • Configuración de la conexión HTTP y envío del cuerpo:
    conn.setRequestMethod("POST");
    conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=9022632e1130lc4");
    BufferedWriter httpRequestBodyWriter = new BufferedWriter(new OutputStreamWriter(outputStreamToRequestBody));
    httpRequestBodyWriter.write(stringRequest);
    httpRequestBodyWriter.flush();
    


    e) Procesamiento de la respuesta

    • La respuesta del servidor se lee carácter por carácter y se guarda como un String:
      Reader in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));
      for (int c = in.read(); c != -1; c = in.read())
          targetString += (char) c;
      
      

      Obtención del TRACKID:

    • Uso de DOM para procesar el XML de la respuesta y extraer el valor del nodo <TRACKID>:

    DocumentBuilder db = dbf.newDocumentBuilder();
    Document doc = db.parse(new InputSource(new StringReader(targetString)));
    Element nl = (Element) doc.getElementsByTagName("TRACKID").item(0);
    String valortrackid = nl.getTextContent();
    


    Conclusión

    Este ejemplo muestra cómo puedes implementar la integración con el SII utilizando Java puro. La clase UploadSii es fácilmente reutilizable y extensible para adaptarse a proyectos más grandes.

    Comentarios

    Entradas populares de este blog

    Configurando Servlets y JSP en Jetty

    Firma de un Documento XML con Certificado Digital en Java para Uso Tributario en Chile

    RESOLUCION SET BASICO DE FACTURA ELECTRÓNICA SII