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
package/src/api/server.js
CHANGED
|
@@ -1,316 +1,316 @@
|
|
|
1
|
-
import 'dotenv/config';
|
|
2
|
-
import express from 'express';
|
|
3
|
-
import swaggerUi from 'swagger-ui-express';
|
|
4
|
-
import YAML from 'yamljs';
|
|
5
|
-
import path from 'path';
|
|
6
|
-
import { fileURLToPath } from 'url';
|
|
7
|
-
import { NFeService } from '../nfe/services/NFeService.js';
|
|
8
|
-
import { NFCeService } from '../nfce/services/NFCeService.js';
|
|
9
|
-
import { NFSeService } from '../nfse/services/NFSeService.js';
|
|
10
|
-
import { SefazEndpointResolver } from '../sefaz/SefazEndpointResolver.js';
|
|
11
|
-
import { NFSeMunicipioResolver } from '../nfse/NFSeMunicipioResolver.js';
|
|
12
|
-
import { SefazError } from '../errors/SefazError.js';
|
|
13
|
-
import { GhinfeError } from '../errors/GhinfeError.js';
|
|
14
|
-
import { XsdValidationError } from '../errors/XsdValidationError.js';
|
|
15
|
-
import { NFSeSoapError } from '../nfse/services/NFSeSoapService.js';
|
|
16
|
-
import { NFSE_LAYOUTS } from '../nfse/constants/municipios.js';
|
|
17
|
-
import { DanfeService } from '../danfe/DanfeService.js';
|
|
18
|
-
import { calcularIBSCBS, buildFragmentoIBSCBS } from '../reforma/ibscbs.js';
|
|
19
|
-
import { SEFAZ_MOCK_RESPONSES } from '../nfe/mocks/sefaz-responses.js';
|
|
20
|
-
|
|
21
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
22
|
-
const openapiPath = path.join(__dirname, '../../docs/openapi.yaml');
|
|
23
|
-
const swaggerDocument = YAML.load(openapiPath);
|
|
24
|
-
|
|
25
|
-
const app = express();
|
|
26
|
-
const port = process.env.PORT ?? 3333;
|
|
27
|
-
|
|
28
|
-
app.use(express.json({ limit: '2mb' }));
|
|
29
|
-
app.use('/docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument, {
|
|
30
|
-
customSiteTitle: 'GHINFE API — Swagger',
|
|
31
|
-
}));
|
|
32
|
-
app.use('/guia', express.static(path.join(__dirname, '../../docs')));
|
|
33
|
-
|
|
34
|
-
app.get('/health', (_req, res) => {
|
|
35
|
-
res.json({ status: 'ok', library: 'ghinfe', version: '0.6.0' });
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
app.post('/nfe/xml', (req, res) => {
|
|
39
|
-
try {
|
|
40
|
-
const validar = req.query.validarXsd === 'true';
|
|
41
|
-
const service = new NFeService({ validarXsd: validar });
|
|
42
|
-
const doc = service.resolverDocumento(req.body);
|
|
43
|
-
const xml = service.gerarXml(doc, { validarXsd: validar });
|
|
44
|
-
res.json({ chaveAcesso: doc.chaveAcesso, xml, xsdValidado: validar });
|
|
45
|
-
} catch (error) {
|
|
46
|
-
handleError(res, error);
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
app.post('/nfe/validar-xsd', (req, res) => {
|
|
51
|
-
try {
|
|
52
|
-
const service = new NFeService();
|
|
53
|
-
const resultado = service.validarXsd(req.body.xml, { preAssinatura: !req.body.xml.includes('</Signature>') });
|
|
54
|
-
res.json(resultado);
|
|
55
|
-
} catch (error) {
|
|
56
|
-
handleError(res, error);
|
|
57
|
-
}
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
app.post('/nfce/xml', (req, res) => {
|
|
61
|
-
try {
|
|
62
|
-
const service = new NFCeService();
|
|
63
|
-
const doc = service.resolverDocumento(req.body);
|
|
64
|
-
const xml = service.gerarXml(doc);
|
|
65
|
-
res.json({ chaveAcesso: doc.chaveAcesso, xml, observacao: 'QR Code v3 é adicionado após assinatura' });
|
|
66
|
-
} catch (error) {
|
|
67
|
-
handleError(res, error);
|
|
68
|
-
}
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
app.post('/nfce/enviar-mock', async (req, res) => {
|
|
72
|
-
try {
|
|
73
|
-
const service = new NFCeService({ mock: true });
|
|
74
|
-
const doc = service.resolverDocumento(req.body);
|
|
75
|
-
const xml = service.gerarXml(doc);
|
|
76
|
-
const retorno = await service.enviar(xml, doc.chaveAcesso);
|
|
77
|
-
res.json({ chaveAcesso: doc.chaveAcesso, xmlGerado: xml, retorno, mock: true });
|
|
78
|
-
} catch (error) {
|
|
79
|
-
handleError(res, error);
|
|
80
|
-
}
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
app.post('/nfe/chave', (req, res) => {
|
|
84
|
-
try {
|
|
85
|
-
const service = new NFeService();
|
|
86
|
-
const chaveAcesso = service.gerarChaveAcesso(req.body);
|
|
87
|
-
res.json({ chaveAcesso, valida: true });
|
|
88
|
-
} catch (error) {
|
|
89
|
-
handleError(res, error);
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
app.post('/nfe/cancelar-mock', async (req, res) => {
|
|
94
|
-
try {
|
|
95
|
-
const service = new NFeService({ mock: true, mockEventoScenario: 'registrado' });
|
|
96
|
-
const xml = service.gerarXmlCancelamento(req.body);
|
|
97
|
-
const retorno = await service.enviarEventoMock(
|
|
98
|
-
xml,
|
|
99
|
-
req.body.chaveAcesso,
|
|
100
|
-
'110111'
|
|
101
|
-
);
|
|
102
|
-
res.json({ xmlGerado: xml, retorno, mock: true });
|
|
103
|
-
} catch (error) {
|
|
104
|
-
handleError(res, error);
|
|
105
|
-
}
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
app.post('/nfe/cce-mock', async (req, res) => {
|
|
109
|
-
try {
|
|
110
|
-
const service = new NFeService({ mock: true, mockEventoScenario: 'registrado' });
|
|
111
|
-
const xml = service.gerarXmlCartaCorrecao(req.body);
|
|
112
|
-
const retorno = await service.enviarEventoMock(
|
|
113
|
-
xml,
|
|
114
|
-
req.body.chaveAcesso,
|
|
115
|
-
'110110'
|
|
116
|
-
);
|
|
117
|
-
res.json({ xmlGerado: xml, retorno, mock: true });
|
|
118
|
-
} catch (error) {
|
|
119
|
-
handleError(res, error);
|
|
120
|
-
}
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
app.post('/nfe/enviar-mock', async (req, res) => {
|
|
124
|
-
try {
|
|
125
|
-
const cenario = req.query.cenario ?? 'autorizado';
|
|
126
|
-
const service = new NFeService({ mock: true, mockScenario: cenario });
|
|
127
|
-
|
|
128
|
-
const doc = service.resolverDocumento(req.body);
|
|
129
|
-
const xml = service.gerarXml(doc);
|
|
130
|
-
const retorno = await service.enviar(xml, doc.chaveAcesso);
|
|
131
|
-
|
|
132
|
-
res.json({
|
|
133
|
-
chaveAcesso: doc.chaveAcesso,
|
|
134
|
-
xmlGerado: xml,
|
|
135
|
-
retorno,
|
|
136
|
-
mock: true,
|
|
137
|
-
});
|
|
138
|
-
} catch (error) {
|
|
139
|
-
handleError(res, error);
|
|
140
|
-
}
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
app.get('/sefaz/endpoints/:uf', (req, res) => {
|
|
144
|
-
try {
|
|
145
|
-
const ambiente = parseInt(req.query.ambiente ?? '2', 10);
|
|
146
|
-
const info = SefazEndpointResolver.obterEndpoints(req.params.uf, ambiente);
|
|
147
|
-
res.json(info);
|
|
148
|
-
} catch (error) {
|
|
149
|
-
handleError(res, error);
|
|
150
|
-
}
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
app.get('/sefaz/ufs', (_req, res) => {
|
|
154
|
-
res.json({ ufs: SefazEndpointResolver.listarUFs() });
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
app.get('/sefaz/status/:uf', async (req, res) => {
|
|
158
|
-
try {
|
|
159
|
-
const ambiente = parseInt(req.query.ambiente ?? '2', 10);
|
|
160
|
-
const service = new NFeService({ mock: true, uf: req.params.uf, ambiente });
|
|
161
|
-
if (req.query.cenario) service.soapService.setMockStatusScenario(req.query.cenario);
|
|
162
|
-
const status = await service.consultarStatus(req.params.uf, ambiente);
|
|
163
|
-
res.json(status);
|
|
164
|
-
} catch (error) {
|
|
165
|
-
handleError(res, error);
|
|
166
|
-
}
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
app.get('/nfse/layouts', (_req, res) => {
|
|
170
|
-
res.json({ layouts: Object.values(NFSE_LAYOUTS) });
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
app.post('/nfse/xml', (req, res) => {
|
|
174
|
-
try {
|
|
175
|
-
const layout = req.query.layout ?? req.body.layout;
|
|
176
|
-
const service = new NFSeService({ layout });
|
|
177
|
-
const xml = service.gerarXml(req.body, layout);
|
|
178
|
-
res.json({ layout: layout ?? service.resolverLayout(req.body), xml });
|
|
179
|
-
} catch (error) {
|
|
180
|
-
handleError(res, error);
|
|
181
|
-
}
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
app.post('/reforma/calcular', (req, res) => {
|
|
185
|
-
try {
|
|
186
|
-
const { baseCalculo, aliquotas } = req.body;
|
|
187
|
-
const reforma = calcularIBSCBS(baseCalculo, aliquotas ?? {});
|
|
188
|
-
res.json({ reforma, fragmentoXml: buildFragmentoIBSCBS(reforma) });
|
|
189
|
-
} catch (error) {
|
|
190
|
-
handleError(res, error);
|
|
191
|
-
}
|
|
192
|
-
});
|
|
193
|
-
|
|
194
|
-
app.post('/nfse/enviar-mock', async (req, res) => {
|
|
195
|
-
try {
|
|
196
|
-
const layout = req.query.layout ?? req.body.layout;
|
|
197
|
-
const service = new NFSeService({ mock: true, layout });
|
|
198
|
-
const resultado = await service.emitir(req.body);
|
|
199
|
-
res.json({ ...resultado, mock: true });
|
|
200
|
-
} catch (error) {
|
|
201
|
-
handleError(res, error);
|
|
202
|
-
}
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
app.post('/nfse/cancelar-mock', async (req, res) => {
|
|
206
|
-
try {
|
|
207
|
-
const service = new NFSeService({ mock: true });
|
|
208
|
-
const resultado = await service.cancelar(req.body);
|
|
209
|
-
res.json({ ...resultado, mock: true });
|
|
210
|
-
} catch (error) {
|
|
211
|
-
handleError(res, error);
|
|
212
|
-
}
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
app.post('/nfse/substituir-mock', async (req, res) => {
|
|
216
|
-
try {
|
|
217
|
-
const service = new NFSeService({ mock: true });
|
|
218
|
-
const resultado = await service.substituir(req.body);
|
|
219
|
-
res.json({ ...resultado, mock: true });
|
|
220
|
-
} catch (error) {
|
|
221
|
-
handleError(res, error);
|
|
222
|
-
}
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
app.post('/danfe', async (req, res) => {
|
|
226
|
-
try {
|
|
227
|
-
const service = new DanfeService();
|
|
228
|
-
const resultado = await service.gerar(req.body.xml, {
|
|
229
|
-
protocolo: req.body.protocolo,
|
|
230
|
-
qrCodeUrl: req.body.qrCodeUrl,
|
|
231
|
-
});
|
|
232
|
-
if (req.query.format === 'html') {
|
|
233
|
-
res.type('html').send(resultado.html);
|
|
234
|
-
return;
|
|
235
|
-
}
|
|
236
|
-
res.json(resultado);
|
|
237
|
-
} catch (error) {
|
|
238
|
-
handleError(res, error);
|
|
239
|
-
}
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
app.get('/nfse/municipios', (_req, res) => {
|
|
243
|
-
const municipios = NFSeMunicipioResolver.listarMunicipios()
|
|
244
|
-
.filter((c) => c !== 'mock')
|
|
245
|
-
.map((codigo) => {
|
|
246
|
-
const info = NFSeMunicipioResolver.resolver(codigo, 2);
|
|
247
|
-
return { codigo, ...info };
|
|
248
|
-
});
|
|
249
|
-
res.json({ municipios, layouts: Object.values(NFSE_LAYOUTS) });
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
app.get('/nfe/cenarios-mock', (_req, res) => {
|
|
253
|
-
res.json({
|
|
254
|
-
cenarios: [
|
|
255
|
-
{ id: 'autorizado', descricao: 'NF-e autorizada (cStat 100)', cStat: '100' },
|
|
256
|
-
{ id: 'duplicidade', descricao: 'Rejeição por duplicidade (cStat 204)', cStat: '204' },
|
|
257
|
-
{ id: 'schema', descricao: 'Rejeição por schema inválido (cStat 225)', cStat: '225' },
|
|
258
|
-
{ id: 'certificado', descricao: 'Certificado inválido (cStat 280)', cStat: '280' },
|
|
259
|
-
{ id: 'paralisado', descricao: 'Serviço paralisado (cStat 108)', cStat: '108' },
|
|
260
|
-
],
|
|
261
|
-
responses: SEFAZ_MOCK_RESPONSES,
|
|
262
|
-
});
|
|
263
|
-
});
|
|
264
|
-
|
|
265
|
-
/**
|
|
266
|
-
* @param {import('express').Response} res
|
|
267
|
-
* @param {unknown} error
|
|
268
|
-
*/
|
|
269
|
-
function handleError(res, error) {
|
|
270
|
-
if (error instanceof SefazError) {
|
|
271
|
-
return res.status(422).json({
|
|
272
|
-
error: error.name,
|
|
273
|
-
code: error.code,
|
|
274
|
-
cStat: error.cStat,
|
|
275
|
-
xMotivo: error.xMotivo,
|
|
276
|
-
details: error.details,
|
|
277
|
-
});
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
if (error instanceof NFSeSoapError) {
|
|
281
|
-
return res.status(422).json({
|
|
282
|
-
error: error.name,
|
|
283
|
-
code: error.code,
|
|
284
|
-
codigo: error.codigo,
|
|
285
|
-
message: error.message,
|
|
286
|
-
});
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
if (error instanceof XsdValidationError) {
|
|
290
|
-
return res.status(422).json({
|
|
291
|
-
error: error.name,
|
|
292
|
-
code: error.code,
|
|
293
|
-
message: error.message,
|
|
294
|
-
erros: error.erros,
|
|
295
|
-
schema: error.schema,
|
|
296
|
-
});
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
if (error instanceof GhinfeError) {
|
|
300
|
-
return res.status(400).json({
|
|
301
|
-
error: error.name,
|
|
302
|
-
code: error.code,
|
|
303
|
-
message: error.message,
|
|
304
|
-
details: error.details,
|
|
305
|
-
});
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
console.error(error);
|
|
309
|
-
res.status(500).json({ error: 'InternalServerError', message: 'Erro interno' });
|
|
310
|
-
}
|
|
311
|
-
|
|
312
|
-
app.listen(port, () => {
|
|
313
|
-
console.log(`GHINFE API rodando em http://localhost:${port}`);
|
|
314
|
-
console.log(`Swagger UI: http://localhost:${port}/docs`);
|
|
315
|
-
console.log(`Guia do desenvolvedor: http://localhost:${port}/guia/developer-guide.html`);
|
|
316
|
-
});
|
|
1
|
+
import 'dotenv/config';
|
|
2
|
+
import express from 'express';
|
|
3
|
+
import swaggerUi from 'swagger-ui-express';
|
|
4
|
+
import YAML from 'yamljs';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
import { fileURLToPath } from 'url';
|
|
7
|
+
import { NFeService } from '../nfe/services/NFeService.js';
|
|
8
|
+
import { NFCeService } from '../nfce/services/NFCeService.js';
|
|
9
|
+
import { NFSeService } from '../nfse/services/NFSeService.js';
|
|
10
|
+
import { SefazEndpointResolver } from '../sefaz/SefazEndpointResolver.js';
|
|
11
|
+
import { NFSeMunicipioResolver } from '../nfse/NFSeMunicipioResolver.js';
|
|
12
|
+
import { SefazError } from '../errors/SefazError.js';
|
|
13
|
+
import { GhinfeError } from '../errors/GhinfeError.js';
|
|
14
|
+
import { XsdValidationError } from '../errors/XsdValidationError.js';
|
|
15
|
+
import { NFSeSoapError } from '../nfse/services/NFSeSoapService.js';
|
|
16
|
+
import { NFSE_LAYOUTS } from '../nfse/constants/municipios.js';
|
|
17
|
+
import { DanfeService } from '../danfe/DanfeService.js';
|
|
18
|
+
import { calcularIBSCBS, buildFragmentoIBSCBS } from '../reforma/ibscbs.js';
|
|
19
|
+
import { SEFAZ_MOCK_RESPONSES } from '../nfe/mocks/sefaz-responses.js';
|
|
20
|
+
|
|
21
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
22
|
+
const openapiPath = path.join(__dirname, '../../docs/openapi.yaml');
|
|
23
|
+
const swaggerDocument = YAML.load(openapiPath);
|
|
24
|
+
|
|
25
|
+
const app = express();
|
|
26
|
+
const port = process.env.PORT ?? 3333;
|
|
27
|
+
|
|
28
|
+
app.use(express.json({ limit: '2mb' }));
|
|
29
|
+
app.use('/docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument, {
|
|
30
|
+
customSiteTitle: 'GHINFE API — Swagger',
|
|
31
|
+
}));
|
|
32
|
+
app.use('/guia', express.static(path.join(__dirname, '../../docs')));
|
|
33
|
+
|
|
34
|
+
app.get('/health', (_req, res) => {
|
|
35
|
+
res.json({ status: 'ok', library: 'ghinfe', version: '0.6.0' });
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
app.post('/nfe/xml', (req, res) => {
|
|
39
|
+
try {
|
|
40
|
+
const validar = req.query.validarXsd === 'true';
|
|
41
|
+
const service = new NFeService({ validarXsd: validar });
|
|
42
|
+
const doc = service.resolverDocumento(req.body);
|
|
43
|
+
const xml = service.gerarXml(doc, { validarXsd: validar });
|
|
44
|
+
res.json({ chaveAcesso: doc.chaveAcesso, xml, xsdValidado: validar });
|
|
45
|
+
} catch (error) {
|
|
46
|
+
handleError(res, error);
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
app.post('/nfe/validar-xsd', (req, res) => {
|
|
51
|
+
try {
|
|
52
|
+
const service = new NFeService();
|
|
53
|
+
const resultado = service.validarXsd(req.body.xml, { preAssinatura: !req.body.xml.includes('</Signature>') });
|
|
54
|
+
res.json(resultado);
|
|
55
|
+
} catch (error) {
|
|
56
|
+
handleError(res, error);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
app.post('/nfce/xml', (req, res) => {
|
|
61
|
+
try {
|
|
62
|
+
const service = new NFCeService();
|
|
63
|
+
const doc = service.resolverDocumento(req.body);
|
|
64
|
+
const xml = service.gerarXml(doc);
|
|
65
|
+
res.json({ chaveAcesso: doc.chaveAcesso, xml, observacao: 'QR Code v3 é adicionado após assinatura' });
|
|
66
|
+
} catch (error) {
|
|
67
|
+
handleError(res, error);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
app.post('/nfce/enviar-mock', async (req, res) => {
|
|
72
|
+
try {
|
|
73
|
+
const service = new NFCeService({ mock: true });
|
|
74
|
+
const doc = service.resolverDocumento(req.body);
|
|
75
|
+
const xml = service.gerarXml(doc);
|
|
76
|
+
const retorno = await service.enviar(xml, doc.chaveAcesso);
|
|
77
|
+
res.json({ chaveAcesso: doc.chaveAcesso, xmlGerado: xml, retorno, mock: true });
|
|
78
|
+
} catch (error) {
|
|
79
|
+
handleError(res, error);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
app.post('/nfe/chave', (req, res) => {
|
|
84
|
+
try {
|
|
85
|
+
const service = new NFeService();
|
|
86
|
+
const chaveAcesso = service.gerarChaveAcesso(req.body);
|
|
87
|
+
res.json({ chaveAcesso, valida: true });
|
|
88
|
+
} catch (error) {
|
|
89
|
+
handleError(res, error);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
app.post('/nfe/cancelar-mock', async (req, res) => {
|
|
94
|
+
try {
|
|
95
|
+
const service = new NFeService({ mock: true, mockEventoScenario: 'registrado' });
|
|
96
|
+
const xml = service.gerarXmlCancelamento(req.body);
|
|
97
|
+
const retorno = await service.enviarEventoMock(
|
|
98
|
+
xml,
|
|
99
|
+
req.body.chaveAcesso,
|
|
100
|
+
'110111'
|
|
101
|
+
);
|
|
102
|
+
res.json({ xmlGerado: xml, retorno, mock: true });
|
|
103
|
+
} catch (error) {
|
|
104
|
+
handleError(res, error);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
app.post('/nfe/cce-mock', async (req, res) => {
|
|
109
|
+
try {
|
|
110
|
+
const service = new NFeService({ mock: true, mockEventoScenario: 'registrado' });
|
|
111
|
+
const xml = service.gerarXmlCartaCorrecao(req.body);
|
|
112
|
+
const retorno = await service.enviarEventoMock(
|
|
113
|
+
xml,
|
|
114
|
+
req.body.chaveAcesso,
|
|
115
|
+
'110110'
|
|
116
|
+
);
|
|
117
|
+
res.json({ xmlGerado: xml, retorno, mock: true });
|
|
118
|
+
} catch (error) {
|
|
119
|
+
handleError(res, error);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
app.post('/nfe/enviar-mock', async (req, res) => {
|
|
124
|
+
try {
|
|
125
|
+
const cenario = req.query.cenario ?? 'autorizado';
|
|
126
|
+
const service = new NFeService({ mock: true, mockScenario: cenario });
|
|
127
|
+
|
|
128
|
+
const doc = service.resolverDocumento(req.body);
|
|
129
|
+
const xml = service.gerarXml(doc);
|
|
130
|
+
const retorno = await service.enviar(xml, doc.chaveAcesso);
|
|
131
|
+
|
|
132
|
+
res.json({
|
|
133
|
+
chaveAcesso: doc.chaveAcesso,
|
|
134
|
+
xmlGerado: xml,
|
|
135
|
+
retorno,
|
|
136
|
+
mock: true,
|
|
137
|
+
});
|
|
138
|
+
} catch (error) {
|
|
139
|
+
handleError(res, error);
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
app.get('/sefaz/endpoints/:uf', (req, res) => {
|
|
144
|
+
try {
|
|
145
|
+
const ambiente = parseInt(req.query.ambiente ?? '2', 10);
|
|
146
|
+
const info = SefazEndpointResolver.obterEndpoints(req.params.uf, ambiente);
|
|
147
|
+
res.json(info);
|
|
148
|
+
} catch (error) {
|
|
149
|
+
handleError(res, error);
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
app.get('/sefaz/ufs', (_req, res) => {
|
|
154
|
+
res.json({ ufs: SefazEndpointResolver.listarUFs() });
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
app.get('/sefaz/status/:uf', async (req, res) => {
|
|
158
|
+
try {
|
|
159
|
+
const ambiente = parseInt(req.query.ambiente ?? '2', 10);
|
|
160
|
+
const service = new NFeService({ mock: true, uf: req.params.uf, ambiente });
|
|
161
|
+
if (req.query.cenario) service.soapService.setMockStatusScenario(req.query.cenario);
|
|
162
|
+
const status = await service.consultarStatus(req.params.uf, ambiente);
|
|
163
|
+
res.json(status);
|
|
164
|
+
} catch (error) {
|
|
165
|
+
handleError(res, error);
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
app.get('/nfse/layouts', (_req, res) => {
|
|
170
|
+
res.json({ layouts: Object.values(NFSE_LAYOUTS) });
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
app.post('/nfse/xml', (req, res) => {
|
|
174
|
+
try {
|
|
175
|
+
const layout = req.query.layout ?? req.body.layout;
|
|
176
|
+
const service = new NFSeService({ layout });
|
|
177
|
+
const xml = service.gerarXml(req.body, layout);
|
|
178
|
+
res.json({ layout: layout ?? service.resolverLayout(req.body), xml });
|
|
179
|
+
} catch (error) {
|
|
180
|
+
handleError(res, error);
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
app.post('/reforma/calcular', (req, res) => {
|
|
185
|
+
try {
|
|
186
|
+
const { baseCalculo, aliquotas } = req.body;
|
|
187
|
+
const reforma = calcularIBSCBS(baseCalculo, aliquotas ?? {});
|
|
188
|
+
res.json({ reforma, fragmentoXml: buildFragmentoIBSCBS(reforma) });
|
|
189
|
+
} catch (error) {
|
|
190
|
+
handleError(res, error);
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
app.post('/nfse/enviar-mock', async (req, res) => {
|
|
195
|
+
try {
|
|
196
|
+
const layout = req.query.layout ?? req.body.layout;
|
|
197
|
+
const service = new NFSeService({ mock: true, layout });
|
|
198
|
+
const resultado = await service.emitir(req.body);
|
|
199
|
+
res.json({ ...resultado, mock: true });
|
|
200
|
+
} catch (error) {
|
|
201
|
+
handleError(res, error);
|
|
202
|
+
}
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
app.post('/nfse/cancelar-mock', async (req, res) => {
|
|
206
|
+
try {
|
|
207
|
+
const service = new NFSeService({ mock: true });
|
|
208
|
+
const resultado = await service.cancelar(req.body);
|
|
209
|
+
res.json({ ...resultado, mock: true });
|
|
210
|
+
} catch (error) {
|
|
211
|
+
handleError(res, error);
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
app.post('/nfse/substituir-mock', async (req, res) => {
|
|
216
|
+
try {
|
|
217
|
+
const service = new NFSeService({ mock: true });
|
|
218
|
+
const resultado = await service.substituir(req.body);
|
|
219
|
+
res.json({ ...resultado, mock: true });
|
|
220
|
+
} catch (error) {
|
|
221
|
+
handleError(res, error);
|
|
222
|
+
}
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
app.post('/danfe', async (req, res) => {
|
|
226
|
+
try {
|
|
227
|
+
const service = new DanfeService();
|
|
228
|
+
const resultado = await service.gerar(req.body.xml, {
|
|
229
|
+
protocolo: req.body.protocolo,
|
|
230
|
+
qrCodeUrl: req.body.qrCodeUrl,
|
|
231
|
+
});
|
|
232
|
+
if (req.query.format === 'html') {
|
|
233
|
+
res.type('html').send(resultado.html);
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
res.json(resultado);
|
|
237
|
+
} catch (error) {
|
|
238
|
+
handleError(res, error);
|
|
239
|
+
}
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
app.get('/nfse/municipios', (_req, res) => {
|
|
243
|
+
const municipios = NFSeMunicipioResolver.listarMunicipios()
|
|
244
|
+
.filter((c) => c !== 'mock')
|
|
245
|
+
.map((codigo) => {
|
|
246
|
+
const info = NFSeMunicipioResolver.resolver(codigo, 2);
|
|
247
|
+
return { codigo, ...info };
|
|
248
|
+
});
|
|
249
|
+
res.json({ municipios, layouts: Object.values(NFSE_LAYOUTS) });
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
app.get('/nfe/cenarios-mock', (_req, res) => {
|
|
253
|
+
res.json({
|
|
254
|
+
cenarios: [
|
|
255
|
+
{ id: 'autorizado', descricao: 'NF-e autorizada (cStat 100)', cStat: '100' },
|
|
256
|
+
{ id: 'duplicidade', descricao: 'Rejeição por duplicidade (cStat 204)', cStat: '204' },
|
|
257
|
+
{ id: 'schema', descricao: 'Rejeição por schema inválido (cStat 225)', cStat: '225' },
|
|
258
|
+
{ id: 'certificado', descricao: 'Certificado inválido (cStat 280)', cStat: '280' },
|
|
259
|
+
{ id: 'paralisado', descricao: 'Serviço paralisado (cStat 108)', cStat: '108' },
|
|
260
|
+
],
|
|
261
|
+
responses: SEFAZ_MOCK_RESPONSES,
|
|
262
|
+
});
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* @param {import('express').Response} res
|
|
267
|
+
* @param {unknown} error
|
|
268
|
+
*/
|
|
269
|
+
function handleError(res, error) {
|
|
270
|
+
if (error instanceof SefazError) {
|
|
271
|
+
return res.status(422).json({
|
|
272
|
+
error: error.name,
|
|
273
|
+
code: error.code,
|
|
274
|
+
cStat: error.cStat,
|
|
275
|
+
xMotivo: error.xMotivo,
|
|
276
|
+
details: error.details,
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (error instanceof NFSeSoapError) {
|
|
281
|
+
return res.status(422).json({
|
|
282
|
+
error: error.name,
|
|
283
|
+
code: error.code,
|
|
284
|
+
codigo: error.codigo,
|
|
285
|
+
message: error.message,
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (error instanceof XsdValidationError) {
|
|
290
|
+
return res.status(422).json({
|
|
291
|
+
error: error.name,
|
|
292
|
+
code: error.code,
|
|
293
|
+
message: error.message,
|
|
294
|
+
erros: error.erros,
|
|
295
|
+
schema: error.schema,
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (error instanceof GhinfeError) {
|
|
300
|
+
return res.status(400).json({
|
|
301
|
+
error: error.name,
|
|
302
|
+
code: error.code,
|
|
303
|
+
message: error.message,
|
|
304
|
+
details: error.details,
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
console.error(error);
|
|
309
|
+
res.status(500).json({ error: 'InternalServerError', message: 'Erro interno' });
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
app.listen(port, () => {
|
|
313
|
+
console.log(`GHINFE API rodando em http://localhost:${port}`);
|
|
314
|
+
console.log(`Swagger UI: http://localhost:${port}/docs`);
|
|
315
|
+
console.log(`Guia do desenvolvedor: http://localhost:${port}/guia/developer-guide.html`);
|
|
316
|
+
});
|