nfse-brazil-national 1.0.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.
@@ -0,0 +1,55 @@
1
+ # This workflow will run tests using node and then publish a package to GitHub Packages and npm Registry when a release is created
2
+ # For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages
3
+
4
+ name: Node.js Package
5
+
6
+ on:
7
+ release:
8
+ types: [created]
9
+
10
+ jobs:
11
+ build:
12
+ runs-on: ubuntu-latest
13
+ steps:
14
+ - uses: actions/checkout@v4
15
+ - uses: actions/setup-node@v4
16
+ with:
17
+ node-version: 20
18
+ - run: npm ci
19
+ - run: npm test
20
+
21
+ publish-gpr:
22
+ needs: build
23
+ runs-on: ubuntu-latest
24
+ permissions:
25
+ contents: read
26
+ packages: write
27
+ steps:
28
+ - uses: actions/checkout@v4
29
+ - uses: actions/setup-node@v4
30
+ with:
31
+ node-version: 20
32
+ registry-url: https://npm.pkg.github.com/
33
+ - run: npm ci
34
+ - run: npm publish
35
+ env:
36
+ NODE_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}}
37
+
38
+ publish-npm:
39
+ needs: build
40
+ runs-on: ubuntu-latest
41
+ environment: production
42
+ permissions:
43
+ id-token: write
44
+ contents: read
45
+ steps:
46
+ - uses: actions/checkout@v6
47
+ - name: Use Node.js
48
+ uses: actions/setup-node@v6
49
+ with:
50
+ node-version: 24
51
+ registry-url: https://registry.npmjs.org/
52
+
53
+ - name: Publish to npm
54
+ run: NODE_AUTH_TOKEN="" npm publish --provenance --access public
55
+ continue-on-error: true
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="JavaScriptLibraryMappings">
4
+ <includedPredefinedLibrary name="Node.js Core" />
5
+ </component>
6
+ </project>
package/.idea/misc.xml ADDED
@@ -0,0 +1,5 @@
1
+ <project version="4">
2
+ <component name="ProjectRootManager" version="2">
3
+ <output url="file://$PROJECT_DIR$/out" />
4
+ </component>
5
+ </project>
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ProjectModuleManager">
4
+ <modules>
5
+ <module fileurl="file://$PROJECT_DIR$/nfse-brazil-national.iml" filepath="$PROJECT_DIR$/nfse-brazil-national.iml" />
6
+ </modules>
7
+ </component>
8
+ </project>
package/.idea/vcs.xml ADDED
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="VcsDirectoryMappings">
4
+ <mapping directory="$PROJECT_DIR$" vcs="Git" />
5
+ </component>
6
+ </project>
package/README.md ADDED
@@ -0,0 +1,105 @@
1
+ # nfse-brazil-national
2
+
3
+ [![NPM version](https://img.shields.io/npm/v/nfse-brazil-national)](https://www.npmjs.com/package/nfse-brazil-national)
4
+
5
+ Biblioteca cliente para integração com a API Nacional da NFS-e (Nota Fiscal de Serviço Eletrônica) do Brasil.
6
+
7
+ Esta biblioteca facilita a geração, assinatura, validação e transmissão de documentos XML (DPS e Eventos) para o ambiente nacional da NFS-e.
8
+
9
+ ## Funcionalidades
10
+
11
+ * **Geração de XML**: Criação de XMLs de DPS e Cancelamento baseados em templates.
12
+ * **Assinatura Digital**: Assinatura XMLDSIG (padrão ICP-Brasil) utilizando certificados A1 (PFX/PKCS#12 ou PEM).
13
+ * **Validação XSD**: Validação local dos XMLs gerados contra os schemas oficiais antes do envio.
14
+ * **Comunicação API**: Métodos para emissão síncrona, consulta e cancelamento.
15
+ * **Helpers**: Utilitários para geração de IDs (DPS/Eventos) e formatação de datas no padrão exigido.
16
+
17
+ ## Instalação
18
+
19
+ ```bash
20
+ npm install nfse-brazil-national
21
+ ```
22
+
23
+ ## Uso
24
+
25
+ ### Configuração
26
+
27
+ Importe a classe e instancie o cliente com as credenciais e o certificado digital.
28
+
29
+ ```javascript
30
+ import { NfseNationalClient } from "nfse-brazil-national";
31
+
32
+ const client = new NfseNationalClient({
33
+ // URL do ambiente (Homologação ou Produção)
34
+ baseURL: "https://sefin.producaorestrita.nfse.gov.br/SefinNacional",
35
+ // Caminho para o certificado PFX ou Buffer
36
+ certificate: "./certificado.pfx",
37
+ // Senha do certificado
38
+ password: "sua-senha-aqui"
39
+ });
40
+ ```
41
+
42
+ ### Emissão de NFS-e (Envio de DPS)
43
+
44
+ Para emitir uma nota, utilize os métodos estáticos para gerar IDs e datas no formato correto.
45
+
46
+ ```javascript
47
+ // 1. Gerar ID e Data formatada
48
+ const idDps = NfseNationalClient.generateDpsId(
49
+ "3304557", // Código Município Emitente (ex: Rio de Janeiro)
50
+ "2", // Tipo Inscrição Federal (2=CNPJ)
51
+ "00000000000191", // CNPJ Emitente
52
+ "900", // Série
53
+ "1" // Número DPS
54
+ );
55
+
56
+ const dataEmissao = NfseNationalClient.generateDateTime(); // Data atual formatada
57
+
58
+ // 2. Montar dados (exemplo simplificado)
59
+ const dadosDps = {
60
+ id: idDps,
61
+ ambiente: "2", // 2-Homologação
62
+ dataEmissao: dataEmissao,
63
+ // ... demais campos (prestador, tomador, servico, valores)
64
+ };
65
+
66
+ // 3. Emitir (Gera XML, Assina, Valida XSD e Envia)
67
+ try {
68
+ const resultado = await client.issueNfse(dadosDps);
69
+ console.log("NFS-e emitida com sucesso!");
70
+ console.log("Chave de Acesso:", resultado.chaveAcesso);
71
+ } catch (error) {
72
+ console.error("Erro na emissão:", error.message);
73
+ }
74
+ ```
75
+
76
+ ### Cancelamento de NFS-e
77
+
78
+ ```javascript
79
+ const chaveAcesso = "332601..."; // Chave da nota a cancelar
80
+
81
+ // 1. Gerar ID do Evento (101101 = Cancelamento)
82
+ const idEvento = NfseNationalClient.generateEventId(chaveAcesso, "101101");
83
+
84
+ const dadosCancelamento = {
85
+ id: idEvento,
86
+ ambiente: "2",
87
+ versaoAplicacao: "1.0.0",
88
+ dataHoraEvento: NfseNationalClient.generateDateTime(),
89
+ chaveAcesso: chaveAcesso,
90
+ codigoMotivo: "1", // 1-Erro na Emissão
91
+ descricaoMotivo: "Cancelamento solicitado via API"
92
+ };
93
+
94
+ // 2. Enviar Cancelamento
95
+ const resultado = await client.cancelNfse(dadosCancelamento, chaveAcesso);
96
+ ```
97
+
98
+ ## API Reference
99
+
100
+ * `issueNfse(dpsData)`: Gera o XML do DPS, assina e envia para a API.
101
+ * `cancelNfse(cancelamentoData, chaveAcesso)`: Gera o XML do evento, assina e envia.
102
+ * `getNfse(chaveAcesso)`: Retorna os dados de uma NFS-e específica.
103
+ * `getDps(idDps)`: Consulta status de um DPS.
104
+ * `generateDpsXml(dpsData)`: Apenas gera o XML assinado (sem enviar).
105
+ * `validateDpsXml(xmlString)`: Valida XML contra o XSD.
@@ -0,0 +1,3 @@
1
+ {
2
+ "lastSequence": 1
3
+ }
@@ -0,0 +1,184 @@
1
+ import { NfseNationalClient } from "../src/index.js";
2
+ import fs from "fs/promises";
3
+ import path from "path";
4
+ import { fileURLToPath } from "url";
5
+
6
+ // --- CONFIGURAÇÃO ---
7
+ const CERTIFICATE_PATH = "...";
8
+ const CERTIFICATE_PASSWORD = "---";
9
+ const SEQUENCE_FILE = path.join(path.dirname(fileURLToPath(import.meta.url)), "sequence.json");
10
+ // --------------------
11
+
12
+ async function getNextSequence() {
13
+ try {
14
+ const data = await fs.readFile(SEQUENCE_FILE, 'utf8');
15
+ const json = JSON.parse(data);
16
+ const next = json.lastSequence + 1;
17
+ await fs.writeFile(SEQUENCE_FILE, JSON.stringify({ lastSequence: next }, null, 2));
18
+ return next;
19
+ } catch (error) {
20
+ // Se o arquivo não existir, começa do 1
21
+ const initial = 1;
22
+ await fs.writeFile(SEQUENCE_FILE, JSON.stringify({ lastSequence: initial }, null, 2));
23
+ return initial;
24
+ }
25
+ }
26
+
27
+ async function main() {
28
+ const baseURL = "https://sefin.producaorestrita.nfse.gov.br/SefinNacional";
29
+ //const baseURL = "https://sefin.nfse.gov.br/SefinNacional";
30
+
31
+ try {
32
+ console.log(`Using certificate: ${CERTIFICATE_PATH}`);
33
+
34
+ const client = new NfseNationalClient({
35
+ baseURL: baseURL,
36
+ certificate: CERTIFICATE_PATH,
37
+ password: CERTIFICATE_PASSWORD
38
+ });
39
+
40
+ // Dados para geração do ID da DPS
41
+ const municipioEmitente = "3304557"; // Rio de Janeiro
42
+ const tipoInscricaoFederal = "2"; // 2-CNPJ
43
+ const inscricaoFederal = "38027543000175"; // CNPJ
44
+ const serieDps = "900"; // Série do XML exemplo
45
+
46
+ // Obtém o próximo número sequencial
47
+ const seq = await getNextSequence();
48
+ const numeroDps = seq.toString();
49
+
50
+ // Gera o ID utilizando o helper da biblioteca
51
+ const idDps = NfseNationalClient.generateDpsId(
52
+ municipioEmitente,
53
+ tipoInscricaoFederal,
54
+ inscricaoFederal,
55
+ serieDps,
56
+ numeroDps
57
+ );
58
+
59
+ const dadosDps = {
60
+ // --- Informações do DPS ---
61
+ id: idDps,
62
+ versaoAplicacao: "1.0.0",
63
+ ambiente: "2", // 1-Produção, 2-Homologação
64
+ dataEmissao: NfseNationalClient.generateDateTime(),
65
+ serie: serieDps,
66
+ numero: numeroDps,
67
+ competencia: "2026-01-01", // Competência do XML exemplo
68
+ tipoEmitente: "1", // 1-Prestador
69
+ municipioEmissao: municipioEmitente,
70
+
71
+ // --- Prestador ---
72
+ prestador: {
73
+ cnpj: inscricaoFederal,
74
+ telefone: "5121026080",
75
+ optanteSimplesNacional: "3", // 1-Não Optante, 2-MEI, 3-ME/EPP
76
+ regimeApuracaoTributacaoSN: "1", // 1-Recolhimento pelo SN
77
+ regimeEspecialTributacao: "0" // 0-Nenhum
78
+ },
79
+
80
+ // --- Tomador ---
81
+ tomador: {
82
+ cpf: "...",
83
+ nome: "...",
84
+
85
+ /*endereco: {
86
+ codigoMunicipio: "...",
87
+ cep: "...",
88
+ logradouro: "...",
89
+ numero: "...",
90
+ bairro: "...",
91
+ complemento: "...",
92
+ },
93
+ telefone: "...",
94
+ email: "..."*/
95
+ },
96
+
97
+ // --- Serviço ---
98
+ servico: {
99
+ municipioPrestacao: municipioEmitente,
100
+ codigoTributacaoNacional: "080201",
101
+ codigoTributacaoMunicipal: "015",
102
+ descricao: "...",
103
+ codigoNbs: "122051900",
104
+ codigoInterno: "0"
105
+ },
106
+
107
+ valores: {
108
+ valorServicos: "10.00",
109
+ tributacaoIssqn: "1",
110
+ tipoRetencaoIssqn: "1",
111
+ //aliquotaIssqn: "0.00",
112
+ tributosDetalhado: {
113
+ federal: "0.00",
114
+ estadual: "0.00",
115
+ municipal: "0.00"
116
+ }
117
+ }
118
+ };
119
+
120
+ console.log("Generating XML...");
121
+ const xmlAssinado = await client.generateDpsXml(dadosDps);
122
+
123
+ console.log("Validating XML against XSD...");
124
+ await client.validateDpsXml(xmlAssinado);
125
+ console.log("XML Valid!");
126
+
127
+ console.log(`Sending DPS (ID: ${dadosDps.id})...`);
128
+
129
+ let resultado;
130
+ resultado = await client.issueNfse(xmlAssinado);
131
+
132
+ if (resultado) {
133
+ console.log("Success! API Response:");
134
+ console.log(JSON.stringify(resultado, null, 2));
135
+ }
136
+
137
+ if (resultado && resultado.chaveAcesso) {
138
+ console.log(`\nAttempting to cancel NFS-e: ${resultado.chaveAcesso}`);
139
+
140
+ const cancelamentoData = {
141
+ id: NfseNationalClient.generateEventId(resultado.chaveAcesso, "101101"),
142
+ ambiente: "2", // 1-Produção, 2-Homologação
143
+ versaoAplicacao: "1.0.0",
144
+ dataHoraEvento: NfseNationalClient.generateDateTime(),
145
+ cnpjAutor: inscricaoFederal,
146
+ chaveAcesso: resultado.chaveAcesso,
147
+ numeroPedido: "001",
148
+ codigoMotivo: "1", // 1-Erro na Emissão
149
+ descricaoMotivo: "Cancellation test via API"
150
+ };
151
+
152
+ console.log("Generating Cancellation XML...");
153
+ const xmlCancelamento = await client.generateCancellationXml(cancelamentoData);
154
+
155
+ console.log("Validating Cancellation XML...");
156
+ await client.validateEventXml(xmlCancelamento);
157
+ console.log("Cancellation XML Valid!");
158
+
159
+ const resultadoCancelamento = await client.cancelNfse(xmlCancelamento, resultado.chaveAcesso);
160
+ console.log("Cancellation successful!");
161
+ console.log(JSON.stringify(resultadoCancelamento, null, 2));
162
+ }
163
+
164
+ } catch (error) {
165
+ console.error("Execution error:");
166
+ if (error.status) {
167
+ console.error(`HTTP Status: ${error.status}`);
168
+ console.error("Details:", JSON.stringify(error.data, null, 2));
169
+ } else {
170
+ console.error("Message:", error.message);
171
+ if (error.cause) {
172
+ console.error("Cause:", error.cause);
173
+ }
174
+ if (error.code) {
175
+ console.error("Error Code:", error.code);
176
+ }
177
+ if (error.originalError && error.originalError.response) {
178
+ console.error("Response Data:", error.originalError.response.data);
179
+ }
180
+ }
181
+ }
182
+ }
183
+
184
+ main();
package/fix-xsd.js ADDED
@@ -0,0 +1,59 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+
5
+ const __filename = fileURLToPath(import.meta.url);
6
+ const __dirname = path.dirname(__filename);
7
+
8
+ const targetFile = path.join(__dirname, 'src', 'templates', 'xsd', 'tiposSimples_v1.00.xsd');
9
+
10
+ async function main() {
11
+ try {
12
+ console.log(`Lendo arquivo: ${targetFile}`);
13
+ let data = await fs.readFile(targetFile, 'utf8');
14
+ let modified = false;
15
+
16
+ // 1. Substituir \d por [0-9] para compatibilidade com libxml2/xmllint
17
+ // O xmllint (usado no macOS/Linux) não suporta o atalho \d em XSD regex, exigindo [0-9]
18
+ if (data.includes('\\d')) {
19
+ // Substitui todas as ocorrências globais de \d por [0-9]
20
+ data = data.replace(/\\d/g, '[0-9]');
21
+ console.log('✅ Todas as ocorrências de \\d substituídas por [0-9].');
22
+ modified = true;
23
+ }
24
+
25
+ // 2. Remover âncoras ^ e $ (XSD 1.0 trata como literais, não como início/fim de string)
26
+ if (data.includes('^') || data.includes('$')) {
27
+ // Remove ^ logo após as aspas de abertura
28
+ data = data.replace(/value="\^/g, 'value="');
29
+
30
+ // Remove $ logo antes das aspas de fechamento
31
+ data = data.replace(/\$"/g, '"');
32
+
33
+ console.log('✅ Âncoras ^ e $ removidas (incompatíveis com XSD 1.0/libxml2).');
34
+ modified = true;
35
+ }
36
+
37
+ // 2. Atualizar versão do Schema para 1.01
38
+ /*const oldVersionStr = 'value="1\\.00"';
39
+ const newVersionStr = 'value="1\\.01"';
40
+
41
+ if (data.includes(oldVersionStr)) {
42
+ data = data.replaceAll(oldVersionStr, newVersionStr);
43
+ console.log('✅ Versão do Schema atualizada para 1.01.');
44
+ modified = true;
45
+ }*/
46
+
47
+
48
+ if (modified) {
49
+ await fs.writeFile(targetFile, data, 'utf8');
50
+ console.log('💾 Arquivo XSD salvo com sucesso.');
51
+ } else {
52
+ console.log('⚠️ Nenhuma alteração necessária (o arquivo já pode estar atualizado).');
53
+ }
54
+ } catch (err) {
55
+ console.error('❌ Erro:', err.message);
56
+ }
57
+ }
58
+
59
+ main();
@@ -0,0 +1,8 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <module type="WEB_MODULE" version="4">
3
+ <component name="NewModuleRootManager" inherit-compiler-output="true">
4
+ <exclude-output />
5
+ <content url="file://$MODULE_DIR$" />
6
+ <orderEntry type="sourceFolder" forTests="false" />
7
+ </component>
8
+ </module>
package/package.json ADDED
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "nfse-brazil-national",
3
+ "version": "1.0.0",
4
+ "description": "Biblioteca cliente para integração com a API Nacional da NFS-e (Nota Fiscal de Serviço Eletrônica) do Brasil.",
5
+ "type": "module",
6
+ "main": "./src/index.js",
7
+ "exports": "./src/index.js",
8
+ "engines": {
9
+ "node": ">=20.0.0"
10
+ },
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "git+https://github.com/flaviolacer/nfse-brazil-national.git"
14
+ },
15
+ "author": "Flavio Lacerda",
16
+ "homepage": "https://github.com/flaviolacer/nfse-brazil-national",
17
+ "scripts": {
18
+ "test": "node --test"
19
+ },
20
+ "public": true,
21
+ "dependencies": {
22
+ "axios": "^1.13.2",
23
+ "handlebars": "^4.7.8",
24
+ "xml-crypto": "^6.0.0",
25
+ "@xmldom/xmldom": "^0.8.10",
26
+ "node-forge": "^1.3.1"
27
+ }
28
+ }