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,175 @@
1
+ /**
2
+ * Constantes e helpers — Reforma Tributária (NT 2025.002 / PL_010).
3
+ * IBS + CBS. Injeção no builder NF-e/NFC-e quando item.reforma ou reformaTributaria.
4
+ */
5
+
6
+ export const REFORMA_LAYOUT = 'PL_010';
7
+
8
+ export const CST_IBSCBS = {
9
+ TRIBUTACAO_INTEGRAL: '000',
10
+ ISENCAO: '100',
11
+ DIFERIMENTO: '200',
12
+ REDUCAO_BASE: '300',
13
+ SUSPENSAO: '400',
14
+ };
15
+
16
+ /**
17
+ * @typedef {Object} TributacaoRTC
18
+ * @property {string} [cst]
19
+ * @property {string} [cClassTrib]
20
+ * @property {number} [vBC]
21
+ * @property {number} [pIBSUF]
22
+ * @property {number} [pIBSMun]
23
+ * @property {number} [pCBS]
24
+ * @property {number} [vIBSUF]
25
+ * @property {number} [vIBSMun]
26
+ * @property {number} [vCBS]
27
+ */
28
+
29
+ /**
30
+ * @param {number} baseCalculo
31
+ * @param {Omit<TributacaoRTC, 'vIBSUF'|'vIBSMun'|'vCBS'|'vBC'>} [aliquotas]
32
+ * @returns {TributacaoRTC & { vBC: number }}
33
+ */
34
+ export function calcularIBSCBS(baseCalculo, aliquotas = {}) {
35
+ const pIBSUF = aliquotas.pIBSUF ?? 0;
36
+ const pIBSMun = aliquotas.pIBSMun ?? 0;
37
+ const pCBS = aliquotas.pCBS ?? 0;
38
+ const base = Number(baseCalculo) || 0;
39
+
40
+ return {
41
+ cst: aliquotas.cst ?? CST_IBSCBS.TRIBUTACAO_INTEGRAL,
42
+ cClassTrib: aliquotas.cClassTrib,
43
+ vBC: base,
44
+ pIBSUF,
45
+ pIBSMun,
46
+ pCBS,
47
+ vIBSUF: Number(((base * pIBSUF) / 100).toFixed(2)),
48
+ vIBSMun: Number(((base * pIBSMun) / 100).toFixed(2)),
49
+ vCBS: Number(((base * pCBS) / 100).toFixed(2)),
50
+ };
51
+ }
52
+
53
+ /**
54
+ * @param {TributacaoRTC} trib
55
+ * @returns {string}
56
+ */
57
+ export function buildFragmentoIBSCBS(trib) {
58
+ const dec = (v, d = 2) => Number(v ?? 0).toFixed(d);
59
+ return `<IBSCBS>
60
+ <CST>${trib.cst ?? CST_IBSCBS.TRIBUTACAO_INTEGRAL}</CST>
61
+ ${trib.cClassTrib ? `<cClassTrib>${trib.cClassTrib}</cClassTrib>` : ''}
62
+ <gIBSCBS>
63
+ <vBC>${dec(trib.vBC ?? 0)}</vBC>
64
+ <gIBSUF>
65
+ <pIBSUF>${dec(trib.pIBSUF, 4)}</pIBSUF>
66
+ <vIBSUF>${dec(trib.vIBSUF)}</vIBSUF>
67
+ </gIBSUF>
68
+ <gIBSMun>
69
+ <pIBSMun>${dec(trib.pIBSMun, 4)}</pIBSMun>
70
+ <vIBSMun>${dec(trib.vIBSMun)}</vIBSMun>
71
+ </gIBSMun>
72
+ <gCBS>
73
+ <pCBS>${dec(trib.pCBS, 4)}</pCBS>
74
+ <vCBS>${dec(trib.vCBS)}</vCBS>
75
+ </gCBS>
76
+ </gIBSCBS>
77
+ </IBSCBS>`;
78
+ }
79
+
80
+ /**
81
+ * Anexa grupo IBSCBS ao nó imposto (xmlbuilder2).
82
+ * @param {import('xmlbuilder2').XMLBuilder} impostoNode
83
+ * @param {TributacaoRTC} trib
84
+ */
85
+ export function appendIBSCBS(impostoNode, trib) {
86
+ if (!trib) return;
87
+ const dec = (v, d = 2) => Number(v ?? 0).toFixed(d);
88
+ const ibs = impostoNode.ele('IBSCBS');
89
+ ibs.ele('CST').txt(trib.cst ?? CST_IBSCBS.TRIBUTACAO_INTEGRAL).up();
90
+ if (trib.cClassTrib) ibs.ele('cClassTrib').txt(trib.cClassTrib).up();
91
+
92
+ const g = ibs.ele('gIBSCBS');
93
+ g.ele('vBC').txt(dec(trib.vBC ?? 0)).up();
94
+
95
+ const gUf = g.ele('gIBSUF');
96
+ gUf.ele('pIBSUF').txt(dec(trib.pIBSUF, 4)).up();
97
+ gUf.ele('vIBSUF').txt(dec(trib.vIBSUF)).up();
98
+ gUf.up();
99
+
100
+ const gMun = g.ele('gIBSMun');
101
+ gMun.ele('pIBSMun').txt(dec(trib.pIBSMun, 4)).up();
102
+ gMun.ele('vIBSMun').txt(dec(trib.vIBSMun)).up();
103
+ gMun.up();
104
+
105
+ const gCbs = g.ele('gCBS');
106
+ gCbs.ele('pCBS').txt(dec(trib.pCBS, 4)).up();
107
+ gCbs.ele('vCBS').txt(dec(trib.vCBS)).up();
108
+ gCbs.up();
109
+
110
+ g.up();
111
+ ibs.up();
112
+ }
113
+
114
+ /**
115
+ * Anexa totais IBS/CBS em &lt;total&gt; (irmão de ICMSTot).
116
+ * @param {import('xmlbuilder2').XMLBuilder} totalParent
117
+ * @param {{ vIBSUF: number, vIBSMun: number, vCBS: number }} totais
118
+ */
119
+ export function appendTotaisIBSCBS(totalParent, totais) {
120
+ if (!totais) return;
121
+ const dec = (v) => Number(v ?? 0).toFixed(2);
122
+ const node = totalParent.ele('IBSCBSTot');
123
+ node.ele('vIBS').txt(dec((totais.vIBSUF ?? 0) + (totais.vIBSMun ?? 0))).up();
124
+ node.ele('vIBSUF').txt(dec(totais.vIBSUF)).up();
125
+ node.ele('vIBSMun').txt(dec(totais.vIBSMun)).up();
126
+ node.ele('vCBS').txt(dec(totais.vCBS)).up();
127
+ node.up();
128
+ }
129
+
130
+ /**
131
+ * @param {Array<{ reforma?: TributacaoRTC, valorTotal?: number }>} itens
132
+ * @param {boolean} [autoCalcular]
133
+ */
134
+ export function normalizarItensReforma(itens = [], autoCalcular = false) {
135
+ return itens.map((item) => {
136
+ if (item.reforma) {
137
+ if (item.reforma.vIBSUF == null && autoCalcular !== false) {
138
+ return {
139
+ ...item,
140
+ reforma: calcularIBSCBS(item.reforma.vBC ?? item.valorTotal ?? 0, item.reforma),
141
+ };
142
+ }
143
+ return item;
144
+ }
145
+ return item;
146
+ });
147
+ }
148
+
149
+ /**
150
+ * @param {Array<{ reforma?: TributacaoRTC }>} itens
151
+ */
152
+ export function somarTotaisReforma(itens = []) {
153
+ return itens.reduce(
154
+ (acc, item) => {
155
+ const r = item.reforma;
156
+ if (!r) return acc;
157
+ acc.vIBSUF += r.vIBSUF ?? 0;
158
+ acc.vIBSMun += r.vIBSMun ?? 0;
159
+ acc.vCBS += r.vCBS ?? 0;
160
+ return acc;
161
+ },
162
+ { vIBSUF: 0, vIBSMun: 0, vCBS: 0 }
163
+ );
164
+ }
165
+
166
+ /**
167
+ * @param {{ reformaTributaria?: boolean, itens?: Array<{ reforma?: unknown }> }} documentoOuOptions
168
+ */
169
+ export function reformaHabilitada(documentoOuOptions = {}) {
170
+ if (documentoOuOptions.reformaTributaria) return true;
171
+ if (Array.isArray(documentoOuOptions.itens)) {
172
+ return documentoOuOptions.itens.some((i) => i.reforma);
173
+ }
174
+ return false;
175
+ }
@@ -0,0 +1,95 @@
1
+ import { UF_AUTORIZADOR, AUTORIZADOR_ENDPOINTS, SEFAZ_SERVICO } from './endpoints.registry.js';
2
+ import { GhinfeError } from '../errors/GhinfeError.js';
3
+
4
+ /**
5
+ * Resolve endpoints SEFAZ por UF, ambiente e tipo de serviço.
6
+ * Elimina necessidade de configurar URLs manualmente em produção.
7
+ */
8
+ export class SefazEndpointResolver {
9
+ /**
10
+ * @param {string} uf Sigla da UF (ex: "SP")
11
+ * @returns {string} Código do autorizador (ex: "SVRS", "SP")
12
+ */
13
+ static obterAutorizador(uf) {
14
+ const autorizador = UF_AUTORIZADOR[uf?.toUpperCase()];
15
+ if (!autorizador) {
16
+ throw new GhinfeError(`UF não mapeada para autorizador SEFAZ: ${uf}`, 'UF_AUTORIZADOR_NOT_FOUND');
17
+ }
18
+ return autorizador;
19
+ }
20
+
21
+ /**
22
+ * @param {string} uf
23
+ * @param {number} [ambiente] 1=Produção, 2=Homologação
24
+ * @returns {Record<string, string>}
25
+ */
26
+ static obterEndpoints(uf, ambiente = 2) {
27
+ const autorizador = this.obterAutorizador(uf);
28
+ const endpoints = AUTORIZADOR_ENDPOINTS[autorizador]?.[ambiente];
29
+
30
+ if (!endpoints) {
31
+ throw new GhinfeError(
32
+ `Endpoints não configurados para autorizador ${autorizador} ambiente ${ambiente}`,
33
+ 'SEFAZ_ENDPOINTS_NOT_FOUND'
34
+ );
35
+ }
36
+
37
+ return { autorizador, ambiente, endpoints };
38
+ }
39
+
40
+ /**
41
+ * @param {{
42
+ * uf: string,
43
+ * ambiente?: number,
44
+ * servico?: string,
45
+ * modelo?: '55'|'65',
46
+ * }} params
47
+ * @returns {string}
48
+ */
49
+ static resolver(params) {
50
+ const { endpoints } = this.obterEndpoints(params.uf, params.ambiente ?? 2);
51
+ const servico = params.servico ?? SEFAZ_SERVICO.AUTORIZACAO;
52
+ const url = endpoints[servico];
53
+
54
+ if (!url) {
55
+ throw new GhinfeError(
56
+ `Serviço "${servico}" não disponível para UF ${params.uf} (autorizador ${this.obterAutorizador(params.uf)})`,
57
+ 'SEFAZ_SERVICO_NOT_FOUND',
58
+ { servico, uf: params.uf, modelo: params.modelo }
59
+ );
60
+ }
61
+
62
+ return url;
63
+ }
64
+
65
+ /**
66
+ * Configuração pronta para NFeSoapService.
67
+ * @param {string} uf
68
+ * @param {number} [ambiente]
69
+ */
70
+ static paraNFe(uf, ambiente = 2) {
71
+ return {
72
+ endpoint: this.resolver({ uf, ambiente, servico: SEFAZ_SERVICO.AUTORIZACAO, modelo: '55' }),
73
+ eventoEndpoint: this.resolver({ uf, ambiente, servico: SEFAZ_SERVICO.EVENTO, modelo: '55' }),
74
+ };
75
+ }
76
+
77
+ /**
78
+ * NFC-e usa os mesmos webservices de autorização na maioria dos estados.
79
+ * @param {string} uf
80
+ * @param {number} [ambiente]
81
+ */
82
+ static paraNFCe(uf, ambiente = 2) {
83
+ return {
84
+ endpoint: this.resolver({ uf, ambiente, servico: SEFAZ_SERVICO.AUTORIZACAO, modelo: '65' }),
85
+ eventoEndpoint: this.resolver({ uf, ambiente, servico: SEFAZ_SERVICO.EVENTO, modelo: '65' }),
86
+ };
87
+ }
88
+
89
+ /** @returns {string[]} */
90
+ static listarUFs() {
91
+ return Object.keys(UF_AUTORIZADOR);
92
+ }
93
+ }
94
+
95
+ export { SEFAZ_SERVICO };
@@ -0,0 +1,205 @@
1
+ /**
2
+ * Mapeamento UF → autorizador SEFAZ (NF-e/NFC-e).
3
+ * Fonte: Manual de Orientação do Contribuinte / tabela de autorizadores.
4
+ */
5
+ export const UF_AUTORIZADOR = {
6
+ AC: 'SVRS', AL: 'SVRS', AM: 'AM', AP: 'SVRS', BA: 'BA',
7
+ CE: 'SVRS', DF: 'SVRS', ES: 'SVRS', GO: 'GO', MA: 'SVAN',
8
+ MG: 'MG', MS: 'MS', MT: 'MT', PA: 'SVAN', PB: 'SVRS',
9
+ PE: 'PE', PI: 'SVRS', PR: 'PR', RJ: 'SVRS', RN: 'SVRS',
10
+ RO: 'SVRS', RR: 'SVRS', RS: 'RS', SC: 'SVRS', SE: 'SVRS',
11
+ SP: 'SP', TO: 'SVRS',
12
+ };
13
+
14
+ /** Tipos de webservice SEFAZ. */
15
+ export const SEFAZ_SERVICO = {
16
+ AUTORIZACAO: 'autorizacao',
17
+ RET_AUTORIZACAO: 'retAutorizacao',
18
+ CONSULTA: 'consulta',
19
+ EVENTO: 'evento',
20
+ INUTILIZACAO: 'inutilizacao',
21
+ STATUS: 'status',
22
+ };
23
+
24
+ /**
25
+ * Endpoints por autorizador e ambiente.
26
+ * ambiente: 1=Produção, 2=Homologação
27
+ */
28
+ export const AUTORIZADOR_ENDPOINTS = {
29
+ SVRS: {
30
+ 1: {
31
+ autorizacao: 'https://nfe.svrs.rs.gov.br/ws/NfeAutorizacao/NFeAutorizacao4.asmx',
32
+ retAutorizacao: 'https://nfe.svrs.rs.gov.br/ws/NfeRetAutorizacao/NFeRetAutorizacao4.asmx',
33
+ consulta: 'https://nfe.svrs.rs.gov.br/ws/NfeConsulta/NfeConsulta4.asmx',
34
+ evento: 'https://nfe.svrs.rs.gov.br/ws/recepcaoevento/ReceptionEvent4.asmx',
35
+ inutilizacao: 'https://nfe.svrs.rs.gov.br/ws/nfeinutilizacao/nfeinutilizacao4.asmx',
36
+ status: 'https://nfe.svrs.rs.gov.br/ws/NfeStatusServico/NfeStatusServico4.asmx',
37
+ },
38
+ 2: {
39
+ autorizacao: 'https://nfe-homologacao.svrs.rs.gov.br/ws/NfeAutorizacao/NFeAutorizacao4.asmx',
40
+ retAutorizacao: 'https://nfe-homologacao.svrs.rs.gov.br/ws/NfeRetAutorizacao/NFeRetAutorizacao4.asmx',
41
+ consulta: 'https://nfe-homologacao.svrs.rs.gov.br/ws/NfeConsulta/NfeConsulta4.asmx',
42
+ evento: 'https://nfe-homologacao.svrs.rs.gov.br/ws/recepcaoevento/ReceptionEvent4.asmx',
43
+ inutilizacao: 'https://nfe-homologacao.svrs.rs.gov.br/ws/nfeinutilizacao/nfeinutilizacao4.asmx',
44
+ status: 'https://nfe-homologacao.svrs.rs.gov.br/ws/NfeStatusServico/NfeStatusServico4.asmx',
45
+ },
46
+ },
47
+ SVAN: {
48
+ 1: {
49
+ autorizacao: 'https://www.sefazvirtual.fazenda.gov.br/NFeAutorizacao4/NFeAutorizacao4.asmx',
50
+ retAutorizacao: 'https://www.sefazvirtual.fazenda.gov.br/NFeRetAutorizacao4/NFeRetAutorizacao4.asmx',
51
+ consulta: 'https://www.sefazvirtual.fazenda.gov.br/NFeConsultaProtocolo4/NFeConsultaProtocolo4.asmx',
52
+ evento: 'https://www.sefazvirtual.fazenda.gov.br/NFeRecepcaoEvento4/NFeRecepcaoEvento4.asmx',
53
+ inutilizacao: 'https://www.sefazvirtual.fazenda.gov.br/NFeInutilizacao4/NFeInutilizacao4.asmx',
54
+ status: 'https://www.sefazvirtual.fazenda.gov.br/NFeStatusServico4/NFeStatusServico4.asmx',
55
+ },
56
+ 2: {
57
+ autorizacao: 'https://hom.sefazvirtual.fazenda.gov.br/NFeAutorizacao4/NFeAutorizacao4.asmx',
58
+ retAutorizacao: 'https://hom.sefazvirtual.fazenda.gov.br/NFeRetAutorizacao4/NFeRetAutorizacao4.asmx',
59
+ consulta: 'https://hom.sefazvirtual.fazenda.gov.br/NFeConsultaProtocolo4/NFeConsultaProtocolo4.asmx',
60
+ evento: 'https://hom.sefazvirtual.fazenda.gov.br/NFeRecepcaoEvento4/NFeRecepcaoEvento4.asmx',
61
+ inutilizacao: 'https://hom.sefazvirtual.fazenda.gov.br/NFeInutilizacao4/NFeInutilizacao4.asmx',
62
+ status: 'https://hom.sefazvirtual.fazenda.gov.br/NFeStatusServico4/NFeStatusServico4.asmx',
63
+ },
64
+ },
65
+ SP: {
66
+ 1: {
67
+ autorizacao: 'https://nfe.fazenda.sp.gov.br/ws/nfeautorizacao4.asmx',
68
+ retAutorizacao: 'https://nfe.fazenda.sp.gov.br/ws/nferetautorizacao4.asmx',
69
+ consulta: 'https://nfe.fazenda.sp.gov.br/ws/nfeconsultaprotocolo4.asmx',
70
+ evento: 'https://nfe.fazenda.sp.gov.br/ws/nferecepcaoevento4.asmx',
71
+ inutilizacao: 'https://nfe.fazenda.sp.gov.br/ws/nfeinutilizacao4.asmx',
72
+ status: 'https://nfe.fazenda.sp.gov.br/ws/nfestatusservico4.asmx',
73
+ },
74
+ 2: {
75
+ autorizacao: 'https://homologacao.nfe.fazenda.sp.gov.br/ws/nfeautorizacao4.asmx',
76
+ retAutorizacao: 'https://homologacao.nfe.fazenda.sp.gov.br/ws/nferetautorizacao4.asmx',
77
+ consulta: 'https://homologacao.nfe.fazenda.sp.gov.br/ws/nfeconsultaprotocolo4.asmx',
78
+ evento: 'https://homologacao.nfe.fazenda.sp.gov.br/ws/nferecepcaoevento4.asmx',
79
+ inutilizacao: 'https://homologacao.nfe.fazenda.sp.gov.br/ws/nfeinutilizacao4.asmx',
80
+ status: 'https://homologacao.nfe.fazenda.sp.gov.br/ws/nfestatusservico4.asmx',
81
+ },
82
+ },
83
+ MG: {
84
+ 1: {
85
+ autorizacao: 'https://nfe.fazenda.mg.gov.br/nfe2/services/NFeAutorizacao4',
86
+ retAutorizacao: 'https://nfe.fazenda.mg.gov.br/nfe2/services/NFeRetAutorizacao4',
87
+ consulta: 'https://nfe.fazenda.mg.gov.br/nfe2/services/NFeConsultaProtocolo4',
88
+ evento: 'https://nfe.fazenda.mg.gov.br/nfe2/services/NFeRecepcaoEvento4',
89
+ inutilizacao: 'https://nfe.fazenda.mg.gov.br/nfe2/services/NFeInutilizacao4',
90
+ status: 'https://nfe.fazenda.mg.gov.br/nfe2/services/NFeStatusServico4',
91
+ },
92
+ 2: {
93
+ autorizacao: 'https://hnfe.fazenda.mg.gov.br/nfe2/services/NFeAutorizacao4',
94
+ retAutorizacao: 'https://hnfe.fazenda.mg.gov.br/nfe2/services/NFeRetAutorizacao4',
95
+ consulta: 'https://hnfe.fazenda.mg.gov.br/nfe2/services/NFeConsultaProtocolo4',
96
+ evento: 'https://hnfe.fazenda.mg.gov.br/nfe2/services/NFeRecepcaoEvento4',
97
+ inutilizacao: 'https://hnfe.fazenda.mg.gov.br/nfe2/services/NFeInutilizacao4',
98
+ status: 'https://hnfe.fazenda.mg.gov.br/nfe2/services/NFeStatusServico4',
99
+ },
100
+ },
101
+ PR: {
102
+ 1: {
103
+ autorizacao: 'https://nfe.sefa.pr.gov.br/nfe/NFeAutorizacao4',
104
+ retAutorizacao: 'https://nfe.sefa.pr.gov.br/nfe/NFeRetAutorizacao4',
105
+ consulta: 'https://nfe.sefa.pr.gov.br/nfe/NFeConsultaProtocolo4',
106
+ evento: 'https://nfe.sefa.pr.gov.br/nfe/NFeRecepcaoEvento4',
107
+ inutilizacao: 'https://nfe.sefa.pr.gov.br/nfe/NFeInutilizacao4',
108
+ status: 'https://nfe.sefa.pr.gov.br/nfe/NFeStatusServico4',
109
+ },
110
+ 2: {
111
+ autorizacao: 'https://homologacao.nfe.sefa.pr.gov.br/nfe/NFeAutorizacao4',
112
+ retAutorizacao: 'https://homologacao.nfe.sefa.pr.gov.br/nfe/NFeRetAutorizacao4',
113
+ consulta: 'https://homologacao.nfe.sefa.pr.gov.br/nfe/NFeConsultaProtocolo4',
114
+ evento: 'https://homologacao.nfe.sefa.pr.gov.br/nfe/NFeRecepcaoEvento4',
115
+ inutilizacao: 'https://homologacao.nfe.sefa.pr.gov.br/nfe/NFeInutilizacao4',
116
+ status: 'https://homologacao.nfe.sefa.pr.gov.br/nfe/NFeStatusServico4',
117
+ },
118
+ },
119
+ RS: {
120
+ 1: {
121
+ autorizacao: 'https://nfe.sefazrs.rs.gov.br/ws/NfeAutorizacao/NFeAutorizacao4.asmx',
122
+ retAutorizacao: 'https://nfe.sefazrs.rs.gov.br/ws/NfeRetAutorizacao/NFeRetAutorizacao4.asmx',
123
+ consulta: 'https://nfe.sefazrs.rs.gov.br/ws/NfeConsulta/NfeConsulta4.asmx',
124
+ evento: 'https://nfe.sefazrs.rs.gov.br/ws/recepcaoevento/ReceptionEvent4.asmx',
125
+ inutilizacao: 'https://nfe.sefazrs.rs.gov.br/ws/nfeinutilizacao/nfeinutilizacao4.asmx',
126
+ status: 'https://nfe.sefazrs.rs.gov.br/ws/NfeStatusServico/NfeStatusServico4.asmx',
127
+ },
128
+ 2: {
129
+ autorizacao: 'https://nfe-homologacao.sefazrs.rs.gov.br/ws/NfeAutorizacao/NFeAutorizacao4.asmx',
130
+ retAutorizacao: 'https://nfe-homologacao.sefazrs.rs.gov.br/ws/NfeRetAutorizacao/NFeRetAutorizacao4.asmx',
131
+ consulta: 'https://nfe-homologacao.sefazrs.rs.gov.br/ws/NfeConsulta/NfeConsulta4.asmx',
132
+ evento: 'https://nfe-homologacao.sefazrs.rs.gov.br/ws/recepcaoevento/ReceptionEvent4.asmx',
133
+ inutilizacao: 'https://nfe-homologacao.sefazrs.rs.gov.br/ws/nfeinutilizacao/nfeinutilizacao4.asmx',
134
+ status: 'https://nfe-homologacao.sefazrs.rs.gov.br/ws/NfeStatusServico/NfeStatusServico4.asmx',
135
+ },
136
+ },
137
+ MS: {
138
+ 1: {
139
+ autorizacao: 'https://nfe.sefaz.ms.gov.br/ws/NfeAutorizacao4',
140
+ evento: 'https://nfe.sefaz.ms.gov.br/ws/NfeRecepcaoEvento4',
141
+ consulta: 'https://nfe.sefaz.ms.gov.br/ws/NfeConsulta4',
142
+ status: 'https://nfe.sefaz.ms.gov.br/ws/NfeStatusServico4',
143
+ },
144
+ 2: {
145
+ autorizacao: 'https://homologacao.nfe.ms.gov.br/ws/NfeAutorizacao4',
146
+ evento: 'https://homologacao.nfe.ms.gov.br/ws/NfeRecepcaoEvento4',
147
+ consulta: 'https://homologacao.nfe.ms.gov.br/ws/NfeConsulta4',
148
+ status: 'https://homologacao.nfe.ms.gov.br/ws/NfeStatusServico4',
149
+ },
150
+ },
151
+ MT: {
152
+ 1: {
153
+ autorizacao: 'https://nfe.sefaz.mt.gov.br/nfews/v2/services/NfeAutorizacao4',
154
+ evento: 'https://nfe.sefaz.mt.gov.br/nfews/v2/services/RecepcaoEvento4',
155
+ consulta: 'https://nfe.sefaz.mt.gov.br/nfews/v2/services/NfeConsulta4',
156
+ },
157
+ 2: {
158
+ autorizacao: 'https://homologacao.sefaz.mt.gov.br/nfews/v2/services/NfeAutorizacao4',
159
+ evento: 'https://homologacao.sefaz.mt.gov.br/nfews/v2/services/RecepcaoEvento4',
160
+ consulta: 'https://homologacao.sefaz.mt.gov.br/nfews/v2/services/NfeConsulta4',
161
+ },
162
+ },
163
+ GO: {
164
+ 1: {
165
+ autorizacao: 'https://nfe.sefaz.go.gov.br/nfe/services/NFeAutorizacao4',
166
+ evento: 'https://nfe.sefaz.go.gov.br/nfe/services/NFeRecepcaoEvento4',
167
+ consulta: 'https://nfe.sefaz.go.gov.br/nfe/services/NFeConsultaProtocolo4',
168
+ },
169
+ 2: {
170
+ autorizacao: 'https://homolog.sefaz.go.gov.br/nfe/services/NFeAutorizacao4',
171
+ evento: 'https://homolog.sefaz.go.gov.br/nfe/services/NFeRecepcaoEvento4',
172
+ consulta: 'https://homolog.sefaz.go.gov.br/nfe/services/NFeConsultaProtocolo4',
173
+ },
174
+ },
175
+ AM: {
176
+ 1: {
177
+ autorizacao: 'https://nfe.sefaz.am.gov.br/services2/services/NfeAutorizacao4',
178
+ evento: 'https://nfe.sefaz.am.gov.br/services2/services/RecepcaoEvento4',
179
+ },
180
+ 2: {
181
+ autorizacao: 'https://homnfe.sefaz.am.gov.br/services2/services/NfeAutorizacao4',
182
+ evento: 'https://homnfe.sefaz.am.gov.br/services2/services/RecepcaoEvento4',
183
+ },
184
+ },
185
+ BA: {
186
+ 1: {
187
+ autorizacao: 'https://nfe.sefaz.ba.gov.br/webservices/NFeAutorizacao4/NFeAutorizacao4.asmx',
188
+ evento: 'https://nfe.sefaz.ba.gov.br/webservices/NFeRecepcaoEvento4/NFeRecepcaoEvento4.asmx',
189
+ },
190
+ 2: {
191
+ autorizacao: 'https://hnfe.sefaz.ba.gov.br/webservices/NFeAutorizacao4/NFeAutorizacao4.asmx',
192
+ evento: 'https://hnfe.sefaz.ba.gov.br/webservices/NFeRecepcaoEvento4/NFeRecepcaoEvento4.asmx',
193
+ },
194
+ },
195
+ PE: {
196
+ 1: {
197
+ autorizacao: 'https://nfe.sefaz.pe.gov.br/nfe-service/services/NFeAutorizacao4',
198
+ evento: 'https://nfe.sefaz.pe.gov.br/nfe-service/services/NFeRecepcaoEvento4',
199
+ },
200
+ 2: {
201
+ autorizacao: 'https://nfehomolog.sefaz.pe.gov.br/nfe-service/services/NFeAutorizacao4',
202
+ evento: 'https://nfehomolog.sefaz.pe.gov.br/nfe-service/services/NFeRecepcaoEvento4',
203
+ },
204
+ },
205
+ };
@@ -0,0 +1,53 @@
1
+ import { UF_IBGE } from '../constants/sefaz.js';
2
+ import { appendIBSCBS } from '../reforma/ibscbs.js';
3
+
4
+ /** @param {string} uf */
5
+ export function codigoUf(uf) {
6
+ return UF_IBGE[uf?.toUpperCase()] ?? '35';
7
+ }
8
+
9
+ /** @param {number} value @param {number} decimals */
10
+ export function formatDecimal(value, decimals) {
11
+ return Number(value).toFixed(decimals);
12
+ }
13
+
14
+ /**
15
+ * @param {import('xmlbuilder2').XMLBuilder} parent
16
+ * @param {string} tag
17
+ * @param {import('../nfe/types/NFeDocument.js').NFeEndereco} endereco
18
+ */
19
+ export function appendEndereco(parent, tag, endereco) {
20
+ const node = parent.ele(tag);
21
+ node.ele('xLgr').txt(endereco.logradouro).up();
22
+ node.ele('nro').txt(endereco.numero).up();
23
+ if (endereco.complemento) node.ele('xCpl').txt(endereco.complemento).up();
24
+ node.ele('xBairro').txt(endereco.bairro).up();
25
+ node.ele('cMun').txt(endereco.codigoMunicipio).up();
26
+ node.ele('xMun').txt(endereco.municipio).up();
27
+ node.ele('UF').txt(endereco.uf).up();
28
+ node.ele('CEP').txt(endereco.cep.replace(/\D/g, '')).up();
29
+ if (endereco.telefone) node.ele('fone').txt(endereco.telefone.replace(/\D/g, '')).up();
30
+ node.up();
31
+ }
32
+
33
+ /**
34
+ * @param {import('xmlbuilder2').XMLBuilder} impostoNode
35
+ * @param {import('../nfe/types/NFeDocument.js').NFeItem} item
36
+ */
37
+ export function appendImpostosItem(impostoNode, item) {
38
+ const icms = impostoNode.ele('ICMS').ele('ICMS00');
39
+ icms.ele('orig').txt('0').up();
40
+ icms.ele('CST').txt(item.cst ?? '00').up();
41
+ icms.ele('modBC').txt('0').up();
42
+ icms.ele('vBC').txt(formatDecimal(item.valorTotal, 2)).up();
43
+ icms.ele('pICMS').txt(formatDecimal(item.aliquotaIcms ?? 0, 4)).up();
44
+ icms.ele('vICMS').txt(formatDecimal(item.valorIcms ?? 0, 2)).up();
45
+ icms.up().up();
46
+
47
+ impostoNode.ele('PIS').ele('PISNT').ele('CST').txt('07').up().up().up();
48
+ impostoNode.ele('COFINS').ele('COFINSNT').ele('CST').txt('07').up().up().up();
49
+
50
+ if (item.reforma) {
51
+ appendIBSCBS(impostoNode, item.reforma);
52
+ }
53
+ }
@@ -0,0 +1,47 @@
1
+ import forge from 'node-forge';
2
+ import { GhinfeError } from '../errors/GhinfeError.js';
3
+
4
+ /**
5
+ * Carrega certificado A1 (.pfx / .p12) e retorna chave privada + certificado PEM.
6
+ * @param {Buffer|string} pfxBuffer
7
+ * @param {string} password
8
+ * @returns {{ privateKeyPem: string, certificatePem: string, certificate: forge.pki.Certificate }}
9
+ */
10
+ export function loadPfxCertificate(pfxBuffer, password) {
11
+ try {
12
+ const buffer = Buffer.isBuffer(pfxBuffer) ? pfxBuffer : Buffer.from(pfxBuffer);
13
+ const p12Asn1 = forge.asn1.fromDer(buffer.toString('binary'));
14
+ const p12 = forge.pkcs12.pkcs12FromAsn1(p12Asn1, password);
15
+
16
+ const certBags = p12.getBags({ bagType: forge.pki.oids.certBag });
17
+ const keyBags = p12.getBags({ bagType: forge.pki.oids.pkcs8ShroudedKeyBag });
18
+
19
+ const certBag = certBags[forge.pki.oids.certBag]?.[0];
20
+ const keyBag =
21
+ keyBags[forge.pki.oids.pkcs8ShroudedKeyBag]?.[0] ??
22
+ p12.getBags({ bagType: forge.pki.oids.keyBag })[forge.pki.oids.keyBag]?.[0];
23
+
24
+ if (!certBag?.cert || !keyBag?.key) {
25
+ throw new GhinfeError(
26
+ 'Certificado ou chave privada não encontrados no arquivo PFX',
27
+ 'CERT_NOT_FOUND'
28
+ );
29
+ }
30
+
31
+ const certificate = certBag.cert;
32
+ const privateKey = keyBag.key;
33
+
34
+ return {
35
+ privateKeyPem: forge.pki.privateKeyToPem(privateKey),
36
+ certificatePem: forge.pki.certificateToPem(certificate),
37
+ certificate,
38
+ };
39
+ } catch (error) {
40
+ if (error instanceof GhinfeError) throw error;
41
+ throw new GhinfeError(
42
+ 'Falha ao carregar certificado PFX. Verifique o arquivo e a senha.',
43
+ 'CERT_LOAD_ERROR',
44
+ error
45
+ );
46
+ }
47
+ }