mendesalexandre / php-nfse-nacional
SDK PHP framework-agnostic pra NFS-e Nacional (Padrão Brasileiro SEFIN) — emissão DPS, consulta, cancelamento e DANFSE (NT 008/2026). PHP 8.1+, PSR-3/PSR-18.
Package info
github.com/mendesalexandre/php-nfse-nacional
pkg:composer/mendesalexandre/php-nfse-nacional
Requires
- php: ^8.1
- ext-dom: *
- ext-libxml: *
- ext-mbstring: *
- ext-openssl: *
- ext-zlib: *
- guzzlehttp/guzzle: ^7.5
- psr/http-client: ^1.0
- psr/http-factory: ^1.0
- psr/http-message: ^1.1 || ^2.0
- psr/log: ^3.0
- tecnickcom/tcpdf: ^6.7
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.0
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^10.0
README
SDK PHP framework-agnostic para integração com NFS-e Nacional (Padrão Brasileiro SEFIN). Funciona em Laravel, Symfony, projeto vanilla — qualquer coisa com PHP 8.1+ e suporte a PSR-3/PSR-18.
📖 Documentação:
- Este README — quickstart, instalação, exemplos rápidos
- MANUAL.md — referência completa da API (cada método: assinatura, parâmetros, retorno, exceções)
- CHANGELOG.md — histórico de versões
examples/— scripts ponta-a-ponta (emitir, cancelar, substituir, consultar, download, danfse-local)
Status
Ciclo de vida da NFS-e completo: emissão, consulta, cancelamento, substituição,
download e DANFSe NT 008/2026. PHPStan level 8 limpo, 102 testes verdes, validado
ponta-a-ponta em homologação SEFIN (NFS-e #57, chave
51079092200179028000138000000000005726057774456203). Pré-1.0 — API pode
sofrer ajustes minor antes do 1.0.0; ver CHANGELOG.
Por que
- Suporte completo ao leiaute SefinNacional 1.6
- Suporte à NT 008/2026 (novo DANFSE válido a partir de 1º/jul/2026)
- Sem dependência de framework — funciona em Laravel, Symfony, projetos vanilla
- Tipagem forte (PHPStan level 8)
- Testes desde o dia 1
Requisitos
- PHP ^8.1
- Extensões:
dom,openssl,libxml,zlib,mbstring - Certificado digital A1 (.pfx) do prestador
- OpenSSL com legacy provider habilitado (rsa-sha1) — ver Configuração OpenSSL
Instalação
composer require mendesalexandre/php-nfse-nacional
Uso rápido
use PhpNfseNacional\NFSe; use PhpNfseNacional\Config; use PhpNfseNacional\Certificate\Certificate; use PhpNfseNacional\DTO\{Prestador, Tomador, Endereco, Servico, Valores, Identificacao}; use PhpNfseNacional\Enums\{Ambiente, RegimeEspecialTributacao}; // 1. Carregue o certificado A1 $cert = Certificate::fromPfxFile('/path/cert.pfx', 'senha-do-pfx'); // 2. Configure o prestador (singleton, uma vez na app) $prestador = new Prestador( cnpj: '12345678000195', inscricaoMunicipal: '11408', razaoSocial: 'EMPRESA XYZ', endereco: new Endereco( logradouro: 'R DAS NOGUEIRAS', numero: '1108', bairro: 'SETOR COMERCIAL', cep: '01310100', codigoMunicipioIbge: '3550308', uf: 'MT', ), regimeEspecial: RegimeEspecialTributacao::NotarioOuRegistrador, ); $config = new Config( prestador: $prestador, ambiente: Ambiente::Homologacao, ); // 3. Crie o SDK $nfse = NFSe::create($config, $cert); // 4. Emita uma NFS-e $resposta = $nfse->emitir( identificacao: new Identificacao(numeroDps: 1, serie: '1'), tomador: new Tomador( documento: '12345678901', nome: 'Cliente Exemplo', endereco: new Endereco( logradouro: 'Rua A', numero: '100', bairro: 'Centro', cep: '01310100', codigoMunicipioIbge: '3550308', uf: 'MT', ), ), servico: new Servico( discriminacao: 'Certidão de matrícula', codigoMunicipioPrestacao: '3550308', ), valores: new Valores( valorServicos: 100.00, deducoesReducoes: 20.00, aliquotaIssqnPercentual: 4.00, ), ); echo "Chave: " . $resposta->chaveAcesso; echo "Número: " . $resposta->numeroNfse;
Cancelamento
use PhpNfseNacional\DTO\MotivoCancelamento; $resposta = $nfse->cancelar( chaveAcesso: '35012345200001234567890123456789012345678123456789', motivo: MotivoCancelamento::ErroEmissao, justificativa: 'Valor da NFS-e divergente do recibo', );
Substituição
Cancela uma NFS-e e a vincula a uma substituidora previamente emitida.
A substituidora precisa ter sido emitida antes via $nfse->emitir(...).
use PhpNfseNacional\DTO\MotivoCancelamento; use PhpNfseNacional\DTO\MotivoSubstituicao; $resposta = $nfse->substituir( chaveOriginal: '51079092200179028000138000000000005726057774456203', chaveSubstituta: '51079092200179028000138000000000005826057774456204', motivo: MotivoSubstituicao::DesenquadramentoSimples, );
Eventos customizados
Pra outros tipos de evento que aparecerem no leiaute, implemente a interface
EventoNfse e use o EventoBuilder diretamente — sem alterar o SDK:
use PhpNfseNacional\Dps\EventoNfse; use PhpNfseNacional\Dps\EventoBuilder; final class MeuEventoCustomizado implements EventoNfse { public function chaveAcesso(): string { return '...'; } public function codigoTipoEvento(): string { return '101102'; } // ex: substituição public function nSequencial(): int { return 1; } public function descricao(): string { return 'Substituição de NFS-e'; } public function camposGrupo(): array { return ['campoX' => 'valor']; } } $xml = (new EventoBuilder($config))->build(new MeuEventoCustomizado()); $xmlAssinado = $signer->sign($xml, 'infPedReg'); $resposta = $client->postXml($endpoints->cancelarNfse($chave), $xmlAssinado);
Configuração OpenSSL
OpenSSL 3.5+ (Fedora 43, RHEL 9) desabilita SHA1 por padrão. A DPS do
SEFIN usa rsa-sha1 — sem habilitar legacy, openssl_sign falha com
error:03000098:digital envelope routines::invalid digest.
Opção 1 (recomendada em prod): env var
Criar /etc/ssl/openssl-sha1.cnf:
openssl_conf = openssl_init [openssl_init] providers = provider_sect [provider_sect] default = default_sect legacy = legacy_sect [default_sect] activate = 1 [legacy_sect] activate = 1
Setar no Supervisor/php-fpm:
environment=OPENSSL_CONF=/etc/ssl/openssl-sha1.cnf
Opção 2 (dev/local): runtime
use PhpNfseNacional\Certificate\Signer; Signer::habilitarLegacyProviderRuntime();
Arquitetura
src/
├── NFSe.php # Facade unificado (entry point)
├── Config.php # Config imutável
├── DTO/ # Dados imutáveis readonly
├── Enums/ # Ambiente, RegimeEspecial, etc.
├── Certificate/ # Carga .pfx + rsa-sha1
├── Dps/ # DpsBuilder + EventoCancelamentoBuilder
├── Sefin/ # SefinClient (HTTP), Endpoints, Resposta
├── Services/ # Emissão, Consulta, Cancelamento, Download
├── Danfse/ # DANFSE NT 008 (parser + layout + generator)
├── Exceptions/
└── Support/ # Documento, TextoSanitizador
Roadmap
- Estrutura + composer + DTOs + Config
- Certificate + Signer rsa-sha1
- DpsBuilder (XML completo)
- SefinClient + EmissaoService
- ConsultaService (status NFS-e, eventos)
- CancelamentoService (e101101)
- DownloadService (XML + PDF cancelada)
- DANFSe PDF — NT 008/2026 (TCPDF + QR Code)
- Testes unitários (PHPUnit) — 62 testes
- PHPStan level 8 limpo
- CI no GitHub Actions (PHP 8.1 – 8.4)
- Validação ponta-a-ponta em homologação SEFIN
- Substituição de NFS-e (evento 101102)
- Examples completos do ciclo de vida (emitir, cancelar, substituir, consultar, download, danfse-local)
- Cobertura ampliada (Certificate, Signer)
- DANFSe customizável (logo do prestador, observações livres)
- API CNC/ADN (
POST /DFe/,GET /DFe/{NSU}) — pra municípios c/ sistema próprio
Licença
MIT