Uso de XSL para transformar la factura electronica XML

Contenido/contents:
Introducion
Cadena Original
Verificacion de sello
Impresion
Pagina anterior/Previous page

Introduccion
Este es un ejemplo para trabajar con la factura electronica (CFD) usando la linea de comandos.
Aqui en la empresa para el trabajo diario NO usamos este codigo, lo que tenemos en produccion es el programa satxmlsv32.php desarrollado en PHP usando la extension openssl.
Este conjunto de pasos me sirvio en su momento para verificar que lo prograame en PHP fue correcto.
Cadena Original
Antes aqui mostraba una plantilla XSLT que yo genere a mano, pero como el SAT ya tiene publicada en su portal la plantilla oficial retiro la mia y cambio el ejemplo a usar la oficial.
Les recomiendo dos cosas:
Descargen los archivos necesarios del sitio oficial del SAT y tenganlos en su servidor local, no intenten accesar el sitio del SAT para cada validacion, de por si ya esta lento ...
Modifiquenlo con cuidado, por ejemplo yo le cambie la version="2.0" a version "1.0" para que no mande warnings el PHP ...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
<xsl:stylesheet version="1.0"
                xmlns:cfdi="http://www.sat.gob.mx/cfd/3"
                xmlns:detallista="http://www.sat.gob.mx/detallista"
                xmlns:divisas="http://www.sat.gob.mx/divisas"
                xmlns:ecb="http://www.sat.gob.mx/ecb"
                xmlns:ecc="http://www.sat.gob.mx/ecc"
                xmlns:fn="http://www.w3.org/2005/xpath-functions"
                xmlns:iedu="http://www.sat.gob.mx/iedu"
                xmlns:implocal="http://www.sat.gob.mx/implocal"
                xmlns:leyendasFisc="http://www.sat.gob.mx/leyendasFiscales"
                xmlns:pfic="http://www.sat.gob.mx/pfic"
                xmlns:psgecfd="http://www.sat.gob.mx/psgecfd"
                xmlns:terceros="http://www.sat.gob.mx/terceros"
                xmlns:tpe="http://www.sat.gob.mx/TuristaPasajeroExtranjero"
                xmlns:ventavehiculos="http://www.sat.gob.mx/ventavehiculos"
                xmlns:xs="http://www.w3.org/2001/XMLSchema"
                xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <!-- Con el siguiente método se establece que la salida deberá ser en texto -->
    <!-- <xsl:output method="text" version="1.0" encoding="UTF-8" indent="no"/> -->
    <xsl:output encoding="UTF-8"
                indent="no"
                method="text"
                version="1.0" />
    <!--  -->
    <xsl:include href="utilerias.xslt" />
    <!--  -->
    <xsl:include href="Divisas.xslt" />
    <xsl:include href="TuristaPasajeroExtranjero.xslt" />
    <xsl:include href="cfdiregistrofiscal.xslt" />
    <xsl:include href="detallista.xslt" />
    <xsl:include href="donat11.xslt" />
    <xsl:include href="ecc.xslt" />
    <xsl:include href="iedu.xslt" />
    <xsl:include href="implocal.xslt" />
    <xsl:include href="leyendasFisc.xslt" />
    <xsl:include href="nomina11.xslt" />
    <xsl:include href="pfic.xslt" />
    <xsl:include href="aerolineas.xslt" />
    <xsl:include href="spei.xslt" />
    <xsl:include href="terceros11.xslt" />
    <xsl:include href="ventavehiculos.xslt" />
    <!-- Aquí iniciamos el procesamiento de la cadena original con su | inicial y el terminador || -->
    <xsl:template match="/">
        |
        <xsl:apply-templates select="/cfdi:Comprobante" />
        ||
    </xsl:template>
    <!-- Aquí iniciamos el procesamiento de los datos incluidos en el comprobante -->
    <xsl:template match="cfdi:Comprobante">
        <!-- Iniciamos el tratamiento de los atributos de comprobante -->
        <xsl:call-template name="Requerido">
            <xsl:with-param name="valor"
                            select="./@version" />
        </xsl:call-template>
        <xsl:call-template name="Requerido">
            <xsl:with-param name="valor"
                            select="./@fecha" />
        </xsl:call-template>
        <xsl:call-template name="Requerido">
            <xsl:with-param name="valor"
                            select="./@tipoDeComprobante" />
        </xsl:call-template>
        <xsl:call-template name="Requerido">
            <xsl:with-param name="valor"
                            select="./@formaDePago" />
        </xsl:call-template>
        <xsl:call-template name="Opcional">
            <xsl:with-param name="valor"
                            select="./@condicionesDePago" />
        </xsl:call-template>
        <xsl:call-template name="Requerido">
            <xsl:with-param name="valor"
                            select="./@subTotal" />
        </xsl:call-template>
        <xsl:call-template name="Opcional">
            <xsl:with-param name="valor"
                            select="./@descuento" />
        </xsl:call-template>
        <xsl:call-template name="Opcional">
            <xsl:with-param name="valor"
                            select="./@TipoCambio" />
        </xsl:call-template>
        <xsl:call-template name="Opcional">
            <xsl:with-param name="valor"
                            select="./@Moneda" />
        </xsl:call-template>
        <xsl:call-template name="Requerido">
            <xsl:with-param name="valor"
                            select="./@total" />
        </xsl:call-template>
        <xsl:call-template name="Requerido">
            <xsl:with-param name="valor"
                            select="./@metodoDePago" />
        </xsl:call-template>
        <xsl:call-template name="Requerido">
            <xsl:with-param name="valor"
                            select="./@LugarExpedicion" />
        </xsl:call-template>
        <xsl:call-template name="Opcional">
            <xsl:with-param name="valor"
                            select="./@NumCtaPago" />
        </xsl:call-template>
        <xsl:call-template name="Opcional">
            <xsl:with-param name="valor"
                            select="./@FolioFiscalOrig" />
        </xsl:call-template>
        <xsl:call-template name="Opcional">
            <xsl:with-param name="valor"
                            select="./@SerieFolioFiscalOrig" />
        </xsl:call-template>
        <xsl:call-template name="Opcional">
            <xsl:with-param name="valor"
                            select="./@FechaFolioFiscalOrig" />
        </xsl:call-template>
        <xsl:call-template name="Opcional">
            <xsl:with-param name="valor"
                            select="./@MontoFolioFiscalOrig" />
        </xsl:call-template>
        <!--  -->
        <xsl:apply-templates select="./cfdi:Emisor" />
        <xsl:apply-templates select="./cfdi:Receptor" />
        <xsl:apply-templates select="./cfdi:Conceptos" />
        <xsl:apply-templates select="./cfdi:Impuestos" />
        <xsl:apply-templates select="./cfdi:Complemento" />
    </xsl:template>
    <!-- Manejador de nodos tipo Emisor -->
    <xsl:template match="cfdi:Emisor">
        <!-- Iniciamos el tratamiento de los atributos del Emisor -->
        <xsl:call-template name="Requerido">
            <xsl:with-param name="valor"
                            select="./@rfc" />
        </xsl:call-template>
        <xsl:call-template name="Opcional">
            <xsl:with-param name="valor"
                            select="./@nombre" />
        </xsl:call-template>
        <!--  -->
        <xsl:apply-templates select="./cfdi:DomicilioFiscal" />
        <xsl:if test="./cfdi:ExpedidoEn">
            <xsl:call-template name="Domicilio">
                <xsl:with-param name="Nodo"
                                select="./cfdi:ExpedidoEn" />
            </xsl:call-template>
        </xsl:if>
        <xsl:for-each select="./cfdi:RegimenFiscal">
            <xsl:call-template name="Requerido">
                <xsl:with-param name="valor"
                                select="./@Regimen" />
            </xsl:call-template>
        </xsl:for-each>
    </xsl:template>
    <!-- Manejador de nodos tipo Receptor -->
    <xsl:template match="cfdi:Receptor">
        <!-- Iniciamos el tratamiento de los atributos del Receptor -->
        <xsl:call-template name="Requerido">
            <xsl:with-param name="valor"
                            select="./@rfc" />
        </xsl:call-template>
        <xsl:call-template name="Opcional">
            <xsl:with-param name="valor"
                            select="./@nombre" />
        </xsl:call-template>
        <!--  -->
        <xsl:if test="./cfdi:Domicilio">
            <xsl:call-template name="Domicilio">
                <xsl:with-param name="Nodo"
                                select="./cfdi:Domicilio" />
            </xsl:call-template>
        </xsl:if>
    </xsl:template>
    <!-- Manejador de nodos tipo Conceptos -->
    <xsl:template match="cfdi:Conceptos">
        <!-- Llamada para procesar los distintos nodos tipo Concepto -->
        <xsl:for-each select="./cfdi:Concepto">
            <xsl:apply-templates select="." />
        </xsl:for-each>
    </xsl:template>
    <!-- Manejador de nodos tipo Impuestos -->
    <xsl:template match="cfdi:Impuestos">
        <xsl:for-each select="./cfdi:Retenciones/cfdi:Retencion">
            <xsl:apply-templates select="." />
        </xsl:for-each>
        <xsl:call-template name="Opcional">
            <xsl:with-param name="valor"
                            select="./@totalImpuestosRetenidos" />
        </xsl:call-template>
        <xsl:for-each select="./cfdi:Traslados/cfdi:Traslado">
            <xsl:apply-templates select="." />
        </xsl:for-each>
        <xsl:call-template name="Opcional">
            <xsl:with-param name="valor"
                            select="./@totalImpuestosTrasladados" />
        </xsl:call-template>
    </xsl:template>
    <!-- Manejador de nodos tipo Retencion -->
    <xsl:template match="cfdi:Retencion">
        <xsl:call-template name="Requerido">
            <xsl:with-param name="valor"
                            select="./@impuesto" />
        </xsl:call-template>
        <xsl:call-template name="Requerido">
            <xsl:with-param name="valor"
                            select="./@importe" />
        </xsl:call-template>
    </xsl:template>
    <!-- Manejador de nodos tipo Traslado -->
    <xsl:template match="cfdi:Traslado">
        <xsl:call-template name="Requerido">
            <xsl:with-param name="valor"
                            select="./@impuesto" />
        </xsl:call-template>
        <xsl:call-template name="Requerido">
            <xsl:with-param name="valor"
                            select="./@tasa" />
        </xsl:call-template>
        <xsl:call-template name="Requerido">
            <xsl:with-param name="valor"
                            select="./@importe" />
        </xsl:call-template>
    </xsl:template>
    <!-- Manejador de nodos tipo Complemento -->
    <xsl:template match="cfdi:Complemento">
        <xsl:for-each select="./*">
            <xsl:apply-templates select="." />
        </xsl:for-each>
    </xsl:template>
    <!--  -->
    <xsl:template match="cfdi:Concepto">
        <!-- Iniciamos el tratamiento de los atributos del Concepto -->
        <xsl:call-template name="Requerido">
            <xsl:with-param name="valor"
                            select="./@cantidad" />
        </xsl:call-template>
        <xsl:call-template name="Requerido">
            <xsl:with-param name="valor"
                            select="./@unidad" />
        </xsl:call-template>
        <xsl:call-template name="Opcional">
            <xsl:with-param name="valor"
                            select="./@noIdentificacion" />
        </xsl:call-template>
        <xsl:call-template name="Requerido">
            <xsl:with-param name="valor"
                            select="./@descripcion" />
        </xsl:call-template>
        <xsl:call-template name="Requerido">
            <xsl:with-param name="valor"
                            select="./@valorUnitario" />
        </xsl:call-template>
        <xsl:call-template name="Requerido">
            <xsl:with-param name="valor"
                            select="./@importe" />
        </xsl:call-template>
        <!--  -->
        <xsl:for-each select=".//cfdi:InformacionAduanera">
            <xsl:apply-templates select="." />
        </xsl:for-each>
        <!-- Llamada al manejador de nodos de Cuenta Predial en caso de existir -->
        <xsl:if test="./cfdi:CuentaPredial">
            <xsl:apply-templates select="./cfdi:CuentaPredial" />
        </xsl:if>
        <!-- Llamada al manejador de nodos de ComplementoConcepto en caso de existir -->
        <xsl:if test="./cfdi:ComplementoConcepto">
            <xsl:apply-templates select="./cfdi:ComplementoConcepto" />
        </xsl:if>
    </xsl:template>
    <!-- Manejador de nodos tipo Información Aduanera -->
    <xsl:template match="cfdi:InformacionAduanera">
        <!-- Manejo de los atributos de la información aduanera -->
        <xsl:call-template name="Requerido">
            <xsl:with-param name="valor"
                            select="./@numero" />
        </xsl:call-template>
        <xsl:call-template name="Requerido">
            <xsl:with-param name="valor"
                            select="./@fecha" />
        </xsl:call-template>
        <xsl:call-template name="Opcional">
            <xsl:with-param name="valor"
                            select="./@aduana" />
        </xsl:call-template>
    </xsl:template>
    <!-- Manejador de nodos tipo Información CuentaPredial -->
    <xsl:template match="cfdi:CuentaPredial">
        <xsl:call-template name="Requerido">
            <xsl:with-param name="valor"
                            select="./@numero" />
        </xsl:call-template>
    </xsl:template>
    <!-- Manejador de nodos tipo ComplementoConcepto -->
    <xsl:template match="cfdi:ComplementoConcepto">
        <xsl:for-each select="./*">
            <xsl:apply-templates select="." />
        </xsl:for-each>
    </xsl:template>
    <!-- Manejador de nodos tipo Domicilio fiscal -->
    <xsl:template match="cfdi:DomicilioFiscal">
        <!-- Iniciamos el tratamiento de los atributos del Domicilio Fiscal -->
        <xsl:call-template name="Requerido">
            <xsl:with-param name="valor"
                            select="./@calle" />
        </xsl:call-template>
        <xsl:call-template name="Opcional">
            <xsl:with-param name="valor"
                            select="./@noExterior" />
        </xsl:call-template>
        <xsl:call-template name="Opcional">
            <xsl:with-param name="valor"
                            select="./@noInterior" />
        </xsl:call-template>
        <xsl:call-template name="Opcional">
            <xsl:with-param name="valor"
                            select="./@colonia" />
        </xsl:call-template>
        <xsl:call-template name="Opcional">
            <xsl:with-param name="valor"
                            select="./@localidad" />
        </xsl:call-template>
        <xsl:call-template name="Opcional">
            <xsl:with-param name="valor"
                            select="./@referencia" />
        </xsl:call-template>
        <xsl:call-template name="Requerido">
            <xsl:with-param name="valor"
                            select="./@municipio" />
        </xsl:call-template>
        <xsl:call-template name="Requerido">
            <xsl:with-param name="valor"
                            select="./@estado" />
        </xsl:call-template>
        <xsl:call-template name="Requerido">
            <xsl:with-param name="valor"
                            select="./@pais" />
        </xsl:call-template>
        <xsl:call-template name="Requerido">
            <xsl:with-param name="valor"
                            select="./@codigoPostal" />
        </xsl:call-template>
    </xsl:template>
    <!-- Manejador de nodos tipo Domicilio -->
    <xsl:template name="Domicilio">
        <xsl:param name="Nodo" />
        <!-- Iniciamos el tratamiento de los atributos del Domicilio -->
        <xsl:call-template name="Opcional">
            <xsl:with-param name="valor"
                            select="$Nodo/@calle" />
        </xsl:call-template>
        <xsl:call-template name="Opcional">
            <xsl:with-param name="valor"
                            select="$Nodo/@noExterior" />
        </xsl:call-template>
        <xsl:call-template name="Opcional">
            <xsl:with-param name="valor"
                            select="$Nodo/@noInterior" />
        </xsl:call-template>
        <xsl:call-template name="Opcional">
            <xsl:with-param name="valor"
                            select="$Nodo/@colonia" />
        </xsl:call-template>
        <xsl:call-template name="Opcional">
            <xsl:with-param name="valor"
                            select="$Nodo/@localidad" />
        </xsl:call-template>
        <xsl:call-template name="Opcional">
            <xsl:with-param name="valor"
                            select="$Nodo/@referencia" />
        </xsl:call-template>
        <xsl:call-template name="Opcional">
            <xsl:with-param name="valor"
                            select="$Nodo/@municipio" />
        </xsl:call-template>
        <xsl:call-template name="Opcional">
            <xsl:with-param name="valor"
                            select="$Nodo/@estado" />
        </xsl:call-template>
        <xsl:call-template name="Requerido">
            <xsl:with-param name="valor"
                            select="$Nodo/@pais" />
        </xsl:call-template>
        <xsl:call-template name="Opcional">
            <xsl:with-param name="valor"
                            select="$Nodo/@codigoPostal" />
        </xsl:call-template>
    </xsl:template>
</xsl:stylesheet>
Vamor a aplicar las reglas de transformacion anteriores al siguiente archivo XML.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
<cfdi:Comprobante LugarExpedicion="PARQUE INDUSTRIAL XALOSTOC 55348 XALOSTOC MEX."
                  certificado="MIIDWjCCAkKgAwIBAgIUMDAwMDExMDAwMDAyMDAwMDAxNTQwDQYJKoZIhvcNAQEFBQAwgcMxGTAXBgNVBAcTEENpdWRhZCBkZSBNZXhpY28xFTATBgNVBAgTDE1leGljbywgRC5GLjELMAkGA1UEBhMCTVgxGjAYBgNVBAMTEUFDIGRlIFBydWViYXMgU0FUMTYwNAYDVQQLFC1BZG1pbmlzdHJhY2nzbiBkZSBTZWd1cmlkYWQgZGUgbGEgSW5mb3JtYWNp824xLjAsBgNVBAoUJVNlcnZpY2lvIGRlIEFkbWluaXN0cmFjafNuIFRyaWJ1dGFyaWEwHhcNMDYwODA0MjIwNDI3WhcNMDgwODAzMjIwNDI3WjBvMSUwIwYDVQQtExxBQUEwMTAxMDFBQUEgLyBBQUFBMDEwMTAxQUFBMR4wHAYDVQQFExUgLyBBQUFBMDEwMTAxSERGUlhYMDExFTATBgNVBAoTDEFBQTAxMDEwMUFBQTEPMA0GA1UECxMGTWF0cml6MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDKRVP186zuDWHP9BDOGPAOfJaqBlKKaNN6FV0mkO6iyG7TlpWrO3IBRBX4lw5k5MEDBwLxFmRQJ68ZHkaPDdBfGi3SO6VA+rkt50tlH5bLcSycWDAkCJ7U72TWDypx69TcafQwpr2vrfXPRmEz/kie5vF0H3tVkVxn5WQ6YUAMeQIDAQABox0wGzAMBgNVHRMBAf8EAjAAMAsGA1UdDwQEAwID6DANBgkqhkiG9w0BAQUFAAOCAQEARQmA3gt+y5vZiFgHQV4XZbdyfHdTlmHV8VwIvHou/ghIfgp3CsazBvputFQlWEKkpYCbbYErFqH8/G4i5ZzxKqr68XT/otEU2YlkMqJA8I/KGVQkMMBQMtpYsI+txw4f1Y72q2x8OdcO7nTtMOpAlwtEIPppLadLE405K9bFMwbyX+NH/w8ZcEF4T3h9T7/5mrqjrssURxR6I8tWI/lXsqg7xMJ5mdnHC9gd89GrEU/BPM5HCVRomQ0FWpik5uMNW5PNUxpYbIAbOfM1sioCJpMhBFMLIhu4Q47C8WM8VqZzzwawDwQG2iOS0rL6d7D5F8SB7Li3zt9vbbMzBc5xGg=="
                  descuento="0"
                  fecha="2013-12-22T10:28:15"
                  folio="000001"
                  formaDePago="EL PAGO DE ESTA FACTURA (CONTRAPRESTACION) SE EFECTUARA EN UNA SOLA EXHIBICION, SI POR ALGUNA RAZON NO FUERA ASI, LO PAGARAN EN LAS PARCIALIDADES RESPECTIVAS"
                  metodoDePago="NO APLICA"
                  noCertificado="00001100000200000154"
                  sello="E0q9sxQ6IfmhONw+BuQPdyoYsMKZ7tqOSYObCg5vCU6IidFNgPnXSswHv34w9ZnWAbMiL687T+B3LvIsj+XYGjR70gtfkSD7buBGlVpgSildMgreVBydMLI/+SjbVlSiR21SYB52HWOcsc4A/43lIVhWcE5cpvIAgGVosjMvy58="
                  serie="FDXA"
                  subTotal="1"
                  tipoDeComprobante="ingreso"
                  total="1.16"
                  version="3.2"
                  xmlns:cfdi="http://www.sat.gob.mx/cfd/3"
                  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                  xsi:schemaLocation="http://www.sat.gob.mx/cfd/3 http://www.sat.gob.mx/sitio_internet/cfd/3/cfdv32.xsd">
    <cfdi:Emisor nombre="FABRICA DE JABON LA CORONA SA DE CV"
                 rfc="FJC780315E91">
        <cfdi:DomicilioFiscal calle="CARLOS B. ZETINA"
                              codigoPostal="55348"
                              colonia="PARQUE INDUSTRIAL XALOSTOC"
                              estado="MEXICO"
                              municipio="ECATEPEC DE MORELOS"
                              noExterior="80"
                              pais="MEXICO" />
        <cfdi:RegimenFiscal Regimen="REGIMEN GENERAL DE LEY DE PERSONAS MORALES" />
    </cfdi:Emisor>
    <cfdi:Receptor nombre="FABRICA DE JABON LA CORONA,S.A.C.V"
                   rfc="XAXX010101000">
        <cfdi:Domicilio calle="CARLOS B. ZETINA"
                        codigoPostal="55340"
                        colonia="FRACC.INDUSTRIAL XALOSTOC"
                        estado="ESTADO DE MEXICO"
                        localidad="ECATEPEC DE MORELOS"
                        municipio="ECATEPEC"
                        noExterior="80"
                        pais="MEXICO" />
    </cfdi:Receptor>
    <cfdi:Conceptos>
        <cfdi:Concepto cantidad="1"
                       descripcion="VARIOS"
                       importe="1.00"
                       unidad="Pieza"
                       valorUnitario="1" />
    </cfdi:Conceptos>
    <cfdi:Impuestos totalImpuestosTrasladados="0.16">
        <cfdi:Traslados>
            <cfdi:Traslado importe="0.16"
                           impuesto="IVA"
                           tasa="16.00" />
        </cfdi:Traslados>
    </cfdi:Impuestos>
</cfdi:Comprobante>
Para aplicar las reglas usamos el comando xsltproc dando como primer argumento el archivo con las reglas de transformacion XSLT y como segundo el nombre del archivo con la factura XML.
$ xsltproc cadenaoriginal_3_2.xslt fact.xml
||3.2|2013-12-22T10:28:15|ingreso|EL PAGO DE ESTA FACTURA (CONTRAPRESTACION) SE EFECTUARA EN UNA SOLA EXHIBICION, SI POR ALGUNA RAZON NO FUERA ASI, LO PAGARAN EN LAS PARCIALIDADES RESPECTIVAS|1|0|1.16|NO APLICA|PARQUE INDUSTRIAL XALOSTOC 55348 XALOSTOC MEX.|FJC780315E91|FABRICA DE JABON LA CORONA SA DE CV|CARLOS B. ZETINA|80|PARQUE INDUSTRIAL XALOSTOC|ECATEPEC DE MORELOS|MEXICO|MEXICO|55348|REGIMEN GENERAL DE LEY DE PERSONAS MORALES|XAXX010101000|FABRICA DE JABON LA CORONA,S.A.C.V|CARLOS B. ZETINA|80|FRACC.INDUSTRIAL XALOSTOC|ECATEPEC DE MORELOS|ECATEPEC|ESTADO DE MEXICO|MEXICO|55340|1|Pieza|VARIOS|1|1.00|IVA|16.00|0.16|0.16|| 
Ya con esa cadena original podemos aplicar las reglas para calcular el sello. El primer paso es calcular el hash sha1.
$ xsltproc cadenaoriginal_3_2.xslt fact.xml | sha1sum | cut -f1 -d \ 
2a4288d0232a09e7d633c307a07fca47f8e8544a
Este hash ya se puede sellar con el certificado (que ya tengo convertido en formato PEM) y ademas codificarlo en formato base64 porque el sello lo deja en binario.
Openssl tiene la facilidad de calcular el sha1 y de sellarlo en un mismo paso, por lo cual ya no hace falta calcular el sha1 en un paso previo.
$ xsltproc cadenaoriginal_3_2.xslt fact.xml | openssl dgst -sha1 -sign 00001100000200000154.key.pem | openssl enc -base64 -A
E0q9sxQ6IfmhONw+BuQPdyoYsMKZ7tqOSYObCg5vCU6IidFNgPnXSswHv34w9ZnWAbMiL687T+B3LvIsj+XYGjR70gtfkSD7buBGlVpgSildMgreVBydMLI/+SjbVlSiR21SYB52HWOcsc4A/43lIVhWcE5cpvIAgGVosjMvy58=
Si son observadores veran que el sello recien calculado es el mismo que esta almacenado en el atributo "sello" del XML. El cual fue calculado con la funcion satxmlsv32 de PHP.
Verificacion del sello
Ahora voy a simular lo que hay que hacer para validar que el sello es correcto, es decir, es el sello correspondiente a la factura firmada por el certificado correspondiente.
Este es una pequen˜a hoja de conversion para extraer el campo del sello.
1
2
3
4
5
6
7
8
<xsl:stylesheet version = '1.0'
    xmlns:xsl='http://www.w3.org/1999/XSL/Transform'
    xmlns:cfdi="http://www.sat.gob.mx/cfd/3">
<xsl:output method = "text" /> 
<xsl:template match="cfdi:Comprobante">
      <xsl:value-of select="@sello"/>
</xsl:template>
</xsl:stylesheet>
Para aplicar las reglas usamos el comando xsltproc dando como primer argumento el archivo con las reglas de transofrmacion XSL y como segundo el nombre del archivo con la factura XML.
$ xsltproc sello.xslt fact.xml
E0q9sxQ6IfmhONw+BuQPdyoYsMKZ7tqOSYObCg5vCU6IidFNgPnXSswHv34w9ZnWAbMiL687T+B3LvIsj+XYGjR70gtfkSD7buBGlVpgSildMgreVBydMLI/+SjbVlSiR21SYB52HWOcsc4A/43lIVhWcE5cpvIAgGVosjMvy58= 
Ahora este campo del sello obtenido del archivo XML lo de-codificamos del codigo base64 y lo dejamos nuevamente como el sello en binario y lo dejamos en un archivo temporal.
$ xsltproc sello.xslt fact.xml | openssl enc -base64 -d -A -out sello
Ahora si ya validamos con la llave publica que el sello corresponda con la cadena original y con la llave privada.
$ xsltproc cadenaoriginal_3_2.xslt fact.xml | openssl dgst -sha1 -verify 00001100000200000154.cer.pem -signature sello
Verified OK
  1. Con esto sabemos que el XML no ha sido modificado desde que se sello. (Integridad).
  2. El sello fue calculado con el poseedor de la llave privada. (Autenticidad)
  3. Al ser el emisor el unico con la llave privada no puede negar que el CFDI fue emitido por el (No repudio).
Generacion de archivo para impresion
Con el siguiente arhivo de transformacion genero un archivo HTML para consultar en un formato 'agradable al usuario' el contenido de la factura XML.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
<xsl:stylesheet version = '1.0'
    xmlns:xsl='http://www.w3.org/1999/XSL/Transform'
    xmlns:cfdi='http://www.sat.gob.mx/cfd/3'>
 
<xsl:output method = "html" /> 
 
<xsl:template match="//cfdi:Comprobante">
   <html>
   <head>
   <link rel="STYLESHEET" media="screen" type="text/css" href="factura.css"/>
   <title>Factura Electronica <xsl:value-of select="@serie"/><xsl:value-of select="@folio"/></title>
   </head>
   <body>
   <table width="100%" border="1">
      <tr><td colspan="2" align="right">
          <table border="1">
               <tr><th class="h1">Serie</th><td class="h1"><xsl:value-of select="@serie"/></td></tr>
               <tr><th class="h1">Folio</th><td class="h1"><xsl:value-of select="@folio"/></td></tr>
               <tr><th class="h1">Fecha</th><td class="h1"><xsl:value-of select="@fecha"/></td></tr>
           </table>
           </td>
           </tr>
      <tr><td width="50%">
           <table width="100%" border="1"><tr><th colspan="2" class="h1">Emisor</th></tr>
               <tr><th>RFC</th><td><xsl:value-of select="cfdi:Emisor/@rfc"/></td></tr>
               <tr><th>Nombre</th><td><xsl:value-of select="cfdi:Emisor/@nombre"/></td></tr>
                <xsl:apply-templates select="//cfdi:DomicilioFiscal"/>
 
             </table>
          </td>
          <td>
          <table width="100%" border="1"><tr><th colspan="2" class="h1">Receptor</th></tr>
             <tr><th>RFC</th><td><xsl:value-of select="cfdi:Receptor/@rfc"/></td></tr>
             <tr><th>Nombre</th><td><xsl:value-of select="cfdi:Receptor/@nombre"/></td></tr>
             <xsl:apply-templates select="//cfdi:Domicilio"/>
 
 
           </table>
           </td>
         </tr>
         <tr><table width="100%" border="1">
             <tr><th>Cantidad</th>
                 <th>Descripcion</th>
                 <th>Precio</th>
                 <th>Importe</th>
             </tr>
             <xsl:apply-templates select="//cfdi:Concepto"/>
             <xsl:for-each select="Concepto">
             </xsl:for-each>
 
             <xsl:apply-templates select="//cfdi:Traslado"/>
            </table>
         </tr>
        </table>
        <hr/>
        <table width="100%" border="1">
            <tr><th>Numero de serie del Certificado</th></tr>
            <tr><td><xsl:value-of select="@noCertificado"/></td></tr>
            <tr><th>Sello Digital</th></tr>
            <tr><td><small><small><xsl:value-of select="@sello"/></small></small></td></tr>
        </table>
        <center>
        Este documento es una impresion de un comprobante fiscal digital por Internet
        </center>
    </body>
    </html>
</xsl:template>
 
 
<xsl:template match="//cfdi:DomicilioFiscal">
    <tr><th colspan="2" class="h2">Domicilio</th></tr>
    <tr><td colspan="2"><xsl:value-of select="@calle"/> # <xsl:value-of select="@noExterior"/> - <xsl:value-of select="@noInterior"/></td></tr>
    <tr><td colspan="2"><xsl:value-of select="@colonia"/></td></tr>
    <tr><td colspan="2"><xsl:value-of select="@localidad"/></td></tr>
    <tr><td colspan="2"><xsl:value-of select="@referencia"/></td></tr>
    <tr><td colspan="2"><xsl:value-of select="@municipio"/>
    <xsl:if test="@codigoPostal"> CODIGO POSTAL <xsl:value-of select="@codigoPostal"/></xsl:if>
         </td></tr>
     <tr><td colspan="2"><xsl:value-of select="@estado"/></td></tr>
     <tr><td colspan="2"><xsl:value-of select="@pais"/></td></tr>
</xsl:template>
 
<xsl:template match="//cfdi:Domicilio">
    <tr><th colspan="2" class="h2">Domicilio</th></tr>
    <tr><td colspan="2"><xsl:value-of select="@calle"/> # <xsl:value-of select="@noExterior"/> - <xsl:value-of select="@noInterior"/></td></tr>
    <tr><td colspan="2"><xsl:value-of select="@colonia"/></td></tr>
    <tr><td colspan="2"><xsl:value-of select="@localidad"/></td></tr>
    <tr><td colspan="2"><xsl:value-of select="@referencia"/></td></tr>
    <tr><td colspan="2"><xsl:value-of select="@municipio"/>
        <xsl:if test="@codigoPostal"> CODIGO POSTAL <xsl:value-of select="@codigoPostal"/></xsl:if>
        </td></tr>
    <tr><td colspan="2"><xsl:value-of select="@estado"/></td></tr>
    <tr><td colspan="2"><xsl:value-of select="@pais"/></td></tr>
</xsl:template>
 
<xsl:template match="//cfdi:Concepto">
    <tr><td align="center"><xsl:value-of select="@cantidad"/></td>
        <td><xsl:value-of select="@descripcion"/></td>
        <td align="right"><xsl:value-of select="@valorUnitario"/></td>
        <td align="right"><xsl:value-of select="@importe"/></td>
    </tr>
</xsl:template>
 
<xsl:template match="//cfdi:Traslado">
    <tr><td colspan="2" align="right"><xsl:value-of select="@impuesto"/></td>
        <td align="right"><xsl:value-of select="@importe"/></td>
        <td><xsl:value-of select="@tasa"/> %</td>
    </tr>
</xsl:template>
 
</xsl:stylesheet>
Invocamos la regla de transformacion para generar el archivo HTML de salida y ya lo podemos consultar o mandar a imprimir.
xsltproc impresion.xslt fact.xml > fact.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
<html xmlns:cfdi="http://www.sat.gob.mx/cfd/3">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="STYLESHEET" media="screen" type="text/css" href="factura.css">
<title>Factura Electronica FDXA000001</title>
</head>
<body>
<table width="100%" border="1">
<tr><td colspan="2" align="right"><table border="1">
<tr>
<th class="h1">Serie</th>
<td class="h1">FDXA</td>
</tr>
<tr>
<th class="h1">Folio</th>
<td class="h1">000001</td>
</tr>
<tr>
<th class="h1">Fecha</th>
<td class="h1">2013-12-22T10:28:15</td>
</tr>
</table></td></tr>
<tr>
<td width="50%"><table width="100%" border="1">
<tr><th colspan="2" class="h1">Emisor</th></tr>
<tr>
<th>RFC</th>
<td>FJC780315E91</td>
</tr>
<tr>
<th>Nombre</th>
<td>FABRICA DE JABON LA CORONA SA DE CV</td>
</tr>
<tr><th colspan="2" class="h2">Domicilio</th></tr>
<tr><td colspan="2">CARLOS B. ZETINA # 80 - </td></tr>
<tr><td colspan="2">PARQUE INDUSTRIAL XALOSTOC</td></tr>
<tr><td colspan="2"></td></tr>
<tr><td colspan="2"></td></tr>
<tr><td colspan="2">ECATEPEC DE MORELOS CODIGO POSTAL 55348</td></tr>
<tr><td colspan="2">MEXICO</td></tr>
<tr><td colspan="2">MEXICO</td></tr>
</table></td>
<td><table width="100%" border="1">
<tr><th colspan="2" class="h1">Receptor</th></tr>
<tr>
<th>RFC</th>
<td>XAXX010101000</td>
</tr>
<tr>
<th>Nombre</th>
<td>FABRICA DE JABON LA CORONA,S.A.C.V</td>
</tr>
<tr><th colspan="2" class="h2">Domicilio</th></tr>
<tr><td colspan="2">CARLOS B. ZETINA # 80 - </td></tr>
<tr><td colspan="2">FRACC.INDUSTRIAL XALOSTOC</td></tr>
<tr><td colspan="2">ECATEPEC DE MORELOS</td></tr>
<tr><td colspan="2"></td></tr>
<tr><td colspan="2">ECATEPEC CODIGO POSTAL 55340</td></tr>
<tr><td colspan="2">ESTADO DE MEXICO</td></tr>
<tr><td colspan="2">MEXICO</td></tr>
</table></td>
</tr>
<tr><table width="100%" border="1">
<tr>
<th>Cantidad</th>
<th>Descripcion</th>
<th>Precio</th>
<th>Importe</th>
</tr>
<tr>
<td align="center">1</td>
<td>VARIOS</td>
<td align="right">1</td>
<td align="right">1.00</td>
</tr>
<tr>
<td colspan="2" align="right">IVA</td>
<td align="right">0.16</td>
<td>16.00 %</td>
</tr>
</table></tr>
</table>
<hr>
<table width="100%" border="1">
<tr><th>Numero de serie del Certificado</th></tr>
<tr><td>00001100000200000154</td></tr>
<tr><th>Sello Digital</th></tr>
<tr><td><small><small>E0q9sxQ6IfmhONw+BuQPdyoYsMKZ7tqOSYObCg5vCU6IidFNgPnXSswHv34w9ZnWAbMiL687T+B3LvIsj+XYGjR70gtfkSD7buBGlVpgSildMgreVBydMLI/+SjbVlSiR21SYB52HWOcsc4A/43lIVhWcE5cpvIAgGVosjMvy58=</small></small></td></tr>
</table>
<center>
        Este documento es una impresion de un comprobante fiscal digital por Internet
        </center>
</body>
</html>
Si desea ver como se ve archivo de consulta de la factura electronica, dele click aqui