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:
- Documento Tributario Electrónico (DTE): Debe estar timbrado, firmado, y envuelto en un sobre electrónico firmado.
- Token de autenticación: Este será enviado como una cookie en la petición HTTP.
- 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
yrutusuario
: 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--";
"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
Publicar un comentario