Skip to content

fabyo/NFEEmissor

Repository files navigation

Logo

NuGet Downloads Build Publish License

Emissor NF-e em .NET para geração, assinatura, autorização em homologação/produção, eventos fiscais, consulta SEFAZ e DANFE, com API stateless, CLI e pacotes NuGet.

Status: projeto em evolução. A emissão em homologação e produção já foram testadas, mas o uso em produção exige validação fiscal, jurídica e operacional no cenário da sua empresa.

Leia Antes De Usar

Sobre a responsabilidade pelo uso

Este projeto nasceu de uma necessidade real e foi construído com cuidado, mas NF-e é um ecossistema complexo. Regras tributárias mudam, cada UF tem suas peculiaridades, cada regime fiscal tem suas exigências, e cada empresa tem um cenário diferente dos demais.

Antes de usar em produção: teste muito. Valide no ambiente de homologação da SEFAZ. Revise os XMLs gerados com alguém que entenda do processo fiscal da sua empresa.

A legislação, as regras de CSOSN/CST, os parâmetros de ICMS, PIS e COFINS variam por UF, regime tributário e atividade econômica, e essa variação é sua responsabilidade conhecer e configurar corretamente.

Ao usar este projeto, você assume total responsabilidade pelos documentos emitidos. O autor disponibiliza o código de boa-fé, mas não presta suporte fiscal nem se responsabiliza por erros, rejeições ou penalidades.

O que este projeto faz

Este projeto emite NF-e modelo 55 usando .NET, certificado digital A1 e webservices da SEFAZ.

Ele possui:

  • Nfe.Api: API HTTP para emissão assíncrona, eventos fiscais, consulta de status e consulta da chave na SEFAZ.
  • Nfe.Core: geração de XML, assinatura digital, envio para SEFAZ, eventos e validações.
  • Nfe.Cli: utilitário local para gerar e assinar XML de NF-e, eventos e inutilização sem enviar para a SEFAZ.

Dependências principais:

  • NFEConsulta: usada para consulta de status do serviço e consulta de NF-e pela chave de acesso.
  • NFEDanfe: usada para geração de DANFE em PDF a partir de XML autorizado (procNFe.xml).
  • NFeSchemaDownloader: usada pela API para sincronizar schemas XSD oficiais quando necessário.

Pacotes NuGet

O empacotamento é separado por responsabilidade:

  • NFEEmissor: biblioteca principal para geração, assinatura e autorização.
  • NFEEmissor.Cli: ferramenta dotnet tool com o comando nfe-emissor para gerar XML assinado localmente.

Nfe.Api não é empacotado como NuGet; ele é uma aplicação HTTP para rodar via Docker ou publicação própria.

Para empacotar localmente:

dotnet pack NfeEmissor.Packages.slnx -o ./artifacts/packages

Para instalar o CLI como tool a partir de um pacote local:

dotnet tool install --global NFEEmissor.Cli \
  --add-source ./artifacts/packages \
  --version 0.3.2

Depois de instalado:

nfe-emissor --help

Quando os pacotes estiverem publicados no NuGet:

dotnet add package NFEEmissor --version 0.3.2
dotnet tool install --global NFEEmissor.Cli --version 0.3.2

Licença: MIT.

Requisitos

  • Docker e Docker Compose.
  • Certificado digital A1 em PEM ou PFX.
  • Dados fiscais reais e coerentes com o certificado.

Os certificados devem ficar em certs/. Essa pasta é ignorada pelo Git.

Exemplo esperado:

certs/cert.pem
certs/key.pem
certs/cert.pfx

Subindo a API

docker compose up -d --build

A API fica disponível em:

http://localhost:5000

Serviços auxiliares:

  • Redis: localhost:6379
  • Seq: http://localhost:8085

Healthcheck:

curl -sS "http://localhost:5000/health"

Modelo stateless

A API foi desenhada para não ser o repositório definitivo dos documentos fiscais.

  • Não há banco de dados obrigatório.
  • Redis é usado apenas para fila, idempotência curta, backoff temporário da SEFAZ e status com TTL.
  • Certificados e senhas colocados na fila Redis são protegidos com AES-256-GCM antes de serem serializados.
  • O status retorna o xmlResult (procNFe.xml) e, quando solicitado, danfePdfBase64.
  • A aplicação cliente deve persistir o XML autorizado e o DANFE em seu próprio storage, banco, disco, S3/MinIO ou sistema fiscal.
  • Para integrar persistência sem mudar o fluxo da API, implemente INfeStorage. A implementação padrão é NoopNfeStorage, que não grava nada.

Por padrão, o resultado temporário expira em 12 horas. Depois disso, a API pode retornar 404 para o correlationId.

Para produção ou múltiplas réplicas da API, configure uma chave compartilhada para proteger as credenciais temporárias da fila:

export Nfe__QueueProtectionKey="use-um-segredo-com-pelo-menos-32-caracteres"

Sem essa configuração, a aplicação usa uma chave aleatória local gerada ao iniciar o processo. Isso é suficiente para desenvolvimento em uma única instância, mas mensagens antigas da fila não poderão ser processadas após restart.

API HTTP

Os exemplos abaixo usam http://localhost:5000 e cobrem os principais endpoints:

  • POST /api/v1/nfe/emitir
  • GET /api/v1/nfe/status/{correlationId}
  • GET /api/v1/nfe/consulta
  • GET /api/v1/nfe/status-servico
  • POST /api/v1/nfe/cancelar
  • POST /api/v1/nfe/cce
  • POST /api/v1/nfe/inutilizar
  • POST /api/v1/certificado/info
  • GET /health
  • GET /api/v1/nfe/schemas

Emitindo uma NF-e

Use o arquivo nota-teste.json como base. Em homologação, mantenha:

{
  "ambienteEmissao": "2",
  "destinatario": {
    "nomeRazaoSocial": "NF-E EMITIDA EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL"
  }
}

Emitir usando certificado PEM

CERT=$(base64 -w0 certs/cert.pem)
KEY=$(base64 -w0 certs/key.pem)

curl -sS -X POST "http://localhost:5000/api/v1/nfe/emitir?gerarDanfe=false" \
  -H "Content-Type: application/json" \
  -H "X-Cert-Pem-Base64: $CERT" \
  -H "X-Key-Pem-Base64: $KEY" \
  --data-binary @nota-teste.json

Resposta esperada:

{
  "correlationId": "3f8a5d63ad894b998b810e509fdf9c4c",
  "status": "Pendente",
  "message": "A nota fiscal foi colocada na fila de processamento."
}

Emitir usando certificado PFX

CERT=$(base64 -w0 certs/cert.pfx)

curl -sS -X POST "http://localhost:5000/api/v1/nfe/emitir?gerarDanfe=false" \
  -H "Content-Type: application/json" \
  -H "X-Certificado-Base64: $CERT" \
  -H "X-Certificado-Senha: sua-senha" \
  --data-binary @nota-teste.json

Consultando o status local da emissão

Depois de emitir, consulte pelo correlationId retornado:

curl -sS "http://localhost:5000/api/v1/nfe/status/3f8a5d63ad894b998b810e509fdf9c4c"

Resposta autorizada:

{
  "correlationId": "3f8a5d63ad894b998b810e509fdf9c4c",
  "status": "Autorizada",
  "chaveAcesso": "35260612345678000195550010000000011000000010",
  "protocolo": "135000000000000",
  "xmlResult": "<?xml version=\"1.0\" encoding=\"utf-8\"?><nfeProc ...",
  "danfePdfBase64": "JVBERi0xLjQK...",
  "expiraEm": "2026-06-25T18:00:00+00:00",
  "ttlSegundos": 43200,
  "storage": {
    "persistido": false,
    "xmlProcNfeUri": null,
    "danfePdfUri": null
  }
}

Salve xmlResult como procNFe.xml. Se danfePdfBase64 vier preenchido, decodifique o Base64 e salve como PDF.

Consultando a chave direto na SEFAZ

Use o endpoint de consulta quando você já tiver uma chave NF-e de 44 dígitos.

Consulta em homologação com PEM

CERT=$(base64 -w0 certs/cert.pem)
KEY=$(base64 -w0 certs/key.pem)

curl -sS "http://localhost:5000/api/v1/nfe/consulta?chave=35260612345678000195550010000000011000000010&uf=SP&ambiente=2" \
  -H "X-Cert-Pem-Base64: $CERT" \
  -H "X-Key-Pem-Base64: $KEY"

Resposta:

{
  "status": "100",
  "motivo": "Autorizado o uso da NF-e",
  "protocolo": "135000000000000",
  "xmlRetorno": null
}

Consulta em produção

Troque ambiente=2 por ambiente=1:

curl -sS "http://localhost:5000/api/v1/nfe/consulta?chave=SUA_CHAVE&uf=SP&ambiente=1" \
  -H "X-Cert-Pem-Base64: $CERT" \
  -H "X-Key-Pem-Base64: $KEY"

Consultando status do serviço SEFAZ

CERT=$(base64 -w0 certs/cert.pfx)

curl -sS "http://localhost:5000/api/v1/nfe/status-servico?uf=SP&ambiente=2" \
  -H "X-Certificado-Base64: $CERT" \
  -H "X-Certificado-Senha: sua-senha"

Se preferir PEM, o endpoint também aceita X-Cert-Pem-Base64 e X-Key-Pem-Base64 no lugar do PFX.

Cancelamento, CC-e e inutilização

Os eventos fiscais usam os mesmos headers de certificado da emissão e consulta. A API aceita PFX:

CERT=$(base64 -w0 certs/cert.pfx)

Ou PEM:

CERT=$(base64 -w0 certs/cert.pem)
KEY=$(base64 -w0 certs/key.pem)

Cancelar NF-e

Use o cancelamento quando a NF-e já foi autorizada e ainda está dentro das regras/prazo da SEFAZ.

curl -sS -X POST "http://localhost:5000/api/v1/nfe/cancelar" \
  -H "Content-Type: application/json" \
  -H "X-Certificado-Base64: $CERT" \
  -H "X-Certificado-Senha: sua-senha" \
  -d '{
    "ambiente": "2",
    "uf": "SP",
    "chaveAcesso": "35260612345678000195550010000000011000000010",
    "cnpjEmitente": "12345678000195",
    "protocoloAutorizacao": "135000000000000",
    "justificativa": "Erro operacional identificado apos autorizacao"
  }'

Resposta autorizada:

{
  "status": "135",
  "motivo": "Evento registrado e vinculado a NF-e",
  "chaveAcesso": "35260612345678000195550010000000011000000010",
  "tipoEvento": "110111",
  "sequenciaEvento": 1,
  "protocolo": "135000000000001",
  "xmlProcEventoNfe": "<procEventoNFe ..."
}

Carta de Correção Eletrônica

Use CC-e apenas para correções permitidas pela legislação. Ela não pode corrigir valores de imposto, remetente/destinatário, data de emissão ou saída.

curl -sS -X POST "http://localhost:5000/api/v1/nfe/cce" \
  -H "Content-Type: application/json" \
  -H "X-Cert-Pem-Base64: $CERT" \
  -H "X-Key-Pem-Base64: $KEY" \
  -d '{
    "ambiente": "2",
    "uf": "SP",
    "chaveAcesso": "35260612345678000195550010000000011000000010",
    "cnpjEmitente": "12345678000195",
    "sequenciaEvento": 1,
    "correcao": "Correção do texto das informações adicionais da nota fiscal"
  }'

Inutilizar numeração

Use inutilização para comunicar uma quebra de sequência de numeração que não será usada.

curl -sS -X POST "http://localhost:5000/api/v1/nfe/inutilizar" \
  -H "Content-Type: application/json" \
  -H "X-Certificado-Base64: $CERT" \
  -H "X-Certificado-Senha: sua-senha" \
  -d '{
    "ambiente": "2",
    "uf": "SP",
    "cnpjEmitente": "12345678000195",
    "ano": "2026",
    "modelo": "55",
    "serie": "1",
    "numeroInicial": 10,
    "numeroFinal": 12,
    "justificativa": "Quebra de sequência por erro operacional interno"
  }'

Resposta autorizada:

{
  "status": "102",
  "motivo": "Inutilizacao de numero homologado",
  "uf": "35",
  "ano": "26",
  "cnpjEmitente": "12345678000195",
  "serie": "1",
  "numeroInicial": 10,
  "numeroFinal": 12,
  "protocolo": "135000000000002"
}

Lendo informações do certificado

CERT=$(base64 -w0 certs/cert.pfx)

curl -sS -X POST "http://localhost:5000/api/v1/certificado/info" \
  -H "X-Certificado-Base64: $CERT" \
  -H "X-Certificado-Senha: sua-senha"

Gerando XML assinado sem enviar para a SEFAZ

Use o CLI quando quiser apenas gerar e assinar o XML localmente.

Com PEM:

docker run --rm \
  -v "$PWD:/src" \
  -w /src \
  mcr.microsoft.com/dotnet/sdk:10.0 \
  dotnet run --project src/Nfe.Cli/Nfe.Cli.csproj -- \
    emitir \
    --json nota-teste.json \
    --cert certs/cert.pem \
    --key certs/key.pem \
    --output-dir out

Com PFX:

docker run --rm \
  -v "$PWD:/src" \
  -w /src \
  mcr.microsoft.com/dotnet/sdk:10.0 \
  dotnet run --project src/Nfe.Cli/Nfe.Cli.csproj -- \
    emitir \
    --json nota-teste.json \
    --cert certs/cert.pfx \
    --senha sua-senha \
    --output-dir out

O XML assinado será salvo em out/.

O CLI também gera XML assinado de eventos e inutilização sem enviar para a SEFAZ:

dotnet run --project src/Nfe.Cli/Nfe.Cli.csproj -- \
  cancelar \
  --json cancelamento.json \
  --cert certs/cert.pfx \
  --senha sua-senha \
  --output-dir out

dotnet run --project src/Nfe.Cli/Nfe.Cli.csproj -- \
  cce \
  --json cce.json \
  --cert certs/cert.pem \
  --key certs/key.pem \
  --output-dir out

dotnet run --project src/Nfe.Cli/Nfe.Cli.csproj -- \
  inutilizar \
  --json inutilizacao.json \
  --cert certs/cert.pfx \
  --senha sua-senha \
  --output-dir out

Gerando DANFE em PDF

Use um XML autorizado/processado (*-procNFe.xml). XML apenas assinado, sem protocolo de autorização, não é suficiente para um DANFE fiscalmente válido.

Na API, informe gerarDanfe=true ao emitir:

curl -sS -X POST "http://localhost:5000/api/v1/nfe/emitir?gerarDanfe=true" \
  -H "Content-Type: application/json" \
  -H "X-Cert-Pem-Base64: $CERT" \
  -H "X-Key-Pem-Base64: $KEY" \
  --data-binary @nota-teste.json

O status retornará danfePdfBase64. Decodifique esse valor e salve como PDF na aplicação cliente.

A geração de PDF usa QuestPDF por meio da dependência NFEDanfe; o projeto configura a licença como LicenseType.Community antes de gerar o PDF:

QuestPDF.Settings.License = LicenseType.Community;

O pacote NFEEmissor.Cli não inclui geração de DANFE para evitar um pacote de ferramenta muito grande. Use a API ou a dependência NFEDanfe diretamente para DANFE.

CNPJ alfanumérico e Reforma Tributária

O projeto aceita CNPJ com letras, preservando os 14 caracteres alfanuméricos no XML e na chave de acesso. Pontuação é removida automaticamente:

{
  "cnpj": "12.ABC.345/0001-88"
}

Também há suporte inicial ao grupo IBSCBS nos impostos do item. O projeto escreve os campos informados e agrega os totais em IBSCBSTot, mas não calcula automaticamente enquadramento, CST, cClassTrib ou alíquotas. Esses valores devem vir do sistema fiscal/tributário do emissor.

O validador XSD pode ser apontado para pacotes de schema mais recentes, como PL_010C/CNPJ Alfa, por configuração:

export Nfe__SchemasPath="/caminho/para/schemas/v4"
export Nfe__TiposBasicosSchema="tiposBasico_PL_010C_v1.30.xsd"
export Nfe__NfeSchema="nfe_PL_010C_v1.30.xsd"
export Nfe__ValidateXsdBeforeSend="true"

Se os nomes não forem informados, o projeto tenta descobrir automaticamente tiposBasico*.xsd e nfe_v*.xsd no diretório configurado.

Na API, Nfe__ValidateXsdBeforeSend vem habilitado por padrão. Se os schemas não estiverem disponíveis ou o XML não validar, a NF-e não é assinada nem enviada para a SEFAZ. Para diagnosticar quais schemas foram carregados:

curl -sS "http://localhost:5000/api/v1/nfe/schemas"

No CLI, a validação XSD é explícita:

nfe-emissor emitir \
  --json nota-teste.json \
  --cert certs/cert.pfx \
  --senha sua-senha \
  --validar-xsd \
  --schemas-path schemas/v4 \
  --tipos-basicos-schema tiposBasico_PL_010C_v1.30.xsd \
  --nfe-schema nfe_PL_010C_v1.30.xsd

Exemplo:

{
  "impostos": {
    "ibsCbs": {
      "cst": "410",
      "codigoClassificacaoTributaria": "410999",
      "baseCalculo": 100.0,
      "ibsUf": {
        "aliquota": 0.1,
        "valor": 0.1
      },
      "ibsMunicipio": {
        "aliquota": 0.0,
        "valor": 0.0
      },
      "cbs": {
        "aliquota": 0.9,
        "valor": 0.9
      }
    }
  }
}

GTIN

Quando codigoEan ou codigoEanTributavel forem informados, o projeto valida GTIN-8, GTIN-12, GTIN-13 ou GTIN-14 pelo dígito verificador antes de gerar o XML. Quando o produto não possuir GTIN, omita o campo ou informe SEM GTIN.

Exemplo:

{
  "codigoEan": "7891234567895",
  "codigoEanTributavel": "7891234567895"
}

Exemplo mínimo de payload

{
  "ambienteEmissao": "2",
  "serie": "1",
  "numeroNfe": 8,
  "naturezaOperacao": "VENDA DE MERCADORIA",
  "tipoOperacao": "1",
  "formatoImpressaoDanfe": "1",
  "tipoEmissao": "1",
  "finalidadeEmissao": "1",
  "consumidorFinal": "1",
  "indicadorPresencaComprador": "9",
  "emitente": {
    "cnpj": "12345678000195",
    "razaoSocial": "EMPRESA EMITENTE TESTE LTDA",
    "nomeFantasia": "EMPRESA EMITENTE TESTE LTDA",
    "inscricaoEstadual": "110042490114",
    "cnaeFiscal": "2500000",
    "codigoRegimeTributario": "3",
    "endereco": {
      "logradouro": "RUA TESTE",
      "numero": "100",
      "bairro": "CENTRO",
      "codigoMunicipio": "3547809",
      "nomeMunicipio": "SAO PAULO",
      "uf": "SP",
      "cep": "01001000",
      "codigoPais": "1058",
      "nomePais": "BRASIL"
    }
  },
  "destinatario": {
    "cnpj": "99999999000191",
    "nomeRazaoSocial": "NF-E EMITIDA EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL",
    "indicadorIe": "9",
    "endereco": {
      "logradouro": "AVENIDA CLIENTE",
      "numero": "200",
      "bairro": "JARDINS",
      "codigoMunicipio": "3550308",
      "nomeMunicipio": "SAO PAULO",
      "uf": "SP",
      "cep": "02002000",
      "codigoPais": "1058",
      "nomePais": "Brasil"
    }
  },
  "produtos": [
    {
      "codigoProduto": "EB.007",
      "descricao": "CACAMBA No2 METALICA ONDULADA 1040X959X660 VW.00001 TARA 70KG",
      "ncmSh": "73090090",
      "cfop": "5102",
      "unidadeComercial": "PC",
      "quantidadeComercial": 3.0,
      "valorUnitarioComercial": 500.0,
      "valorBruto": 1500.0,
      "unidadeTributavel": "PC",
      "quantidadeTributavel": 3.0,
      "valorUnitarioTributavel": 500.0,
      "indicadorComposicaoTotal": "1",
      "impostos": {
        "icms": {
          "cst": "00",
          "origem": "0",
          "baseCalculo": 1500.0,
          "aliquota": 18.0,
          "valor": 270.0
        },
        "pis": {
          "cst": "01",
          "baseCalculo": 1500.0,
          "aliquota": 1.65,
          "valor": 24.75
        },
        "cofins": {
          "cst": "01",
          "baseCalculo": 1500.0,
          "aliquota": 7.6,
          "valor": 114.0
        }
      }
    }
  ],
  "transporte": {
    "modalidadeFrete": "9"
  },
  "pagamentos": [
    {
      "meioPagamento": "15",
      "valor": 1500.0
    }
  ]
}

Observações importantes

  • ambienteEmissao: 1 para produção, 2 para homologação.
  • indicadorComposicaoTotal: 1 compõe o total da NF-e, 0 não compõe.
  • Em homologação, a razão social do destinatário deve ser NF-E EMITIDA EM AMBIENTE DE HOMOLOGACAO - SEM VALOR FISCAL.
  • CST com benefício fiscal pode exigir cBenef, conforme regra da UF.
  • O projeto aplica backoff temporário quando a SEFAZ retorna 656 - Consumo Indevido.
  • certs/, out/, tmp-nfe-out/, schemas/ e *-procNFe.xml são ignorados pelo Git.

🔗 Projetos relacionados

Projeto Descrição
NFeSchemaDownloader Mantém os Schemas XML (XSD) da SEFAZ sempre atualizados automaticamente
NFEConsulta Consulta NF-e, valida XML e verifica status oficial da SEFAZ
NFEDanfe Biblioteca .NET para gerar DANFE em PDF a partir de XML de NF-e autorizada.

Testes

docker run --rm \
  -v "$PWD:/src" \
  -w /src \
  mcr.microsoft.com/dotnet/sdk:10.0 \
  dotnet test tests/Nfe.UnitTests/Nfe.UnitTests.csproj

👨‍💻 Autor

Fabyo Guimarães Oliveira

About

Emissor NF-e em .NET para geração, assinatura, autorização em homologação/produção, consulta SEFAZ e DANFE, com API stateless, CLI e pacotes NuGet.

Topics

Resources

License

Security policy

Stars

3 stars

Watchers

0 watching

Forks

Sponsor this project

 

Packages

 
 
 

Contributors