@useinvio/nfse-sdk 3.0.0 → 3.2.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 (120) hide show
  1. package/CHANGELOG.md +176 -0
  2. package/README.en.md +146 -0
  3. package/README.md +77 -518
  4. package/dist/agent.d.ts +131 -0
  5. package/dist/agent.js +551 -0
  6. package/dist/agent.js.map +1 -0
  7. package/dist/certificate.d.ts +96 -0
  8. package/dist/certificate.js +218 -0
  9. package/dist/certificate.js.map +1 -0
  10. package/dist/certificateValidation.d.ts +11 -6
  11. package/dist/certificateValidation.js +23 -12
  12. package/dist/certificateValidation.js.map +1 -1
  13. package/dist/cli.d.ts +2 -0
  14. package/dist/cli.js +520 -0
  15. package/dist/cli.js.map +1 -0
  16. package/dist/cliBin.d.ts +2 -0
  17. package/dist/cliBin.js +6 -0
  18. package/dist/cliBin.js.map +1 -0
  19. package/dist/cliExample.d.ts +6 -0
  20. package/dist/cliExample.js +29 -0
  21. package/dist/cliExample.js.map +1 -0
  22. package/dist/client.d.ts +51 -4
  23. package/dist/client.js +41 -10
  24. package/dist/client.js.map +1 -1
  25. package/dist/config.d.ts +3 -0
  26. package/dist/config.js +8 -0
  27. package/dist/config.js.map +1 -1
  28. package/dist/consultas.d.ts +34 -0
  29. package/dist/consultas.js +61 -0
  30. package/dist/consultas.js.map +1 -0
  31. package/dist/dpsJson.js +1 -41
  32. package/dist/dpsJson.js.map +1 -1
  33. package/dist/dpsJsonSchema.d.ts +17 -0
  34. package/dist/dpsJsonSchema.js +184 -0
  35. package/dist/dpsJsonSchema.js.map +1 -0
  36. package/dist/emissaoNota.d.ts +11 -6
  37. package/dist/emissaoNota.js +10 -6
  38. package/dist/emissaoNota.js.map +1 -1
  39. package/dist/eventoJson.d.ts +76 -0
  40. package/dist/eventoJson.js +128 -0
  41. package/dist/eventoJson.js.map +1 -0
  42. package/dist/eventosNfse.d.ts +38 -0
  43. package/dist/eventosNfse.js +73 -0
  44. package/dist/eventosNfse.js.map +1 -0
  45. package/dist/fiscalDomains.d.ts +22 -0
  46. package/dist/fiscalDomains.js +99 -0
  47. package/dist/fiscalDomains.js.map +1 -0
  48. package/dist/fiscalValidation.js +21 -53
  49. package/dist/fiscalValidation.js.map +1 -1
  50. package/dist/gzipB64.d.ts +5 -0
  51. package/dist/gzipB64.js +12 -0
  52. package/dist/gzipB64.js.map +1 -1
  53. package/dist/index.d.ts +22 -7
  54. package/dist/index.js +11 -4
  55. package/dist/index.js.map +1 -1
  56. package/dist/jsonSchemaCheck.d.ts +12 -0
  57. package/dist/jsonSchemaCheck.js +70 -0
  58. package/dist/jsonSchemaCheck.js.map +1 -0
  59. package/dist/loadPfx.d.ts +2 -0
  60. package/dist/loadPfx.js +2 -1
  61. package/dist/loadPfx.js.map +1 -1
  62. package/dist/mcpCli.d.ts +2 -0
  63. package/dist/mcpCli.js +7 -0
  64. package/dist/mcpCli.js.map +1 -0
  65. package/dist/mcpServer.d.ts +49 -0
  66. package/dist/mcpServer.js +159 -0
  67. package/dist/mcpServer.js.map +1 -0
  68. package/dist/nfseXml.d.ts +48 -0
  69. package/dist/nfseXml.js +86 -0
  70. package/dist/nfseXml.js.map +1 -0
  71. package/dist/rejeicoes.d.ts +50 -0
  72. package/dist/rejeicoes.js +62 -0
  73. package/dist/rejeicoes.js.map +1 -0
  74. package/dist/rejeicoesCatalog.d.ts +10 -0
  75. package/dist/rejeicoesCatalog.js +4449 -0
  76. package/dist/rejeicoesCatalog.js.map +1 -0
  77. package/dist/sefinClient.d.ts +107 -7
  78. package/dist/sefinClient.js +232 -22
  79. package/dist/sefinClient.js.map +1 -1
  80. package/dist/signXml.d.ts +3 -3
  81. package/dist/signXml.js +17 -7
  82. package/dist/signXml.js.map +1 -1
  83. package/dist/testing.d.ts +80 -0
  84. package/dist/testing.js +285 -0
  85. package/dist/testing.js.map +1 -0
  86. package/dist/xmlBuild.d.ts +11 -0
  87. package/dist/xmlBuild.js +44 -0
  88. package/dist/xmlBuild.js.map +1 -0
  89. package/dist/xmlInspection.d.ts +1 -1
  90. package/dist/xmlInspection.js +10 -9
  91. package/dist/xmlInspection.js.map +1 -1
  92. package/dist/xsdValidation.d.ts +24 -5
  93. package/dist/xsdValidation.js +97 -67
  94. package/dist/xsdValidation.js.map +1 -1
  95. package/docs/README.md +47 -0
  96. package/docs/REGRAS_NEGOCIO_EMISSAO.md +8 -8
  97. package/docs/conceitos/como-funciona.md +87 -0
  98. package/docs/guias/adn.md +51 -0
  99. package/docs/guias/agentes-ia.md +111 -0
  100. package/docs/guias/cancelar.md +70 -0
  101. package/docs/guias/certificado.md +88 -0
  102. package/docs/guias/cli.md +68 -0
  103. package/docs/guias/consultar.md +54 -0
  104. package/docs/guias/emitir.md +172 -0
  105. package/docs/guias/recuperar-emissao.md +33 -0
  106. package/docs/guias/rejeicoes.md +70 -0
  107. package/docs/guias/testes.md +79 -0
  108. package/docs/guias/timeouts.md +33 -0
  109. package/docs/referencia/ambientes.md +35 -0
  110. package/docs/referencia/api.md +137 -0
  111. package/docs/referencia/erros.md +55 -0
  112. package/docs/tutoriais/primeira-nfse.md +130 -0
  113. package/examples/README.md +3 -1
  114. package/examples/cancel-nfse.ts +29 -0
  115. package/examples/client-resource.ts +3 -5
  116. package/examples/emit-homologation.ts +3 -4
  117. package/examples/handle-errors.ts +3 -3
  118. package/examples/recover-emission.ts +29 -0
  119. package/llms.txt +97 -82
  120. package/package.json +31 -6
@@ -0,0 +1,35 @@
1
+ # Ambientes
2
+
3
+ | Chave | Alias no `NfseClient` | SEFIN (emissão, consulta, eventos) | ADN (DANFSe, distribuição, parâmetros) |
4
+ |---|---|---|---|
5
+ | `restrita` (padrão) | `sandbox` | `https://sefin.producaorestrita.nfse.gov.br/SefinNacional` | `https://adn.producaorestrita.nfse.gov.br` |
6
+ | `producao` | `production` | `https://sefin.nfse.gov.br/SefinNacional` | `https://adn.nfse.gov.br` |
7
+
8
+ ```ts
9
+ import { resolveAdnBaseUrl, resolveSefinBaseUrl, TP_AMB } from '@useinvio/nfse-sdk';
10
+
11
+ resolveSefinBaseUrl('restrita'); // URL da SEFIN
12
+ resolveAdnBaseUrl('producao'); // URL do ADN
13
+ TP_AMB.restrita; // '2', valor de <tpAmb> no XML
14
+ ```
15
+
16
+ - `restrita` é a produção restrita oficial: as notas não têm validade jurídica,
17
+ mas passam pelas mesmas validações. Use-a para testes e homologação.
18
+ - O `tpAmb` dentro do XML precisa coincidir com o ambiente de envio; o SDK
19
+ recusa a divergência antes de transmitir.
20
+ - No `NfseClient`, o ambiente segue: opção da chamada → `defaults.ambiente` →
21
+ `environment` do construtor. O cliente não lembra o ambiente da última emissão.
22
+
23
+ ## Endpoints usados
24
+
25
+ | Operação | Método e caminho | Serviço |
26
+ |---|---|---|
27
+ | Emitir | `POST /nfse` | SEFIN |
28
+ | Consultar NFS-e | `GET /nfse/{chave}` | SEFIN |
29
+ | Consultar DPS | `GET /dps/{id}` | SEFIN |
30
+ | Registrar evento | `POST /nfse/{chave}/eventos` | SEFIN |
31
+ | Consultar evento | `GET /nfse/{chave}/eventos/{tpEvento}/{nSeqEvento}` | SEFIN |
32
+ | DANFSe | `GET /danfse/{chave}` | ADN |
33
+ | Distribuição | `GET /contribuintes/DFe/{nsu}` | ADN |
34
+ | Eventos por chave | `GET /contribuintes/NFSe/{chave}/Eventos` | ADN |
35
+ | Parâmetros municipais | `GET /parametrizacao/...` | ADN |
@@ -0,0 +1,137 @@
1
+ # Referência da API
2
+
3
+ Tudo é importado da raiz do pacote, exceto as ferramentas de agente
4
+ (`@useinvio/nfse-sdk/agent`). `certificado` é um `NfseCertificate` (ou o legado
5
+ `PfxMaterial`); `ambiente` é `'restrita' | 'producao'` e o padrão é `restrita`;
6
+ `options` é [`SefinRequestOptions`](../guias/timeouts.md).
7
+
8
+ ## Cliente
9
+
10
+ | API | Descrição |
11
+ |---|---|
12
+ | `new NfseClient({ environment?, certificate, defaults?, request? })` | Configura certificado, ambiente, defaults do JSON e opções de transporte uma vez. |
13
+ | `client.invoices.create(input)` / `.buildDpsJson(input)` | Emite, ou só combina o JSON com os defaults. |
14
+ | `client.invoices.retrieve(chave)` / `.get(chave)` | Consulta tipada / crua. |
15
+ | `client.invoices.getByDpsId(dpsId)` / `.recover(dpsId)` | Consulta pela DPS / recuperação após timeout. |
16
+ | `client.invoices.cancel(input)` / `.cancelBySubstitution(input)` | Eventos e101101 / e105102. |
17
+ | `client.invoices.danfse(chave)` | PDF do DANFSe. |
18
+ | `client.events.get(chave, tpEvento, nSeq?)` / `.listByAccessKey(chave)` | Eventos (SEFIN / ADN). |
19
+ | `client.distribution.byNsu(nsu, opts?)` | Distribuição por NSU (ADN). |
20
+ | `client.municipalParameters.agreement/rate/rateHistory/specialRegimes/withholdings/benefit` | Parâmetros municipais (ADN). |
21
+
22
+ ## Emissão
23
+
24
+ | Função | Descrição |
25
+ |---|---|
26
+ | `emitirNfse(input, certificado, options?)` | Emite a partir de XML ou JSON. Retorna `ResultadoEmissaoNota`. |
27
+ | `prepararNota(input, certificado, options?)` | Valida, assina e verifica a assinatura, sem enviar. Retorna `NotaPreparada`. |
28
+ | `transmitirNotaPreparada(nota, certificado, options?)` | Envia uma `NotaPreparada`. |
29
+ | `buildDpsFromJson(nota)` / `buildDpsId(...)` | Monta o XML não assinado / o Id de 45 caracteres. |
30
+ | `validateDpsJsonRequest(nota)` / `assertValidDpsJsonRequest(nota)` | Valida o JSON, sem lançar / lançando `DpsFiscalValidationError`. |
31
+ | `validateDpsXmlAgainstXsd(xml)` | Valida a DPS contra o XSD oficial v1.01. |
32
+ | `DPS_JSON_REQUEST_SCHEMA` | JSON Schema 2020-12 do `DpsJsonRequest`. |
33
+
34
+ ## Consulta
35
+
36
+ | Função | Descrição |
37
+ |---|---|
38
+ | `obterNfse(chave, certificado, ambiente?, options?)` | `GET /nfse/{chave}` decodificado: `{ chaveAcesso, nfseXml, resumo }`. |
39
+ | `recuperarNfsePorDps(dpsId, certificado, ambiente?, options?)` | `GET /dps/{id}` + `GET /nfse/{chave}`; `null` se a DPS não existe. |
40
+ | `lerNfse(xml)` | Lê um XML de NFS-e autorizada em `NfseResumo`. |
41
+ | `decodificarRespostaNfse(status, body)` | Decodifica uma resposta crua de `GET /nfse/{chave}`. |
42
+ | `consultarNfse(...)` / `consultarDps(...)` | Respostas cruas `{ status, body }`. |
43
+ | `baixarDanfse(chave, certificado, ambiente?, options?)` | PDF do DANFSe (ADN). |
44
+
45
+ ## Eventos
46
+
47
+ | Função | Descrição |
48
+ |---|---|
49
+ | `cancelarNfse(input, certificado, options?)` | Cancelamento e101101. |
50
+ | `cancelarNfsePorSubstituicao(input, certificado, options?)` | Cancelamento por substituição e105102. |
51
+ | `buildCancelamentoFromJson(input)` / `buildCancelamentoPorSubstituicaoFromJson(input)` | Monta o pedido sem assinar. |
52
+ | `registrarEvento(built, certificado, options?)` | Valida, assina e envia um pedido montado. |
53
+ | `validatePedRegEventoXmlAgainstXsd(xml)` | Valida um pedido de evento contra o XSD. |
54
+ | `consultarEvento(chave, tpEvento, certificado, ambiente?, { nSeqEvento? })` | Consulta um evento (SEFIN). |
55
+ | `enviarEvento(xmlGZipB64, certificado, chave, ambiente?)` | Envia um pedido já assinado e compactado. |
56
+ | `TP_EVENTO`, `MOTIVO_CANCELAMENTO`, `MOTIVO_SUBSTITUICAO` | Códigos com nomes legíveis. |
57
+
58
+ ## ADN
59
+
60
+ `distribuirDfePorNsu`, `consultarEventosPorChaveAdn`, `consultarConvenio`,
61
+ `consultarAliquota`, `consultarHistoricoAliquotas`, `consultarRegimesEspeciais`,
62
+ `consultarRetencoes`, `consultarBeneficio`. Assinaturas e endpoints em
63
+ [Distribuição e parâmetros municipais](../guias/adn.md).
64
+
65
+ ## Linha de comando
66
+
67
+ `nfse` (bin): `init`, `doctor`, `cert`, `validate`, `xml`, `emit`, `get`, `danfse`,
68
+ `cancel`, `recover`, `explain`, `mcp`. Veja [Usar pelo terminal](../guias/cli.md).
69
+
70
+ ## Rejeições
71
+
72
+ | Função | Descrição |
73
+ |---|---|
74
+ | `explicarRejeicao(codigoOuErro)` | Mensagem, regra, campo e nível oficiais de um código, ou `conhecida: false`. |
75
+ | `explicarErros(erros)` | Explica todos os erros de uma rejeição. |
76
+ | `buscarRejeicao(codigo)` / `listarRejeicoes()` | Acesso direto ao catálogo oficial. |
77
+ | `REJEICOES_FONTES` | Anexos oficiais usados no catálogo, com URL. |
78
+
79
+ Uma página por código: [docs/rejeicoes](../rejeicoes/README.md).
80
+
81
+ ## Testes (`@useinvio/nfse-sdk/testing`)
82
+
83
+ | Função | Descrição |
84
+ |---|---|
85
+ | `createTestCertificate(opts?)` | Certificado A1 de teste com CNPJ/CPF nos campos ICP-Brasil. |
86
+ | `installMockSefin(opts?)` | SEFIN e ADN simulados no processo; `rejectNext`, `failNext`, `dropResponseNext`, `route`, `restore`. |
87
+
88
+ Guia: [Testar sem certificado](../guias/testes.md).
89
+
90
+ ## Agentes e MCP
91
+
92
+ | API | Descrição |
93
+ |---|---|
94
+ | `nfseAgentTools` (`/agent`) | Ferramentas só de leitura. |
95
+ | `createNfseAgentToolkit({ allowWrite?, confirmationTtlMs? })` (`/agent`) | Leitura + emissão e cancelamento em duas etapas. |
96
+ | `toAgentIssues(error)` (`/agent`) | Converte erros do SDK em issues para o modelo. |
97
+ | `createNfseMcpServer(opts)` / `runNfseMcpStdio(opts?)` (`/mcp`) | Servidor MCP sem dependências. |
98
+ | `nfse-mcp` (bin) | `npx -y @useinvio/nfse-mcp` ou `npx -y -p @useinvio/nfse-sdk nfse-mcp`. |
99
+
100
+ Guia: [Agentes de IA e MCP](../guias/agentes-ia.md).
101
+
102
+ ## Certificado
103
+
104
+ | API | Descrição |
105
+ |---|---|
106
+ | `NfseCertificate.fromEnv/fromFile/fromBase64/fromPfx/fromPem` | Carrega o A1 sem expor a chave privada. |
107
+ | `certificado.cnpj`, `.cpf`, `.notBefore`, `.notAfter`, `.fingerprintSha256` | Metadados públicos. |
108
+ | `certificado.isValidAt(data?)`, `.daysUntilExpiry()`, `.destroy()` | Vigência e descarte do agente mTLS. |
109
+ | `assertCertificateValidity(certificado, now?)` | Vigência; aplicada antes de assinar e de enviar. |
110
+ | `assertCertificateForProvider(certificado, documento, now?)` | Vigência e titular. |
111
+ | `NFSE_CERT_ENV` | Nomes das variáveis de ambiente lidas por `fromEnv`. |
112
+ | `loadPfx`, `loadPfxFromBuffer` | Legado: retornam a chave em PEM. |
113
+
114
+ ## Baixo nível
115
+
116
+ | Função | Descrição |
117
+ |---|---|
118
+ | `signDps(xml, dpsId, certificado)` / `signEnveloped(xml, id, elemento, certificado)` | Assinatura XMLDSIG. |
119
+ | `verifyDps(xml, certPem)` | Verifica uma assinatura. |
120
+ | `gzipBase64(xml)` / `gunzipBase64(b64)` / `gunzipBase64Flexivel(b64)` | Compactação. |
121
+ | `transmitirDpsCompactada(b64, certificado, ambiente?, options?)` | `POST /nfse` cru. |
122
+ | `extrairErros(body)` | Normaliza erros da SEFIN. |
123
+ | `assertChaveAcesso(chave)` / `assertDpsId(id)` | Validação de identificadores. |
124
+ | `resolveSefinBaseUrl(ambiente)` / `resolveAdnBaseUrl(ambiente)` | URLs base. |
125
+ | `setSefinRequestObserver(fn)` / `createSefinLatencyTracker(opts?)` | Métricas de latência ([METRICS.md](../METRICS.md)). |
126
+
127
+ ## Constantes
128
+
129
+ `SEFIN_BASE_URL`, `ADN_BASE_URL`, `TP_AMB`, `DEFAULT_AMBIENTE`, `DPS_SCHEMA_VERSION`.
130
+
131
+ ## Tipos
132
+
133
+ Os tipos públicos estão em [`index.ts`](../../index.ts). Os principais:
134
+ `DpsJsonRequest`, `ResultadoEmissaoNota`, `NotaPreparada`, `NfseConsultada`,
135
+ `NfseResumo`, `ResultadoEvento`, `CancelamentoNfseInput`,
136
+ `CancelamentoPorSubstituicaoInput`, `SefinRequestOptions`, `SefinResposta`,
137
+ `SefinErro`, `CertificateInput`, `NfseClientOptions`.
@@ -0,0 +1,55 @@
1
+ # Erros
2
+
3
+ Todos os erros estendem `Error` e têm `name` igual ao nome da classe. Como
4
+ tratar cada um: [Tratar rejeições e erros](../guias/rejeicoes.md).
5
+
6
+ ## Antes de enviar (nada chegou à SEFIN)
7
+
8
+ | Classe | Campos | Quando |
9
+ |---|---|---|
10
+ | `DpsFiscalValidationError` | `issues`, `report` | O JSON da DPS é inválido. Cada issue tem `code`, `path`, `severity`, `source`, `message`, `suggestion?`. |
11
+ | `DpsXsdValidationError` | `details` | O XML da DPS viola o XSD oficial v1.01. |
12
+ | `PedRegEventoXsdValidationError` | `details` | O pedido de evento viola o XSD oficial. |
13
+ | `EventoInvalidoError` | `code = 'EVENTO_INVALID'`, `field` | Entrada de evento inválida (chave, motivo, autor). |
14
+ | `CertificateValidationError` | `code` | Problema no A1; códigos abaixo. |
15
+ | `ChaveAcessoInvalidaError` | `code = 'CHAVE_ACESSO_INVALID_FORMAT'`, `chaveAcesso` | Chave sem exatamente 50 dígitos. |
16
+ | `SefinParametroInvalidoError` | `code = 'SEFIN_PARAMETER_INVALID'`, `field`, `value` | Id da DPS, tipo de evento, NSU, município ou outro parâmetro de caminho com formato errado. |
17
+ | `XsdEngineUnavailableError` | `cause` | O motor XSD (WebAssembly) falhou. Infraestrutura, não rejeição fiscal. |
18
+
19
+ ### Códigos de `CertificateValidationError`
20
+
21
+ | `code` | Quando |
22
+ |---|---|
23
+ | `CERTIFICATE_INVALID_PASSWORD` | Senha incorreta. |
24
+ | `CERTIFICATE_UNREADABLE` | O conteúdo não é um PFX/PEM válido. |
25
+ | `CERTIFICATE_MISSING` | `fromEnv` sem `NFSE_CERT_PATH` nem `NFSE_CERT_BASE64`. |
26
+ | `CERTIFICATE_PASSWORD_MISSING` | `fromEnv` sem `NFSE_CERT_PASSWORD` nem `NFSE_CERT_PASSWORD_FILE`. |
27
+ | `CERTIFICATE_NOT_YET_VALID` | Antes de `notBefore`. |
28
+ | `CERTIFICATE_EXPIRED` | Depois de `notAfter`. |
29
+ | `CERTIFICATE_HOLDER_MISMATCH` | O titular não é o CPF/CNPJ informado. |
30
+
31
+ ## Depois de enviar (a SEFIN respondeu)
32
+
33
+ | Classe | Campos | Quando |
34
+ |---|---|---|
35
+ | `EmitirNotaError` | `status`, `erros`, `dpsId`, `body` | Emissão rejeitada, ou HTTP 2xx sem XML autorizado (`INVALID_AUTHORIZATION_RESPONSE`). |
36
+ | `RegistrarEventoError` | `status`, `erros`, `pedRegEventoId`, `body` | Evento rejeitado. |
37
+ | `ConsultaNfseError` | `status`, `erros`, `body`, `notFound` | Consulta falhou; `notFound` para HTTP 404. |
38
+
39
+ `erros` é uma lista de `SefinErro` (`Codigo`, `Descricao`, `Complemento?`) com o
40
+ código oficial da SEFIN preservado.
41
+
42
+ ## Erros de rede
43
+
44
+ Timeouts e falhas de conexão chegam como o `Error` original do Node (por exemplo
45
+ `ECONNRESET` ou `SEFIN request timed out after 60000ms`). Depois de um desses
46
+ numa emissão, o resultado é incerto: [recupere pela DPS](../guias/recuperar-emissao.md).
47
+
48
+ ## Ferramentas de agente
49
+
50
+ `toAgentIssues(error)` converte os erros acima em issues com `code`, `path`,
51
+ `severity`, `source` e `suggestion`. Códigos próprios das ferramentas:
52
+ `UNSUPPORTED_FIELD`, `AMBIENTE_NOT_ALLOWED`, `CERTIFICATE_REQUIRED`,
53
+ `XSD_INVALID` e `SEFIN_REJECTED`. As ferramentas de escrita acrescentam
54
+ `CONFIRMATION_NOT_FOUND` (id desconhecido, usado ou expirado) e
55
+ `TRANSMISSION_UNCERTAIN` (falha de rede no envio; repita o confirm).
@@ -0,0 +1,130 @@
1
+ # Tutorial: sua primeira NFS-e na produção restrita
2
+
3
+ Neste tutorial você emite, consulta, baixa o DANFSe e cancela uma NFS-e na
4
+ produção restrita, o ambiente oficial de testes. Nada aqui tem validade jurídica.
5
+ Leva uns 15 minutos.
6
+
7
+ **Você vai precisar de:**
8
+
9
+ - Node.js 20.6 ou mais recente (para `--env-file`);
10
+ - um certificado A1 (`.pfx`) ICP-Brasil do prestador e a senha;
11
+ - o prestador habilitado no Emissor Nacional, com o município conveniado.
12
+
13
+ ## 1. Crie o projeto
14
+
15
+ ```bash
16
+ mkdir minha-nfse && cd minha-nfse
17
+ npm init -y && npm pkg set type=module
18
+ npm install @useinvio/nfse-sdk
19
+ ```
20
+
21
+ ## 2. Configure o certificado
22
+
23
+ Copie o `.pfx` para a pasta e crie um `.env` (não versione nenhum dos dois):
24
+
25
+ ```bash
26
+ NFSE_CERT_PATH=./certificado.pfx
27
+ NFSE_CERT_PASSWORD=sua-senha
28
+ ```
29
+
30
+ Confira se o SDK lê o certificado:
31
+
32
+ ```bash
33
+ node --env-file=.env -e "import('@useinvio/nfse-sdk').then(({ NfseCertificate }) => console.log(NfseCertificate.fromEnv()))"
34
+ ```
35
+
36
+ A saída mostra só o CNPJ, a validade e o fingerprint, como
37
+ `NfseCertificate { cnpj=12345678000195, notAfter=..., fingerprintSha256=... }`.
38
+ Se aparecer `CERTIFICATE_INVALID_PASSWORD`, revise a senha.
39
+
40
+ ## 3. Emita a nota
41
+
42
+ Crie `emitir.mjs`. Troque o CNPJ, os municípios (código IBGE) e os códigos de
43
+ serviço pelos do seu prestador; os valores abaixo são ilustrativos.
44
+
45
+ ```js
46
+ import { emitirNfse, EmitirNotaError, NfseCertificate } from '@useinvio/nfse-sdk';
47
+
48
+ const certificado = NfseCertificate.fromEnv();
49
+
50
+ const nota = {
51
+ ambiente: 'restrita',
52
+ prestador: {
53
+ cnpj: '12345678000195', // o CNPJ do certificado: certificado.cnpj
54
+ cLocEmi: '4106902',
55
+ serie: '1601',
56
+ opSimpNac: '1',
57
+ regEspTrib: '0',
58
+ },
59
+ servico: {
60
+ cTribNac: '010201',
61
+ xDescServ: 'Desenvolvimento de software',
62
+ cLocPrestacao: '4106902',
63
+ },
64
+ emissao: {
65
+ nDPS: '1',
66
+ valores: { vServ: '10.00' },
67
+ tributacaoMunicipal: { tribISSQN: '1', tpRetISSQN: '1' },
68
+ tributacaoFederal: { piscofins: { CST: '07' } },
69
+ totTrib: { pTotTribFed: '0.00', pTotTribEst: '0.00', pTotTribMun: '5.00' },
70
+ },
71
+ };
72
+
73
+ try {
74
+ const resultado = await emitirNfse(nota, certificado);
75
+ console.log('Chave de acesso:', resultado.chaveAcesso);
76
+ } catch (error) {
77
+ if (!(error instanceof EmitirNotaError)) throw error;
78
+ for (const rejeicao of error.erros) console.error(rejeicao.Codigo, rejeicao.Descricao);
79
+ }
80
+ ```
81
+
82
+ ```bash
83
+ node --env-file=.env emitir.mjs
84
+ ```
85
+
86
+ Se a SEFIN rejeitar, a mensagem traz o código oficial da rejeição.
87
+ Corrija o campo indicado e rode de novo. Depois de uma emissão autorizada,
88
+ **incremente `nDPS`** para a próxima nota: cada número de DPS gera uma nota só.
89
+
90
+ ## 4. Consulte e baixe o DANFSe
91
+
92
+ Crie `consultar.mjs` e passe a chave de acesso como argumento:
93
+
94
+ ```js
95
+ import { writeFile } from 'node:fs/promises';
96
+ import { baixarDanfse, NfseCertificate, obterNfse } from '@useinvio/nfse-sdk';
97
+
98
+ const certificado = NfseCertificate.fromEnv();
99
+ const chave = process.argv[2];
100
+
101
+ const nota = await obterNfse(chave, certificado, 'restrita');
102
+ console.log(nota.resumo.nNFSe, nota.resumo.cStat, nota.resumo.valores.vLiq);
103
+
104
+ const danfse = await baixarDanfse(chave, certificado, 'restrita');
105
+ if (danfse.pdf) await writeFile('danfse.pdf', danfse.pdf);
106
+ ```
107
+
108
+ ```bash
109
+ node --env-file=.env consultar.mjs <chave-de-acesso>
110
+ ```
111
+
112
+ ## 5. Cancele
113
+
114
+ ```js
115
+ import { cancelarNfse, NfseCertificate } from '@useinvio/nfse-sdk';
116
+
117
+ const evento = await cancelarNfse(
118
+ { ambiente: 'restrita', chaveAcesso: process.argv[2], cMotivo: '1', xMotivo: 'Teste de integracao do SDK' },
119
+ NfseCertificate.fromEnv(),
120
+ );
121
+ console.log('Cancelada:', evento.pedRegEventoId);
122
+ ```
123
+
124
+ ## Próximos passos
125
+
126
+ - Leve o fluxo para a sua aplicação: [Emitir uma NFS-e](../guias/emitir.md).
127
+ - Proteja-se de timeouts: [Recuperar uma emissão](../guias/recuperar-emissao.md).
128
+ - Entenda o que acontece por baixo: [Como o SDK funciona](../conceitos/como-funciona.md).
129
+ - Antes de ir para `producao`, revise a [matriz fiscal](../FISCAL_MATRIX.md) com
130
+ quem cuida da parte tributária.
@@ -11,5 +11,7 @@ The payloads in `payloads.ts` are shared by these examples and exercised by the
11
11
  - [`emit-homologation.ts`](./emit-homologation.ts): emit in the restricted/sandbox SEFIN environment.
12
12
  - [`client-resource.ts`](./client-resource.ts): use `NfseClient` with certificate and defaults configured once.
13
13
  - [`handle-errors.ts`](./handle-errors.ts): normalize SEFIN rejection details for your application.
14
+ - [`cancel-nfse.ts`](./cancel-nfse.ts): download the DANFSe PDF and cancel an NFS-e (event e101101).
15
+ - [`recover-emission.ts`](./recover-emission.ts): recover an emission after a timeout with `GET /dps/{id}` instead of resending.
14
16
 
15
- The examples assume Node.js 20+ and a valid A1/PFX certificate. They are intentionally explicit about fiscal fields; the SDK does not infer municipal taxation, withholding, CST or total tax burden.
17
+ The examples assume Node.js 20+ and a valid A1/PFX certificate, loaded with `NfseCertificate.fromEnv()` from `NFSE_CERT_PATH` (or `NFSE_CERT_BASE64`) and `NFSE_CERT_PASSWORD` (or `NFSE_CERT_PASSWORD_FILE`). They are intentionally explicit about fiscal fields; the SDK does not infer municipal taxation, withholding, CST or total tax burden.
@@ -0,0 +1,29 @@
1
+ import { writeFile } from 'node:fs/promises';
2
+ import { NfseCertificate, NfseClient, RegistrarEventoError } from '@useinvio/nfse-sdk';
3
+
4
+ async function archiveAndCancel(chaveAcesso: string) {
5
+ const client = new NfseClient({
6
+ environment: 'sandbox',
7
+ certificate: NfseCertificate.fromEnv(), // NFSE_CERT_PATH + NFSE_CERT_PASSWORD
8
+ request: { timeoutMs: 30_000, retries: 2 }, // retries apply to GETs only
9
+ });
10
+
11
+ // Keep the official DANFSe PDF before cancelling.
12
+ const danfse = await client.invoices.danfse(chaveAcesso);
13
+ if (danfse.pdf) await writeFile(`danfse-${chaveAcesso}.pdf`, danfse.pdf);
14
+
15
+ try {
16
+ // `autor` defaults to the certificate holder's CNPJ/CPF.
17
+ const evento = await client.invoices.cancel({
18
+ chaveAcesso,
19
+ cMotivo: '1', // 1 = erro na emissao, 2 = servico nao prestado, 9 = outros
20
+ xMotivo: 'Nota emitida com valor incorreto',
21
+ });
22
+ console.log('Evento registrado:', evento.pedRegEventoId);
23
+ } catch (error) {
24
+ if (!(error instanceof RegistrarEventoError)) throw error;
25
+ console.error('Cancelamento rejeitado:', error.status, error.erros);
26
+ }
27
+ }
28
+
29
+ void archiveAndCancel(process.argv[2] ?? '0'.repeat(50));
@@ -1,12 +1,10 @@
1
- import { NfseClient } from '@useinvio/nfse-sdk';
1
+ import { NfseCertificate, NfseClient } from '@useinvio/nfse-sdk';
2
2
  import { nationalInvoice } from './payloads.js';
3
3
 
4
4
  const client = new NfseClient({
5
5
  environment: 'sandbox',
6
- certificate: {
7
- content: process.env.NFSE_CERTIFICATE_BASE64!,
8
- password: process.env.NFSE_CERTIFICATE_PASSWORD!,
9
- },
6
+ // NFSE_CERT_PATH or NFSE_CERT_BASE64, plus NFSE_CERT_PASSWORD or NFSE_CERT_PASSWORD_FILE
7
+ certificate: NfseCertificate.fromEnv(),
10
8
  defaults: { prestador: nationalInvoice.prestador, servico: nationalInvoice.servico },
11
9
  });
12
10
 
@@ -1,11 +1,10 @@
1
- import { EmitirNotaError, emitirNfse, loadPfx } from '@useinvio/nfse-sdk';
1
+ import { EmitirNotaError, emitirNfse, NfseCertificate } from '@useinvio/nfse-sdk';
2
2
  import { nationalInvoice } from './payloads.js';
3
3
 
4
- const pfx = loadPfx('./certificado.pfx', process.env.PFX_PASSWORD!);
5
-
4
+ const certificate = NfseCertificate.fromEnv(); // NFSE_CERT_PATH + NFSE_CERT_PASSWORD
6
5
 
7
6
  try {
8
- const resultado = await emitirNfse(nationalInvoice, pfx);
7
+ const resultado = await emitirNfse(nationalInvoice, certificate);
9
8
  console.log(resultado.chaveAcesso);
10
9
  console.log(resultado.nfseXml);
11
10
  } catch (error) {
@@ -1,11 +1,11 @@
1
- import { EmitirNotaError, emitirNfse, loadPfx, type DpsJsonRequest } from '@useinvio/nfse-sdk';
1
+ import { EmitirNotaError, emitirNfse, NfseCertificate, type DpsJsonRequest } from '@useinvio/nfse-sdk';
2
2
  import { nationalInvoice } from './payloads.js';
3
3
 
4
4
  async function emitWithStructuredErrors(nota: DpsJsonRequest) {
5
- const pfx = loadPfx('./certificado.pfx', process.env.PFX_PASSWORD!);
5
+ const certificate = NfseCertificate.fromEnv(); // NFSE_CERT_PATH + NFSE_CERT_PASSWORD
6
6
 
7
7
  try {
8
- return await emitirNfse(nota, pfx);
8
+ return await emitirNfse(nota, certificate);
9
9
  } catch (error) {
10
10
  if (!(error instanceof EmitirNotaError)) throw error;
11
11
 
@@ -0,0 +1,29 @@
1
+ import {
2
+ NfseCertificate,
3
+ prepararNota,
4
+ recuperarNfsePorDps,
5
+ transmitirNotaPreparada,
6
+ type DpsJsonRequest,
7
+ } from '@useinvio/nfse-sdk';
8
+ import { nationalInvoice } from './payloads.js';
9
+
10
+ /**
11
+ * Safe emission: persist the signed DPS before sending, and after a timeout ask
12
+ * SEFIN whether the DPS was processed instead of sending it again.
13
+ */
14
+ async function emitIdempotently(nota: DpsJsonRequest) {
15
+ const certificate = NfseCertificate.fromEnv();
16
+ const prepared = prepararNota(nota, certificate);
17
+ // await db.save(prepared.dpsId, prepared.signedXml);
18
+
19
+ try {
20
+ return await transmitirNotaPreparada(prepared, certificate, { timeoutMs: 30_000 });
21
+ } catch (error) {
22
+ if (error instanceof Error && error.name === 'EmitirNotaError') throw error; // SEFIN answered: a real rejection
23
+ const existing = await recuperarNfsePorDps(prepared.dpsId, certificate, prepared.ambiente, { retries: 3 });
24
+ if (existing) return existing; // authorized before the connection dropped
25
+ return transmitirNotaPreparada(prepared, certificate); // never reached SEFIN: same DPS, same Id
26
+ }
27
+ }
28
+
29
+ void emitIdempotently(nationalInvoice);