bigdata-mx / factura-electronica

Librería de componentes Java para el desarrollo de aplicaciones de Factura Electrónica (CFDI)
Apache License 2.0
94 stars 107 forks source link

Nuevas validaciones #120

Open ingtemix opened 9 years ago

ingtemix commented 9 years ago

Hola compañeros me acabo de dar cuenta que la aplicación no valida ciertos complementos por la cadena original.

Pueden indicarme como descargar el codigo para poder colaborar con ustedes.

Saludos!!!

SoporteCORASA commented 9 years ago

Yo ya lo implementé, el problema es que tengo una versión a la que le he metido mucha mano y por eso no le hago un push, Segun recuerdo pondré mas o menos los pasos para que alguien lo implemente en la versión original y lo suba. 1.- Conseguir los XSD y XSLT (podemos descargar las diferentes versiones directo del SAT). 2.- Agregarlo dentro de la carpeta main/resources (de preferencia en carpetas separadas, por ejemplo yo tengo la carpeta "VentaVehiculos/v10" y VentaVehiculos/v11" tanto en la carpeta de los XSLT como de los XSD 3.- Agregar al POM todos los XSD y XSLT (que quedarán algo así en el caso del complemento VentaVehiculov11) * * schema18-xjc-11 * * xjc * * * mx.bigdata.sat.common.ventavehiculos11.schema * src/main/resources/xsd/common/ventavehiculos/v11 * ${project.build.directory}/generated-sources/xjc18-11
* ${project.build.directory}/generated-sources/xjc18-11/.staleFlag * true *
* 4.- En el archivo "CadenaOriginal_32.xslt" hay que agregar todos los complemento nuevos que queremos validar, aproximadamente en la linea 24 comienza la definición, en mi caso quedó asi. __ * __ * __ * __ * __ * __ * __ * _ Recuerdo que cuando tenia el proyecto original, habia algunas validaciones de xslt de la versión 3.0 que estaban mal, así que podriamos revisar tambien el archivo cadenaoriginal del cfdi3.0 y revisar las rutas en caso de que lo necesiten. 5.- En la clase CFDv32.java, tenemos un arreglo de Strings con el nombre XSD, ahi hay que agregar las rutas de los XSD que se validan en la versión 3.2, así mismo en la clase CFDv3 hay que agregar los de la versión 3.0, en mi caso quedó algo similar a esto: private static final String[] XSD = new String[] { "/xsd/v32/cfdv32.xsd", "/xsd/v3/TimbreFiscalDigital.xsd", "/xsd/common/TuristaPasajeroExtranjero/TuristaPasajeroExtranjero.xsd", "/xsd/common/detallista/detallista.xsd", "/xsd/common/divisas/divisas.xsd", "/xsd/common/donat/v11/donat11.xsd", "/xsd/common/ecb/ecb.xsd", "/xsd/common/ecc/ecc.xsd", "/xsd/common/iedu/iedu.xsd", "/xsd/common/implocal/implocal.xsd", "/xsd/common/leyendasFisc/leyendasFisc.xsd", "/xsd/common/pfic/pfic.xsd", "/xsd/common/psgcfdsp/psgcfdsp.xsd", "/xsd/common/psgecfd/psgecfd.xsd", "/xsd/common/terceros/terceros11.xsd", "/xsd/common/ventavehiculos/v11/ventavehiculos11.xsd", "/xsd/common/nomina/nomina11.xsd", "/xsd/common/spei/spei.xsd", "/xsd/common/cfdiregistrofiscal/cfdiregistrofiscal.xsd", "/xsd/common/pagoenespecie/pagoenespecie.xsd", "/xsd/common/consumodecombustibles/consumodecombustibles.xsd", "/xsd/common/valesdedespensa/valesdedespensa.xsd", "/xsd/common/aerolineas/aerolineas.xsd", "/xsd/common/notariospublicos/notariospublicos.xsd", "/xsd/common/vehiculousado/vehiculousado.xsd" }; Si haciendo estos cambios, avisame para revisar que mas le moví (fue hace ya casi un año de que hice esos cambios.

Espero que te haya ayudado. Saludos.

ingtemix commented 9 years ago

Amigo podrías explicarme como tener un ambiente de trabajo con el codigo fuente???

En caso contrario apoyarme con estos cambios.

Saludos!!!

SoporteCORASA commented 9 years ago

Explicar paso a paso como tener el ambiente funcional y óptimo es algo complejo pienso yo. Pero puedo darte un par de tips para facilitarte el aprendizaje. Primero que nada te recomiendo uses Netbeans pues al parecer se creó en ese ambiente y es el mas "amigable" para usar en este proyecto. He probado con eclipse y no es tan amigable, requiere de ciertas configuraciones. También te recomiendo aprender un poco de Git (lo básico) para aprendas a hacer los pull, commits, push etc... Ya que hayas aprendido un poco de Git (hay muchísima documentación en internet), tendrás que hacer un pull (como la palabra lo dice, "jalar" el código fuente a tu equipo) y realizar los cambios (los cambios en el código si podría ayudarte mas explicitamente). Después tendrías que hacer el commit y el push (puede que todo esto suene un poco raro, pero seguramente lo entenderás cuando veas Git). No estoy muy seguro como funcionen pues jamas he usado git para repositorios públicos, pero seguro no será muy dificil. Tamben asumo que ya debes tener conocimientos previos de Java. Espero haberte ayudado al menos un poco.

ingtemix commented 9 years ago

Hola amigo si uso Java pero en especial nunca he usado Git por eso la pregunta para obtener el codigo y de ahí empezarlo a modificar.

Uso Netbeans tratare de armar mi ambiente de pruebas cualquier cosa me puedo apoyar contigo??

Saludos!!!

SoporteCORASA commented 9 years ago

De Git no ocupas saber la gran cosa, en internet veras muchos tutoriales de "primeros pasos con git" y cosas así. Claro, cualquier duda puedes consultarme, aunque te comento, no se mucho de Git, solo lo básico, pero te apoyo en lo que pueda. A la hora de realizar el push me imagino que alguien podra apoyarnos, ya que como te comento, solo he trabajado con repositorios locales. Saludos.

ingtemix commented 9 years ago

Listo Amigo ya pude clonar el codigo solo estoy revisando algunos problemas con algunas referencias.

Cualquier duda te pregunto.

Puedes darme tu correo electronico.

Saludos!!!

ingtemix commented 9 years ago

Amigo carga el código en Netbeans y me marca 2 errores con problemas de Referencia podrías apoyarme con dichos detalles???

Saludos!!!

SoporteCORASA commented 9 years ago

Podrías colocar los errores mas especificamente? Te los lanza en consola? Saludos.

ingtemix commented 9 years ago

Me los lanza en esta clase en especifico

public class PrivateKeyLoader implements KeyLoader {

@Getter
PrivateKey key;

public PrivateKeyLoader(String privateKeyLocation, String keyPassword) {
    this.setPrivateKey(privateKeyLocation, keyPassword);
}

public PrivateKeyLoader(InputStream privateKeyInputStream, String keyPassword) {
    this.setPrivateKey(privateKeyInputStream, keyPassword);
}

Me pide implementar todos los metodos abstractos.

Saludos!!!

SoporteCORASA commented 9 years ago

Según recuerdo eso es de la librería lombok, Tienes conocimeintos de Maven? Agrega la dependencia: org.projectlombok lombok 1.14.8 y Dale "Build with dependencies". Me avisas en que resultó.

ingtemix commented 9 years ago

Tengo poco conocimiento de Maven, pero por lo que pude ver es que esa referencia ya se encuentra en el archivo pom.xml

Lo hago pero me sigue mandando el mismo error.

Saludos!!!

SoporteCORASA commented 9 years ago

Aun dándole "Build with Dependencies" ?

ingtemix commented 9 years ago

Si amigo lo que hice ahorita fue colocar esto:

@Override public T getKey() { return (T) this.key; }

Y funciona pero al realizar la validación del timbre de un xml me marca error.

Saludos!!!

SoporteCORASA commented 9 years ago

El detalle es que la librería lombok genera ciertos valores de manera dinámica, y si haces esto lo que hará será que definirás el método como fijo (un método vacio) que no te serviría para nada. Te recomiendo eliminar del POM la librería lombok que te comenté, y posteriormente la agregues por medio del "Add Dependency" de netbeans (NO directamente en el POM) para que netbeans descargue de nuevo las librerías. Quedo en espera de tu respuesta,

SoporteCORASA commented 9 years ago

Se me olvidaba! En su momento me topé con problemas con la versión de Java 8, te recomiendo te asegures que está usando la versión 7, y de no ser así, le asignes esa. Saludos.

ingtemix commented 9 years ago

Amigo lo tengo con Java 7, oyes una pregunta al verificar el tfd me marca un error cosa que no tenia en la versión 2.3

Saludos!!!

SoporteCORASA commented 9 years ago

Que te marca de error? Intentaste lo del archivo POM que te comenté atras?

ingtemix commented 9 years ago

Si man y no funcion. Por otro lado realice una correccion a la clase TFD1c32.

package mx.bigdata.sat.cfdi;

import java.io.ByteArrayOutputStream; import java.io.OutputStream; import java.math.BigInteger; import java.security.PrivateKey; import java.security.Signature; import java.security.cert.X509Certificate; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.UUID;

import javax.xml.XMLConstants; import javax.xml.bind.JAXBContext; import javax.xml.bind.Marshaller; import javax.xml.bind.util.JAXBSource; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.Result; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import javax.xml.validation.Schema; import javax.xml.validation.SchemaFactory; import javax.xml.validation.Validator;

import mx.bigdata.sat.cfdi.schema.ObjectFactory; import mx.bigdata.sat.cfdi.schema.TimbreFiscalDigital; import mx.bigdata.sat.common.ComprobanteBase; import mx.bigdata.sat.common.NamespacePrefixMapperImpl;

import org.apache.commons.codec.binary.Base64; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.xml.sax.ErrorHandler;

public final class TFDv1c32 {

private static final String XSLT = "/xslt/cadenaoriginal_TFD_1_0.xslt";

private static final String XSD = "/xsd/v32/TimbreFiscalDigital.xsd";

private static final JAXBContext CONTEXT = createContext();

private static final JAXBContext createContext() {
    try {
        return JAXBContext.newInstance(new Class[] {mx.bigdata.sat.cfdi.v32.schema.TimbreFiscalDigital.class});
    } catch (Exception e) {
        throw new Error(e);
    }
}

private final ComprobanteBase document;

private final mx.bigdata.sat.cfdi.v32.schema.TimbreFiscalDigital tfd;

private final X509Certificate cert;

private TransformerFactory tf;

public TFDv1c32(CFDI cfd, X509Certificate cert) throws Exception {
    this(cfd, cert, UUID.randomUUID(), new Date());
}

TFDv1c32(CFDI cfd, X509Certificate cert, UUID uuid, Date date) throws Exception {
    this.cert = cert;
    this.document = cfd.getComprobante();
    this.tfd = getTimbreFiscalDigital(document, uuid, date);
}

public void setTransformerFactory(TransformerFactory tf) {
    this.tf = tf;
}

public int timbrar(PrivateKey key) throws Exception {
    if (tfd.getSelloSAT() != null) {
        return 304;
    }
    String signature = getSignature(key);
    tfd.setSelloSAT(signature);
    stamp();
    return 300;
}

public void validar() throws Exception {
    validar(null);
}

public void validar(ErrorHandler handler) throws Exception {
    SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
    Schema schema = sf.newSchema(getClass().getResource(XSD));
    Validator validator = schema.newValidator();
    if (handler != null) {
        validator.setErrorHandler(handler);
    }
    validator.validate(new JAXBSource(CONTEXT, tfd));
}

public int verificar() throws Exception {
    if (tfd == null) {
        return 601; //No contiene timbrado
    }
    Base64 b64 = new Base64();
    System.out.println("HOLA");
    String sigStr = tfd.getSelloSAT();
    System.out.println(tfd);
    System.out.println("1.- " + tfd.getNoCertificadoSAT());
    System.out.println("2.- " + tfd.getSelloCFD());
    System.out.println("3.- " + tfd.getSelloSAT());
    System.out.println("4.- " + tfd.getUUID());
    System.out.println("5.- " + tfd.getVersion());
    System.out.println("6.- " + tfd.getFechaTimbrado());
    byte[] signature = b64.decode(sigStr);
    byte[] bytes = getOriginalBytes();
    Signature sig = Signature.getInstance("SHA1withRSA");
    sig.initVerify(cert);
    sig.update(bytes);
    boolean verified = sig.verify(signature);
    return verified ? 600 : 602; //Sello del timbrado no valido
}

public String getCadenaOriginal() throws Exception {
    byte[] bytes = getOriginalBytes();
    return new String(bytes);
}

public void guardar(OutputStream out) throws Exception {
    Marshaller m = CONTEXT.createMarshaller();
    m.setProperty("com.sun.xml.bind.namespacePrefixMapper",
            new NamespacePrefixMapperImpl(CFDv32.PREFIXES));
    m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
    m.setProperty(Marshaller.JAXB_SCHEMA_LOCATION,
            "http://www.sat.gob.mx/cfd/3  "
            + "http://www.sat.gob.mx/sitio_internet/cfd/3/cfdv32.xsd");
    m.marshal(document.getComprobante(), out);
}

mx.bigdata.sat.cfdi.v32.schema.TimbreFiscalDigital getTimbre() {
    return tfd;
}

byte[] getOriginalBytes() throws Exception {
    JAXBSource in = new JAXBSource(CONTEXT, tfd);
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    Result out = new StreamResult(baos);
    TransformerFactory factory = tf;
    if (factory == null) {
        factory = TransformerFactory.newInstance();
    }
    Transformer transformer = factory
            .newTransformer(new StreamSource(getClass().getResourceAsStream(XSLT)));
    transformer.transform(in, out);
    return baos.toByteArray();
}

String getSignature(PrivateKey key) throws Exception {
    byte[] bytes = getOriginalBytes();
    Signature sig = Signature.getInstance("SHA1withRSA");
    sig.initSign(key);
    sig.update(bytes);
    byte[] signed = sig.sign();
    Base64 b64 = new Base64(-1);
    return b64.encodeToString(signed);
}

private void stamp() throws Exception {
    Element element = marshalTFD();
    document.setComplemento(element);
}

private Element marshalTFD() throws Exception {
    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    dbf.setNamespaceAware(true);
    DocumentBuilder db = dbf.newDocumentBuilder();
    Document doc = db.newDocument();
    Marshaller m = CONTEXT.createMarshaller();
    m.setProperty("com.sun.xml.bind.namespacePrefixMapper",
            new NamespacePrefixMapperImpl(CFDv3.PREFIXES));
    m.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
    m.setProperty(Marshaller.JAXB_SCHEMA_LOCATION,
            "http://www.sat.gob.mx/TimbreFiscalDigital TimbreFiscalDigital.xsd");
    m.marshal(tfd, doc);
    return doc.getDocumentElement();
}

private TimbreFiscalDigital createStamp(UUID uuid, Date date) {
    ObjectFactory of = new ObjectFactory();
    TimbreFiscalDigital tfd = of.createTimbreFiscalDigital();
    tfd.setVersion("1.0");
    tfd.setFechaTimbrado(date);
    tfd.setSelloCFD(document.getSello());
    tfd.setUUID(uuid.toString());
    BigInteger bi = cert.getSerialNumber();
    tfd.setNoCertificadoSAT(new String(bi.toByteArray()));
    return tfd;
}

private mx.bigdata.sat.cfdi.v32.schema.TimbreFiscalDigital getTimbreFiscalDigital(ComprobanteBase document, UUID uuid, Date date) throws Exception {
    Iterator i$;
    if (document.hasComplemento()) {
        List list = document.getComplementoGetAny();
        for (i$ = list.iterator(); i$.hasNext();) {
            Object o = i$.next();
            System.out.println(o.getClass());
            if ((o instanceof mx.bigdata.sat.cfdi.v32.schema.TimbreFiscalDigital)) {
                System.out.println("HOLA");
                return (mx.bigdata.sat.cfdi.v32.schema.TimbreFiscalDigital) o;
            }
        }
    }
    return null;

    /*if (document.hasComplemento()) {
     List<Object> list = document.getComplementoGetAny();
     for (Object o : list) {
     if (o instanceof TimbreFiscalDigital) {
     System.out.println("TIMBRE");
     return (TimbreFiscalDigital) o;
     }
     }
     }
     System.out.println("NO ENOCNTRE");
     return;*/
}

}

En el metodo private mx.bigdata.sat.cfdi.v32.schema.TimbreFiscalDigital getTimbreFiscalDigital(ComprobanteBase document, UUID uuid, Date date)

El timbre fiscal no coincide y nunca entra al if if ((o instanceof mx.bigdata.sat.cfdi.v32.schema.TimbreFiscalDigital)) siempre lo manda a timbrar y eso me regresa un null en el selloSAT.

Con eso ya funciono.

Saludos!!!

SoporteCORASA commented 9 years ago

Podrías preguntar a los dueños del repositorio como realizar el push y así actualizar la libreria.

ingtemix commented 9 years ago

Amigo efectivamente el día de ayer estuve realizando pruebas del metodo verificar y si funciono así tambien ya agregue los nuevos complementos con las indicaciones que me diste y funciono a la perfección amigo.

Muchas gracias. Saludos!!!

ingtemix commented 9 years ago

Hola Amigo buen dia,

Fijate que estoy realizando una validación de sello y veo que al generar la cadena original si tengo un dato como .91 me lo convierte a 0.91 lo que me hace tener inválido el sello.

Tendrás algún comentario al respecto.

SoporteCORASA commented 9 years ago

Claro que si, ya que yo tambien utilicé esta libreria para hacer un validador, tengo bastantes cambios que le hice pero ya que tiene mucha mano mía, no quise darles el push pero dejame busco esa parte del código.

SoporteCORASA commented 9 years ago

Tienes que modificar tu método de GetOriginalBytes, y enviarle un inputstream para que lea el archivo exactamente como es y no lo convierta como me comentas, el método que debes colocar es este.

byte[] getOriginalBytes(InputStream in) throws Exception{
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    try {
        Source source = new StreamSource(in);
        Source xsl = new StreamSource(getClass().getResourceAsStream(XSLT));
        Result out = new StreamResult(baos);
        TransformerFactory factory = tf;
        if (factory == null) {
            factory = TransformerFactory.newInstance();
            factory.setURIResolver(new URIResolverImpl());
        }
        Transformer transformer = factory
                .newTransformer(new StreamSource(getClass().getResourceAsStream(XSLT)));
        transformer.transform(source, out);
    } finally {
        in.close();
    }
    return baos.toByteArray();
}

Ya el inputstream puedes enviarselo como gustes, si ocupas más ayuda hazmelo saber. Saludos.

ingtemix commented 9 years ago

Hola Amigo buen día,

Ya detecte el problema y veía que radicaba que en source mandaban la clase comprobante y por ende el .91 lo convertía a BigDecimal. Eres muy acertado en tus comentarios ya pude resolver el detalle.

Saludos!!!

SoporteCORASA commented 9 years ago

Lo que se te ofrezca, saludos

ingtemix commented 9 years ago

Hola Amigo,

Una pregunta esta librería puede ayudarme a realizar la firma para la cancelación de documentos.

Quedo a espera de tus comentarios. Saludos!!!

SoporteCORASA commented 9 years ago

Pues cancelación de que manera? No creo que esta librería tenga de manera nativa la cancelación pues los PAC's manejan diferentes métodos y servicio, hasta donde tengo entendido no hay uno "genérico" para cancelar en el SAT por medio de un WebService.

ingtemix commented 9 years ago

Hola amigo lo que tengo que hacer es firmar la cancelación a traves del .key y .cer.

Ya lo pude realizar pero obtengo espacios entre el sello esto es correcto.

MIIEgzCCA2ugAwIBAgIUMDAwMDEwMDAwMDAyMDA1NzYxMjcwDQYJKoZIhvcNAQEFBQAwggGVMTgw NgYDVQQDDC9BLkMuIGRlbCBTZXJ2aWNpbyBkZSBBZG1pbmlzdHJhY2nDs24gVHJpYnV0YXJpYTEv MC0GA1UECgwmU2VydmljaW8gZGUgQWRtaW5pc3RyYWNpw7NuIFRyaWJ1dGFyaWExODA2BgNVBAsM L0FkbWluaXN0cmFjacOzbiBkZSBTZWd1cmlkYWQgZGUgbGEgSW5mb3JtYWNpw7NuMSEwHwYJKoZI hvcNAQkBFhJhc2lzbmV0QHNhdC5nb2IubXgxJjAkBgNVBAkMHUF2LiBIaWRhbGdvIDc3LCBDb2wu IEd1ZXJyZXJvMQ4wDAYDVQQRDAUwNjMwMDELMAkGA1UEBhMCTVgxGTAXBgNVBAgMEERpc3RyaXRv IEZlZGVyYWwxFDASBgNVBAcMC0N1YXVodMOpbW9jMRUwEwYDVQQtEwxTQVQ5NzA3MDFOTjMxPjA8 BgkqhkiG9w0BCQIML1Jlc3BvbnNhYmxlOiBDZWNpbGlhIEd1aWxsZXJtaW5hIEdhcmPDrWEgR3Vl cnJhMB4XDTEyMDMxNTIxMDI0OFoXDTE2MDMxNTIxMDI0OFowgcQxIjAgBgNVBAMTGUVESUZJQ0FD SU9ORVMgR0ggU0EgREUgQ1YxIjAgBgNVBCkTGUVESUZJQ0FDSU9ORVMgR0ggU0EgREUgQ1YxIjAg BgNVBAoTGUVESUZJQ0FDSU9ORVMgR0ggU0EgREUgQ1YxJTAjBgNVBC0THEVHSDAzMDEyNzQyQSAv IFJFQko2OTEyMjk4UzYxHjAcBgNVBAUTFSAvIFJFQko2OTEyMjlIREdZTFMwMjEPMA0GA1UECxMG dW5pZGFkMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC278yhGUai6sONoTLPhmtxmJQ6l2sC EzKIht02RrGCNQzvOjw70S3uXOflcsnOiuGu6oS7uSC9IuciCfm4xyXzXxNeCSjxCIG4b+U5IzTZ VGZvu+DgJToC3HHo6rYpX+haSBGM8uPmZa/7mCIslYk6ML35Vsnze/DJ/ezwoi0WFQIDAQABox0w GzAMBgNVHRMBAf8EAjAAMAsGA1UdDwQEAwIGwDANBgkqhkiG9w0BAQUFAAOCAQEALln7MKifnbS1 0F6Fi3SP4YAQzTsWwQa20LBo8d64rkZm366cQ7cyqe0dxAON0eS+eCi2Ny0rHkeew18AcKIek/B3 W+OIsdFhNSFE+G2OVbngC51DaFs7nIk5kWhDCHmNPVna/7zsDTMIGAs8mI4d1rtboqhuwQW2er9U 9d4m7MGAVPWYL28FGKdeNl8J3pCJOv2WSEdo6IO+jFEfcdnt4gQlaex8/HpBLRA/v8thcEfmZxhP dDr5ZZ2KkLU+NU6JN5gkOb9mhYEXcd8jWtjWG7em8l+YHVcjvYawMsdUtvDgJCyO4vYFqRmOcjet UQf0cMjciqDIvye+hcDv+oc/cQ== kqebRSA979YydquWVT46zAvaAEpGffopVBdL5bN8Cil3nK1soaciaC93wMOplmuVhuyoY3m75uMN bvWSFq/nz4hINZMhAcHFpIh9fQ7ezeOPRKT73PUIrTC+VUrli84CYgxrs6UchU3wqK5y4o1R4OBa iPxruxMPppUGDL0gcQQ=
SoporteCORASA commented 9 years ago

Probablemente esto se deba a un error en la codificación, quizá podría apoyarte un poco más si me indicaras el método o librería que utilizas y exactamente que es lo que estas enviando. Saludos.