UNPKG

node-sped-nfe-custom

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

802 lines (801 loc) 40.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); this.ultimoEventoXml = null; 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) => { try { 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); const 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)) } } } }, }; const xmlLote = await this.json2xml(jsonXmlLote); console.log('\n📤 XML do Lote enviado:\n', xmlLote); const tempUF = urlEventos(__classPrivateFieldGet(this, _Tools_config, "f").UF, __classPrivateFieldGet(this, _Tools_config, "f").versao); const endpoint = tempUF[`mod${__classPrivateFieldGet(this, _Tools_config, "f").mod}`][(__classPrivateFieldGet(this, _Tools_config, "f").tpAmb == 1 ? "producao" : "homologacao")].NFeAutorizacao; const req = https.request(endpoint, { method: 'POST', headers: { 'Content-Type': 'application/soap+xml; charset=utf-8', 'Content-Length': Buffer.byteLength(xmlLote), }, rejectUnauthorized: false, ...await __classPrivateFieldGet(this, _Tools_instances, "m", _Tools_certTools).call(this) }, (res) => { let responseData = ''; res.on('data', (chunk) => { responseData += chunk; }); res.on('end', async () => { console.log('\n📥 XML de resposta da SEFAZ:\n', responseData); try { const soapLimpo = await __classPrivateFieldGet(this, _Tools_instances, "m", _Tools_limparSoap).call(this, responseData); if (soapLimpo.includes('<faultcode>') || soapLimpo.includes('<soap:Fault>')) { console.warn('⚠️ SOAP Fault detectado na resposta!'); } resolve(soapLimpo); } catch (error) { console.warn('⚠️ Erro ao limpar o SOAP. Retornando conteúdo bruto.'); resolve(responseData); } }); }); req.setTimeout(__classPrivateFieldGet(this, _Tools_config, "f").timeout * 1000, () => { reject({ name: 'TimeoutError', message: 'A operação foi abortada por timeout da requisição à SEFAZ.' }); req.destroy(); // Cancela a requisição }); req.on('error', (erro) => { console.error('\n❌ Erro na requisição à SEFAZ:\n', erro); reject(erro); }); req.write(xmlLote); req.end(); } catch (erro) { console.error('\n❌ Erro inesperado na preparação ou envio do lote:\n', erro); reject(erro); } }); } async xmlSign(xmlJSON, data = { tag: "infNFe" }) { return new Promise(async (resolve, reject) => { if (!data.tag) data.tag = "infNFe"; const tag = data.tag; let xml = await this.xml2json(xmlJSON); // NFC-e tratamento de qrCode if (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, tag)) }; } // Evento else if (tag === "infEvento") { xml.envEvento.evento = { ...xml.envEvento.evento, ...await xml2json(await __classPrivateFieldGet(this, _Tools_instances, "m", _Tools_getSignature).call(this, xmlJSON, tag)) }; } // 🆕 Inutilização - Aqui está o novo tratamento else if (tag === "infInut") { const assinatura = await xml2json(await __classPrivateFieldGet(this, _Tools_instances, "m", _Tools_getSignature).call(this, xmlJSON, tag)); if (!assinatura.Signature) { throw new Error("Assinatura não encontrada no XML assinado."); } xml.inutNFe = { ...xml.inutNFe, Signature: assinatura.Signature }; } const finalXml = await json2xml(xml); resolve(finalXml); }); } 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); 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 { console.log('[SEFAZ EVENTO] Início do envio do evento'); 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 idLote = (() => { const agora = new Date(); const pad = (n) => String(n).padStart(2, '0'); let lote = `${String(agora.getFullYear()).slice(2)}${pad(agora.getMonth() + 1)}${pad(agora.getDate())}${pad(agora.getHours())}${pad(agora.getMinutes())}${pad(agora.getSeconds())}`; while (lote.length < 15) { lote += Math.floor(Math.random() * 10); } return lote; })(); 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'; // Tipos de evento if (tpEvento === "110111") { 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") { 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") { if (!xJust) throw "sefazEvento({xJust}) obrigatório para Operação não realizada!"; detEvento.xJust = xJust; } 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: idLote, evento: { "@xmlns": "http://www.portalfiscal.inf.br/nfe", "@versao": "1.00", infEvento: { "@Id": `ID${tpEvento}${chNFe}${String(nSeqEvento).padStart(2, '0')}`, cOrgao, tpAmb: __classPrivateFieldGet(this, _Tools_config, "f").tpAmb, CNPJ: __classPrivateFieldGet(this, _Tools_config, "f").CNPJ, chNFe: chNFe, dhEvento, tpEvento, nSeqEvento, verEvento: "1.00", detEvento: detEvento } } } }; console.log('[SEFAZ EVENTO] Gerando XML assinado...'); let xml = await json2xml(evento); xml = await this.xmlSign(xml, { tag: "infEvento" }); this.ultimoEventoXml = xml; console.log('[SEFAZ EVENTO] Validando XML...'); await __classPrivateFieldGet(this, _Tools_instances, "m", _Tools_xmlValido).call(this, xml, `envEvento_v1.00`).catch(reject); const soapEnvelope = 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(xml), "@xmlns": "http://www.portalfiscal.inf.br/nfe/wsdl/NFeRecepcaoEvento4" } } } }); console.log('[SEFAZ EVENTO] Enviando requisição HTTPS para SEFAZ...'); 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': Buffer.byteLength(soapEnvelope), }, rejectUnauthorized: false, ...await __classPrivateFieldGet(this, _Tools_instances, "m", _Tools_certTools).call(this) }, (res) => { let data = ""; res.on('data', (chunk) => { console.log('[SEFAZ EVENTO] Chunk recebido:', chunk.toString()); data += chunk; }); res.on('end', async () => { console.log('[SEFAZ EVENTO] Resposta completa recebida.'); try { const resposta = await __classPrivateFieldGet(this, _Tools_instances, "m", _Tools_limparSoap).call(this, data); console.log('[SEFAZ EVENTO] XML limpo:', resposta); resolve(resposta); } catch (e) { console.warn('[SEFAZ EVENTO] Falha ao limpar SOAP:', e); resolve(data); } }); }); req.setTimeout(__classPrivateFieldGet(this, _Tools_config, "f").timeout * 1000, () => { console.error('[SEFAZ EVENTO] Timeout da requisição HTTPS.'); reject({ name: "TimeoutError", message: "A operação foi abortada por timeout" }); req.destroy(); }); req.on("error", (err) => { console.error('[SEFAZ EVENTO] Erro na requisição HTTPS:', err); reject(err); }); req.write(soapEnvelope); req.end(); } catch (erro) { console.error('[SEFAZ EVENTO] Erro geral:', erro); reject(erro); } }); } async sefazInutiliza({ cUF, ano = new Date().getFullYear().toString().slice(-2), CNPJ, modelo = "55", serie, nIni, nFin, xJust, tpAmb = 1, versao = "4.00" }) { try { if (!cUF || !CNPJ || !serie || !nIni || !nFin || !xJust) { throw new Error("Parâmetros obrigatórios ausentes para inutilização."); } const strSerie = String(serie).padStart(3, "0"); const strInicio = String(nIni).padStart(9, "0"); const strFinal = String(nFin).padStart(9, "0"); const idInut = `ID${cUF}${ano}${CNPJ}${modelo}${strSerie}${strInicio}${strFinal}`; const json = { inutNFe: { "@xmlns": "http://www.portalfiscal.inf.br/nfe", "@versao": versao, infInut: { "@Id": idInut, tpAmb, xServ: "INUTILIZAR", cUF, ano, CNPJ, mod: modelo, serie, nNFIni: nIni, nNFFin: nFin, xJust, } } }; console.log('[SEFAZ INUTILIZAÇÃO] Gerando XML...'); const xml = await json2xml(json); console.log('[DEBUG] XML Gerado:', xml); console.log('[SEFAZ INUTILIZAÇÃO] Assinando XML...'); const xmlAssinado = await this.xmlSign(xml, { tag: "infInut" }); // ✅ Deve gerar <Signature> corretamente POSICIONADA console.log('[DEBUG] XML ASSINADO:', xmlAssinado); console.log('[SEFAZ INUTILIZAÇÃO] Validando XML...'); try { await __classPrivateFieldGet(this, _Tools_instances, "m", _Tools_xmlValido).call(this, xmlAssinado, `inutNFe_v${versao}`); } catch (e) { console.error('[ERRO VALIDAÇÃO XML]:', e); throw new Error("XML inválido: " + (e?.message ?? JSON.stringify(e))); } console.log('[SEFAZ INUTILIZAÇÃO] Montando SOAP envelope...'); const soapEnvelope = ` <soap12:Envelope xmlns:soap12="http://www.w3.org/2003/05/soap-envelope"> <soap12:Body> <nfeDadosMsg xmlns="http://www.portalfiscal.inf.br/nfe/wsdl/NFeInutilizacao4"> ${xmlAssinado} </nfeDadosMsg> </soap12:Body> </soap12:Envelope>`.trim(); console.log("---- SOAP ENVELOPE ----"); console.log(soapEnvelope); console.log("------------------------"); const uf = cUF2UF[cUF]; const tempUF = urlEventos(uf, __classPrivateFieldGet(this, _Tools_config, "f").versao); const url = tempUF[`mod${modelo}`][tpAmb === 1 ? "producao" : "homologacao"].NFeInutilizacao; console.log('[SEFAZ INUTILIZAÇÃO] Enviando requisição HTTPS...'); return new Promise(async (resolve, reject) => { const req = https.request(url, { method: "POST", headers: { "Content-Type": "application/soap+xml; charset=utf-8", "Content-Length": Buffer.byteLength(soapEnvelope) }, rejectUnauthorized: false, ...await __classPrivateFieldGet(this, _Tools_instances, "m", _Tools_certTools).call(this) }, res => { let response = ""; res.on("data", chunk => response += chunk.toString()); res.on("end", async () => { try { const limpo = await __classPrivateFieldGet(this, _Tools_instances, "m", _Tools_limparSoap).call(this, response); resolve(limpo); } catch (e) { console.warn('[SEFAZ INUTILIZAÇÃO] Falha ao limpar SOAP:', e); resolve(response); } }); }); req.on("error", reject); req.setTimeout(__classPrivateFieldGet(this, _Tools_config, "f").timeout * 1000, () => { req.destroy(); reject(new Error("Timeout na requisição à SEFAZ.")); }); req.write(soapEnvelope); req.end(); }); } catch (erro) { console.error('[SEFAZ INUTILIZAÇÃO] Erro geral:', erro); throw 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 = [ '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 };