ghinfe 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/.env.example +15 -0
  2. package/LICENSE +21 -0
  3. package/README.md +413 -0
  4. package/docs/developer-guide.html +421 -0
  5. package/docs/openapi.yaml +428 -0
  6. package/examples/emitir-nfce.js +51 -0
  7. package/examples/emitir-nfe.js +83 -0
  8. package/examples/emitir-nfse.js +46 -0
  9. package/examples/endpoints-sefaz.js +14 -0
  10. package/examples/eventos-nfe.js +49 -0
  11. package/examples/gerar-chave.js +25 -0
  12. package/examples/gerar-danfe.js +75 -0
  13. package/examples/layouts-nfse.js +51 -0
  14. package/examples/mock-sefaz.js +52 -0
  15. package/examples/nfse-eventos.js +57 -0
  16. package/examples/reforma-ibscbs.js +27 -0
  17. package/examples/reforma-nfe-xml.js +52 -0
  18. package/examples/status-sefaz.js +12 -0
  19. package/examples/validar-xsd.js +48 -0
  20. package/package.json +68 -0
  21. package/schemas/nfe/PL_009_V4/consReciNFe_v4.00.xsd +9 -0
  22. package/schemas/nfe/PL_009_V4/consSitNFe_v4.00.xsd +9 -0
  23. package/schemas/nfe/PL_009_V4/consStatServ_v4.00.xsd +9 -0
  24. package/schemas/nfe/PL_009_V4/enviNFe_v3.10.xsd +9 -0
  25. package/schemas/nfe/PL_009_V4/enviNFe_v4.00.xsd +9 -0
  26. package/schemas/nfe/PL_009_V4/inutNFe_v4.00.xsd +9 -0
  27. package/schemas/nfe/PL_009_V4/leiauteNFe_v4.00.xsd +7339 -0
  28. package/schemas/nfe/PL_009_V4/nfe_v4.00.xsd +9 -0
  29. package/schemas/nfe/PL_009_V4/tiposBasico_v4.00.xsd +598 -0
  30. package/schemas/nfe/PL_009_V4/xmldsig-core-schema_v1.01.xsd +98 -0
  31. package/scripts/download-schemas.js +39 -0
  32. package/scripts/test-xsd.js +34 -0
  33. package/src/api/server.js +316 -0
  34. package/src/constants/sefaz.js +69 -0
  35. package/src/danfe/DanfeService.js +205 -0
  36. package/src/danfe/xml-extract.js +69 -0
  37. package/src/errors/GhinfeError.js +13 -0
  38. package/src/errors/SefazError.js +25 -0
  39. package/src/errors/XsdValidationError.js +15 -0
  40. package/src/index.js +60 -0
  41. package/src/nfce/builders/NFCeXmlBuilder.js +178 -0
  42. package/src/nfce/constants/qrcode.js +26 -0
  43. package/src/nfce/services/NFCeService.js +188 -0
  44. package/src/nfce/services/NFCeXmlService.js +38 -0
  45. package/src/nfce/types/NFCeDocument.js +42 -0
  46. package/src/nfce/utils/qrcode.utils.js +92 -0
  47. package/src/nfe/builders/NFeEventoXmlBuilder.js +110 -0
  48. package/src/nfe/builders/NFeXmlBuilder.js +215 -0
  49. package/src/nfe/mocks/sefaz-responses.js +195 -0
  50. package/src/nfe/services/NFeEventoService.js +95 -0
  51. package/src/nfe/services/NFeService.js +196 -0
  52. package/src/nfe/services/NFeSignService.js +111 -0
  53. package/src/nfe/services/NFeSoapService.js +437 -0
  54. package/src/nfe/services/NFeXmlService.js +41 -0
  55. package/src/nfe/services/NFeXsdService.js +56 -0
  56. package/src/nfe/types/NFeDocument.js +92 -0
  57. package/src/nfe/types/NFeEvento.js +44 -0
  58. package/src/nfse/NFSeMunicipioResolver.js +42 -0
  59. package/src/nfse/builders/NFSeEventoXmlBuilder.js +92 -0
  60. package/src/nfse/builders/NFSeXmlBuilder.js +16 -0
  61. package/src/nfse/constants/municipios.js +82 -0
  62. package/src/nfse/layouts/adapters.js +202 -0
  63. package/src/nfse/mocks/nfse-responses.js +142 -0
  64. package/src/nfse/services/NFSeService.js +191 -0
  65. package/src/nfse/services/NFSeSoapService.js +232 -0
  66. package/src/nfse/services/NFSeXmlService.js +12 -0
  67. package/src/nfse/types/NFSeDocument.js +54 -0
  68. package/src/queue/NFCeQueue.js +63 -0
  69. package/src/queue/NFSeQueue.js +59 -0
  70. package/src/queue/NFeQueue.js +76 -0
  71. package/src/reforma/ibscbs.js +175 -0
  72. package/src/sefaz/SefazEndpointResolver.js +95 -0
  73. package/src/sefaz/endpoints.registry.js +205 -0
  74. package/src/shared/fiscal.helpers.js +53 -0
  75. package/src/utils/certificate.utils.js +47 -0
  76. package/src/utils/chave.utils.js +117 -0
  77. package/src/utils/date.utils.js +26 -0
  78. package/src/utils/xml.utils.js +44 -0
  79. package/src/validation/XsdValidator.js +114 -0
  80. package/src/validation/schemas.js +14 -0
@@ -0,0 +1,98 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <!-- ***************************************************-->
3
+ <!-- *** Schema específico para assinaturas XML ***-->
4
+ <!-- *** a partir de certificados do padrão (X509) ***-->
5
+ <!-- *** ICP-Brasil - Projeto Nota Fiscal Eletrônica ***-->
6
+ <!-- ***************************************************-->
7
+ <!-- Schema for XML Signatures-->
8
+ <schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:ds="http://www.w3.org/2000/09/xmldsig#" targetNamespace="http://www.w3.org/2000/09/xmldsig#" elementFormDefault="qualified" attributeFormDefault="unqualified" version="0.1">
9
+ <element name="Signature" type="ds:SignatureType"/>
10
+ <complexType name="SignatureType">
11
+ <sequence>
12
+ <element name="SignedInfo" type="ds:SignedInfoType"/>
13
+ <element name="SignatureValue" type="ds:SignatureValueType"/>
14
+ <element name="KeyInfo" type="ds:KeyInfoType"/>
15
+ </sequence>
16
+ <attribute name="Id" type="ID" use="optional"/>
17
+ </complexType>
18
+ <complexType name="SignatureValueType">
19
+ <simpleContent>
20
+ <extension base="base64Binary">
21
+ <attribute name="Id" type="ID" use="optional"/>
22
+ </extension>
23
+ </simpleContent>
24
+ </complexType>
25
+ <complexType name="SignedInfoType">
26
+ <sequence>
27
+ <element name="CanonicalizationMethod">
28
+ <complexType>
29
+ <attribute name="Algorithm" type="anyURI" use="required" fixed="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
30
+ </complexType>
31
+ </element>
32
+ <element name="SignatureMethod">
33
+ <complexType>
34
+ <attribute name="Algorithm" type="anyURI" use="required" fixed="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
35
+ </complexType>
36
+ </element>
37
+ <element name="Reference" type="ds:ReferenceType"/>
38
+ </sequence>
39
+ <attribute name="Id" type="ID" use="optional"/>
40
+ </complexType>
41
+ <complexType name="ReferenceType">
42
+ <sequence>
43
+ <element name="Transforms" type="ds:TransformsType">
44
+ <!-- Garante a unicidade do atributo -->
45
+ <unique name="unique_Transf_Alg">
46
+ <selector xpath="./*"/>
47
+ <field xpath="@Algorithm"/>
48
+ </unique>
49
+ </element>
50
+ <element name="DigestMethod">
51
+ <complexType>
52
+ <attribute name="Algorithm" type="anyURI" use="required" fixed="http://www.w3.org/2000/09/xmldsig#sha1"/>
53
+ </complexType>
54
+ </element>
55
+ <element name="DigestValue" type="ds:DigestValueType"/>
56
+ </sequence>
57
+ <attribute name="Id" type="ID" use="optional"/>
58
+ <attribute name="URI" use="required">
59
+ <simpleType>
60
+ <restriction base="anyURI">
61
+ <minLength value="2"/>
62
+ </restriction>
63
+ </simpleType>
64
+ </attribute>
65
+ <attribute name="Type" type="anyURI" use="optional"/>
66
+ </complexType>
67
+ <complexType name="TransformsType">
68
+ <sequence>
69
+ <element name="Transform" type="ds:TransformType" minOccurs="2" maxOccurs="2"/>
70
+ </sequence>
71
+ </complexType>
72
+ <complexType name="TransformType">
73
+ <sequence minOccurs="0" maxOccurs="unbounded">
74
+ <element name="XPath" type="string"/>
75
+ </sequence>
76
+ <attribute name="Algorithm" type="ds:TTransformURI" use="required"/>
77
+ </complexType>
78
+ <complexType name="KeyInfoType">
79
+ <sequence>
80
+ <element name="X509Data" type="ds:X509DataType"/>
81
+ </sequence>
82
+ <attribute name="Id" type="ID" use="optional"/>
83
+ </complexType>
84
+ <complexType name="X509DataType">
85
+ <sequence>
86
+ <element name="X509Certificate" type="base64Binary"/>
87
+ </sequence>
88
+ </complexType>
89
+ <simpleType name="DigestValueType">
90
+ <restriction base="base64Binary"/>
91
+ </simpleType>
92
+ <simpleType name="TTransformURI">
93
+ <restriction base="anyURI">
94
+ <enumeration value="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
95
+ <enumeration value="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
96
+ </restriction>
97
+ </simpleType>
98
+ </schema>
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Baixa schemas XSD oficiais NF-e/NFC-e (PL_009_V4) do repositório nfephp-org/sped-nfe.
3
+ * Execute: node scripts/download-schemas.js
4
+ */
5
+ import fs from 'fs';
6
+ import path from 'path';
7
+ import { fileURLToPath } from 'url';
8
+
9
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
10
+ const OUT_DIR = path.resolve(__dirname, '../schemas/nfe/PL_009_V4');
11
+ const BASE = 'https://raw.githubusercontent.com/nfephp-org/sped-nfe/master/schemes/PL_009_V4';
12
+
13
+ const FILES = [
14
+ 'nfe_v4.00.xsd',
15
+ 'leiauteNFe_v4.00.xsd',
16
+ 'tiposBasico_v4.00.xsd',
17
+ 'xmldsig-core-schema_v1.01.xsd',
18
+ 'enviNFe_v4.00.xsd',
19
+ 'inutNFe_v4.00.xsd',
20
+ 'consReciNFe_v4.00.xsd',
21
+ 'consSitNFe_v4.00.xsd',
22
+ 'consStatServ_v4.00.xsd',
23
+ ];
24
+
25
+ fs.mkdirSync(OUT_DIR, { recursive: true });
26
+
27
+ for (const file of FILES) {
28
+ const url = `${BASE}/${file}`;
29
+ const dest = path.join(OUT_DIR, file);
30
+ const res = await fetch(url);
31
+ if (!res.ok) {
32
+ console.warn(`⚠ Não foi possível baixar: ${file}`);
33
+ continue;
34
+ }
35
+ fs.writeFileSync(dest, await res.text());
36
+ console.log(`✓ ${file}`);
37
+ }
38
+
39
+ console.log(`\nSchemas salvos em: ${OUT_DIR}`);
@@ -0,0 +1,34 @@
1
+ import libxmljs from 'libxmljs2';
2
+ import fs from 'fs';
3
+ import path from 'path';
4
+ import { pathToFileURL } from 'url';
5
+ import { NFeService } from '../src/index.js';
6
+
7
+ const dir = path.resolve('schemas/nfe/PL_009_V4');
8
+ const baseUrl = pathToFileURL(dir + path.sep).href;
9
+ const xsd = libxmljs.parseXml(fs.readFileSync(path.join(dir, 'nfe_v4.00.xsd'), 'utf8'), { baseUrl });
10
+
11
+ const documento = {
12
+ chaveAcesso: '35260712345678000199550010000000011000000011',
13
+ ambiente: 2,
14
+ ide: { uf: 'SP', codigoMunicipio: '3550308', serie: 1, numero: 1 },
15
+ emitente: {
16
+ cnpj: '12345678000199', razaoSocial: 'EMPRESA EXEMPLO LTDA', ie: '123456789012', crt: '3',
17
+ endereco: { logradouro: 'Rua Exemplo', numero: '100', bairro: 'Centro', codigoMunicipio: '3550308', municipio: 'Sao Paulo', uf: 'SP', cep: '01001000' },
18
+ },
19
+ destinatario: {
20
+ cpf: '12345678909', razaoSocial: 'CONSUMIDOR', indIEDest: 9,
21
+ endereco: { logradouro: 'Rua B', numero: '2', bairro: 'Centro', codigoMunicipio: '3550308', municipio: 'Sao Paulo', uf: 'SP', cep: '01001000' },
22
+ },
23
+ itens: [{ codigo: '001', descricao: 'PRODUTO TESTE', ncm: '84713012', cfop: '5102', unidade: 'UN', quantidade: 1, valorUnitario: 100, valorTotal: 100 }],
24
+ };
25
+
26
+ let xml = new NFeService().gerarXml(documento);
27
+ const id = `NFe${documento.chaveAcesso}`;
28
+ const stub = `<Signature xmlns="http://www.w3.org/2000/09/xmldsig#"><SignedInfo><CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/><SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/><Reference URI="#${id}"><Transforms><Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/><Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/></Transforms><DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/><DigestValue>ZHVtbXk=</DigestValue></Reference></SignedInfo><SignatureValue>ZHVtbXk=</SignatureValue><KeyInfo><X509Data><X509Certificate>ZHVtbXk=</X509Certificate></X509Data></KeyInfo></Signature>`;
29
+ xml = xml.replace('</NFe>', `${stub}</NFe>`);
30
+
31
+ const doc = libxmljs.parseXml(xml);
32
+ const valid = doc.validate(xsd);
33
+ console.log('valid:', valid);
34
+ if (!valid) doc.validationErrors.forEach((e) => console.log(e.message));
@@ -0,0 +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
+ });
@@ -0,0 +1,69 @@
1
+ /** Códigos de UF conforme tabela IBGE (últimos 2 dígitos da chave NF-e). */
2
+ export const UF_IBGE = {
3
+ AC: '12',
4
+ AL: '27',
5
+ AM: '13',
6
+ AP: '16',
7
+ BA: '29',
8
+ CE: '23',
9
+ DF: '53',
10
+ ES: '32',
11
+ GO: '52',
12
+ MA: '21',
13
+ MG: '31',
14
+ MS: '50',
15
+ MT: '51',
16
+ PA: '15',
17
+ PB: '25',
18
+ PE: '26',
19
+ PI: '22',
20
+ PR: '41',
21
+ RJ: '33',
22
+ RN: '24',
23
+ RO: '11',
24
+ RR: '14',
25
+ RS: '43',
26
+ SC: '42',
27
+ SE: '28',
28
+ SP: '35',
29
+ TO: '17',
30
+ };
31
+
32
+ /** 1 = Produção, 2 = Homologação. */
33
+ export const AMBIENTE = {
34
+ PRODUCAO: 1,
35
+ HOMOLOGACAO: 2,
36
+ };
37
+
38
+ /** Layout NF-e versão 4.00. */
39
+ export const NFE_VERSAO = '4.00';
40
+
41
+ /** Endpoints SEFAZ (homologação) — variam por UF; use como referência. */
42
+ export const SEFAZ_URLS = {
43
+ SP: {
44
+ autorizacao: 'https://homologacao.nfe.fazenda.sp.gov.br/ws/nfeautorizacao4.asmx',
45
+ retAutorizacao: 'https://homologacao.nfe.fazenda.sp.gov.br/ws/nferetautorizacao4.asmx',
46
+ consulta: 'https://homologacao.nfe.fazenda.sp.gov.br/ws/nfeconsultaprotocolo4.asmx',
47
+ evento: 'https://homologacao.nfe.fazenda.sp.gov.br/ws/nferecepcaoevento4.asmx',
48
+ },
49
+ };
50
+
51
+ /** cStat mais comuns retornados pela SEFAZ. */
52
+ export const SEFAZ_STATUS = {
53
+ AUTORIZADO: '100',
54
+ CANCELADO: '101',
55
+ LOTE_RECEBIDO: '103',
56
+ LOTE_PROCESSADO: '104',
57
+ LOTE_EM_PROCESSAMENTO: '105',
58
+ SERVICO_EM_OPERACAO: '107',
59
+ SERVICO_PARALISADO: '108',
60
+ SERVICO_PARALISADO_SEM_PREVISAO: '109',
61
+ USO_DENEGADO: '110',
62
+ REJEICAO_DUPLICIDADE: '204',
63
+ REJEICAO_CHAVE_INVALIDA: '236',
64
+ REJEICAO_CERTIFICADO: '280',
65
+ REJEICAO_SCHEMA: '225',
66
+ EVENTO_REGISTRADO: '135',
67
+ EVENTO_LOTE_PROCESSADO: '128',
68
+ EVENTO_REJEICAO: '491',
69
+ };