ghinfe 0.6.0 → 0.6.2
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.
- package/.env.example +24 -15
- package/LICENSE +21 -21
- package/README.md +491 -413
- package/docs/developer-guide.html +421 -421
- package/docs/openapi.yaml +428 -428
- package/examples/dce-homologacao.js +141 -0
- package/examples/emitir-nfce.js +51 -51
- package/examples/emitir-nfe.js +83 -83
- package/examples/emitir-nfse.js +46 -46
- package/examples/endpoints-sefaz.js +14 -14
- package/examples/eventos-nfe.js +49 -49
- package/examples/gerar-chave.js +25 -25
- package/examples/gerar-dace.js +59 -0
- package/examples/gerar-danfe.js +75 -75
- package/examples/gerar-dce.js +58 -0
- package/examples/layouts-nfse.js +51 -51
- package/examples/mock-sefaz.js +52 -52
- package/examples/nfse-eventos.js +57 -57
- package/examples/reforma-ibscbs.js +27 -27
- package/examples/reforma-nfe-xml.js +52 -52
- package/examples/status-sefaz.js +12 -12
- package/examples/validar-xsd.js +48 -48
- package/package.json +59 -68
- package/schemas/dce/PL_DCe_100a_NT2024.001/consulta/consSitDCeTiposBasico_v1.00.xsd +123 -0
- package/schemas/dce/PL_DCe_100a_NT2024.001/consulta/consSitDCe_v1.00.xsd +10 -0
- package/schemas/dce/PL_DCe_100a_NT2024.001/consulta/consStatServDCeTiposBasico_v1.00.xsd +110 -0
- package/schemas/dce/PL_DCe_100a_NT2024.001/consulta/consStatServDCe_v1.00.xsd +10 -0
- package/schemas/dce/PL_DCe_100a_NT2024.001/consulta/dceTiposBasico_v1.00.xsd +1388 -0
- package/schemas/dce/PL_DCe_100a_NT2024.001/consulta/retConsSitDCe_v1.00.xsd +10 -0
- package/schemas/dce/PL_DCe_100a_NT2024.001/consulta/retConsStatServDCe_v1.00.xsd +10 -0
- package/schemas/dce/PL_DCe_100a_NT2024.001/consulta/tiposGeralDCe_v1.00.xsd +712 -0
- package/schemas/dce/PL_DCe_100a_NT2024.001/consulta/xmldsig-core-schema_v1.01.xsd +98 -0
- package/schemas/dce/PL_DCe_100a_NT2024.001/dceTiposBasico_v1.00.xsd +1410 -0
- package/schemas/dce/PL_DCe_100a_NT2024.001/dce_v1.00.xsd +10 -0
- package/schemas/dce/PL_DCe_100a_NT2024.001/evento/evCancDCe_v1.00.xsd +36 -0
- package/schemas/dce/PL_DCe_100a_NT2024.001/evento/eventoDCeTiposBasico_v1.00.xsd +283 -0
- package/schemas/dce/PL_DCe_100a_NT2024.001/evento/eventoDCe_v1.00.xsd +9 -0
- package/schemas/dce/PL_DCe_100a_NT2024.001/evento/procEventoDCe_v1.00.xsd +10 -0
- package/schemas/dce/PL_DCe_100a_NT2024.001/evento/retEventoDCe_v1.00.xsd +9 -0
- package/schemas/dce/PL_DCe_100a_NT2024.001/evento/tiposGeralDCe_v1.00.xsd +712 -0
- package/schemas/dce/PL_DCe_100a_NT2024.001/evento/xmldsig-core-schema_v1.01.xsd +98 -0
- package/schemas/dce/PL_DCe_100a_NT2024.001/procDCe_v1.00.xsd +37 -0
- package/schemas/dce/PL_DCe_100a_NT2024.001/retDCe_v1.00.xsd +9 -0
- package/schemas/dce/PL_DCe_100a_NT2024.001/tiposGeralDCe_v1.00.xsd +712 -0
- package/schemas/dce/PL_DCe_100a_NT2024.001/xmldsig-core-schema_v1.01.xsd +98 -0
- package/schemas/nfe/PL_009_V4/enviNFe_v3.10.xsd +8 -8
- package/schemas/nfe/PL_009_V4/xmldsig-core-schema_v1.01.xsd +97 -97
- package/scripts/download-schemas.js +39 -39
- package/scripts/test-xsd.js +34 -34
- package/src/api/server.js +316 -316
- package/src/constants/sefaz.js +69 -69
- package/src/danfe/DanfeService.js +205 -205
- package/src/danfe/xml-extract.js +69 -69
- package/src/dce/builders/DCeEventoXmlBuilder.js +86 -0
- package/src/dce/builders/DCeXmlBuilder.js +345 -0
- package/src/dce/constants/dce.js +87 -0
- package/src/dce/dace/DaceService.js +356 -0
- package/src/dce/dace/dace-extract.js +114 -0
- package/src/dce/services/DCeService.js +263 -0
- package/src/dce/services/DCeSoapService.js +235 -0
- package/src/dce/services/DCeXmlService.js +67 -0
- package/src/dce/types/DCeDocument.js +106 -0
- package/src/dce/utils/qrcode.utils.js +59 -0
- package/src/errors/GhinfeError.js +13 -13
- package/src/errors/SefazError.js +25 -25
- package/src/errors/XsdValidationError.js +15 -15
- package/src/index.js +84 -60
- package/src/nfce/builders/NFCeXmlBuilder.js +178 -178
- package/src/nfce/constants/qrcode.js +26 -26
- package/src/nfce/services/NFCeService.js +188 -188
- package/src/nfce/services/NFCeXmlService.js +38 -38
- package/src/nfce/types/NFCeDocument.js +42 -42
- package/src/nfce/utils/qrcode.utils.js +92 -92
- package/src/nfe/builders/NFeEventoXmlBuilder.js +110 -110
- package/src/nfe/builders/NFeXmlBuilder.js +215 -215
- package/src/nfe/mocks/sefaz-responses.js +195 -195
- package/src/nfe/services/NFeEventoService.js +95 -95
- package/src/nfe/services/NFeService.js +196 -196
- package/src/nfe/services/NFeSignService.js +127 -111
- package/src/nfe/services/NFeSoapService.js +437 -437
- package/src/nfe/services/NFeXmlService.js +41 -41
- package/src/nfe/services/NFeXsdService.js +56 -56
- package/src/nfe/types/NFeDocument.js +92 -92
- package/src/nfe/types/NFeEvento.js +44 -44
- package/src/nfse/NFSeMunicipioResolver.js +42 -42
- package/src/nfse/builders/NFSeEventoXmlBuilder.js +92 -92
- package/src/nfse/builders/NFSeXmlBuilder.js +16 -16
- package/src/nfse/constants/municipios.js +82 -82
- package/src/nfse/layouts/adapters.js +202 -202
- package/src/nfse/mocks/nfse-responses.js +142 -142
- package/src/nfse/services/NFSeService.js +191 -191
- package/src/nfse/services/NFSeSoapService.js +232 -232
- package/src/nfse/services/NFSeXmlService.js +12 -12
- package/src/nfse/types/NFSeDocument.js +54 -54
- package/src/queue/NFCeQueue.js +63 -63
- package/src/queue/NFSeQueue.js +59 -59
- package/src/queue/NFeQueue.js +76 -76
- package/src/reforma/ibscbs.js +175 -175
- package/src/sefaz/SefazEndpointResolver.js +95 -95
- package/src/sefaz/ca/icp-brasil-raiz-v10.pem +38 -0
- package/src/sefaz/endpoints.registry.js +205 -205
- package/src/sefaz/httpsAgent.js +33 -0
- package/src/shared/fiscal.helpers.js +53 -53
- package/src/utils/certificate.utils.js +69 -47
- package/src/utils/chave.utils.js +205 -117
- package/src/utils/code128.utils.js +67 -0
- package/src/utils/date.utils.js +26 -26
- package/src/utils/xml.utils.js +44 -44
- package/src/validation/XsdValidator.js +133 -114
- package/src/validation/schemas.js +18 -14
|
@@ -1,196 +1,196 @@
|
|
|
1
|
-
import { NFeXmlService } from './NFeXmlService.js';
|
|
2
|
-
import { NFeSignService } from './NFeSignService.js';
|
|
3
|
-
import { NFeSoapService } from './NFeSoapService.js';
|
|
4
|
-
import { NFeEventoService } from './NFeEventoService.js';
|
|
5
|
-
import { GhinfeError } from '../../errors/GhinfeError.js';
|
|
6
|
-
import { gerarChaveAcesso, validarChaveAcesso } from '../../utils/chave.utils.js';
|
|
7
|
-
import { SefazEndpointResolver } from '../../sefaz/SefazEndpointResolver.js';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* @param {{
|
|
11
|
-
* mock?: boolean,
|
|
12
|
-
* uf?: string,
|
|
13
|
-
* ambiente?: number,
|
|
14
|
-
* sefazEndpoint?: string,
|
|
15
|
-
* sefazEventoEndpoint?: string,
|
|
16
|
-
* mockScenario?: string,
|
|
17
|
-
* mockEventoScenario?: string,
|
|
18
|
-
* }} options
|
|
19
|
-
*/
|
|
20
|
-
function buildSoapOptions(options) {
|
|
21
|
-
if (options.mock ?? true) {
|
|
22
|
-
return {
|
|
23
|
-
mock: true,
|
|
24
|
-
endpoint: options.sefazEndpoint,
|
|
25
|
-
eventoEndpoint: options.sefazEventoEndpoint,
|
|
26
|
-
mockScenario: options.mockScenario,
|
|
27
|
-
mockEventoScenario: options.mockEventoScenario,
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
if (options.sefazEndpoint) {
|
|
32
|
-
return {
|
|
33
|
-
mock: false,
|
|
34
|
-
endpoint: options.sefazEndpoint,
|
|
35
|
-
eventoEndpoint: options.sefazEventoEndpoint,
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (options.uf) {
|
|
40
|
-
const urls = SefazEndpointResolver.paraNFe(options.uf, options.ambiente ?? 2);
|
|
41
|
-
return { mock: false, endpoint: urls.endpoint, eventoEndpoint: urls.eventoEndpoint };
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return { mock: true };
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Orquestrador principal da NF-e — une geração, assinatura e envio.
|
|
49
|
-
*/
|
|
50
|
-
export class NFeService {
|
|
51
|
-
/**
|
|
52
|
-
* @param {{
|
|
53
|
-
* mock?: boolean,
|
|
54
|
-
* uf?: string,
|
|
55
|
-
* ambiente?: number,
|
|
56
|
-
* sefazEndpoint?: string,
|
|
57
|
-
* sefazEventoEndpoint?: string,
|
|
58
|
-
* mockScenario?: string,
|
|
59
|
-
* mockEventoScenario?: string,
|
|
60
|
-
* validarXsd?: boolean,
|
|
61
|
-
* }} [options]
|
|
62
|
-
*/
|
|
63
|
-
constructor(options = {}) {
|
|
64
|
-
this.options = options;
|
|
65
|
-
const soapOptions = buildSoapOptions(options);
|
|
66
|
-
|
|
67
|
-
this.xmlService = new NFeXmlService({ validarXsd: options.validarXsd });
|
|
68
|
-
this.signService = new NFeSignService();
|
|
69
|
-
this.soapService = new NFeSoapService(soapOptions);
|
|
70
|
-
this.eventoService = new NFeEventoService(soapOptions);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/** Resolve endpoints SEFAZ para a UF do documento. */
|
|
74
|
-
configurarUf(uf, ambiente = 2) {
|
|
75
|
-
const urls = SefazEndpointResolver.paraNFe(uf, ambiente);
|
|
76
|
-
this.soapService.endpoint = urls.endpoint;
|
|
77
|
-
this.soapService.eventoEndpoint = urls.eventoEndpoint;
|
|
78
|
-
this.soapService.mock = false;
|
|
79
|
-
this.eventoService.soapService.endpoint = urls.endpoint;
|
|
80
|
-
this.eventoService.soapService.eventoEndpoint = urls.eventoEndpoint;
|
|
81
|
-
this.eventoService.soapService.mock = false;
|
|
82
|
-
return urls;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/** @param {Parameters<typeof gerarChaveAcesso>[0]} params */
|
|
86
|
-
gerarChaveAcesso(params) {
|
|
87
|
-
return gerarChaveAcesso(params);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/** @param {import('../types/NFeDocument.js').NFeDocument} documento */
|
|
91
|
-
resolverDocumento(documento) {
|
|
92
|
-
if (documento.chaveAcesso) {
|
|
93
|
-
if (!validarChaveAcesso(documento.chaveAcesso)) {
|
|
94
|
-
throw new GhinfeError('chaveAcesso com dígito verificador inválido', 'CHAVE_DV_INVALIDO');
|
|
95
|
-
}
|
|
96
|
-
return documento;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
const chaveAcesso = gerarChaveAcesso({
|
|
100
|
-
uf: documento.ide.uf,
|
|
101
|
-
cnpj: documento.emitente.cnpj,
|
|
102
|
-
modelo: '55',
|
|
103
|
-
serie: documento.ide.serie ?? 1,
|
|
104
|
-
numero: documento.ide.numero,
|
|
105
|
-
tipoEmissao: documento.ide.tipoEmissao ?? 1,
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
return { ...documento, chaveAcesso };
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
async emitir(documento, certificado, idLote) {
|
|
112
|
-
const doc = this.resolverDocumento(documento);
|
|
113
|
-
|
|
114
|
-
if (!this.soapService.mock && this.options.uf === undefined && !this.options.sefazEndpoint) {
|
|
115
|
-
this.configurarUf(doc.ide.uf, doc.ambiente ?? 2);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const xml = this.xmlService.gerarXml(doc);
|
|
119
|
-
const xmlAssinado = this.signService.assinar(xml, certificado.pfx, certificado.senha);
|
|
120
|
-
const retorno = await this.soapService.autorizar(xmlAssinado, doc.chaveAcesso, idLote);
|
|
121
|
-
|
|
122
|
-
return { chaveAcesso: doc.chaveAcesso, xmlGerado: xml, xmlAssinado, retorno };
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
gerarXml(documento, options = {}) {
|
|
126
|
-
const doc = this.resolverDocumento(documento);
|
|
127
|
-
return this.xmlService.gerarXml(doc, options);
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
gerarXmlAssinado(documento, certificado) {
|
|
131
|
-
const doc = this.resolverDocumento(documento);
|
|
132
|
-
const xml = this.xmlService.gerarXml(doc);
|
|
133
|
-
return this.signService.assinar(xml, certificado.pfx, certificado.senha);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
async enviar(xmlAssinado, chaveAcesso) {
|
|
137
|
-
return this.soapService.autorizar(xmlAssinado, chaveAcesso);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
validarXsd(xml, options = {}) {
|
|
141
|
-
return this.xmlService.validarXml(xml, options);
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
async cancelar(dados, certificado) {
|
|
145
|
-
return this.eventoService.cancelar(dados, certificado);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
async cartaCorrecao(dados, certificado) {
|
|
149
|
-
return this.eventoService.cartaCorrecao(dados, certificado);
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
gerarXmlCancelamento(dados) {
|
|
153
|
-
return this.eventoService.gerarXmlCancelamento(dados);
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
gerarXmlCartaCorrecao(dados) {
|
|
157
|
-
return this.eventoService.gerarXmlCartaCorrecao(dados);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
async enviarEventoMock(xmlEvento, chaveAcesso, tpEvento) {
|
|
161
|
-
return this.eventoService.enviar(xmlEvento, chaveAcesso, tpEvento);
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Consulta status do webservice SEFAZ (NfeStatusServico4).
|
|
166
|
-
* @param {string} [uf]
|
|
167
|
-
* @param {number} [ambiente]
|
|
168
|
-
*/
|
|
169
|
-
async consultarStatus(uf, ambiente) {
|
|
170
|
-
const ufConsulta = uf ?? this.options.uf;
|
|
171
|
-
if (!ufConsulta) {
|
|
172
|
-
throw new GhinfeError('UF é obrigatória para consultar status SEFAZ', 'STATUS_UF_REQUIRED');
|
|
173
|
-
}
|
|
174
|
-
return this.soapService.consultarStatus(ufConsulta, ambiente ?? this.options.ambiente ?? 2);
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
toPersistencePayload(resultado) {
|
|
178
|
-
if (!resultado.retorno.autorizado) {
|
|
179
|
-
throw new GhinfeError('Não é possível persistir NF-e não autorizada', 'NFE_NOT_AUTHORIZED', resultado.retorno);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
return {
|
|
183
|
-
chave_acesso: resultado.chaveAcesso,
|
|
184
|
-
xml_nfe: resultado.xmlAssinado,
|
|
185
|
-
xml_protocolo: resultado.retorno.xmlProtocolo,
|
|
186
|
-
protocolo: resultado.retorno.protocolo,
|
|
187
|
-
status: resultado.retorno.cStat,
|
|
188
|
-
motivo: resultado.retorno.xMotivo,
|
|
189
|
-
autorizado_em: new Date(),
|
|
190
|
-
};
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
toEventoPersistencePayload(resultado) {
|
|
194
|
-
return this.eventoService.toPersistencePayload(resultado);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
1
|
+
import { NFeXmlService } from './NFeXmlService.js';
|
|
2
|
+
import { NFeSignService } from './NFeSignService.js';
|
|
3
|
+
import { NFeSoapService } from './NFeSoapService.js';
|
|
4
|
+
import { NFeEventoService } from './NFeEventoService.js';
|
|
5
|
+
import { GhinfeError } from '../../errors/GhinfeError.js';
|
|
6
|
+
import { gerarChaveAcesso, validarChaveAcesso } from '../../utils/chave.utils.js';
|
|
7
|
+
import { SefazEndpointResolver } from '../../sefaz/SefazEndpointResolver.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @param {{
|
|
11
|
+
* mock?: boolean,
|
|
12
|
+
* uf?: string,
|
|
13
|
+
* ambiente?: number,
|
|
14
|
+
* sefazEndpoint?: string,
|
|
15
|
+
* sefazEventoEndpoint?: string,
|
|
16
|
+
* mockScenario?: string,
|
|
17
|
+
* mockEventoScenario?: string,
|
|
18
|
+
* }} options
|
|
19
|
+
*/
|
|
20
|
+
function buildSoapOptions(options) {
|
|
21
|
+
if (options.mock ?? true) {
|
|
22
|
+
return {
|
|
23
|
+
mock: true,
|
|
24
|
+
endpoint: options.sefazEndpoint,
|
|
25
|
+
eventoEndpoint: options.sefazEventoEndpoint,
|
|
26
|
+
mockScenario: options.mockScenario,
|
|
27
|
+
mockEventoScenario: options.mockEventoScenario,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (options.sefazEndpoint) {
|
|
32
|
+
return {
|
|
33
|
+
mock: false,
|
|
34
|
+
endpoint: options.sefazEndpoint,
|
|
35
|
+
eventoEndpoint: options.sefazEventoEndpoint,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (options.uf) {
|
|
40
|
+
const urls = SefazEndpointResolver.paraNFe(options.uf, options.ambiente ?? 2);
|
|
41
|
+
return { mock: false, endpoint: urls.endpoint, eventoEndpoint: urls.eventoEndpoint };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return { mock: true };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Orquestrador principal da NF-e — une geração, assinatura e envio.
|
|
49
|
+
*/
|
|
50
|
+
export class NFeService {
|
|
51
|
+
/**
|
|
52
|
+
* @param {{
|
|
53
|
+
* mock?: boolean,
|
|
54
|
+
* uf?: string,
|
|
55
|
+
* ambiente?: number,
|
|
56
|
+
* sefazEndpoint?: string,
|
|
57
|
+
* sefazEventoEndpoint?: string,
|
|
58
|
+
* mockScenario?: string,
|
|
59
|
+
* mockEventoScenario?: string,
|
|
60
|
+
* validarXsd?: boolean,
|
|
61
|
+
* }} [options]
|
|
62
|
+
*/
|
|
63
|
+
constructor(options = {}) {
|
|
64
|
+
this.options = options;
|
|
65
|
+
const soapOptions = buildSoapOptions(options);
|
|
66
|
+
|
|
67
|
+
this.xmlService = new NFeXmlService({ validarXsd: options.validarXsd });
|
|
68
|
+
this.signService = new NFeSignService();
|
|
69
|
+
this.soapService = new NFeSoapService(soapOptions);
|
|
70
|
+
this.eventoService = new NFeEventoService(soapOptions);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Resolve endpoints SEFAZ para a UF do documento. */
|
|
74
|
+
configurarUf(uf, ambiente = 2) {
|
|
75
|
+
const urls = SefazEndpointResolver.paraNFe(uf, ambiente);
|
|
76
|
+
this.soapService.endpoint = urls.endpoint;
|
|
77
|
+
this.soapService.eventoEndpoint = urls.eventoEndpoint;
|
|
78
|
+
this.soapService.mock = false;
|
|
79
|
+
this.eventoService.soapService.endpoint = urls.endpoint;
|
|
80
|
+
this.eventoService.soapService.eventoEndpoint = urls.eventoEndpoint;
|
|
81
|
+
this.eventoService.soapService.mock = false;
|
|
82
|
+
return urls;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** @param {Parameters<typeof gerarChaveAcesso>[0]} params */
|
|
86
|
+
gerarChaveAcesso(params) {
|
|
87
|
+
return gerarChaveAcesso(params);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** @param {import('../types/NFeDocument.js').NFeDocument} documento */
|
|
91
|
+
resolverDocumento(documento) {
|
|
92
|
+
if (documento.chaveAcesso) {
|
|
93
|
+
if (!validarChaveAcesso(documento.chaveAcesso)) {
|
|
94
|
+
throw new GhinfeError('chaveAcesso com dígito verificador inválido', 'CHAVE_DV_INVALIDO');
|
|
95
|
+
}
|
|
96
|
+
return documento;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const chaveAcesso = gerarChaveAcesso({
|
|
100
|
+
uf: documento.ide.uf,
|
|
101
|
+
cnpj: documento.emitente.cnpj,
|
|
102
|
+
modelo: '55',
|
|
103
|
+
serie: documento.ide.serie ?? 1,
|
|
104
|
+
numero: documento.ide.numero,
|
|
105
|
+
tipoEmissao: documento.ide.tipoEmissao ?? 1,
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
return { ...documento, chaveAcesso };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async emitir(documento, certificado, idLote) {
|
|
112
|
+
const doc = this.resolverDocumento(documento);
|
|
113
|
+
|
|
114
|
+
if (!this.soapService.mock && this.options.uf === undefined && !this.options.sefazEndpoint) {
|
|
115
|
+
this.configurarUf(doc.ide.uf, doc.ambiente ?? 2);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const xml = this.xmlService.gerarXml(doc);
|
|
119
|
+
const xmlAssinado = this.signService.assinar(xml, certificado.pfx, certificado.senha);
|
|
120
|
+
const retorno = await this.soapService.autorizar(xmlAssinado, doc.chaveAcesso, idLote);
|
|
121
|
+
|
|
122
|
+
return { chaveAcesso: doc.chaveAcesso, xmlGerado: xml, xmlAssinado, retorno };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
gerarXml(documento, options = {}) {
|
|
126
|
+
const doc = this.resolverDocumento(documento);
|
|
127
|
+
return this.xmlService.gerarXml(doc, options);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
gerarXmlAssinado(documento, certificado) {
|
|
131
|
+
const doc = this.resolverDocumento(documento);
|
|
132
|
+
const xml = this.xmlService.gerarXml(doc);
|
|
133
|
+
return this.signService.assinar(xml, certificado.pfx, certificado.senha);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async enviar(xmlAssinado, chaveAcesso) {
|
|
137
|
+
return this.soapService.autorizar(xmlAssinado, chaveAcesso);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
validarXsd(xml, options = {}) {
|
|
141
|
+
return this.xmlService.validarXml(xml, options);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
async cancelar(dados, certificado) {
|
|
145
|
+
return this.eventoService.cancelar(dados, certificado);
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async cartaCorrecao(dados, certificado) {
|
|
149
|
+
return this.eventoService.cartaCorrecao(dados, certificado);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
gerarXmlCancelamento(dados) {
|
|
153
|
+
return this.eventoService.gerarXmlCancelamento(dados);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
gerarXmlCartaCorrecao(dados) {
|
|
157
|
+
return this.eventoService.gerarXmlCartaCorrecao(dados);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
async enviarEventoMock(xmlEvento, chaveAcesso, tpEvento) {
|
|
161
|
+
return this.eventoService.enviar(xmlEvento, chaveAcesso, tpEvento);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Consulta status do webservice SEFAZ (NfeStatusServico4).
|
|
166
|
+
* @param {string} [uf]
|
|
167
|
+
* @param {number} [ambiente]
|
|
168
|
+
*/
|
|
169
|
+
async consultarStatus(uf, ambiente) {
|
|
170
|
+
const ufConsulta = uf ?? this.options.uf;
|
|
171
|
+
if (!ufConsulta) {
|
|
172
|
+
throw new GhinfeError('UF é obrigatória para consultar status SEFAZ', 'STATUS_UF_REQUIRED');
|
|
173
|
+
}
|
|
174
|
+
return this.soapService.consultarStatus(ufConsulta, ambiente ?? this.options.ambiente ?? 2);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
toPersistencePayload(resultado) {
|
|
178
|
+
if (!resultado.retorno.autorizado) {
|
|
179
|
+
throw new GhinfeError('Não é possível persistir NF-e não autorizada', 'NFE_NOT_AUTHORIZED', resultado.retorno);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return {
|
|
183
|
+
chave_acesso: resultado.chaveAcesso,
|
|
184
|
+
xml_nfe: resultado.xmlAssinado,
|
|
185
|
+
xml_protocolo: resultado.retorno.xmlProtocolo,
|
|
186
|
+
protocolo: resultado.retorno.protocolo,
|
|
187
|
+
status: resultado.retorno.cStat,
|
|
188
|
+
motivo: resultado.retorno.xMotivo,
|
|
189
|
+
autorizado_em: new Date(),
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
toEventoPersistencePayload(resultado) {
|
|
194
|
+
return this.eventoService.toPersistencePayload(resultado);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
@@ -1,111 +1,127 @@
|
|
|
1
|
-
import { SignedXml } from 'xml-crypto';
|
|
2
|
-
import { DOMParser } from '@xmldom/xmldom';
|
|
3
|
-
import { loadPfxCertificate } from '../../utils/certificate.utils.js';
|
|
4
|
-
import { GhinfeError } from '../../errors/GhinfeError.js';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Serviço de assinatura digital XML-DSig para NF-e (certificado A1 .pfx).
|
|
8
|
-
* Assina o elemento infNFe com algoritmo exigido pela SEFAZ.
|
|
9
|
-
*/
|
|
10
|
-
export class NFeSignService {
|
|
11
|
-
/**
|
|
12
|
-
* Assina o XML da NF-e usando certificado A1.
|
|
13
|
-
* @param {string} xmlNFe XML da NF-e sem assinatura
|
|
14
|
-
* @param {Buffer|string} pfxBuffer Conteúdo do arquivo .pfx
|
|
15
|
-
* @param {string} pfxPassword Senha do certificado
|
|
16
|
-
* @returns {string} XML assinado (NFe + Signature)
|
|
17
|
-
*/
|
|
18
|
-
assinar(xmlNFe, pfxBuffer, pfxPassword) {
|
|
19
|
-
return this.#assinarElemento(xmlNFe, 'infNFe', pfxBuffer, pfxPassword);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Assina XML de evento NF-e (cancelamento, CC-e, etc.).
|
|
24
|
-
* @param {string} xmlEvento
|
|
25
|
-
* @param {Buffer|string} pfxBuffer
|
|
26
|
-
* @param {string} pfxPassword
|
|
27
|
-
* @returns {string}
|
|
28
|
-
*/
|
|
29
|
-
assinarEvento(xmlEvento, pfxBuffer, pfxPassword) {
|
|
30
|
-
return this.#assinarElemento(xmlEvento, 'infEvento', pfxBuffer, pfxPassword);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Assina XML
|
|
35
|
-
*
|
|
36
|
-
* @param {
|
|
37
|
-
* @param {string}
|
|
38
|
-
* @
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
*
|
|
50
|
-
* @param {string}
|
|
51
|
-
* @param {Buffer|string} pfxBuffer
|
|
52
|
-
* @param {string} pfxPassword
|
|
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
|
-
}
|
|
1
|
+
import { SignedXml } from 'xml-crypto';
|
|
2
|
+
import { DOMParser } from '@xmldom/xmldom';
|
|
3
|
+
import { loadPfxCertificate } from '../../utils/certificate.utils.js';
|
|
4
|
+
import { GhinfeError } from '../../errors/GhinfeError.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Serviço de assinatura digital XML-DSig para NF-e (certificado A1 .pfx).
|
|
8
|
+
* Assina o elemento infNFe com algoritmo exigido pela SEFAZ.
|
|
9
|
+
*/
|
|
10
|
+
export class NFeSignService {
|
|
11
|
+
/**
|
|
12
|
+
* Assina o XML da NF-e usando certificado A1.
|
|
13
|
+
* @param {string} xmlNFe XML da NF-e sem assinatura
|
|
14
|
+
* @param {Buffer|string} pfxBuffer Conteúdo do arquivo .pfx
|
|
15
|
+
* @param {string} pfxPassword Senha do certificado
|
|
16
|
+
* @returns {string} XML assinado (NFe + Signature)
|
|
17
|
+
*/
|
|
18
|
+
assinar(xmlNFe, pfxBuffer, pfxPassword) {
|
|
19
|
+
return this.#assinarElemento(xmlNFe, 'infNFe', pfxBuffer, pfxPassword);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Assina XML de evento NF-e (cancelamento, CC-e, etc.).
|
|
24
|
+
* @param {string} xmlEvento
|
|
25
|
+
* @param {Buffer|string} pfxBuffer
|
|
26
|
+
* @param {string} pfxPassword
|
|
27
|
+
* @returns {string}
|
|
28
|
+
*/
|
|
29
|
+
assinarEvento(xmlEvento, pfxBuffer, pfxPassword) {
|
|
30
|
+
return this.#assinarElemento(xmlEvento, 'infEvento', pfxBuffer, pfxPassword);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Assina XML da DC-e (elemento infDCe). A Signature é anexada como último
|
|
35
|
+
* filho de DCe, após infDCeSupl, conforme sequência do XSD.
|
|
36
|
+
* @param {string} xmlDCe
|
|
37
|
+
* @param {Buffer|string} pfxBuffer
|
|
38
|
+
* @param {string} pfxPassword
|
|
39
|
+
* @returns {string}
|
|
40
|
+
*/
|
|
41
|
+
assinarDCe(xmlDCe, pfxBuffer, pfxPassword) {
|
|
42
|
+
return this.#assinarElemento(xmlDCe, 'infDCe', pfxBuffer, pfxPassword, {
|
|
43
|
+
reference: "/*[local-name()='DCe']",
|
|
44
|
+
action: 'append',
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Assina XML de RPS/NFS-e (elemento InfRps — ABRASF e derivados).
|
|
50
|
+
* @param {string} xmlRps
|
|
51
|
+
* @param {Buffer|string} pfxBuffer
|
|
52
|
+
* @param {string} pfxPassword
|
|
53
|
+
* @returns {string}
|
|
54
|
+
*/
|
|
55
|
+
assinarRps(xmlRps, pfxBuffer, pfxPassword) {
|
|
56
|
+
let tag = 'InfRps';
|
|
57
|
+
if (xmlRps.includes('InfDeclaracaoPrestacaoServico')) tag = 'InfDeclaracaoPrestacaoServico';
|
|
58
|
+
else if (xmlRps.includes('<infRps') || xmlRps.includes('infRps ')) tag = 'infRps';
|
|
59
|
+
else if (xmlRps.includes('<InfRps') || xmlRps.includes('InfRps ')) tag = 'InfRps';
|
|
60
|
+
return this.#assinarElemento(xmlRps, tag, pfxBuffer, pfxPassword);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @param {string} xml
|
|
65
|
+
* @param {string} tagName
|
|
66
|
+
* @param {Buffer|string} pfxBuffer
|
|
67
|
+
* @param {string} pfxPassword
|
|
68
|
+
* @param {{ reference: string, action: 'after'|'append' }} [location] Padrão: logo após o elemento assinado
|
|
69
|
+
*/
|
|
70
|
+
#assinarElemento(xml, tagName, pfxBuffer, pfxPassword, location) {
|
|
71
|
+
const { privateKeyPem, certificatePem } = loadPfxCertificate(pfxBuffer, pfxPassword);
|
|
72
|
+
|
|
73
|
+
const doc = new DOMParser().parseFromString(xml, 'text/xml');
|
|
74
|
+
const elemento = doc.getElementsByTagName(tagName)[0];
|
|
75
|
+
|
|
76
|
+
if (!elemento) {
|
|
77
|
+
throw new GhinfeError(`Elemento ${tagName} não encontrado no XML`, `SIGN_${tagName}_NOT_FOUND`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const id = elemento.getAttribute('Id');
|
|
81
|
+
if (!id) {
|
|
82
|
+
throw new GhinfeError(`Atributo Id do ${tagName} é obrigatório para assinatura`, 'SIGN_MISSING_ID');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const signedXml = new SignedXml({
|
|
86
|
+
privateKey: privateKeyPem,
|
|
87
|
+
publicCert: certificatePem,
|
|
88
|
+
signatureAlgorithm: 'http://www.w3.org/2000/09/xmldsig#rsa-sha1',
|
|
89
|
+
canonicalizationAlgorithm: 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315',
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
signedXml.addReference({
|
|
93
|
+
xpath: `//*[local-name()='${tagName}']`,
|
|
94
|
+
transforms: [
|
|
95
|
+
'http://www.w3.org/2000/09/xmldsig#enveloped-signature',
|
|
96
|
+
'http://www.w3.org/TR/2001/REC-xml-c14n-20010315',
|
|
97
|
+
],
|
|
98
|
+
digestAlgorithm: 'http://www.w3.org/2000/09/xmldsig#sha1',
|
|
99
|
+
uri: `#${id}`,
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
signedXml.computeSignature(xml, {
|
|
103
|
+
location: location ?? { reference: `//*[local-name()='${tagName}']`, action: 'after' },
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
return signedXml.getSignedXml();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Extrai informações do certificado para validação pré-envio.
|
|
111
|
+
* @param {Buffer|string} pfxBuffer
|
|
112
|
+
* @param {string} pfxPassword
|
|
113
|
+
*/
|
|
114
|
+
obterInfoCertificado(pfxBuffer, pfxPassword) {
|
|
115
|
+
const { certificate } = loadPfxCertificate(pfxBuffer, pfxPassword);
|
|
116
|
+
const subject = certificate.subject.attributes
|
|
117
|
+
.map((attr) => `${attr.shortName}=${attr.value}`)
|
|
118
|
+
.join(', ');
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
subject,
|
|
122
|
+
validoDe: certificate.validity.notBefore,
|
|
123
|
+
validoAte: certificate.validity.notAfter,
|
|
124
|
+
serialNumber: certificate.serialNumber,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
}
|