UNPKG

node-sped-nfe

Version:

Seja bem-vindo(a) à **Biblioteca de Emissão de NF-e** — sua parceira definitiva para integrar **emissão de Nota Fiscal Eletrônica modelo 55 (NF-e)** e **modelo 65 (NFC-e)** em aplicações modernas, com simplicidade, robustez e total conformidade com a legi

685 lines (684 loc) 34.9 kB
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) { if (kind === "m") throw new TypeError("Private method is not writable"); if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it"); return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value; }; var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) { if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter"); if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it"); return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver); }; var _Tools_instances, _Tools_cert, _Tools_pem, _Tools_config, _Tools_getSignature, _Tools_gerarQRCodeNFCe, _Tools_descEvento, _Tools_xmlValido, _Tools_certTools, _Tools_limparSoap; import { XMLBuilder } from "fast-xml-parser"; import https from "https"; import { spawnSync } from "child_process"; import tmp from "tmp"; import crypto from "crypto"; import { urlEventos } from "./eventos.js"; import fs from "fs"; import path from 'path'; import { fileURLToPath } from 'url'; import pem from 'pem'; import { cUF2UF, json2xml, xml2json, formatData, UF2cUF } from "./extras.js"; import { SignedXml } from 'xml-crypto'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); class Tools { constructor(config = { mod: "", xmllint: 'xmllint', UF: '', tpAmb: 2, CSC: "", CSCid: "", versao: "4.00", timeout: 30, openssl: null, CPF: "", CNPJ: "" }, certificado = { pfx: "", senha: "" }) { _Tools_instances.add(this); _Tools_cert.set(this, void 0); _Tools_pem.set(this, { key: "", // A chave privada extraída do PKCS#12, em formato PEM cert: "", // O certificado extraído, em formato PEM ca: [] // Uma lista de certificados da cadeia (se houver), ou null }); _Tools_config.set(this, void 0); if (typeof config != "object") throw "Tools({config},{}): Config deve ser um objecto!"; if (typeof config.UF == "undefined") throw "Tools({...,UF:?},{}): UF não definida!"; if (typeof config.tpAmb == "undefined") throw "Tools({...,tpAmb:?},{}): tpAmb não definida!"; if (typeof config.versao == "undefined") throw "Tools({...,versao:?},{}): versao não definida!"; //Default do sistema if (typeof config.timeout == "undefined") config.timeout = 30; if (typeof config.xmllint == "undefined") config.xmllint = 'xmllint'; if (typeof config.openssl == "undefined") config.openssl = null; //Configurar certificado __classPrivateFieldSet(this, _Tools_config, config, "f"); __classPrivateFieldSet(this, _Tools_cert, certificado, "f"); } sefazEnviaLote(xml, data = { idLote: 1, indSinc: 0, compactar: false }) { return new Promise(async (resolve, reject) => { if (typeof data.idLote == "undefined") data.idLote = 1; if (typeof data.indSinc == "undefined") data.indSinc = 0; if (typeof data.compactar == "undefined") data.compactar = false; await __classPrivateFieldGet(this, _Tools_instances, "m", _Tools_certTools).call(this); let jsonXmlLote = { "soap:Envelope": { "@xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance", "@xmlns:xsd": "http://www.w3.org/2001/XMLSchema", "@xmlns:soap": "http://www.w3.org/2003/05/soap-envelope", "soap:Body": { "nfeDadosMsg": { "@xmlns": "http://www.portalfiscal.inf.br/nfe/wsdl/NFeAutorizacao4", "enviNFe": { ...{ "@xmlns": "http://www.portalfiscal.inf.br/nfe", "@versao": "4.00", "idLote": data.idLote, "indSinc": data.indSinc, }, ...(await this.xml2json(xml)) } } } }, }; let xmlLote = await this.json2xml(jsonXmlLote); try { let tempUF = urlEventos(__classPrivateFieldGet(this, _Tools_config, "f").UF, __classPrivateFieldGet(this, _Tools_config, "f").versao); const req = https.request(tempUF[`mod${__classPrivateFieldGet(this, _Tools_config, "f").mod}`][(__classPrivateFieldGet(this, _Tools_config, "f").tpAmb == 1 ? "producao" : "homologacao")].NFeAutorizacao, { ...{ method: 'POST', headers: { 'Content-Type': 'application/soap+xml; charset=utf-8', 'Content-Length': xmlLote.length, }, rejectUnauthorized: false }, ...await __classPrivateFieldGet(this, _Tools_instances, "m", _Tools_certTools).call(this) }, (res) => { let data = ''; res.on('data', (chunk) => { data += chunk; }); res.on('end', async () => { try { resolve(await __classPrivateFieldGet(this, _Tools_instances, "m", _Tools_limparSoap).call(this, data)); } catch (error) { resolve(data); } }); }); req.setTimeout(__classPrivateFieldGet(this, _Tools_config, "f").timeout * 1000, () => { reject({ name: 'TimeoutError', message: 'The operation was aborted due to timeout' }); req.destroy(); // cancela a requisição }); req.on('error', (erro) => { reject(erro); }); req.write(xmlLote); req.end(); } catch (erro) { reject(erro); } }); } async xmlSign(xmlJSON, data = { tag: "infNFe" }) { return new Promise(async (resvol, reject) => { if (data.tag === undefined) data.tag = "infNFe"; var xml = await this.xml2json(xmlJSON); if (data.tag == "infNFe") { if (xml.NFe.infNFe.ide.mod * 1 == 65) { xml.NFe.infNFeSupl.qrCode = __classPrivateFieldGet(this, _Tools_instances, "m", _Tools_gerarQRCodeNFCe).call(this, xml.NFe, "2", __classPrivateFieldGet(this, _Tools_config, "f").CSCid, __classPrivateFieldGet(this, _Tools_config, "f").CSC); xmlJSON = await json2xml(xml); } xml.NFe = { ...xml.NFe, ...await xml2json(await __classPrivateFieldGet(this, _Tools_instances, "m", _Tools_getSignature).call(this, xmlJSON, data.tag)) }; } else if (data.tag == "infEvento") { xml.envEvento.evento = { ...xml.envEvento.evento, ...(await xml2json(await __classPrivateFieldGet(this, _Tools_instances, "m", _Tools_getSignature).call(this, xmlJSON, data.tag))) }; } resvol(await json2xml(xml)); }); } async xml2json(xml) { return new Promise((resvol, reject) => { xml2json(xml).then(resvol).catch(reject); }); } async json2xml(obj) { return new Promise((resvol, reject) => { json2xml(obj).then(resvol).catch(reject); }); } //Obter certificado async getCertificado() { return new Promise(async (resvol, reject) => { __classPrivateFieldGet(this, _Tools_instances, "m", _Tools_certTools).call(this).then(resvol).catch(reject); }); } //Consulta NFe consultarNFe(chNFe) { return new Promise(async (resolve, reject) => { if (!chNFe || chNFe.length !== 44) { return reject("consultarNFe(chNFe) -> chave inválida!"); } let cUF = `${chNFe}`.substring(0, 2); let UF = cUF2UF[cUF]; let mod = `${chNFe}`.substring(20, 22); //https://www.nfe.fazenda.gov.br/portal/webservices.aspx?AspxAutoDetectCookieSupport=1 if (['AC', 'ES', 'RN', 'PB', 'SC'].includes(UF)) UF = 'SVRS'; if (typeof __classPrivateFieldGet(this, _Tools_config, "f").tpAmb === "undefined") throw "consultarNFe({...tpAmb}) -> não definido!"; let consSitNFe = { "@xmlns": "http://www.portalfiscal.inf.br/nfe", "@versao": "4.00", "tpAmb": __classPrivateFieldGet(this, _Tools_config, "f").tpAmb, "xServ": "CONSULTAR", "chNFe": chNFe }; let xmlObj = { "soap:Envelope": { "@xmlns:soap": "http://www.w3.org/2003/05/soap-envelope", "@xmlns:nfe": "http://www.portalfiscal.inf.br/nfe/wsdl/NFeConsultaProtocolo4", "soap:Body": { "nfe:nfeDadosMsg": { "consSitNFe": consSitNFe } } } }; try { const builder = new XMLBuilder({ ignoreAttributes: false, attributeNamePrefix: "@" }); // Validação do XML interno (opcional) await __classPrivateFieldGet(this, _Tools_instances, "m", _Tools_xmlValido).call(this, builder.build({ consSitNFe }), `consSitNFe_v${__classPrivateFieldGet(this, _Tools_config, "f").versao}`).catch(reject); ; const xml = builder.build(xmlObj); let tempUF = urlEventos(UF, __classPrivateFieldGet(this, _Tools_config, "f").versao); const url = tempUF[`mod${mod}`][(__classPrivateFieldGet(this, _Tools_config, "f").tpAmb == 1 ? "producao" : "homologacao")].NFeConsultaProtocolo; const req = https.request(url, { method: 'POST', headers: { 'Content-Type': 'application/soap+xml; charset=utf-8', 'Content-Length': xml.length, }, rejectUnauthorized: false, ...await __classPrivateFieldGet(this, _Tools_instances, "m", _Tools_certTools).call(this) }, (res) => { let data = ''; res.on('data', (chunk) => data += chunk); res.on('end', async () => { try { resolve(await __classPrivateFieldGet(this, _Tools_instances, "m", _Tools_limparSoap).call(this, data)); } catch (error) { resolve(data); } }); }); req.setTimeout(__classPrivateFieldGet(this, _Tools_config, "f").timeout * 1000, () => { reject({ name: 'TimeoutError', message: 'The operation was aborted due to timeout' }); req.destroy(); // cancela a requisição }); req.on('error', (err) => reject(err)); req.write(xml); req.end(); } catch (err) { reject(err); } }); } async sefazEvento({ chNFe = "", tpEvento = "", nProt = "", xJust = "", nSeqEvento = 1, dhEvento = formatData() }) { return new Promise(async (resolve, reject) => { try { if (!chNFe) throw "sefazEvento({chNFe}) -> não definido!"; if (!tpEvento) throw "sefazEvento({tpEvento}) -> não definido!"; if (!__classPrivateFieldGet(this, _Tools_config, "f").CNPJ && !__classPrivateFieldGet(this, _Tools_config, "f").CPF) throw "new Tools({CNPJ|CPF}) -> não definido!"; const geradorLote = function () { const agora = new Date(); const ano = agora.getFullYear().toString().slice(2); // Só os 2 últimos dígitos do ano const mes = String(agora.getMonth() + 1).padStart(2, '0'); const dia = String(agora.getDate()).padStart(2, '0'); const hora = String(agora.getHours()).padStart(2, '0'); const minuto = String(agora.getMinutes()).padStart(2, '0'); const segundo = String(agora.getSeconds()).padStart(2, '0'); // Junta tudo let idLote = `${ano}${mes}${dia}${hora}${minuto}${segundo}`; // Se ainda tiver menos de 15 dígitos, adiciona um número aleatório no final while (idLote.length < 15) { idLote += Math.floor(Math.random() * 10); // Adiciona dígitos aleatórios } return idLote; }; let detEvento = { "@versao": "1.00", "descEvento": __classPrivateFieldGet(this, _Tools_instances, "m", _Tools_descEvento).call(this, `${tpEvento}`) }; const cOrgao = !['210200', '210210', '210220', '210240'].includes(tpEvento) ? chNFe.substring(0, 2) : '91'; // Adicionar campos específicos por tipo de evento if (tpEvento === "110111") { // Cancelamento if (!nProt) throw "sefazEvento({nProt}) obrigatório para Cancelamento!"; if (!xJust) throw "sefazEvento({xJust}) obrigatório para Cancelamento!"; detEvento["nProt"] = nProt; detEvento["xJust"] = xJust; } else if (tpEvento === "110110") { // Carta de Correção if (!xJust) throw "sefazEvento({xJust}) obrigatório para Carta de Correção!"; detEvento["xCorrecao"] = xJust; detEvento["xCondUso"] = "A Carta de Correcao e disciplinada pelo paragrafo 1o-A do art. 7o do Convenio S/N, de 15 de dezembro de 1970 e pode ser utilizada para regularizacao de erro ocorrido na emissao de documento fiscal, desde que o erro nao esteja relacionado com: I - as variaveis que determinam o valor do imposto tais como: base de calculo, aliquota, diferenca de preco, quantidade, valor da operacao ou da prestacao; II - a correcao de dados cadastrais que implique mudanca do remetente ou do destinatario; III - a data de emissao ou de saida."; } else if (tpEvento === "210240") { // Operação não realizada if (!xJust) throw "sefazEvento({xJust}) obrigatório para Operação não realizada!"; detEvento["xJust"] = xJust; } // Ciência (210210), Confirmação (210200), Desconhecimento (210220) não precisam de campos extras const tempUF = urlEventos(cUF2UF[cOrgao], __classPrivateFieldGet(this, _Tools_config, "f").versao); const evento = { "envEvento": { "@xmlns": "http://www.portalfiscal.inf.br/nfe", "@versao": "1.00", "idLote": "250429141621528", "evento": { "@xmlns": "http://www.portalfiscal.inf.br/nfe", "@versao": "1.00", "infEvento": { "@Id": `ID${tpEvento}${chNFe}${nSeqEvento.toString().padStart(2, '0')}`, cOrgao, "tpAmb": __classPrivateFieldGet(this, _Tools_config, "f").tpAmb, "CNPJ": __classPrivateFieldGet(this, _Tools_config, "f").CNPJ, "chNFe": chNFe, dhEvento, "tpEvento": tpEvento, "nSeqEvento": nSeqEvento, "verEvento": "1.00", "detEvento": detEvento } } } }; let xmlSing = await json2xml(evento); xmlSing = await this.xmlSign(xmlSing, { tag: "infEvento" }); //Assinado await __classPrivateFieldGet(this, _Tools_instances, "m", _Tools_xmlValido).call(this, xmlSing, `envEvento_v1.00`).catch(reject); //Validar corpo xmlSing = await json2xml({ "soap:Envelope": { "@xmlns:soap": "http://www.w3.org/2003/05/soap-envelope", "@xmlns:nfe": "http://www.portalfiscal.inf.br/nfe/wsdl/NFeRecepcaoEvento4", "soap:Body": { "nfe:nfeDadosMsg": { ...await xml2json(xmlSing), "@xmlns": "http://www.portalfiscal.inf.br/nfe/wsdl/NFeRecepcaoEvento4" } } } }); try { const req = https.request(tempUF[`mod${chNFe.substring(20, 22)}`][(__classPrivateFieldGet(this, _Tools_config, "f").tpAmb == 1 ? "producao" : "homologacao")].NFeRecepcaoEvento, { ...{ method: 'POST', headers: { 'Content-Type': 'application/soap+xml; charset=utf-8', 'Content-Length': xmlSing.length, }, rejectUnauthorized: false, }, ...await __classPrivateFieldGet(this, _Tools_instances, "m", _Tools_certTools).call(this) }, (res) => { let data = ''; res.on('data', (chunk) => { data += chunk; }); res.on('end', async () => { try { resolve(await __classPrivateFieldGet(this, _Tools_instances, "m", _Tools_limparSoap).call(this, data)); } catch (error) { resolve(data); } }); }); req.setTimeout(__classPrivateFieldGet(this, _Tools_config, "f").timeout * 1000, () => { reject({ name: 'TimeoutError', message: 'The operation was aborted due to timeout' }); req.destroy(); // cancela a requisição }); req.on('error', (erro) => { reject(erro); }); req.write(xmlSing); req.end(); } catch (erro) { reject(erro); } } catch (erro) { reject(erro); } }); } async sefazDistDFe({ ultNSU = undefined, chNFe = undefined }) { return new Promise(async (resolve, reject) => { try { if (!chNFe && !ultNSU) throw "sefazDistDFe({chNFe|ultNSU})"; if (!__classPrivateFieldGet(this, _Tools_config, "f").CNPJ) throw "CNPJ não definido!"; if (__classPrivateFieldGet(this, _Tools_config, "f").CNPJ.length !== 14) throw "CNPJ inválido!"; // Gera o XML da consulta // Prepara o SOAP var xmlSing = await json2xml({ "distDFeInt": { "@xmlns": "http://www.portalfiscal.inf.br/nfe", "@versao": "1.01", "tpAmb": 1, // 1 = produção, 2 = homologação "cUFAutor": UF2cUF[__classPrivateFieldGet(this, _Tools_config, "f").UF], // "AN" - Ambiente Nacional "CNPJ": __classPrivateFieldGet(this, _Tools_config, "f").CNPJ, ...(typeof ultNSU != "undefined" ? { "distNSU": { "ultNSU": `${ultNSU}`.padStart(15, '0') } } : {}), ...(typeof chNFe != "undefined" ? { "consChNFe": { "chNFe": chNFe } } : {}) } }); await __classPrivateFieldGet(this, _Tools_instances, "m", _Tools_xmlValido).call(this, xmlSing, `distDFeInt_v1.01`).catch(reject); //Validar corpo const tempUF = urlEventos(`AN`, __classPrivateFieldGet(this, _Tools_config, "f").versao); xmlSing = await json2xml({ "soap:Envelope": { "@xmlns:xsi": "http://www.w3.org/2001/XMLSchema-instance", "@xmlns:xsd": "http://www.w3.org/2001/XMLSchema", "@xmlns:soap": "http://www.w3.org/2003/05/soap-envelope", "soap:Body": { "nfeDistDFeInteresse": { "@xmlns": "http://www.portalfiscal.inf.br/nfe/wsdl/NFeDistribuicaoDFe", "nfeDadosMsg": { ...{ "@xmlns": "http://www.portalfiscal.inf.br/nfe/wsdl/NFeDistribuicaoDFe" }, ...await xml2json(xmlSing) } } } } }); // HTTPS Request const req = https.request(tempUF[`mod${__classPrivateFieldGet(this, _Tools_config, "f").mod}`][(__classPrivateFieldGet(this, _Tools_config, "f").tpAmb == 1 ? "producao" : "homologacao")].NFeDistribuicaoDFe, { ...{ method: 'POST', headers: { 'Content-Type': 'application/soap+xml; charset=utf-8', 'Content-Length': xmlSing.length, }, rejectUnauthorized: false }, ...await __classPrivateFieldGet(this, _Tools_instances, "m", _Tools_certTools).call(this) }, (res) => { let data = ''; res.on('data', (chunk) => { data += chunk; }); res.on('end', async () => { try { resolve(await __classPrivateFieldGet(this, _Tools_instances, "m", _Tools_limparSoap).call(this, data)); } catch (error) { resolve(data); } }); }); req.setTimeout(__classPrivateFieldGet(this, _Tools_config, "f").timeout * 1000, () => { reject({ name: 'TimeoutError', message: 'The operation was aborted due to timeout' }); req.destroy(); // cancela a requisição }); req.on('error', (erro) => { reject(erro); }); req.write(xmlSing); req.end(); } catch (erro) { reject(erro); } }); } //Consulta status sefaz async sefazStatus() { return new Promise(async (resolve, reject) => { if (typeof __classPrivateFieldGet(this, _Tools_config, "f").UF == "undefined") throw "sefazStatus({...UF}) -> não definido!"; if (typeof __classPrivateFieldGet(this, _Tools_config, "f").tpAmb == "undefined") throw "sefazStatus({...tpAmb}) -> não definido!"; if (typeof __classPrivateFieldGet(this, _Tools_config, "f").mod == "undefined") throw "sefazStatus({...mod}) -> não definido!"; let tempUF = urlEventos(__classPrivateFieldGet(this, _Tools_config, "f").UF, __classPrivateFieldGet(this, _Tools_config, "f").versao); //Separado para validar o corpo da consulta let consStatServ = { "@versao": "4.00", "@xmlns": "http://www.portalfiscal.inf.br/nfe", "tpAmb": __classPrivateFieldGet(this, _Tools_config, "f").tpAmb, "cUF": tempUF.cUF, "xServ": "STATUS" }; let xmlObj = { "soap:Envelope": { "@xmlns:soap": "http://www.w3.org/2003/05/soap-envelope", "@xmlns:nfe": "http://www.portalfiscal.inf.br/nfe/wsdl/NFeStatusServico4", "soap:Body": { "nfe:nfeDadosMsg": { consStatServ } } } }; try { let tempBuild = new XMLBuilder({ ignoreAttributes: false, attributeNamePrefix: "@" }); //Validação await __classPrivateFieldGet(this, _Tools_instances, "m", _Tools_xmlValido).call(this, tempBuild.build({ consStatServ }), `consStatServ_v${__classPrivateFieldGet(this, _Tools_config, "f").versao}`).catch(reject); let tempUF = urlEventos(__classPrivateFieldGet(this, _Tools_config, "f").UF, __classPrivateFieldGet(this, _Tools_config, "f").versao); let xml = tempBuild.build(xmlObj); const req = https.request(tempUF[`mod${__classPrivateFieldGet(this, _Tools_config, "f").mod}`][(__classPrivateFieldGet(this, _Tools_config, "f").tpAmb == 1 ? "producao" : "homologacao")].NFeStatusServico, { ...{ method: 'POST', headers: { 'Content-Type': 'application/soap+xml; charset=utf-8', 'Content-Length': xml.length, }, rejectUnauthorized: false }, ...await __classPrivateFieldGet(this, _Tools_instances, "m", _Tools_certTools).call(this) }, (res) => { let data = ''; res.on('data', (chunk) => { data += chunk; }); res.on('end', async () => { try { resolve(await __classPrivateFieldGet(this, _Tools_instances, "m", _Tools_limparSoap).call(this, data)); } catch (error) { resolve(data); } }); }); req.setTimeout(__classPrivateFieldGet(this, _Tools_config, "f").timeout * 1000, () => { reject({ name: 'TimeoutError', message: 'The operation was aborted due to timeout' }); req.destroy(); // cancela a requisição }); req.on('error', (erro) => { reject(erro); }); req.write(xml); req.end(); } catch (erro) { reject(erro); } }); } async validarNFe(xml) { return new Promise((resolve, reject) => { __classPrivateFieldGet(this, _Tools_instances, "m", _Tools_xmlValido).call(this, xml, `nfe_v${__classPrivateFieldGet(this, _Tools_config, "f").versao}`).then(resolve).catch(reject); }); } } _Tools_cert = new WeakMap(), _Tools_pem = new WeakMap(), _Tools_config = new WeakMap(), _Tools_instances = new WeakSet(), _Tools_getSignature = //Responsavel por gerar assinatura async function _Tools_getSignature(xmlJSON, tag) { return new Promise(async (resvol, reject) => { let tempPem = await __classPrivateFieldGet(this, _Tools_instances, "m", _Tools_certTools).call(this); const sig = new SignedXml({ privateKey: tempPem.key, canonicalizationAlgorithm: 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315', signatureAlgorithm: 'http://www.w3.org/2000/09/xmldsig#rsa-sha1', publicCert: tempPem.pem, getKeyInfoContent: (args) => { const cert = tempPem.cert .toString() .replace('-----BEGIN CERTIFICATE-----', '') .replace('-----END CERTIFICATE-----', '') .replace(/\r?\n|\r/g, ''); return `<X509Data><X509Certificate>${cert}</X509Certificate></X509Data>`; } }); sig.addReference({ xpath: `//*[local-name(.)='${tag}']`, transforms: [ 'http://www.w3.org/2000/09/xmldsig#enveloped-signature', 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315' ], digestAlgorithm: 'http://www.w3.org/2000/09/xmldsig#sha1' }); sig.computeSignature(xmlJSON, { location: { reference: `//*[local-name()='${tag}']`, action: 'after' // <- insere DENTRO da tag <evento> } }); return resvol(sig.getSignatureXml()); }); }, _Tools_gerarQRCodeNFCe = function _Tools_gerarQRCodeNFCe(NFe, versaoQRCode = "2", idCSC, CSC) { let s = '|', concat, hash; if (NFe.infNFe.ide.tpEmis == 1) { concat = [NFe.infNFe['@Id'].replace("NFe", ""), versaoQRCode, NFe.infNFe.ide.tpAmb, Number(idCSC)].join(s); } else { let hexDigestValue = Buffer.from(NFe.Signature.SignedInfo.Reference.DigestValue).toString('hex'); concat = [NFe.infNFe['@Id'].replace("NFe", ""), versaoQRCode, NFe.infNFe.ide.tpAmb, NFe.infNFe.ide.dhEmi, NFe.infNFe.total.ICMSTot.vNF, hexDigestValue, Number(idCSC)].join(s); } hash = crypto.createHash('sha1').update(concat + CSC).digest('hex'); return NFe.infNFeSupl.qrCode + '?p=' + concat + s + hash; }, _Tools_descEvento = function _Tools_descEvento(tpEvento) { const eventos = { "110110": "Carta de Correcao", "110111": "Cancelamento", "210200": "Confirmacao da Operacao", "210210": "Ciencia da Operacao", "210220": "Desconhecimento da Operacao", "210240": "Operacao nao Realizada" }; return eventos[tpEvento] || "Evento"; }, _Tools_xmlValido = //Validar XML da NFe, somente apos assinar async function _Tools_xmlValido(xml, xsd) { return new Promise((resolve, reject) => { const xmlFile = tmp.fileSync({ mode: 0o644, prefix: 'xml-', postfix: '.xml' }); fs.writeFileSync(xmlFile.name, xml, { encoding: 'utf8' }); const schemaPath = path.resolve(__dirname, `../../schemas/PL_010_V1/${xsd}.xsd`); const verif = spawnSync(__classPrivateFieldGet(this, _Tools_config, "f").xmllint, ['--noout', '--schema', schemaPath, xmlFile.name], { encoding: 'utf8' }); xmlFile.removeCallback(); // Aqui, usamos o operador de encadeamento opcional (?.) if (verif.error) { return reject("Biblioteca xmllint não encontrada!"); } else if (!verif.stderr.includes(".xml validates")) { return reject(verif.stderr.replace(/\/tmp\/[^:\s]+\.xml/g, '') // Remove os caminhos /tmp/*.xml .replace(/\s{2,}/g, ' ') // Ajusta múltiplos espaços para um só .trim()); // Remove espaços no começo e fim) } else { resolve(true); } }); }, _Tools_certTools = function _Tools_certTools() { return new Promise(async (resvol, reject) => { if (__classPrivateFieldGet(this, _Tools_pem, "f").key != "") resvol(__classPrivateFieldGet(this, _Tools_pem, "f")); if (__classPrivateFieldGet(this, _Tools_config, "f").openssl != null) { pem.config({ pathOpenSSL: __classPrivateFieldGet(this, _Tools_config, "f").openssl }); } pem.readPkcs12(__classPrivateFieldGet(this, _Tools_cert, "f").pfx, { p12Password: __classPrivateFieldGet(this, _Tools_cert, "f").senha }, (err, myPem) => { if (err) return reject(err); // <-- importante! __classPrivateFieldSet(this, _Tools_pem, myPem, "f"); resvol(__classPrivateFieldGet(this, _Tools_pem, "f")); }); }); }, _Tools_limparSoap = //Remove coisas inuteis da resposta do sefaz async function _Tools_limparSoap(xml) { if (xml == "Bad Request") throw xml; const clear = [ 'env:Envelope', 'env:Body', 'S:Envelope', 'S:Body', 'soapenv:Envelope', 'soapenv:Body', 'soap:Envelope', 'soap:Body', 'nfeResultMsg', 'nfeDistDFeInteresseResponse' ]; let jXml = await xml2json(xml); let index = 0; while (index < clear.length) { if (typeof jXml[clear[index]] !== "undefined") { jXml = jXml[clear[index]]; index = 0; // reinicia a busca no novo nível } else { index++; } } return await json2xml(jXml); }; export { Tools };