edu.utn.frba.grupo5303.serverenviolibre.services.WSAfipService.java Source code

Java tutorial

Introduction

Here is the source code for edu.utn.frba.grupo5303.serverenviolibre.services.WSAfipService.java

Source

/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package edu.utn.frba.grupo5303.serverenviolibre.services;

import afip.homo.wsfact.AlicIva;
import afip.homo.wsfact.ArrayOfAlicIva;
import afip.homo.wsfact.ArrayOfFECAEDetRequest;
import afip.homo.wsfact.FEAuthRequest;
import afip.homo.wsfact.FECAECabRequest;
import afip.homo.wsfact.FECAEDetRequest;
import afip.homo.wsfact.FECAERequest;
import afip.homo.wsfact.FECAEResponse;
import afip.homo.wsfact.FECAESolicitar;
import afip.homo.wsfact.FECAESolicitarResponse;
import afip.homo.wsfact.FECompUltimoAutorizado;
import afip.homo.wsfact.FECompUltimoAutorizadoResponse;
import afip.homo.wsfact.FERecuperaLastCbteResponse;
import edu.utn.frba.grupo5303.serverenviolibre.apis.WSAfipAPI;
import edu.utn.frba.grupo5303.serverenviolibre.beans.TicketAccesoAfip;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.PropertySource;

import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.Security;
import java.security.cert.CertStore;
import java.security.cert.CollectionCertStoreParameters;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Date;
import java.util.GregorianCalendar;

import org.apache.axis.client.Call;
import org.apache.axis.client.Service;
import org.apache.axis.encoding.Base64;
import org.apache.axis.encoding.XMLType;

import com.sun.org.apache.xerces.internal.jaxp.datatype.XMLGregorianCalendarImpl;
import edu.utn.frba.grupo5303.serverenviolibre.beans.CAE;
import edu.utn.frba.grupo5303.serverenviolibre.beans.DatosFacturacion;
import edu.utn.frba.grupo5303.serverenviolibre.config.HoraArgentina;
import edu.utn.frba.grupo5303.serverenviolibre.excepciones.ErrorUltimoNumeroException;
import java.io.BufferedReader;
import javax.xml.rpc.ParameterMode;
import org.bouncycastle.cms.CMSProcessable;
import org.bouncycastle.cms.CMSProcessableByteArray;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.StringReader;
import java.io.StringWriter;
import java.net.URL;
import java.net.URLConnection;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.PropertyException;
import javax.xml.bind.Unmarshaller;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;

import org.dom4j.Document;
import org.dom4j.io.SAXReader;
import org.springframework.core.env.Environment;

/**
 *
 * @author flpitu88
 */
@org.springframework.stereotype.Service
@PropertySource({ "classpath:wsaa_client.properties" })
public class WSAfipService implements WSAfipAPI {

    @Autowired
    private Environment env;

    private static final Logger logger = Logger.getLogger(WSAfipService.class.getName());

    @Override
    public CAE obtenerCAEAfip(DatosFacturacion df) {
        TicketAccesoAfip ta = obtenerTicketDeAcceso();

        logger.log(java.util.logging.Level.INFO, "Token: {0}\nSign: {1}",
                new Object[] { ta.getToken(), ta.getSign() });

        CAE respuestaCAE = null;
        try {
            int ultimoNro = obtenerUltimoNroComprobante(ta);

            logger.log(java.util.logging.Level.INFO, "Ultimo numero de comprobante: {0}", ultimoNro);

            String soapXml = generarXMLPedidoCAE(ta, ultimoNro, df);
            URL url = new URL("https://wswhomo.afip.gov.ar/wsfev1/service.asmx");
            URLConnection conn = url.openConnection();

            // Set the necessary header fields
            conn.setRequestProperty("SOAPAction", "https://ar.gov.afip.dif.FEV1/FECAESolicitar");
            conn.setRequestProperty("Content-type",
                    "application/soap+xml;charset=UTF-8;action=\"http://ar.gov.afip.dif.FEV1/FECAESolicitar\"");
            conn.setRequestProperty("Accept-Encoding", "gzip,deflate");
            conn.setDoOutput(true);
            // Send the request
            OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
            wr.write(soapXml);
            wr.flush();
            // Read the response
            BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            String line;

            line = rd.readLine();

            logger.log(Level.INFO, "####   RESPUESTA AFIP CAE  : " + line);

            XMLInputFactory xif = XMLInputFactory.newFactory();
            XMLStreamReader xsr = xif.createXMLStreamReader(new StringReader(line));
            xsr.nextTag(); // Advance to Envelope tag
            xsr.nextTag(); // Advance to Body tag
            xsr.nextTag(); // Advance to getNumberResponse tag

            JAXBContext jc = JAXBContext.newInstance(FECAESolicitarResponse.class);
            Unmarshaller unmarshaller = jc.createUnmarshaller();
            JAXBElement<FECAESolicitarResponse> je = unmarshaller.unmarshal(xsr, FECAESolicitarResponse.class);

            FECAESolicitarResponse resp = je.getValue();

            FECAEResponse fresp = resp.getFECAESolicitarResult();

            respuestaCAE = new CAE(fresp.getFeDetResp().getFECAEDetResponse().get(0).getCAE(),
                    fresp.getFeDetResp().getFECAEDetResponse().get(0).getCAEFchVto(), ultimoNro + 1);

        } catch (ErrorUltimoNumeroException e) {
            logger.log(java.util.logging.Level.SEVERE, "---### Exception ###---: {0}", e);
        } catch (JAXBException | XMLStreamException | IOException ex) {
            logger.log(java.util.logging.Level.SEVERE, "---### Exception ###---: {0}", ex);
        }
        return respuestaCAE;
    }

    /////////////////////////////////////////////////////////////
    // Metodos privados utilitarios para obtener el CAE
    /////////////////////////////////////////////////////////////
    //
    private TicketAccesoAfip obtenerTicketDeAcceso() {

        TicketAccesoAfip ticket = deboTramitarTicket();

        if (ticket != null) {
            return ticket;
        } else {
            String LoginTicketResponse = null;

            System.setProperty("http.proxyHost", "");
            System.setProperty("http.proxyPort", "80");

            String endpoint = env.getProperty("endpoint");
            String service = env.getProperty("service");
            String dstDN = env.getProperty("dstdn");

            String p12file = env.getProperty("keystore");
            String signer = env.getProperty("keystore-signer");
            String p12pass = env.getProperty("keystore-password");

            // Set proxy system vars
            System.setProperty("http.proxyHost", "");
            System.setProperty("http.proxyPort", "");
            System.setProperty("http.proxyUser", "");
            System.setProperty("http.proxyPassword", "");

            // Set the keystore used by SSL
            System.setProperty("javax.net.ssl.trustStore", env.getProperty("trustStore"));
            System.setProperty("javax.net.ssl.trustStorePassword", env.getProperty("trustStore_password"));

            Long TicketTime = new Long(env.getProperty("TicketTime"));

            // Create LoginTicketRequest_xml_cms
            byte[] LoginTicketRequest_xml_cms = create_cms(p12file, p12pass, signer, dstDN, service, TicketTime);

            // Invoke AFIP wsaa and get LoginTicketResponse
            try {
                LoginTicketResponse = invoke_wsaa(LoginTicketRequest_xml_cms, endpoint);
            } catch (Exception e) {
                logger.log(Level.SEVERE, "Error al invocar web service de Afip", e);
                logger.log(Level.SEVERE, "---= Exception =---:{0}", e);
            }

            // Get token & sign from LoginTicketResponse
            try {
                Reader tokenReader = new StringReader(LoginTicketResponse);
                Document tokenDoc = new SAXReader(false).read(tokenReader);

                String token = tokenDoc.valueOf("/loginTicketResponse/credentials/token");
                String sign = tokenDoc.valueOf("/loginTicketResponse/credentials/sign");

                ticket = new TicketAccesoAfip(token, sign);

                /**
                 * Guardo los datos del ticket de acceso en un archivo para
                 * revisar si tengo que volver a tramitarlo
                 */
                Properties prop = new Properties();
                OutputStream output = null;
                output = new FileOutputStream(env.getProperty("pathTicketAcceso") + "ticketAcceso.properties");
                prop.setProperty("token", token);
                prop.setProperty("sign", sign);
                prop.setProperty("time", HoraArgentina.getFechaYHoraActualArgentinaString());
                prop.store(output, null);

            } catch (Exception e) {
                logger.log(Level.SEVERE, "---### Exception ###---: {0}", e);
            }
            return ticket;
        }
    }

    private String invoke_wsaa(byte[] LoginTicketRequest_xml_cms, String endpoint) throws Exception {

        String LoginTicketResponse = null;
        try {

            Service service = new Service();
            Call call = (Call) service.createCall();

            //
            // Prepare the call for the Web service
            //
            call.setTargetEndpointAddress(new java.net.URL(endpoint));
            call.setOperationName("loginCms");
            call.addParameter("request", XMLType.XSD_STRING, ParameterMode.IN);
            call.setReturnType(XMLType.XSD_STRING);

            //
            // Make the actual call and assign the answer to a String
            //
            LoginTicketResponse = (String) call.invoke(new Object[] { Base64.encode(LoginTicketRequest_xml_cms) });

        } catch (Exception e) {
            logger.log(Level.SEVERE, "---### Exception ###---: {0}", e);
        }
        return (LoginTicketResponse);
    }

    //
    // Create the CMS Message
    //
    private byte[] create_cms(String p12file, String p12pass, String signer, String dstDN, String service,
            Long TicketTime) {

        PrivateKey pKey = null;
        X509Certificate pCertificate = null;
        byte[] asn1_cms = null;
        CertStore cstore = null;
        String LoginTicketRequest_xml;
        String SignerDN = null;

        ArrayList<X509Certificate> certList = null;

        //
        // Manage Keys & Certificates
        //
        try {
            // Create a keystore using keys from the pkcs#12 p12file
            KeyStore ks = KeyStore.getInstance("pkcs12");
            InputStream p12stream = getClass().getResourceAsStream(p12file);
            ks.load(p12stream, p12pass.toCharArray());
            p12stream.close();

            // Get Certificate & Private key from KeyStore
            pKey = (PrivateKey) ks.getKey(signer, p12pass.toCharArray());
            pCertificate = (X509Certificate) ks.getCertificate(signer);
            SignerDN = pCertificate.getSubjectDN().toString();

            // Create a list of Certificates to include in the final CMS
            certList = new ArrayList<X509Certificate>();
            certList.add(pCertificate);

            if (Security.getProvider("BC") == null) {
                Security.addProvider(new BouncyCastleProvider());
            }

            cstore = CertStore.getInstance("Collection", new CollectionCertStoreParameters(certList), "BC");
        } catch (Exception e) {
            logger.log(Level.SEVERE, "---### Exception ###---: {0}", e);
        }

        //
        // Create XML Message
        // 
        LoginTicketRequest_xml = create_LoginTicketRequest(SignerDN, dstDN, service, TicketTime);

        //
        // Create CMS Message
        //
        try {
            // Create a new empty CMS Message
            CMSSignedDataGenerator gen = new CMSSignedDataGenerator();

            // Add a Signer to the Message
            gen.addSigner(pKey, pCertificate, CMSSignedDataGenerator.DIGEST_SHA1);

            // Add the Certificate to the Message
            gen.addCertificatesAndCRLs(cstore);

            // Add the data (XML) to the Message
            CMSProcessable data = new CMSProcessableByteArray(LoginTicketRequest_xml.getBytes());

            // Add a Sign of the Data to the Message
            CMSSignedData signed = gen.generate(data, true, "BC");

            asn1_cms = signed.getEncoded();

        } catch (Exception e) {
            logger.log(Level.SEVERE, "---### Exception ###---: {0}", e);
        }

        return (asn1_cms);
    }

    private int obtenerUltimoNroComprobante(TicketAccesoAfip ta) throws ErrorUltimoNumeroException {

        int ultComprobante = -1;
        try {
            String soapXml = generarXMLUltimoComprobante(ta);
            URL url = new URL("https://wswhomo.afip.gov.ar/wsfev1/service.asmx");
            URLConnection conn = url.openConnection();

            // Set the necessary header fields
            conn.setRequestProperty("SOAPAction", "http://ar.gov.afip.dif.FEV1/FECompUltimoAutorizado");
            conn.setRequestProperty("Content-type",
                    "application/soap+xml;charset=UTF-8;action=\"http://ar.gov.afip.dif.FEV1/FECompUltimoAutorizado");
            conn.setRequestProperty("Accept-Encoding", "gzip,deflate");
            conn.setDoOutput(true);
            // Send the request
            OutputStreamWriter wr = new OutputStreamWriter(conn.getOutputStream());
            wr.write(soapXml);
            wr.flush();
            // Read the response
            BufferedReader rd = new BufferedReader(new InputStreamReader(conn.getInputStream()));
            String line;
            line = rd.readLine();

            XMLInputFactory xif = XMLInputFactory.newFactory();
            XMLStreamReader xsr = xif.createXMLStreamReader(new StringReader(line));
            xsr.nextTag();
            while (!xsr.getLocalName().equals("FECompUltimoAutorizadoResponse")) {
                xsr.nextTag();
            }

            JAXBContext jc = JAXBContext.newInstance(FECompUltimoAutorizadoResponse.class);
            Unmarshaller unmarshaller = jc.createUnmarshaller();
            JAXBElement<FECompUltimoAutorizadoResponse> je = unmarshaller.unmarshal(xsr,
                    FECompUltimoAutorizadoResponse.class);

            FECompUltimoAutorizadoResponse resp = je.getValue();

            FERecuperaLastCbteResponse fresp = resp.getFECompUltimoAutorizadoResult();

            ultComprobante = fresp.getCbteNro();
        } catch (Exception e) {
            throw new ErrorUltimoNumeroException(e.getStackTrace().toString());
        }
        return ultComprobante;
    }

    /**
     * Metodo auxiliar que verifica si el Ticket de Acceso todavia esta vigente
     * o si se debe gestionar uno nuevo.
     *
     * @return
     */
    private TicketAccesoAfip deboTramitarTicket() {
        try {
            Properties prop = new Properties();
            InputStream input = null;
            input = new FileInputStream(env.getProperty("pathTicketAcceso") + "ticketAcceso.properties");
            // load a properties file
            prop.load(input);

            String time = prop.getProperty("time");
            DateTimeFormatter formatterConHora = DateTimeFormatter.ofPattern("dd/MM/yyyy HH:mm");
            LocalDateTime fecha = LocalDateTime.parse(time, formatterConHora);
            if (HoraArgentina.getFechaYHoraActualArgentina().isAfter(fecha.plusHours(12))) {
                logger.info("Debo tramitar nuevamente el Ticket de acceso por estar vencido");
                return null;
            } else {
                TicketAccesoAfip ticket = new TicketAccesoAfip(prop.getProperty("token"), prop.getProperty("sign"));
                logger.info("El ticket de acceso an es vlido");
                return ticket;
            }
        } catch (IOException ex) {
            logger.info("Debo tramitar nuevamente el Ticket de acceso porque no hay archivo");
            return null;
        }
    }

    // ------------------------------------------------------------------------------------
    // Metodos para obtener los Strings de los XML correspondientes
    // ------------------------------------------------------------------------------------
    private String create_LoginTicketRequest(String SignerDN, String dstDN, String service, Long TicketTime) {

        String LoginTicketRequest_xml;

        Date GenTime = new Date();
        GregorianCalendar gentime = new GregorianCalendar();
        GregorianCalendar exptime = new GregorianCalendar();
        String UniqueId = new Long(GenTime.getTime() / 1000).toString();

        exptime.setTime(new Date(GenTime.getTime() + TicketTime));

        XMLGregorianCalendarImpl XMLGenTime = new XMLGregorianCalendarImpl(gentime);
        XMLGregorianCalendarImpl XMLExpTime = new XMLGregorianCalendarImpl(exptime);

        LoginTicketRequest_xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>"
                + "<loginTicketRequest version=\"1.0\">" + "<header>" + "<source>" + SignerDN + "</source>"
                + "<destination>" + dstDN + "</destination>" + "<uniqueId>" + UniqueId + "</uniqueId>"
                + "<generationTime>" + XMLGenTime + "</generationTime>" + "<expirationTime>" + XMLExpTime
                + "</expirationTime>" + "</header>" + "<service>" + service + "</service>"
                + "</loginTicketRequest>";

        return (LoginTicketRequest_xml);
    }

    private String generarXMLUltimoComprobante(TicketAccesoAfip ta) throws JAXBException {
        FECompUltimoAutorizado req = new FECompUltimoAutorizado();
        FEAuthRequest auth = new FEAuthRequest();
        // Configuracion de autorizacion
        auth.setCuit(20334428878l);
        auth.setToken(ta.getToken());
        auth.setSign(ta.getSign());
        req.setAuth(auth);

        req.setCbteTipo(6);
        req.setPtoVta(12);

        JAXBContext jaxbContext = JAXBContext.newInstance(FECompUltimoAutorizado.class);
        Marshaller jaxbMarshaller = jaxbContext.createMarshaller();

        // output pretty printed
        jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

        StringWriter sw = new StringWriter();
        jaxbMarshaller.marshal(req, sw);
        String xmlString = sw.toString();

        String nuevoEncabezado = "<soap:Envelope xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:ar=\"http://ar.gov.afip.dif.FEV1/\"><soap:Header/><soap:Body>";
        String exEncabezado = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>";
        String pedido = xmlString.replace(exEncabezado, nuevoEncabezado);

        return pedido + "</soap:Body></soap:Envelope>";
    }

    private String generarXMLPedidoCAE(TicketAccesoAfip ta, int ultCompro, DatosFacturacion df)
            throws PropertyException, JAXBException {
        FECAESolicitar req = new FECAESolicitar();
        FECAERequest feCAEReq = new FECAERequest();
        FEAuthRequest auth = new FEAuthRequest();

        FECAECabRequest cabReq = new FECAECabRequest();

        ArrayOfFECAEDetRequest array = new ArrayOfFECAEDetRequest();

        FECAEDetRequest detail1 = new FECAEDetRequest();

        // Configuracion de autorizacion
        auth.setCuit(20334428878l);
        auth.setToken(ta.getToken());
        auth.setSign(ta.getSign());
        req.setAuth(auth);

        // Configuracion de cabecera de pedido
        cabReq.setCbteTipo(6); // Factura B
        cabReq.setPtoVta(12); // Nro de Punto de Venta
        cabReq.setCantReg(1); // 1 solo registro de factura
        feCAEReq.setFeCabReq(cabReq);

        // Configuracion de un item de detalle
        detail1.setConcepto(2); // Servicios
        if (df.getUsuario().getCuil() < 10000000000l) {
            detail1.setDocTipo(99); // DNI
            detail1.setDocNro(0l);
        } else {
            detail1.setDocTipo(86); // CUIL
            detail1.setDocNro(df.getUsuario().getCuil()); // Nro. de CUIL
        }
        detail1.setCbteDesde(ultCompro + 1); // Proximo numero disponible
        detail1.setCbteHasta(ultCompro + 1);
        detail1.setImpTotal(df.getImporteTotal()); // Importe Total de la Fact
        detail1.setImpTotConc(0);
        detail1.setImpNeto(df.getImporteServicio()); // Importe neto de los nexos
        detail1.setImpOpEx(0);
        detail1.setImpIVA(df.getImporteIva()); // Iva por el nexo
        detail1.setImpTrib(0);
        detail1.setMonId("PES"); // Pesos Argentinos
        detail1.setMonCotiz(1); // Cuando son pesos argentinos va 1
        detail1.setCbteFch(HoraArgentina.getFechaActualArgentinaParaFactura());
        detail1.setFchServDesde(df.getFechaPrimera());
        detail1.setFchServHasta(df.getFechaUltima());
        detail1.setFchVtoPago(HoraArgentina.getFechaVencimientoArgentinaParaWSAfip());
        ArrayOfAlicIva listIva = new ArrayOfAlicIva();
        AlicIva iva = new AlicIva();
        iva.setBaseImp(df.getImporteServicio()); // Base imponible sobre el que se calcula
        iva.setId(5);
        iva.setImporte(df.getImporteIva()); // Importe total de IVA calculado
        listIva.getAlicIva().add(iva);
        detail1.setIva(listIva);

        array.getFECAEDetRequest().add(detail1);
        feCAEReq.setFeDetReq(array);

        req.setFeCAEReq(feCAEReq);

        JAXBContext jaxbContext = JAXBContext.newInstance(FECAESolicitar.class);
        Marshaller jaxbMarshaller = jaxbContext.createMarshaller();

        // output pretty printed
        jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

        StringWriter sw = new StringWriter();
        jaxbMarshaller.marshal(req, sw);
        //        jaxbMarshaller.marshal(req, System.out);
        String xmlString = sw.toString();

        String nuevoEncabezado = "<soap:Envelope xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\" xmlns:ar=\"http://ar.gov.afip.dif.FEV1/\"><soap:Header/><soap:Body>";
        String exEncabezado = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>";
        String pedido = xmlString.replace(exEncabezado, nuevoEncabezado);

        return pedido + "</soap:Body></soap:Envelope>";
    }

}