facturacion digital
TRANSCRIPT
Facturación Digital Comprobante Fiscal Digital por Internet Descripción del proceso paso a paso para poder generar tus propios Comprobantes Fiscales (CFDI), con ejemplos explicados y comentados usando JAVA
2010
Departamento de Informacion y de Sistemas Universidad de Montemorelos
19/10/2010
Contenido FACTURACION DIGITAL .................................................................................................................. 2
Introduccion .................................................................................................................................. 2
Algunas definiciones ...................................................................................................................... 2
Contenido del Anexo 20 Anexo 20 de la Primera Resolución de Modificaciones a la Resolución
Miscelánea Fiscal para 2010, publicada el 14 de septiembre de 2010. ........................................ 3
Pasos para la creación de un CFDI .................................................................................................. 4
PASO 1: Crear estructura de clases y documentación básica ....................................................... 4
Algunas notas aclaratorias y recomendaciones: ...................................................................... 7
Fuente y sitios recomendados: ............................................................................................... 8
PASO 2: Generar Cadena Original del CFDI SIN TimbreFiscalDigital ............................................. 8
Reglas Generales para la generación de la Cadena Original .................................................... 8
Secuencia de Formación de la Cadena Original ....................................................................... 9
Algunas notas aclaratorias y recomendaciones: .................................................................... 13
Fuente y sitios recomendados: ............................................................................................. 13
PASO 3: Generar Sello Digital ................................................................................................... 13
Procedimiento...................................................................................................................... 14
Algunas notas aclaratorias y recomendaciones: .................................................................... 16
Fuente y sitios recomendados: ............................................................................................. 16
PASO 4: Generar y validar XML del Comprobante ..................................................................... 17
Algunas notas aclaratorias y recomendaciones: ..................................................................... 23
Fuente y sitios recomendados: ............................................................................................. 24
PASO 5: Generar Cadena Original del Timbre Fiscal Digital ....................................................... 24
Ejemplo de cadena original de un timbre: ............................................................................ 24
Fuente y sitios recomendados: ............................................................................................. 25
PASO 6: Generar y validar XML del Timbre Fiscal Digital .......................................................... 25
Fuente y sitios recomendados: ............................................................................................. 25
PASO 7: Juntar (concatenar) el XML obtenido del Comprobante + XML del TimbreFiscalDigital 25
Fuente y sitios recomendados: ............................................................................................. 26
OTROS ..................................................................................................................................... 26
FACTURACION DIGITAL
Introduccion Después de mucho leer, investigar en foros, entender el Anexo 20 del SAT y demás, esto es
lo que hemos logrado entender y que nos queda claro.
Aclaro que todo lo que se muestre a continuación está basado en la versión 3 de los
Comprobantes Digitales, que entrara en vigor a partir del 1/Enero/2011 y construido sobre
el lenguaje de programación JAVA.
Algunas definiciones CFDI: Comprobante Fiscal Digital por Internet
Anexo 20: Tu Biblia para la Facturacion Digital. Es un pdf hecho por el SAT donde esta
detallado y especificado los estándares y formatos que deben cumplir los CFD o CFDI
Cadena Original: El elemento a sellar. secuencia de datos formada con la información
contenida dentro del comprobante fiscal digital por internet, establecida en el Rubro II.A
“Estándar de comprobante fiscal digital por internet” de este anexo (Anexo 20).
Sello Digital: Resultado de la secuencia de algoritmos aplicados a la Cadena Original para
hacer que el Comprobante Fiscal tenga las características de: Infalsificable y Único.
XSD: Es un lenguaje de esquema utilizado para describir la estructura y las restricciones de
los contenidos de los documentos XML de una forma muy precisa, más allá de las normas
sintácticas impuestas por el propio lenguaje XML. En palabras que nosotros hemos
entendido, el XSD no es básicamente una plantilla donde se especifica la estructura, tipos
de datos y requisitos que debe cumplir un documento XML. Y se usa precisamente para
verificar si un documento XML cumple o no con los elementos de la plantilla.
Contenido del Anexo 20 Anexo 20 de la Primera Resolución de
Modificaciones a la Resolución Miscelánea Fiscal para 2010, publicada el
14 de septiembre de 2010. Cuyo contenido trata de los Medios electrónicos
I. Del Comprobante Fiscal Digital:
A. Características técnicas del archivo que contenga el informe mensual de comprobantes
fiscales digitales emitidos
B. Estándar de comprobante fiscal digital
C. Generación de sellos digitales para comprobantes fiscales digitales
II. Del Comprobante Fiscal Digital por Internet:
A. Estándar de comprobante fiscal digital por internet
B. Generación de sellos digitales para comprobantes fiscales digitales por internet
C. Estándar y uso del complemento obligatorio: Timbre Fiscal Digital del SAT
D. Estándar del servicio de cancelación
E. Especificación técnica del código de barras bidimensional
III. De los distintos medios de comprobación digital:
A. Estándares y especificaciones técnicas que deberán cumplir las aplicaciones
informáticas para la generación de claves de criptografía asimétrica a utilizar para Firma
Electrónica Avanzada
B. Uso de la facilidad de nodos opcionales y
C. Uso de la facilidad de ensobretado
Pasos para la creación de un CFDI
PASO 1: Crear estructura de clases y documentación básica
Estas son las clases que deben crearse con sus atributos y tipo. Se muestran en orden
jerárquico. Fueron extraidas directamente del Anexo 20. Solo se detallan algunos atributos
que se consideran son un poco complicado de entender o que se prestan a confusión; mas
características deberán consultarse directamente en el Anexo 20 del SAT.
TIPOS SIMPLES
Comprobante
String versión //3.0
String serie //Serie para control interno del Contribuyente (1-25 caracteres)
String folio //Folio para control interno del Contribuyente
Date fecha
String sello //Sello Digital del Comprobante (en Base 64)
String formaDePago
String noCertificado //Número de serie del certificado de sello digital (lo
otorga el SAT)
String certificado //Certificado del sello digital (en Base 64)
String condicionesDePago
T_Importe subtotal //Suma de los importes de los Conceptos antes de
descuentos e impuestos
T_Importe descuento
String motivoDescuento
String tipoCambio //Tipo de Cambio según la moneda usada
String moneda //Moneda utilizada para expresar los montos
T_Importe total //Suma del total – descuentos aplicables + descuentos
trasladados – impuestos retenidos
String metodoDePago
String tipoDeComprobante
Emisor emisor
Receptor receptor
Conceptos
Impuestos
Complemento //Nodo opcional donde se incluirá el complemento Timbre
Fiscal Digital de manera obligatoria y los nodos complementarios
determinados por el SAT, de acuerdo a las disposiciones particulares a un
sector o actividad específica.
Addenda // Nodo opcional para recibir las extensiones al presente formato
que sean de utilidad al contribuyente. Para las reglas de uso del mismo,
referirse al formato de origen
Emisor
T_Rfc rfc
String nombre
DomicilioFiscal domicilioFiscal
ExpedidoEn expedidoEn
DomicilioFiscal
UbicacionFiscal ubicacionFiscal
ExpedidoEn
Ubicación
Receptor
T_Rfc rfc
String nombre
Domicilio domicilio
Domicilio
Ubicación
Conceptos
List<Concepto> conceptos
BigInteger sumaImportesConceptos //Atributo agregado por nosotros para guardar
la suma total de los importes de los conceptos agregados
Concepto
Double cantidad
String unidad
String noIdentificacion
String descripción
T_Importe valorUnitario
T_Importe importe //cantidad * valorUnitario
InformacionAduanera informacionAduanera
CuentaPredial cuentaPredial
ComplementoConcepto complementoConcepto
Parte
InformacionAduanera
T_InformacionAduanera
CuentaPredial
Numero
ComplementoConcepto
// Nodo opcional donde se incluirán los nodos complementarios de extensión al
concepto, definidos por el SAT, de acuerdo a disposiciones particulares a un sector
o actividad específica.
Parte
Double cantidad
String unidad
String noIdentificacion
String descripcion
T_Importe valorUnitario
T_Importe importe //cantidad * valorUnitario
InformacionAduanera informacionAduanera
Impuestos
T_Importe totalImpuestosRetenidos //Total de impuestos retenidos, obtenidos de los
Conceptos
T_Importe totalImpuestosTrasladados //Total de impuestos trasladados, obtenidos
de los Conceptos
Retenciones
Traslados
Retenciones
List<Retencion> retenciones
T_Importe sumaImportesRetenciones // Atributo agregado por nosotros para
guardar la suma total de los importes de las retenciones agregadas
Retencion
String impuesto //ISR, IVA
T_Importe importe
Traslados
List<Traslado> traslados
T_Importe sumaImportesTraslados // Atributo agregado por nosotros para guardar
la suma total de los importes de los traslados agregados
Traslado
String impuesto //IVA, IEPS
T_Importe tasa
T_Importe importe
Complemento
TimbreFiscalDigital timbreFiscalDigital
Addenda
TIPOS COMPLEJOS
T_Ubicacion
String calle
String noExterior
String noInterior
String colonia
String localidad
String referencia
String municipio
String estado
String país
String coidoPostal
T_UnicacionFiscal
Mismos atributos que T_Ubicacion
T_InformacionAduanera
String numero
Date fecha
String aduana
T_Rfc
String rfc //12 a 13 caracteres
T_Importe
BigDecimal importe //Aquí usamos un BigDecimal, debido a que, por su exactitud,
consideramos que es lo mejor para manejar cantidades de dinero
Algunas notas aclaratorias y recomendaciones:
Recomiendo comentar todas las clases, poniendo a cada una la descripción que se
encuentra en los diagramas del Anexo 20
Usar BigDecimal para las unidades de dinero debido a que, según el API Java, es
totalmente exacto
En las clases de Conceptos, Retenciones y Traslados, agregar los atributos de
sumaImportes* y sobreescribir el método del add de Java para que al momento de
agregar un Concepto/Retencion/Traslado nos guarde la suma de los importe y no
tener que hacer ciclos después que nos quitaran tiempo al generar el Comprobante
Usar herencia para las siguientes clases: Ubicación, UbicacionFiscal, Domicilio,
DomicilioFiscal,ExpedidoEn, tomando como clase padre a Ubicación ya que es la
clase que no tiene ningún nodo requerido, y de ahí extender los demas
Fuente y sitios recomendados:
Anexo 20 paginas 55-76
http://www.google.com.mx/url?sa=t&source=web&cd=1&ved=0CBQQFjAA&url=
ftp%3A%2F%2Fftp2.sat.gob.mx%2Fasistencia_servicio_ftp%2Fpublicaciones%2F
anteproyectos%2FAnteproyecto_A20_03092010.doc&rct=j&q=anexo%2020%20v
3%20del%20sat&ei=Gz28TI3mG5CCsQPZlZzuDg&usg=AFQjCNGRtPPl8xCqSX
AdiRD5Yo6BzUQS3w&sig2=k8508zZvokRiBatwzWv-qg&cad=rja –
Anteproyecto del Anexo 20 V3
ftp://ftp2.sat.gob.mx/asistencia_servicio_ftp/publicaciones/legislacion10/a20rmf_23
092010.doc - Anexo 20 de la Primera Resolución de Modificaciones a la
Resolución Miscelánea Fiscal para 2010, publicada el 14 de septiembre de 2010.
Publicado en el DOF del 23 de septiembre de 2010
http://code.google.com/p/cx-faktura/ - Codigo java para generar un CFD versión2
http://www.facturae.es/ES-
ES/DESCARGAS/DESCARGASDESARROLLO/Paginas/APIJava2.aspx - API
JAVA para la generación de CFDs en España
http://www.nic.cl/anuncios/2003-08-07.html - API JAVA para la generación de
CFDs en Chile
PASO 2: Generar Cadena Original del CFDI SIN TimbreFiscalDigital En este punto existen 2 opciones:
1.- Generar el xml del Comprobante de acuerdo al formato del Anexo 20 y a partir del xml
generar la Cadena Original
2.- Generar “a mano” la Cadena Original y posteriormente el xml del Comprobante.
Nosotros elegimos por razones de tiempo, generar “a mano” la cadena original. Esto lo
logramos de la siguiente manera: Cada clase u objeto se encarga de generar su propia
cadena original de acuerdo al estándar publicado por el SAT, de tal manera que la clase
Comprobante, solo tiene que concatenar al generar su propia cadena Original, todas las
“cadenas originales” de los elementos que requiere.
Estos son los requisitos que deben cumplir la generación de la Cadena Original:
Reglas Generales para la generación de la Cadena Original: 1. Ninguno de los atributos que conforman al comprobante fiscal digital deberá
contener el carácter | (“pipe”) debido a que este será utilizado como carácter de
control en la formación de la cadena original.
2. El inicio de la cadena original se encuentra marcado mediante una secuencia de
caracteres || (doble “pipe”).
3. Se expresará únicamente la información del dato sin expresar el atributo al que hace
referencia. Esto es, si la serie del comprobante es la “A” solo se expresará |A| y
nunca |Serie A|.
4. Cada dato individual se encontrará separado de su dato subsiguiente, en caso de
existir, mediante un carácter | (“pipe” sencillo).
5. Los espacios en blanco que se presenten dentro de la cadena original serán tratados
de la siguiente manera:
a. Se deberán remplazar todos los tabuladores, retornos de carro y saltos de
línea por espacios en blanco.
b. Acto seguido se elimina cualquier carácter en blanco al principio y al final
de cada separador | (“pipe” sencillo).
c. Finalmente, toda secuencia de caracteres en blanco intermedias se sustituyen
por un único carácter en blanco.
6. Los datos opcionales no expresados, no aparecerán en la cadena original y no
tendrán delimitador alguno.
7. El final de la cadena original será expresado mediante una cadena de caracteres ||
(doble “pipe”).
8. Toda la cadena de original se expresará en el formato de codificación UTF-8.
9. El nodo o nodos adicionales <ComplementoConcepto> se integrarán a la cadena
original como se indica en la secuencia de formación en su numeral 10, respetando
la secuencia de formación y número de orden del ComplemetoConcepto.
10. El nodo o nodos adicionales <Complemento> se integraran al final de la cadena
original respetando la secuencia de formación para cada complemento y número de
orden del Complemento.
11. El nodo Timbre Fiscal Digital del SAT será integrado posterior a la validación
realizada por un proveedor autorizado por el SAT que forma parte de la
Certificación Digital del SAT. Dicho nodo no se integrará a la formación de la
cadena original del CFDI, las reglas de conformación de la cadena original del nodo
se describen en el rubro II.C del presente anexo.
Y la Secuencia de Formación para la Cadena Original es esta:
Secuencia de Formación de la Cadena Original
1. Información del nodo Comprobante
a. version
b. fecha
c. tipoDeComprobante
d. formaDePago
e. condicionesDePago
f. subTotal
g. descuento
h. TipoCambio
i. Moneda
j. total
2. Información del nodo Emisor
a. rfc
b. nombre
3. Información del nodo DomicilioFiscal
a. calle
b. noExterior
c. noInterior
d. colonia
e. localidad
f. referencia
g. municipio
h. estado
i. pais
j. codigoPostal
4. Información del nodo ExpedidoEn
a. calle
b. noExterior
c. noInterior
d. colonia
e. localidad
f. referencia
g. municipio
h. estado
i. pais
j. codigoPostal
5. Información del nodo Receptor
a. rfc
b. nombre
6. Información del nodo Domicilio
a. calle
b. noExterior
c. noInterior
d. colonia
e. localidad
f. referencia
g. municipio
h. estado
i. pais
j. codigoPostal
7. Información de cada nodo Concepto
nota: esta secuencia deberá ser repetida por cada nodo Concepto relacionado
a. cantidad
b. unidad
c. noIdentificacion
d. descripcion
e. valorUnitario
f. importe
g. InformacionAduanera nota: esta secuencia deberá ser repetida por cada nodo
InformacionAduanera de forma indistinta a su grado de dependencia
i. numero
ii. fecha
iii. aduana
h. Información del nodo CuentaPredial
i. numero
8. Información del nodo ComplementoConcepto de acuerdo con lo expresado en el
Rubro III.B.
9. Información de cada nodo Retencion
nota: esta secuencia a, b, deberá ser repetida por cada nodo Retención relacionado,
el total de impuestos retenidos no se repite.
a. impuesto
b. importe
c. totalImpuestosRetenidos
10. Información de cada nodo Traslado
nota: esta secuencia a, b, deberá ser repetida por cada nodo Traslado relacionado, el
total de impuestos trasladados no se repite.
a. Impuesto
b. tasa
c. importe
d. totalImpuestosTrasladados
11. Información del nodo Complemento de acuerdo con lo expresado en el Rubro III.B.
Ahora, muestro un ejemplo de la manera en que generamos la Cadena Original
public class Comprobante{
private String versión = “3.0”;
.
.
.
private Emisor emisor;
public String getCadenaOriginal(){
String cadena = "||"; //inicio de la cadena original
sdf = new SimpleDateFormat(Constants.DATE_FORMATO_CFDI);
//para formatear la fecha
//Informacion del Nodo Comprobante
cadena += version + "|" + sdf.format(fecha) + "|" +
tipoDeComprobante + "|" + formaDePago + "|" +
(!getCondicionesDePago().equals("") ? condicionesDePago + "|"
: "" ) +
getSubtotal().getImporte().toString() + "|" +
(!getDescuento().getImporte().toString().equals("0.0") ?
getDescuento().getImporte().toString() + "|" : "") +
(!getTipoDeCambio().equals("") ? getTipoDeCambio() + "|" :
"") + (!getMoneda().equals("") ? getMoneda() + "|" : "") +
getTotal().getImporte().toString()+ "|";
//Informacion del Nodo Emisor (Domicilio Fiscal, ExpedidoEn)
cadena += emisor.getCadenaOriginal() + "|";
//Informacion del Nodo Receptor (Domicilio)
cadena += receptor.getCadenaOriginal() + "|";
//Informacion del Nodo Conceptos
cadena += conceptos.getCadenaOriginal() + "|";
//Informacion del Nodo Impuestos
//Informacion del Nodo Retenciones
cadena += impuestos.getRetenciones().getCadenaOriginal() +
"|";
//Informacion del Nodo Traslados
cadena += impuestos.getCadenaOriginalTraslados();
//Informacion del Nodo Complemento
cadena += "||";
try{
cadena = new String(cadena.getBytes("UTF-8"));
//Codificacion de la Cadena a UTF-8
}
catch(UnsupportedEncodingException uee){
System.out.println("Error al codificar la cadena a UTF-
8");
}
return cadena;
}
}
public class Emisor {
private T_Rfc rfc;
private String nombre;
private DomicilioFiscal domicilioFiscal;
private ExpedidoEn expedidoEn;
public String getCadenaOriginal() {
String cadena = "";
//Informacion del Nodo Emisor
cadena += this.rfc.getCadenaOriginal() + "|" + nombre;
//Informacion del Nodo Domicilio Fiscal
if(domicilioFiscal != null &&
!domicilioFiscal.getCadenaOriginal().equals("")){
cadena += "|" + domicilioFiscal.getCadenaOriginal();
}
//Informacion del Nodo ExpedidoEn
if(expedidoEn != null &&
!expedidoEn.getCadenaOriginal().equals("")){
cadena += "|" + expedidoEn.getCadenaOriginal();
}
return cadena;
}
}
public class T_Rfc {
private String rfc; public String getCadenaOriginal(){ //Regresa la Cadena Original del
rfc
return this.rfc;
}
}
Algunas notas aclaratorias y recomendaciones:
Los elementos que son REQUERIDOS el Comprobante no necesariamente son
requeridos por la Cadena Original.
Los elementos REQUERIDOS por la Cadena Original algunos son OPCIONALES
en el Comprobante.
Por lo tanto, los elementos OPCIONALES que no se encuentren el Comprobante,
NO APARECERAN en la Cadena Original.
Notese en el ejemplo anterior, en el elemento Comprobante, que la Cadena Original
se regresa ya codificada en el formato UTF-8, el cual es el estándar establecido por
el Anexo 20
Fuente y sitios recomendados:
Anexo 20 paginas 95-97
http://code.google.com/p/cx-faktura/ - Codigo java para generar un CFD versión2.
PASO 3: Generar Sello Digital
A continuación se describe el procedimiento para la generación del Sello Digital tal y como
lo describe el Anexo 20 del SAT. Hay que recordar que la generación del “sello digital” no
es más que aplicar una serie de algoritmos a la Cadena Original, que convertirán a esta en
una cadena encriptada y es este resultado, lo que conocemos como “sello digital”, al igual
que un Comprobante “sellado”, es un Comprobante que contiene un cadena original que ya
ha sido procesada con los algoritmos necesarios y convertida en el atributo “sello” del
Comprobante.
Procedimiento
En pasos sencillos, el procedimiento es este:
I. Obtener la Cadena Original
II. Aplicar el método de digestión SHA-1 a la Cadena Original (por ahora no hay
nodos Complementarios asi que no nos causaran problemas ya que no se toman en
cuenta)
III. Encriptar el resultado obtenido en el paso anterior usando el algoritmo de
encriptación RSA, usando para ello la Clave Privada correspondiente al Certificado
Digital del Emisor del Mensaje y Sello Digital (certificado y llave del certificado)
IV. El resultado obtenido en el paso anterior, codificarlo a Base 64 (consiste en la
asociación de cada 6 bits de la secuencia a un elemento de un "alfabeto" que consta
de 64 caracteres imprimibles)
Aquí un ejemplo de Sello Digital:
GqDiRrea6+E2wQhqOCVzwME4866yVEME/8PD1S1g6AV48D8VrLhKUDq0Sjqnp9Iwf
MAbX0ggwUCLRKa+Hg5q8aYhya63If2HVqH1sA08poer080P1J6Z+BwTrQkhcb5Jw8jE
NXoErkFE8qdOcIdFFAuZPVT+9mkTb0Xn5Emu5U8=
Tal y como lo describe el SAT, el procedimiento es este:
Para toda cadena original a ser sellada digitalmente, la secuencia de algoritmos a aplicar es
la siguiente:
I. Aplicar el método de digestión SHA-1 a la cadena original a sellar incluyendo los
nodos Complementarios. Este procedimiento genera una salida de 160 bits (20
bytes) para todo mensaje. La posibilidad de encontrar dos mensajes distintos que
produzcan una misma salida es de 1 en 2160, y por lo tanto en esta posibilidad se
basa la inalterabilidad del sello, así como su no reutilización. Es de hecho una
medida de la integridad del mensaje sellado, pues toda alteración del mismo
provocará una digestión totalmente diferente, por lo que no se podrá autentificar el
mensaje. SHA-1 no requiere semilla alguna. El algoritmo cambia su estado de
bloque en bloque de acuerdo a la entrada previa.
II. Con la clave privada correspondiente al certificado digital del emisor del mensaje y
del sello digital, encriptar la digestión del mensaje obtenida en el paso I utilizando
para ello el algoritmo de encripción RSA. Nota: La mayor parte del software
comercial podría generar los pasos I y II invocando una sola función y
especificando una constante simbólica. En el SAT este procedimiento se hace en
pasos separados, lo cual es totalmente equivalente. Es importante resaltar que
prácticamente todo el software criptográfico comercial incluye APIs o expone
métodos en sus productos que permiten implementar la secuencia de algoritmos
aquí descrita. La clave privada solo debe mantenerse en memoria durante la llamada
a la función de encripción; inmediatamente después de su uso debe ser eliminada de
su registro de memoria mediante la sobre escritura de secuencias binarias alternadas
de "unos" y "ceros".
III. El resultado será una cadena binaria que no necesariamente consta de caracteres
imprimibles, por lo que deberá traducirse a una cadena que sí conste solamente de
tales caracteres. Para ello se utilizará el modo de expresión de secuencias de bytes
denominado "Base 64", que consiste en la asociación de cada 6 bits de la secuencia
a un elemento de un "alfabeto" que consta de 64 caracteres imprimibles. Puesto que
con 6 bits se pueden expresar los números del 0 al 63, si a cada uno de estos valores
se le asocia un elemento del alfabeto se garantiza que todo byte de la secuencia
original puede ser mapeado a un elemento del alfabeto Base 64, y los dos bits
restantes formarán parte del siguiente elemento a mapear. Este mecanismo de
expresión de cadenas binarias produce un incremento de 25% en el tamaño de las
cadenas imprimibles respecto de la original. La codificación en base 64, así como su
decodificación, se hará tomando los bloques a procesar en el sentido de su lectura,
es decir, de izquierda a derecha. El alfabeto a utilizar se expresa en el siguiente
catálogo
A continuación la manera en la que generamos el Sello Digital
public class GeneradorSelloDigital {
public String getSelloDigital(String cadenaOriginal) throws
GeneralSecurityException{
Security.addProvider(new BouncyCastleProvider()); //Esta es la
manera en la que se añade el Wrapper de BouncyCastle para hacer la
encriptacion. Lo que hace esta línea es que se usen las clases del
paquete Security de BouncyCastle en lugar de las de Java originales
System.out.println("Cadena Original "+cadenaOriginal);
FileInputStream archivoClavePrivada=null;
try{
archivoClavePrivada=new
FileInputStream("Path_del_archivo_emisor.key");
}catch(FileNotFoundException fnfe){
fnfe.printStackTrace();
}
String password="claveDelArchivo";
byte[] clavePrivada = getBytes(archivoClavePrivada);
PKCS8Key pkcs8 = new PKCS8Key(clavePrivada,
password.toCharArray());
PrivateKey pk = pkcs8.getPrivateKey();
Signature firma = Signature.getInstance("SHA1withRSA");
//Aplicacion de la Digestion con SHA-1 y encriptado a con RSA en un solo
paso
firma.initSign(pk);
String selloDigital = null;
try {
firma.update(cadenaOriginal.getBytes("UTF-8"));
//Codificacion del resultado a UTF-8
BASE64Encoder b64 = new BASE64Encoder();
selloDigital = b64.encode(firma.sign()); //Conversion del
resultado anterior a Base64
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
System.out.println("Sello Digital "+selloDigital);
return selloDigital;
}
/**
* Metodo que convierte un input stream con la llave privada a un
array de bytes
* @param is InputSteam con la clave privada
* @return Arreglo de bytes con la clave privada
*/
private byte[] getBytes(InputStream is) {
int totalBytes = 714;
byte[] buffer = null;
try {
buffer = new byte[totalBytes];
is.read(buffer, 0, totalBytes);
is.close();
} catch (IOException e) {
e.printStackTrace();
}
return buffer;
}
}
Algunas notas aclaratorias y recomendaciones:
Al parecer, las únicas librerías aceptadas por el SAT para la generación de la firma
digital son: Open SSL y BouncyCastle, el cual cuenta con un Wrapper para Java,
que es lo que nosotros usamos
Una vez generado el Sello Digital, deberemos asignarlo en los siguientes lugares:
I. En el Comprobante, en el atributo “sello”
II. En el Nodo de Complemento Timbre Fiscal, en el atributo “selloCFD”
Fuente y sitios recomendados:
Anexo 20 paginas 97-98
NOTA: Todos los links ftp del SAT deben abrirse con Internet Explorer
ftp://ftp2.sat.gob.mx/asistencia_servicio_ftp/publicaciones/solcedi/Certif_PACyEmi
sor.zip - Certificado PAC y Emisor
http://www.javamexico.org/blogs/nopalin/facturacion_electronica - Encriptación y
codificación a Base 64, pero usando MD5 (usado en los CFD v2)
http://uetiko-developer.blogspot.com/2010/02/como-obtener-el-sello-digital-con-
java.html -Obtención del Sello Digital en una sola Clase totalmente comentada,
pero usando MD5
http://es.debugmodeon.com/articulo/generar-hash-sha-1-de-una-cadena-en-java -
Generación del Hash SHA-1 con Java
http://renetrejo.blogspot.com/2007/11/facturacin-electrnica.html - Generacion Sello
Digital usando MD5 con Java
http://www.sat.gob.mx/sitio_internet/e_sat/comprobantes_fiscales/15_6522.html -
OpenSSL y BouncyCastle, son las unicas opciones que muestra el SAT para generar
el Sello
http://www.validacfd.com/phpbb3/viewtopic.php?f=5&t=34 OpenSSL y Java no
firman igual
http://www.bouncycastle.org Sitio oficial Bouncy Castle
http://www.openssl.org Sitio Oficial Open SSL
PASO 4: Generar y validar XML del Comprobante
En este paso, nosotros hicimos uso JDOM, que es una biblioteca de código abierto para
manipulaciones de datos XML optimizados para Java.
Esta es la manera en la que logramos generar el XML:
public class CrearXML {
protected Locale local = new java.util.Locale
(Constants.LOCALE_LANGUAGE, Constants.LOCALE_COUNTRY,
Constants.LOCALE_VARIANT);
private SimpleDateFormat sdf = new SimpleDateFormat
(Constants.DATE_FORMATO_CFDI, local);
private Document xml=new Document();
private Element root;
/**
* Este metodo genera el Elemento Comprobante del XML
* @param comprobante
*/
public void generaRaiz(Comprobante comprobante) {
//crea un elemento raiz y le asigna los namespaces y los xsd
root = new
Element(Constants.COMPROBANTE_COMPROBANTE,Constants.COMPROBANTE_PREFIX_NA
MESPACE,Constants.COMPROBANTE_NAMESPACE_URI);
//root = new
Element(Constants.COMPROBANTE_COMPROBANTE,Constants.COMPROBANTE_NAMESPACE
_URI);
Namespace
namespace=Namespace.getNamespace(Constants.COMPROBANTE_PREFIX,
Constants.COMPROBANTE_URI_PREFIX);
root.setAttribute(Constants.COMPROBANTE_SCHEMA,Constants.COMPROBANTE_SCHE
MA_XSD,namespace);
root.addNamespaceDeclaration(namespace);
//se empieza a construir el comprobante
root.setAttribute(Constants.COMPROBANTE_VERSION,
Constants.VERSION_COMPROBANTE_TRES);
if(comprobante.getSerie()!=null&&!comprobante.getSerie().trim().equals(""
)){
root.setAttribute(Constants.COMPROBANTE_SERIE,
comprobante.getSerie());
}
if(comprobante.getFolio()!=null&&!comprobante.getFolio().trim().equals(""
)){
root.setAttribute(Constants.COMPROBANTE_FOLIO,
comprobante.getFolio());
}
root.setAttribute(Constants.COMPROBANTE_FECHA,
sdf.format(comprobante.getFecha()));
root.setAttribute(Constants.COMPROBANTE_SELLO,
comprobante.getSello());
.
.
.
//se generan el Nodo Emisor
generaEmisor(comprobante.getEmisor());
generaReceptor(comprobante.getReceptor());
generaConceptos(comprobante.getConceptos());
generaImpuestos(comprobante.getImpuestos());
getXml().setRootElement(root);
}
public void generaEmisor(Emisor emisor) {
//se le agrega el namespace al emisor
Element emisorElement = new
Element(Constants.COMPROBANTE_EMISOR,Constants.COMPROBANTE_PREFIX_NAMESPA
CE,Constants.COMPROBANTE_NAMESPACE_URI);
emisorElement.setAttribute(Constants.COMPROBANTE_RFC,
emisor.getRfc().getRfc());
emisorElement.setAttribute(Constants.COMPROBANTE_NOMBRE,
emisor.getNombre());
//se crea el nodo domiciliofiscal
Element domicilioFiscal = new
Element(Constants.COMPROBANTE_DOMICILIO_FISCAL,Constants.COMPROBANTE_PREF
IX_NAMESPACE,Constants.COMPROBANTE_NAMESPACE_URI);
domicilioFiscal.setAttribute(Constants.COMPROBANTE_DOMICILIO_GENERICO_CAL
LE, emisor.getDomicilioFiscal().getCalle());
if(emisor.getDomicilioFiscal().getNoExterior()!=null&&!emisor.getDomicili
oFiscal().getNoExterior().trim().equals("")){
domicilioFiscal.setAttribute(Constants.COMPROBANTE_DOMICILIO_GENERICO_NO_
EXTERIOR, emisor.getDomicilioFiscal().getNoExterior());
}
if(emisor.getDomicilioFiscal().getNoInterior()!=null&&!emisor.getDomicili
oFiscal().getNoInterior().trim().equals("")){
domicilioFiscal.setAttribute(Constants.COMPROBANTE_DOMICILIO_GENERICO_NO_
INTERIOR, emisor.getDomicilioFiscal().getNoInterior());
}
.
.
.
domicilioFiscal.setAttribute(Constants.COMPROBANTE_DOMICILIO_GENERICO_MUN
ICIPIO, emisor.getDomicilioFiscal().getMunicipio());
domicilioFiscal.setAttribute(Constants.COMPROBANTE_DOMICILIO_GENERICO_EST
ADO, emisor.getDomicilioFiscal().getEstado());
.
.
.
//se agrega el domicilio fiscal al nodo emisor
emisorElement.addContent(domicilioFiscal);
//se agrega el nodo emisor al elemento comprobante
root.addContent(emisorElement);
}
public void generaReceptor(Receptor receptor) {
//se crea el nodo receptor
Element rec = new
Element(Constants.COMPROBANTE_RECEPTOR,Constants.COMPROBANTE_PREFIX_NAMES
PACE,Constants.COMPROBANTE_NAMESPACE_URI);
rec.setAttribute(Constants.COMPROBANTE_RFC,
receptor.getRfc().getRfc());
if(receptor.getNombre()!=null&&!receptor.getNombre().trim().equals("")){
rec.setAttribute(Constants.COMPROBANTE_NOMBRE,
receptor.getNombre());
}
//se crea el nodo domicilio
Element domicilio = new
Element(Constants.COMPROBANTE_DOMICILIO,Constants.COMPROBANTE_PREFIX_NAME
SPACE,Constants.COMPROBANTE_NAMESPACE_URI);
if(receptor.getDomicilio().getCalle()!=null&&!receptor.getDomicilio().get
Calle().trim().equals("")){
domicilio.setAttribute(Constants.COMPROBANTE_DOMICILIO_GENERICO_CALLE,
receptor.getDomicilio().getCalle());
}
if(receptor.getDomicilio().getNoExterior()!=null&&!receptor.getDomicilio(
).getNoExterior().trim().equals("")){
domicilio.setAttribute(Constants.COMPROBANTE_DOMICILIO_GENERICO_NO_EXTERI
OR, receptor.getDomicilio().getNoExterior());
}
.
.
.
//se agrega el domicilio al receptor
rec.addContent(domicilio);
//se agrega el nodo receptor al comprobante
root.addContent(rec);
}
public void generaConceptos(Conceptos concp) {
Element conceptos = new
Element(Constants.COMPROBANTE_CONCEPTOS,Constants.COMPROBANTE_PREFIX_NAME
SPACE,Constants.COMPROBANTE_NAMESPACE_URI);
for (Concepto con : concp) {
Element concepto = new
Element(Constants.COMPROBANTE_CONCEPTO,Constants.COMPROBANTE_PREFIX_NAMES
PACE,Constants.COMPROBANTE_NAMESPACE_URI);
concepto.setAttribute(Constants.COMPROBANTE_CONCEPTO_CANTIDAD,
con.getCantidad().toString());
if(con.getUnidad()!=null&&!con.getUnidad().trim().equals("")){
concepto.setAttribute(Constants.COMPROBANTE_CONCEPTO_UNIDAD,
con.getUnidad());
}
if(con.getNoIdentificacion()!=null&&!con.getNoIdentificacion().trim().equ
als("")){
concepto.setAttribute(Constants.COMPROBANTE_CONCEPTO_NO_IDENTIFICACION,
con.getNoIdentificacion());
}
concepto.setAttribute(Constants.COMPROBANTE_CONCEPTO_DESCRIPCION,
con.getDescripcion());
concepto.setAttribute(Constants.COMPROBANTE_CONCEPTO_VALOR_UNITARIO,
con.getValorUnitario().getImporte().toString());
concepto.setAttribute(Constants.COMPROBANTE_CONCEPTO_IMPORTE,
con.getImporte().getImporte().toString());
//checar los nodos opcionales de conceptos
InformacionAduanera,CuentaPredial,ComplementoConcepto,Parte
conceptos.addContent(concepto);
}
root.addContent(conceptos);
}
public void generaImpuestos(Impuestos imp){
//checar los elementos de impuestos
Element impuestos=new Element(Constants.COMPROBANTE_IMPUESTOS,
Constants.COMPROBANTE_PREFIX_NAMESPACE,Constants.COMPROBANTE_NAMESPACE_UR
I);
root.addContent(impuestos);
}
public boolean creaAndValidaXML(Comprobante comprobante){
boolean response=false;
generaRaiz(comprobante);
//Instancia La clase que da la salida XML al archivo
XMLOutputter outputter = new XMLOutputter();
//Formato XML Arreglado Con Identacion
Format formato = Format.getPrettyFormat();
outputter.setFormat(formato);
//Crea el archivo aunque no de manera logica
File archivoXml = new File("/home/eder/comprobante.xml");
try {
//se instancia la clase que va a escrbir el archivo en disco
Writer write = new FileWriter(archivoXml);
//se escribe el archivo en disco
outputter.output(getXml(),write);
} catch (IOException e) {
System.err.println(e);
}
//se instancia la clase que validara el XSD
SAXBuilder builder = new
SAXBuilder("org.apache.xerces.parsers.SAXParser", true);
builder.setFeature("http://apache.org/xml/features/validation/schema",
true);
builder.setFeature("http://apache.org/xml/features/validation/schema-
full-checking", true);
builder.setProperty("http://apache.org/xml/properties/schema/external-
schemaLocation", Constants.COMPROBANTE_SCHEMA_XSD);
builder.setValidation(true);
//se imprime el documento si se logro cumplir con el XSD
try {
Document document = builder.build(archivoXml);
outputter.output(document, System.out);
response=true;
} catch (JDOMException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return response;
}
/**
* @return the xml
*/
public Document getXml() {
return xml;
}
}
A continuación muestro las Constantes con sus valores que usamos
//Comprobante Fiscal Digital-START
public static final String COMPROBANTE_COMPROBANTE = "Comprobante";
public static final String COMPROBANTE_VERSION = "version";
public static final String COMPROBANTE_NUM_VERSION = "3.0";
public static final String COMPROBANTE_FOLIO = "folio";
public static final String COMPROBANTE_SERIE = "serie";
public static final String COMPROBANTE_FECHA = "fecha";
public static final String COMPROBANTE_SELLO = "sello";
public static final String COMPROBANTE_NO_CERTIFICADO =
"noCertificado";
public static final String COMPROBANTE_CERTIFICADO = "certificado";
public static final String COMPROBANTE_CONDICIONES_PAGO =
"condicionesDePago";
public static final String COMPROBANTE_SUBTOTAL = "subTotal";
public static final String COMPROBANTE_DESCUENTO = "descuento";
public static final String COMPROBANTE_MOTIVO_DESCUENTO =
"motivoDescuento";
public static final String COMPROBANTE_TIPO_CAMBIO = "TipoCambio";
public static final String COMPROBANTE_MONEDA = "Moneda";
public static final String COMPROBANTE_TOTAL = "total";
public static final String COMPROBANTE_NO_APROBACION =
"noAprobacion";
public static final String COMPROBANTE_ANO_APROBACION =
"anoAprobacion";
public static final String COMPROBANTE_FORMA_PAGO = "formaDePago";
public static final String COMPROBANTE_METODO_DE_PAGO =
"metodoDePago";
public static final String COMPROBANTE_TIPO_COMPROBANTE =
"tipoDeComprobante";
public static final Integer NUMERO_DECIMALES_IMPORTE = 6;
public static final String VERSION_COMPROBANTE_TRES = "3.0";
public static final String COMPROBANTE_RFC = "rfc";
public static final String COMPROBANTE_NOMBRE = "nombre";
//Emisor
public static final String COMPROBANTE_EMISOR = "Emisor";
//Receptor
public static final String COMPROBANTE_RECEPTOR = "Receptor";
//Domicilios
public static final String COMPROBANTE_DOMICILIO_FISCAL =
"DomicilioFiscal";
public static final String COMPROBANTE_DOMICILIO = "Domicilio";
//DomicilioGenerico
public static final String COMPROBANTE_DOMICILIO_GENERICO_CALLE =
"calle";
public static final String COMPROBANTE_DOMICILIO_GENERICO_NO_EXTERIOR
= "noExterior";
public static final String COMPROBANTE_DOMICILIO_GENERICO_NO_INTERIOR
= "noInterior";
public static final String COMPROBANTE_DOMICILIO_GENERICO_COLONIA =
"colonia";
public static final String COMPROBANTE_DOMICILIO_GENERICO_PAIS =
"pais";
public static final String
COMPROBANTE_DOMICILIO_GENERICO_CODIGO_POSTAL = "codigoPostal";
public static final String COMPROBANTE_DOMICILIO_GENERICO_ESTADO =
"estado";
public static final String COMPROBANTE_DOMICILIO_GENERICO_REFERENCIA
= "referencia";
public static final String COMPROBANTE_DOMICILIO_GENERICO_LOCALIDAD =
"localidad";
public static final String COMPROBANTE_DOMICILIO_GENERICO_MUNICIPIO =
"municipio";
//DomicilioGenerico
//Conceptos
public static final String COMPROBANTE_CONCEPTOS = "Conceptos";
public static final String COMPROBANTE_CONCEPTO = "Concepto";
public static final String COMPROBANTE_CONCEPTO_CANTIDAD =
"cantidad";
public static final String COMPROBANTE_CONCEPTO_UNIDAD = "unidad";
public static final String COMPROBANTE_CONCEPTO_NO_IDENTIFICACION =
"noIdentificacion";
public static final String COMPROBANTE_CONCEPTO_DESCRIPCION =
"descripcion";
public static final String COMPROBANTE_CONCEPTO_VALOR_UNITARIO =
"valorUnitario";
public static final String COMPROBANTE_CONCEPTO_IMPORTE = "importe";
public static final String COMPROBANTE_NAMESPACE_URI =
"http://www.sat.gob.mx/cfd/3";
//Impuestos
public static final String COMPROBANTE_IMPUESTOS = "Impuestos";
public static final String COMPROBANTE_IMPUESTOS_TOTAL_IMPUESTOS =
"totalImpuestosTrasladados";
//NameSpaces
public static final String COMPROBANTE_PREFIX = "xsi";
public static final String COMPROBANTE_PREFIX_NAMESPACE = "cfdi";
public static final String COMPROBANTE_URI_PREFIX =
"http://www.w3.org/2001/XMLSchema-instance";
public static final String COMPROBANTE_SCHEMA = "schemaLocation";
public static final String COMPROBANTE_SCHEMA_XSD =
"http://www.sat.gob.mx/cfd/3
http://www.sat.gob.mx/sitio_internet/cfd/3/cfdv3.xsd";
//TimbreFiscal-START http://www.sat.gob.mx/cfd/3
public static final String COMPROBANTE_TIMBRE_FISCAL =
"TimbreFiscalDigital";
public static final String VERSION_TIMBRE_FISCAL = "2.0";
public static final String COMPROBANTE_TIMBRE_FISCAL_UUID = "UUID";
public static final String COMPROBANTE_TIMBRE_FISCAL_FECHA_TIMBRADO =
"FechaTimbrado";
public static final String COMPROBANTE_TIMBRE_FISCAL_SELLO_CFD =
"selloCFD";
public static final String
COMPROBANTE_TIMBRE_FISCAL_NO_CERTIFICADO_SAT = "noCertificadoSAT";
public static final String COMPROBANTE_TIMBRE_FISCAL_SELLO_SAT =
"selloSAT";
//TimbreFiscal-END
//Encriptacion-START
public static final String ALGORITMO_DIGESTION_AND_CIFRADO =
"SHA1withRSA";
Algunas notas aclaratorias y recomendaciones:
El archivo no esta completo. Se omitieron ciertas partes que se considera son
repetitivas, al igual que explicar línea a línea el código seria muy “pesado”; por eso
mostramos el código usado con sus respectivos comentarios, con la esperanza que
sea entendible para quien lo lea.
Recomendamos el uso de Constantes para los Namespaces y para las demás
constantes.
Nótese el uso de la clase java SimpleDateFormat, la cual es usada para dar formato
a las fechas, siendo para ello el formato establecido por el SAT: ISO 1801, la cual
queda de esta manera: new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
Notese el método creaAndValidaXML, que es el método en el cual se genera el
archivo XML y se graba en el disco duro y al mismo tiempo se valida contra el
XSD proveido por el SAT
Nótese todas las validaciones hechas. Si el atributo es requerido se añade por default
a la generación del XML, si no lo es, se valida si esta rellenado ese atributo; si lo
está, se añade al XML y si no, se ignora.
En este paso se ha creado solamente el XML del Comprobante SIN
TimbreFiscalDigital debido a que el SAT provee 2 archivos XSD. Uno para el CFD
(Sin Timbre Fiscal Digital) y otro solo para el Timbre Fiscal Digital.
Fuente y sitios recomendados:
ftp://ftp2.sat.gob.mx/asistencia_servicio_ftp/publicaciones/solcedi/cfdv3.xsd - XSD
para el Comprobante SIN Timbre Fiscal Digital
ftp://ftp2.sat.gob.mx/asistencia_servicio_ftp/publicaciones/solcedi/TimbreFiscalDig
ital.xsd - XSD para el Timbre Fiscal Digital
PASO 5: Generar Cadena Original del Timbre Fiscal Digital
Este es el orden de los elementos que se integraran a la Cadena Original del Timbre Fiscal
Digital, tomado del Anexo 20. Recordar que la cadena debe estar codificada con UTF-8:
“La secuencia de formación será siempre en el orden que se expresa a continuación,
tomando en cuenta las reglas generales expresadas en el párrafo anterior.
a. Atributos del elemento raíz TimbreFiscalDigital
1. version
2. UUID
3. FechaTimbrado
4. selloCFD
5. noCertificadoSAT”
Ejemplo de cadena original de un timbre:
||1.0|ad662d33-6934-459c-a128-bdf0393e0f44|2001-12-
17T09:30:47Z|iYyIk1MtEPzTxY3h57kYJnEXNae9lvLMgAq3jGMePsDtEOF6XLWbrV2
GL/2TX00vP2+YsPN+5UmyRdzMLZGEfESiNQF9fotNbtA487dWnCf5pUu0ikVpgHvpY
7YoA4lB1D/JWc+zntkgW+Ig49WnlKyXi0LOlBOVuxckDb7EAx4=|12345678901234
567890||
“Nota: El atributo selloCFD será el sello previo del Comprobante Fiscal Digital, el sello del
timbre será guardado dentro del atributo selloSAT. Esta cadena original será sellada
utilizando el algoritmo de digestión SHA-1.”
Ahora, bien, hasta donde hemos logrado comprender, el Timbre Fiscal Digital se añadirá al
Comprobante, como un elemento del elemento Complemento (No ComplementoConcepto).
Los únicos atributos que deberán estar rellenados en el TimbreFiscalDigital son: “versión”,
y “selloCFD”. Los demás elementos serán rellenados por el SAT cuando haga la validación
de nuestro CFDI (se entiende que el CFDI se enviara al SAT por internet).
“El resultado de la validación de un CFDI, asignación de un folio fiscal e incorporación del
sello digital del SAT se entenderá como el Timbrado Fiscal Digital. El folio fiscal digital
será referido como el UUID. Para integrar el complemento TimbreFiscalDigital a un
comprobante fiscal digital por Internet, la estructura resultante deberá integrarse como un
nodo hijo del nodo Comprobante/Complemento/TimbreFiscalDigital. Adicional a su
inclusión, se deberá definir el namespace correspondiente dentro del nodo Comprobante,
así como referenciar la ubicación pública del esquema XSD correspondiente”.
Fuente y sitios recomendados:
Anexo 20 página 103
Anexo 20, pagina 104 – Explicación de los namespaces del Timbre Fiscal Digital
PASO 6: Generar y validar XML del Timbre Fiscal Digital
Una vez que el CFDI ha sido validado por el SAT y EL Timbre Fiscal Digital ha sido
rellenado con los campos de “uuid”, “selloSAT”, “fechaTimbrado” y “noCertificadoSAT”,
toca el momento de generar el XML del Timbre Fiscal Digital, el cual se generara y
validara de la misma forma que el del Comprobante.
Fuente y sitios recomendados:
ftp://ftp2.sat.gob.mx/asistencia_servicio_ftp/publicaciones/solcedi/TimbreFiscalDig
ital.xsd -XSD para el Timbre Fiscal Digital
PASO 7: Juntar (concatenar) el XML obtenido del Comprobante + XML del
TimbreFiscalDigital
Como ultimo paso, se juntara en un solo archivo el XML del Comprobante y el XML del
Timbre Fiscal Digital y se tendrá un solo archivo que es en si nuestra “Factura” o
“Comprobante Fiscal Digital por Internet”.
Fuente y sitios recomendados:
ftp://ftp2.sat.gob.mx/asistencia_servicio_ftp/publicaciones/solcedi/ejemplo1%20cfd
v3.xml – Ejemplo oficial del SAT de un CFDI
http://www.sat.gob.mx/sitio_internet/asistencia_contribuyente/principiantes/compro
bantes_fiscales/66_19430.html - Utilerías del SAT para los CFDI
OTROS Sobre el Uso de la Addenda:
http://www.sat.gob.mx/sitio_internet/e_sat/comprobantes_fiscales/15_6601.html