node-sped-nfe 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +75 -0
  2. package/docs/README.md +49 -0
  3. package/docs/xml.md +1653 -0
  4. package/index.js +5 -0
  5. package/package.json +19 -0
  6. package/testes/assinar.js +16 -0
  7. package/testes/nfe.js +166 -0
  8. package/testes/nfe.json +194 -0
  9. package/testes/nfe.xml +292 -0
  10. package/testes/nfe_guara.xml +1 -0
  11. package/testes/nfe_guara_sign.xml +1 -0
  12. package/testes/nfe_guara_sign_lote.xml +1 -0
  13. package/testes/nfe_teste.json +45 -0
  14. package/utils/eventos.js +32 -0
  15. package/utils/make.js +798 -0
  16. package/utils/schemas/consReciNFe_v4.00.xsd +9 -0
  17. package/utils/schemas/consSitNFe_v4.00.xsd +9 -0
  18. package/utils/schemas/consStatServ_v4.00.xsd +9 -0
  19. package/utils/schemas/enviNFe_v4.00.xsd +9 -0
  20. package/utils/schemas/inutNFe_v4.00.xsd +9 -0
  21. package/utils/schemas/leiauteConsSitNFe_v4.00.xsd +502 -0
  22. package/utils/schemas/leiauteConsStatServ_v4.00.xsd +98 -0
  23. package/utils/schemas/leiauteInutNFe_v4.00.xsd +193 -0
  24. package/utils/schemas/leiauteNFe_v4.00.xsd +7412 -0
  25. package/utils/schemas/nfe_v4.00.xsd +9 -0
  26. package/utils/schemas/procInutNFe_v4.00.xsd +9 -0
  27. package/utils/schemas/procNFe_v4.00.xsd +9 -0
  28. package/utils/schemas/retConsReciNFe_v4.00.xsd +9 -0
  29. package/utils/schemas/retConsSitNFe_v4.00.xsd +9 -0
  30. package/utils/schemas/retConsStatServ_v4.00.xsd +9 -0
  31. package/utils/schemas/retEnviNFe_v4.00.xsd +9 -0
  32. package/utils/schemas/retInutNFe_v4.00.xsd +9 -0
  33. package/utils/schemas/tiposBasico_v4.00.xsd +598 -0
  34. package/utils/schemas/xmldsig-core-schema_v1.01.xsd +98 -0
  35. package/utils/sefaz.js +84 -0
  36. package/utils/tools.js +277 -0
  37. package/utils/xmllint.js +244195 -0
@@ -0,0 +1,98 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!-- ***************************************************-->
3
+ <!-- *** Schema específico para assinaturas XML ***-->
4
+ <!-- *** a partir de certificados do padrão (X509) ***-->
5
+ <!-- *** ICP-Brasil - Projeto Nota Fiscal Eletrônica ***-->
6
+ <!-- ***************************************************-->
7
+ <!-- Schema for XML Signatures-->
8
+ <schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" targetNamespace="http://www.w3.org/2000/09/xmldsig#" elementFormDefault="qualified" attributeFormDefault="unqualified" version="0.1">
9
+ <element name="Signature" type="ds:SignatureType"/>
10
+ <complexType name="SignatureType">
11
+ <sequence>
12
+ <element name="SignedInfo" type="ds:SignedInfoType"/>
13
+ <element name="SignatureValue" type="ds:SignatureValueType"/>
14
+ <element name="KeyInfo" type="ds:KeyInfoType"/>
15
+ </sequence>
16
+ <attribute name="Id" type="ID" use="optional"/>
17
+ </complexType>
18
+ <complexType name="SignatureValueType">
19
+ <simpleContent>
20
+ <extension base="base64Binary">
21
+ <attribute name="Id" type="ID" use="optional"/>
22
+ </extension>
23
+ </simpleContent>
24
+ </complexType>
25
+ <complexType name="SignedInfoType">
26
+ <sequence>
27
+ <element name="CanonicalizationMethod">
28
+ <complexType>
29
+ <attribute name="Algorithm" type="anyURI" use="required" fixed="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
30
+ </complexType>
31
+ </element>
32
+ <element name="SignatureMethod">
33
+ <complexType>
34
+ <attribute name="Algorithm" type="anyURI" use="required" fixed="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
35
+ </complexType>
36
+ </element>
37
+ <element name="Reference" type="ds:ReferenceType"/>
38
+ </sequence>
39
+ <attribute name="Id" type="ID" use="optional"/>
40
+ </complexType>
41
+ <complexType name="ReferenceType">
42
+ <sequence>
43
+ <element name="Transforms" type="ds:TransformsType">
44
+ <!-- Garante a unicidade do atributo -->
45
+ <unique name="unique_Transf_Alg">
46
+ <selector xpath="./*"/>
47
+ <field xpath="@Algorithm"/>
48
+ </unique>
49
+ </element>
50
+ <element name="DigestMethod">
51
+ <complexType>
52
+ <attribute name="Algorithm" type="anyURI" use="required" fixed="http://www.w3.org/2000/09/xmldsig#sha1"/>
53
+ </complexType>
54
+ </element>
55
+ <element name="DigestValue" type="ds:DigestValueType"/>
56
+ </sequence>
57
+ <attribute name="Id" type="ID" use="optional"/>
58
+ <attribute name="URI" use="required">
59
+ <simpleType>
60
+ <restriction base="anyURI">
61
+ <minLength value="2"/>
62
+ </restriction>
63
+ </simpleType>
64
+ </attribute>
65
+ <attribute name="Type" type="anyURI" use="optional"/>
66
+ </complexType>
67
+ <complexType name="TransformsType">
68
+ <sequence>
69
+ <element name="Transform" type="ds:TransformType" minOccurs="2" maxOccurs="2"/>
70
+ </sequence>
71
+ </complexType>
72
+ <complexType name="TransformType">
73
+ <sequence minOccurs="0" maxOccurs="unbounded">
74
+ <element name="XPath" type="string"/>
75
+ </sequence>
76
+ <attribute name="Algorithm" type="ds:TTransformURI" use="required"/>
77
+ </complexType>
78
+ <complexType name="KeyInfoType">
79
+ <sequence>
80
+ <element name="X509Data" type="ds:X509DataType"/>
81
+ </sequence>
82
+ <attribute name="Id" type="ID" use="optional"/>
83
+ </complexType>
84
+ <complexType name="X509DataType">
85
+ <sequence>
86
+ <element name="X509Certificate" type="base64Binary"/>
87
+ </sequence>
88
+ </complexType>
89
+ <simpleType name="DigestValueType">
90
+ <restriction base="base64Binary"/>
91
+ </simpleType>
92
+ <simpleType name="TTransformURI">
93
+ <restriction base="anyURI">
94
+ <enumeration value="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
95
+ <enumeration value="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
96
+ </restriction>
97
+ </simpleType>
98
+ </schema>
package/utils/sefaz.js ADDED
@@ -0,0 +1,84 @@
1
+ 'use strict'
2
+ import axios from "axios"
3
+ import https from "https"
4
+
5
+
6
+ class Instance {
7
+ constructor(opts) {
8
+ const { baseURL, ca, cert, key } = opts
9
+
10
+ const AgentOptions = Object.assign(
11
+ {
12
+ cert: cert,
13
+ key: key,
14
+ ca: ca,
15
+ },
16
+ { ...opts.httpsOptions }
17
+ )
18
+
19
+ const httpsAgent = new https.Agent(AgentOptions)
20
+
21
+ const requestOptions = Object.assign(
22
+ {
23
+ baseURL: baseURL,
24
+ headers: {
25
+ 'User-Agent': `node-nfe/1.0`,
26
+ 'Content-Type': 'application/soap+xml; charset=utf-8',
27
+ },
28
+ httpsAgent: httpsAgent,
29
+ timeout: 60000,
30
+ },
31
+ { ...opts.requestOptions }
32
+ )
33
+
34
+ const instance = axios.create({
35
+ ...requestOptions,
36
+ })
37
+
38
+ this.instance = instance
39
+ }
40
+
41
+ /**
42
+ * @returns {Promise<{status: number, data: string}>}
43
+ */
44
+ async request(config) {
45
+ try {
46
+ const response = await this.instance(config)
47
+
48
+ const { status, data } = response
49
+
50
+ return { status, data }
51
+ } catch (error) {
52
+ if (error.response) {
53
+ const { status, data } = error.response
54
+
55
+ return { status, data }
56
+ } else if (error.request) {
57
+ if (error.code === 'ECONNABORTED') {
58
+ const retorno = {
59
+ status: 504,
60
+ data: `<error>${error.message || error}</error>`,
61
+ }
62
+
63
+ return retorno
64
+ }
65
+
66
+ const retorno = {
67
+ status: 502,
68
+ data: `<error>${error.message || error}</error>`,
69
+ }
70
+
71
+ return retorno
72
+ } else {
73
+ const retorno = {
74
+ status: 500,
75
+ data: `<error>${error.message || error}</error>`,
76
+ }
77
+
78
+ return retorno
79
+ }
80
+ }
81
+ }
82
+ }
83
+
84
+ module.exports = Instance
package/utils/tools.js ADDED
@@ -0,0 +1,277 @@
1
+ import { XMLParser, XMLBuilder, XMLValidator } from "fast-xml-parser";
2
+ import pem from "pem";
3
+ import https from "https";
4
+ import { spawnSync } from "child_process"
5
+ import tmp from "tmp"
6
+ import crypto from "crypto";
7
+ import { urlServicos } from "./eventos.js"
8
+ import fs from "fs"
9
+
10
+
11
+ class Tools {
12
+ #cert = new Object();
13
+ #xmlTools = {
14
+ XMLParser: null,
15
+ XMLBuilder: null,
16
+ }
17
+ #pem = null;
18
+ #config = null;
19
+
20
+ constructor(config = { mod: "", xmllint: 'xmllint' }, certificado = { pfx: "", senha: "" }) {
21
+ //Configurar certificado
22
+ this.#config = config;
23
+ this.#cert = certificado;
24
+ this.#xmlTools.XMLBuilder = new XMLBuilder({
25
+ ignoreAttributes: false,
26
+ attributeNamePrefix: "@",
27
+ parseTagValue: false, // Evita conversão automática de valores
28
+ parseNodeValue: false, // Mantém valores como strings
29
+ });
30
+ this.#xmlTools.XMLParser = new XMLParser({
31
+ ignoreAttributes: false,
32
+ attributeNamePrefix: "@",
33
+ parseTagValue: false, // Evita conversão automática de valores
34
+ parseNodeValue: false, // Mantém valores como strings
35
+ });
36
+ }
37
+
38
+ sefazEnviaLote(xml = [], data = { idLote: 1, indSinc: 0, compactar: false }) {
39
+ return new Promise(async (resvol, reject) => {
40
+ if (typeof data.idLote == "undefined") data.idLote = 1;
41
+ if (typeof data.indSinc == "undefined") data.indSinc = 0;
42
+ if (typeof data.compactar == "undefined") data.compactar = false;
43
+
44
+ await this.#certTools();
45
+ let xmlLote = {
46
+ "soap:Envelope": {
47
+ "@xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance",
48
+ "@xmlns:xsd": "http://www.w3.org/2001/XMLSchema",
49
+ "@xmlns:soap": "http://www.w3.org/2003/05/soap-envelope",
50
+ "soap:Body": {
51
+ "nfeDadosMsg": {
52
+ "@xmlns": "http://www.portalfiscal.inf.br/nfe/wsdl/NFeAutorizacao4",
53
+ "enviNFe": {
54
+ ...{
55
+ "@xmlns": "http://www.portalfiscal.inf.br/nfe",
56
+ "@versao": "4.00",
57
+ "idLote": data.idLote,
58
+ "indSinc": data.indSinc,
59
+ },
60
+ ...(await this.xml2json(xml))
61
+ }
62
+ }
63
+ }
64
+ },
65
+ }
66
+ xmlLote = await this.json2xml(xmlLote);
67
+ fs.writeFileSync("testes/nfe_guara_sign_lote.xml", xmlLote, { encoding: "utf8" });
68
+ try {
69
+ const req = https.request(urlServicos[`${this.#config.cUF}`][`mod_${this.#config.mod}`].NFeAutorizacao[(this.#config.tpAmb == 1 ? "producao" : "homologacao")], {
70
+ ...{
71
+ method: 'POST',
72
+ headers: {
73
+ 'Content-Type': 'application/soap+xml; charset=utf-8',
74
+ 'Content-Length': xmlLote.length,
75
+ },
76
+ rejectUnauthorized: false
77
+ },
78
+ ...this.#pem
79
+ }, (res) => {
80
+ let data = '';
81
+
82
+ res.on('data', (chunk) => {
83
+ data += chunk;
84
+ });
85
+
86
+ res.on('end', () => {
87
+ resvol(data);
88
+ });
89
+ });
90
+
91
+ req.on('error', (erro) => {
92
+ reject(erro);
93
+ });
94
+ req.write(xmlLote);
95
+ req.end();
96
+ } catch (erro) {
97
+ reject(erro);
98
+ }
99
+ })
100
+ }
101
+
102
+ async xmlSign(xml, data = {}) {
103
+ return new Promise(async (resvol, reject) => {
104
+ if (data.tag === undefined) data.tag = "infNFe";
105
+
106
+ //Obter a tag que ira ser assinada
107
+ xml = await this.xml2json(xml);
108
+ let pem = await this.getCertificado();
109
+ const sign = crypto.createSign('RSA-SHA1'); // Correção: Alterado para RSA-SHA1
110
+ let signedInfo = {
111
+ "SignedInfo": {
112
+ "@xmlns": "http://www.w3.org/2000/09/xmldsig#",
113
+ "CanonicalizationMethod": {
114
+ "@Algorithm": "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"
115
+ },
116
+ "SignatureMethod": {
117
+ "@Algorithm": "http://www.w3.org/2000/09/xmldsig#rsa-sha1" // Mantém SHA1
118
+ },
119
+ "Reference": {
120
+ "@URI": `#${xml.NFe.infNFe['@Id']}`,
121
+ "Transforms": {
122
+ "Transform": [
123
+ {
124
+ "@Algorithm": "http://www.w3.org/2000/09/xmldsig#enveloped-signature"
125
+ },
126
+ {
127
+ "@Algorithm": "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"
128
+ },
129
+ ]
130
+ },
131
+ "DigestMethod": {
132
+ "@Algorithm": "http://www.w3.org/2000/09/xmldsig#sha1" // Mantém SHA1
133
+ },
134
+ "DigestValue": crypto.createHash('sha1').update(await this.json2xml({ infNFe: xml.NFe.infNFe }), 'utf8') // Mantém Hash SHA1
135
+ .digest('base64')
136
+
137
+ }
138
+ }
139
+ }
140
+ sign.update(await this.json2xml(signedInfo), 'utf8');
141
+ xml.NFe.Signature = {
142
+ ...signedInfo,
143
+ ...{
144
+ "SignatureValue": sign.sign(pem.key, 'base64'),
145
+ "KeyInfo": {
146
+ "X509Data": {
147
+ "X509Certificate": pem.cert.replace(/-----BEGIN CERTIFICATE-----/g, '').replace(/-----END CERTIFICATE-----/g, '').replace(/\r\n/g, '')
148
+ }
149
+ },
150
+ "@xmlns": "http://www.w3.org/2000/09/xmldsig#"
151
+ }
152
+ }
153
+ this.json2xml(xml).then(async res => {
154
+ this.#xmlValido(res);
155
+ resvol(res);
156
+ }).catch(err => {
157
+ reject(err)
158
+ })
159
+ })
160
+ }
161
+
162
+ async xml2json(xml) {
163
+ return new Promise((resvol, reject) => {
164
+ resvol(this.#xmlTools.XMLParser.parse(xml))
165
+ })
166
+ }
167
+
168
+ async json2xml(obj) {
169
+ return new Promise((resvol, reject) => {
170
+ resvol(this.#xmlTools.XMLBuilder.build(obj))
171
+ })
172
+ }
173
+
174
+ async getCertificado() {
175
+ return new Promise(async (resvol, reject) => {
176
+ await this.#certTools().then(resvol).catch(reject)
177
+ })
178
+ }
179
+
180
+ //Consulta status sefaz
181
+ async sefazStatus() {
182
+ return new Promise(async (resvol, reject) => {
183
+ await this.#certTools();
184
+
185
+ if (typeof this.#config.cUF == "undefined") throw "sefazStatus({...cUF}) -> não definido!";
186
+ if (typeof this.#config.tpAmb == "undefined") throw "sefazStatus({...tpAmb}) -> não definido!";
187
+ if (typeof this.#config.mod == "undefined") throw "sefazStatus({...mod}) -> não definido!";
188
+
189
+ let xml = {
190
+ "soap:Envelope": {
191
+ "@xmlns:soap": "http://www.w3.org/2003/05/soap-envelope",
192
+ "@xmlns:nfe": "http://www.portalfiscal.inf.br/nfe/wsdl/NFeStatusServico4",
193
+ "soap:Body": {
194
+ "nfe:nfeDadosMsg": {
195
+ "consStatServ": {
196
+ "@versao": "4.00",
197
+ "@xmlns": "http://www.portalfiscal.inf.br/nfe",
198
+ "tpAmb": this.#config.tpAmb,
199
+ "cUF": this.#config.cUF,
200
+ "xServ": "STATUS"
201
+ }
202
+ }
203
+ }
204
+ }
205
+ }
206
+
207
+ try {
208
+ let tempBuild = new XMLBuilder({
209
+ ignoreAttributes: false,
210
+ attributeNamePrefix: "@"
211
+ });
212
+ xml = tempBuild.build(xml);
213
+
214
+ const req = https.request(urlServicos[`${this.#config.cUF}`][`mod_${this.#config.mod}`].NfeStatusServico[(this.#config.tpAmb == 1 ? "producao" : "homologacao")], {
215
+ ...{
216
+ method: 'POST',
217
+ headers: {
218
+ 'Content-Type': 'application/soap+xml; charset=utf-8',
219
+ 'Content-Length': xml.length,
220
+ },
221
+ rejectUnauthorized: false
222
+ },
223
+ ...this.#pem
224
+ }, (res) => {
225
+ let data = '';
226
+
227
+ res.on('data', (chunk) => {
228
+ data += chunk;
229
+ });
230
+
231
+ res.on('end', () => {
232
+ resvol(data);
233
+ });
234
+ });
235
+
236
+ req.on('error', (erro) => {
237
+ reject(erro);
238
+ });
239
+
240
+ req.write(xml);
241
+ req.end();
242
+ } catch (erro) {
243
+ reject(erro);
244
+ }
245
+ })
246
+ }
247
+
248
+
249
+ //Validar XML da NFe, somente apos assinar
250
+ async #xmlValido(xml) {
251
+ let xmlFile = tmp.fileSync({ mode: 0o644, prefix: 'xml-', postfix: '.xml' });
252
+ fs.writeFileSync(xmlFile.name, xml, { encoding: "utf8" });
253
+ let verif = spawnSync(this.#config.xmllint, ['--noout', '--schema', `./utils/schemas/nfe_v4.00.xsd`, xmlFile.name], { encoding: 'utf8' });
254
+ xmlFile.removeCallback();
255
+
256
+ if (verif.errno == -4058) {
257
+ //xmllint - não instalado ou informado path
258
+ throw ("Biblioteca xmllint não encontrado!")
259
+ } else if (!verif.stderr.includes(".xml validates")) {
260
+ throw (verif.stderr)
261
+ }
262
+ return 1;
263
+ }
264
+
265
+
266
+ //Extrair dados do certificado pem
267
+ #certTools() {
268
+ return new Promise(async (resvol, reject) => {
269
+ if (this.#pem != null) resvol(this.#pem);
270
+ pem.readPkcs12(this.#cert.pfx, { p12Password: this.#cert.senha }, (err, myPem) => {
271
+ this.#pem = myPem;
272
+ resvol(myPem);
273
+ });
274
+ })
275
+ }
276
+ }
277
+ export { Tools }