Skip to content

useinvio/nfse-sdk

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

43 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

@useinvio/nfse-sdk

npm version npm access Node.js

Website

English version

image

SDK TypeScript para integração com a NFS-e Nacional (SEFIN).

Transforme JSON em DPS/NFS-e, assine com XMLDSIG, compacte em GZip/Base64 e transmita por mTLS sem escrever XML fiscal na mão.

O SDK cuida da camada protocolar: certificado A1, montagem de DPS, validações estruturais, assinatura XML, compactação, envio, consulta e normalização de rejeições da SEFIN. A aplicação [...]

Para quem é: ERPs, SaaS financeiros, plataformas de cobrança, contabilidades digitais e times Node.js/TypeScript que precisam integrar com a NFS-e Nacional.

O que ele não é: um motor fiscal, consultor tributário ou substituto da validação contábil do seu produto.


Índice


Instalação

npm install @useinvio/nfse-sdk

Node.js ≥ 20 é obrigatório.


Quick start

O caminho mais comum: emitir uma nota a partir de JSON declarativo.

import { emitirNfse, loadPfx, EmitirNotaError } from '@useinvio/nfse-sdk';

const pfx = loadPfx('./certificado.pfx', process.env.PFX_PASSWORD!);

const nota = {
  ambiente: 'restrita' as const,
  prestador: {
    cnpj: '12345678000195',
    tpInsc: '2',
    cLocEmi: '4106902',
    serie: '1601',
    opSimpNac: '1',
    regEspTrib: '0',
  },
  servico: {
    cTribNac: '010201',
    xDescServ: 'Desenvolvimento de software',
    cLocPrestacao: '4106902',
    cNBS: '115022000',
  },
  emissao: {
    nDPS: '1',
    dhEmi: '2026-06-15T10:30:00-03:00',
    dCompet: '2026-06-01',
    valores: { vServ: '1500.00' },
    tributacaoMunicipal: {
      tribISSQN: '3',
      cPaisResult: 'BR',
      tpRetISSQN: '1',
    },
    tributacaoFederal: {
      piscofins: { CST: '07' },
    },
    totTrib: {
      pTotTribFed: '0.00',
      pTotTribEst: '0.00',
      pTotTribMun: '5.00',
    },
  },
};

try {
  const resultado = await emitirNfse(nota, pfx);
  console.log(resultado.chaveAcesso); // chave de 50 dígitos
  console.log(resultado.nfseXml);    // XML autorizado pela SEFIN
} catch (error) {
  if (error instanceof EmitirNotaError) {
    for (const rejeicao of error.erros) {
      console.error(rejeicao.Codigo, rejeicao.Descricao);
    }
  }
}

Por que usar

  • Entrada JSON tipada para construir DPS no layout nacional.
  • Assinatura XMLDSIG, GZip/Base64 e mTLS encapsulados em uma API Node.js.
  • Erros oficiais da SEFIN preservados em formato estruturado.
  • Builder separado para inspecionar ou salvar o XML antes de transmitir.
  • Cliente NfseClient para configurar certificado, ambiente e defaults uma vez.
  • Validação explícita de campos fiscais obrigatórios, sem defaults silenciosos.

Escolhendo o ponto de entrada

Situação O que usar
Quer uma API de cliente com client.invoices.create(...) new NfseClient(...)
Outro sistema já gera o XML da DPS emitirNfse(xmlString, pfx)
Você monta os dados e quer que a SDK construa o XML emitirNfse(notaJson, pfx)
Precisa inspecionar ou salvar o XML antes de enviar buildDpsFromJson(nota)
Fluxo customizado (assinar, compactar ou enviar separadamente) funções de baixo nível

Cliente orientado a recursos

Use NfseClient quando preferir configurar ambiente e certificado uma vez e emitir notas por recursos, como client.invoices.create(...).

O cliente usa os mesmos campos do JSON declarativo (prestador, servico e emissao). Os defaults seguem o mesmo formato e podem ser sobrescritos em cada chamada.

import { NfseClient } from '@useinvio/nfse-sdk';

const client = new NfseClient({
  environment: 'sandbox',
  certificate: {
    content: process.env.NFSE_CERTIFICATE!, // PFX em base64
    password: process.env.NFSE_CERTIFICATE_PASSWORD!,
  },
  defaults: {
    prestador: {
      cnpj: '12345678000195',
      tpInsc: '2',
      cLocEmi: '4106902',
      serie: '1601',
      opSimpNac: '1',
      regEspTrib: '0',
    },
    servico: {
      cTribNac: '010201',
      xDescServ: 'Servico ficticio de desenvolvimento de software',
      cLocPrestacao: '4106902',
    },
  },
});

const invoice = await client.invoices.create({
  emissao: {
    nDPS: '1',
    valores: { vServ: '1000.00' },
    tomador: {
      CPF: '00000000000',
      xNome: 'Cliente Exemplo',
    },
    tributacaoMunicipal: {
      tribISSQN: '3',
      tpRetISSQN: '1',
    },
    tributacaoFederal: {
      piscofins: { CST: '07' },
    },
    totTrib: {
      pTotTribFed: '0.00',
      pTotTribEst: '0.00',
      pTotTribMun: '5.00',
    },
  },
});

console.log(invoice.chaveAcesso);
console.log(invoice.nfseXml);

environment: 'sandbox' é alias de restrita, e production é alias de producao. O campo certificate.content aceita o conteúdo binário do PFX em Buffer ou o PFX codificado em base64, formato comum para variáveis de ambiente.

Para inspecionar a DPS gerada sem assinar nem enviar:

const dps = client.invoices.buildDpsJson({
  prestador: {
    serie: '1701',
  },
  emissao: {
    nDPS: '2',
    valores: { vServ: '2500.00' },
  },
});

Certificado A1

A partir de arquivo

import { loadPfx } from '@useinvio/nfse-sdk';

const pfx = loadPfx('./certificado.pfx', process.env.PFX_PASSWORD!);

A partir de buffer (ex.: certificado descriptografado do banco)

import { loadPfxFromBuffer } from '@useinvio/nfse-sdk';

const pfx = loadPfxFromBuffer(pfxBuffer, password);

O objeto pfx retornado contém chave privada e certificado em PEM, pronto para assinar e fazer mTLS.


Emitir a partir de XML pronto

Use quando outro sistema (ERP, sistema legado) já gera o XML da DPS no layout nacional.

import { emitirNfse, loadPfx } from '@useinvio/nfse-sdk';

const pfx = loadPfx('./certificado.pfx', process.env.PFX_PASSWORD!);

const resultado = await emitirNfse(dpsXml, pfx, { ambiente: 'restrita' });

console.log(resultado.chaveAcesso);
console.log(resultado.nfseXml);

O XML precisa ter o atributo Id em <infDPS>. Se não tiver, passe options.dpsId manualmente.

await emitirNfse(dpsXml, pfx, { dpsId: 'DPS0001', ambiente: 'restrita' });

Emitir a partir de JSON declarativo

Use quando a aplicação mantém os perfis de prestador e serviço e quer que a SDK monte o XML.

Para a referência completa de campos aceitos no JSON, consulte JSON_MAPPING.md. O builder valida formatos e combinações mínimas antes de gerar XML, mas não escolhe códigos fiscais por você. Campos como tributacaoMunicipal e totTrib precisam ser informados explicita[...]

import { emitirNfse, loadPfx, type DpsJsonRequest } from '@useinvio/nfse-sdk';

const nota: DpsJsonRequest = {
  ambiente: 'restrita',
  prestador: {
    cnpj: '12345678000195',
    tpInsc: '2',
    cLocEmi: '4106902',
    serie: '1601',
    opSimpNac: '1',
    regEspTrib: '0',
  },
  servico: {
    cTribNac: '010201',
    xDescServ: 'Desenvolvimento de software',
    cLocPrestacao: '4106902',
    cNBS: '115022000',
  },
  emissao: {
    nDPS: '1',
    dhEmi: '2026-06-15T10:30:00-03:00',
    dCompet: '2026-06-01',
    valores: { vServ: '1500.00' },
    tributacaoMunicipal: {
      tribISSQN: '3',
      cPaisResult: 'BR',
      tpRetISSQN: '1',
    },
    tributacaoFederal: {
      piscofins: { CST: '07' },
    },
    totTrib: {
      pTotTribFed: '0.00',
      pTotTribEst: '0.00',
      pTotTribMun: '5.00',
    },
  },
};

const pfx = loadPfx('./certificado.pfx', process.env.PFX_PASSWORD!);
const resultado = await emitirNfse(nota, pfx);

pTotTribFed, pTotTribEst e pTotTribMun são percentuais com duas casas decimais (ex.: "5.00"), diferentes das alíquotas com quatro casas (pAliq). O shape simplificado legado de tribNac é rejeitado: IBS/CBS no layout nacional v1.01 exige o bloco RTC IBSCBS, ainda não implementado nesta SDK.


Inspecionar o XML antes de enviar

Útil para comparar com um XML de referência, salvar snapshots ou depurar rejeições de schema.

import { buildDpsFromJson } from '@useinvio/nfse-sdk';

const { id, xml } = buildDpsFromJson(nota);

console.log(id);  // ex.: "DPS..."
console.log(xml); // XML não assinado da DPS

Tratar rejeições da SEFIN

emitirNfse lança EmitirNotaError quando a SEFIN retorna HTTP fora de 2xx ou resposta sem XML autorizado.

import { EmitirNotaError, emitirNfse } from '@useinvio/nfse-sdk';

try {
  await emitirNfse(nota, pfx);
} catch (error) {
  if (!(error instanceof EmitirNotaError)) throw error;

  console.log('HTTP:', error.status);
  console.log('DPS ID:', error.dpsId);

  for (const rejeicao of error.erros) {
    console.log(rejeicao.Codigo);       // código oficial SEFIN
    console.log(rejeicao.Descricao);    // descrição da rejeição
    console.log(rejeicao.Complemento);  // detalhe adicional (quando presente)
  }
}

Consultar uma NFS-e emitida

import { consultarNfse } from '@useinvio/nfse-sdk';

const resposta = await consultarNfse(chaveAcesso, pfx, 'restrita');

if (resposta.status === 200) {
  console.log(resposta.body);
} else {
  console.error('Falha na consulta:', resposta.status, resposta.body);
}

Enviar evento (ex.: cancelamento)

import { enviarEvento, gzipBase64, signEnveloped } from '@useinvio/nfse-sdk';

const signedEventXml = signEnveloped(pedRegXml, pedRegId, 'infPedReg', pfx);
const pedRegXmlGZipB64 = gzipBase64(signedEventXml);

const resposta = await enviarEvento(pedRegXmlGZipB64, pfx, chaveAcesso, 'restrita');

Série e número da DPS

A SEFIN identifica uma DPS pela combinação de prestador, município, serie e nDPS.

// série no perfil do prestador (padrão para todas as DPS)
prestador: {
  serie: '1601',
}

// ou por emissão (sobrescreve prestador.serie apenas para esta DPS)
emissao: {
  serie: '1601',
  nDPS: '4',
}

O padrão usual é manter uma serie fixa e incrementar nDPS em cada nova DPS:

serie 1601, nDPS 1
serie 1601, nDPS 2
serie 1601, nDPS 3

O Emissor Web usa séries como 70000. Para emissão via API, use uma série válida para API (ex.: 1601) e controle o nDPS na sua aplicação.


Ambientes

Chave URL
restrita https://sefin.producaorestrita.nfse.gov.br/SefinNacional
producao https://sefin.nfse.gov.br/SefinNacional
import { resolveSefinBaseUrl } from '@useinvio/nfse-sdk';

const baseUrl = resolveSefinBaseUrl('restrita');

Observabilidade da SEFIN

O SDK pode emitir métricas de latência das chamadas mTLS feitas contra a SEFIN quando NFSE_SEFIN_LATENCY_METRICS=1 está configurada. Por padrão, percentis como p50Ms, p95Ms e p99Ms ficam desligados e precisam de includePercentiles: true. Essas métricas medem o round-trip externo da SEFIN, separado da latência total da sua API.

Leia o guia completo em docs/METRICS.md.


Referência da API

Funções principais

Função Descrição
new NfseClient(options) Cria cliente com ambiente, certificado e defaults no mesmo formato do JSON declarativo.
emitirNfse(input, pfx, options?) Emite uma NFS-e a partir de XML ou JSON. Retorna ResultadoEmissaoNota.
consultarNfse(chaveAcesso, pfx, ambiente?) Consulta uma NFS-e pela chave de acesso.
enviarEvento(xmlGzipB64, pfx, chaveAcesso, ambiente?) Envia evento fiscal (cancelamento, etc.).
buildDpsFromJson(nota) Monta o XML da DPS sem assinar nem enviar.
setSefinRequestObserver(observer) Registra um hook para observar latência/status das chamadas à SEFIN.
createSefinLatencyTracker(options?) Agrega amostras recentes e pode calcular percentis quando includePercentiles estiver habilitado.

Certificado

Função Descrição
loadPfx(path, password) Carrega certificado A1 a partir de arquivo.
loadPfxFromBuffer(buffer, password) Carrega certificado A1 a partir de buffer.

Funções de baixo nível

Função Descrição
signDps(xml, dpsId, pfx) Assina XML de DPS com XMLDSIG.
signEnveloped(xml, id, ref, pfx) Assina XML genérico (eventos).
verifyDps(xml) Verifica assinatura de uma DPS.
gzipBase64(xml) Compacta XML em GZip e codifica em Base64.
gunzipBase64(b64) Descompacta resposta GZip/Base64.
extrairErros(body) Normaliza erros oficiais de uma resposta da SEFIN.
resolveSefinBaseUrl(ambiente) Retorna a URL base do ambiente.

Tipos exportados

import type {
  Ambiente,
  CreateInvoiceInput,
  DpsJsonInput,
  DpsJsonRequest,
  EmitirNotaOptions,
  NfseClientDpsDefaults,
  NfseClientOptions,
  NotaInput,
  PfxMaterial,
  PrestadorProfile,
  ResultadoEmissaoNota,
  SefinErro,
  SefinResposta,
  ServicoProfile,
} from '@useinvio/nfse-sdk';

Constantes

import { SEFIN_BASE_URL, TP_AMB, DEFAULT_AMBIENTE } from '@useinvio/nfse-sdk';

Desenvolvimento

npm install
npm run typecheck   # verifica tipos sem compilar
npm run build       # compila para dist/
npm test            # build + tipos + testes unitários

npm test também valida uma DPS gerada contra os XSD oficiais em schemas/nfse/v1.01/Schemas/1.01. Para compatibilidade com xmllint, o teste corrige em uma cópia temporária a âncora regex[...]

Uso local em outro projeto

{
  "dependencies": {
    "@useinvio/nfse-sdk": "file:../nfse-sdk"
  }
}

Publicação no npm

Pacote publicado publicamente no npm como @useinvio/nfse-sdk.

npm install
npm run typecheck
npm run build
npm pack --dry-run   # verifica o que será publicado
npm publish

O pacote usa publishConfig.access = public, necessário para publicar um pacote escopado público no npm.


Divulgação e exemplos

  • Exemplos copiáveis ficam em examples/.
  • Material de lançamento, mensagens para redes e prompt de landing page ficam em docs/launch.md.

Assistente GPT

Para criar um GPT que ajude outros devs a implementar e documentar esta SDK, use:


Princípios de design

  • Sem dependência de banco de dados, config de aplicação ou estado de tenant.
  • Funções pequenas, testáveis e reutilizáveis.
  • Erros oficiais da SEFIN são preservados na íntegra, não engolidos.
  • O SDK transporta declarações fiscais; não valida se estão contabilmente corretas.

About

SDK TypeScript para emitir, consultar e gerenciar NFS-e Nacional.

Topics

Resources

License

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Packages

 
 
 

Contributors