PHPackages                             danielbbarcelos/laravel-notas-fiscais - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. danielbbarcelos/laravel-notas-fiscais

ActiveLibrary[API Development](/categories/api)

danielbbarcelos/laravel-notas-fiscais
=====================================

Emissão de notas fiscais de serviço (NFS-e) sob um contrato único, com drivers por provedor (IPM Atende.Net, ...). Laravel 11+.

v1.1.0(today)02↑2900%MITPHPPHP ^8.2

Since Jul 1Pushed todayCompare

[ Source](https://github.com/danielbbarcelos/laravel-notas-fiscais)[ Packagist](https://packagist.org/packages/danielbbarcelos/laravel-notas-fiscais)[ RSS](/packages/danielbbarcelos-laravel-notas-fiscais/feed)WikiDiscussions main Synced today

READMEChangelog (2)Dependencies (6)Versions (3)Used By (0)

laravel-notas-fiscais
=====================

[](#laravel-notas-fiscais)

[![Latest Version on Packagist](https://camo.githubusercontent.com/b4520e7f1e72b0e0ee9b705d708014604cc0baf8358a210652eb7ec6a910d999/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f64616e69656c6262617263656c6f732f6c61726176656c2d6e6f7461732d666973636169732e737667)](https://packagist.org/packages/danielbbarcelos/laravel-notas-fiscais)[![License: MIT](https://camo.githubusercontent.com/7013272bd27ece47364536a221edb554cd69683b68a46fc0ee96881174c4214c/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c75652e737667)](LICENSE)[![PHP Version](https://camo.githubusercontent.com/e2df0db5111cc996698331f261bf2635d5d982b997163dd61bcd20b2f000f216/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7068702d253545382e322d3838393242462e737667)](https://php.net)[![Laravel](https://camo.githubusercontent.com/6951544e0b2f3885155a07a261266e88706a4ebb585a9a57c76267502bbf0533/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c61726176656c2d31312532422d4646324432302e737667)](https://laravel.com)

> Repositório: **[github.com/danielbbarcelos/laravel-notas-fiscais](https://github.com/danielbbarcelos/laravel-notas-fiscais)**

Emissão de notas fiscais de serviço (NFS-e) sob um **contrato único**, com **drivers por provedor**. Laravel 11+.

Hoje implementa o provedor **IPM Atende.Net** (NFS-e municipal) em dois padrões. A arquitetura de contracts é genérica para acomodar novos provedores — e, futuramente, outros tipos de documento (NF-e, NFC-e).

Qual driver usar (IPM)
----------------------

[](#qual-driver-usar-ipm)

O Atende.Net expõe a NFS-e em **dois padrões diferentes**, por município:

DriverPadrãoServiçoQuando usar`ipm`REST proprietário (NTE 35/2021)`WNERestServiceNFSe`Municípios com o REST ``/multipart`ipm-abrasf`**ABRASF 2.04 (SOAP)**`WNENotaFiscalEletronicaNfe`Municípios ABRASF — ex.: **Pouso Alegre/MG**Ambos autenticam por **login/senha (Basic)** — sem certificado. Para descobrir qual o município usa, teste o endpoint: se o REST responder `503 "Serviço não disponível"`e o SOAP responder, use `ipm-abrasf`. Note que o ABRASF identifica o município pelo **código IBGE** (7 dígitos), enquanto o REST usa o **código TOM**.

### Notas práticas do ABRASF (validado em produção — Pouso Alegre/MG)

[](#notas-práticas-do-abrasf-validado-em-produção--pouso-alegremg)

Emissão e cancelamento reais já validados ponta a ponta. Pontos que costumam barrar:

- **Autenticação**: a senha é a **específica do WebService** (pode diferir da senha do portal). `401 "Acesso Negado"` = credencial/login rejeitado.
- **Competência (`NotaServico::competencia`)**: use a **data atual**; competência retroativa é recusada (`L1029`) salvo autorização do município.
- **`ItemServico::codigoItemListaServico`**: deve estar no formato pontuado (`99.99`, `99.99.99` ou `99.99.99.999`) e **vinculado ao cadastro econômico** do prestador. Ex.: o código `10101` do portal vira `01.01.01` (zero à esquerda). Em formato errado → `L1099`; código não vinculado → `L1003`.
- **CNAE (`ItemServico::codigoCnae`)** e item devem ser **coerentes** entre si.
- **Modo teste**: o `` exige a operação de lote síncrono, que em alguns municípios (Pouso Alegre) tem bug no servidor. Para validar, prefira \*\*emitir real
    - cancelar\*\* (a operação `GerarNfse`, usada na emissão padrão, é a estável).
- **Link do PDF**: o `GerarNfseResposta` desses municípios **não** retorna o link. Configure `link_template` (config `notas-fiscais.drivers.ipm-abrasf.link_template`, env `IPM_ABRASF_LINK_TEMPLATE`) com a URL de consulta/autenticidade do Atende.Net e o placeholder `{codigo}` (e opcionalmente `{numero}`); o driver preenche `NotaEmitida::link` automaticamente. Para achar a URL e o `{ID}` do serviço, abra uma NFS-e na consulta de autenticidade do portal e copie a URL, trocando o código por `{codigo}`. Ex.: `https://{cidade}.atende.net/?pg=autoatendimento#!/tipo/servico/valor/213/padrao/1/load/1/identificador/{codigo}`(`valor/213` é o serviço de autenticidade de NFS-e, global no Atende.Net). Confirmado para **Pouso Alegre/MG** (abre a nota com botões XML IPM / XML Abrasf / Imprimir): `https://pousoalegre.atende.net/autoatendimento/servicos/consulta-de-autenticidade-de-nota-fiscal-eletronica-nfse/detalhar/1/identificador/{codigo}`

Instalação
----------

[](#instalação)

```
composer require danielbbarcelos/laravel-notas-fiscais
php artisan vendor:publish --tag=notas-fiscais-config
```

> **Integrando numa API?** Veja o **[Guia de integração Laravel](docs/integracao-laravel.md)** — passo a passo multi-tenant/SaaS com endpoints REST, service, persistência e PDF.

Configuração
------------

[](#configuração)

`config/notas-fiscais.php` (ou via `.env`):

```
NFSE_DRIVER=ipm
IPM_BASE_URL=https://riodosul.atende.net/atende.php?pg=rest&service=WNERestServiceNFSe&cidade=padrao
IPM_CPF_CNPJ=12345678000199   # login do Web Service (prestador)
IPM_SENHA=sua-senha
IPM_CIDADE_TOM=8055           # código TOM do município do prestador
```

> A URL do Web Service varia por município. Solicite acesso no Portal do Cidadão da prefeitura ("Emissão de NFS-e por WebService").

Uso
---

[](#uso)

```
use DanielBBarcelos\NotasFiscais\Facades\NotaFiscal;
use DanielBBarcelos\NotasFiscais\Data\Nfse\{NotaServico, ItemServico, Tomador};
use DanielBBarcelos\NotasFiscais\Data\Shared\{Valor, Endereco};
use DanielBBarcelos\NotasFiscais\Enums\{TipoTomador, SituacaoTributaria};

$nota = new NotaServico(
    serie: 1,
    dataFatoGerador: '15/01/2026',
    valorTotal: Valor::reais('1000.00'),
    tomador: new Tomador(
        tipo: TipoTomador::Juridica,
        identificacao: '12.345.678/0001-95',
        nomeRazaoSocial: 'Empresa Tomadora LTDA',
        endereco: new Endereco(logradouro: 'Rua das Flores', numero: '123', bairro: 'Centro', codigoMunicipio: '8055', cep: '89160-000'),
        email: 'tomador@exemplo.com.br',
    ),
    itens: [
        new ItemServico(
            codigoItemListaServico: '010700',
            descritivo: 'Desenvolvimento de software sob encomenda',
            aliquota: 3.0,
            situacaoTributaria: SituacaoTributaria::TributadaIntegralmente,
            valorTributavel: Valor::reais('1000.00'),
            codigoLocalPrestacao: '8055',
        ),
    ],
);

// Driver padrão (config) ou explícito:
$emitida = NotaFiscal::nfse()->emitir($nota);
$emitida = NotaFiscal::driver('ipm')->nfse()->emitir($nota);

$emitida->numero;             // 1293
$emitida->codigoVerificacao;  // 8357...913
$emitida->link;               // URL do PDF
$emitida->emitida();          // true
```

Cancelamento e consulta:

```
use DanielBBarcelos\NotasFiscais\Data\Nfse\Cancelamento;

NotaFiscal::nfse()->cancelar(new Cancelamento(numero: 1293, serie: 1, motivo: 'Erro de digitação'));

NotaFiscal::nfse()->consultar(numero: 1293, serie: 1, cadastro: '8055');
NotaFiscal::nfse()->consultarPorAutenticidade('8357...913');
```

Erros do provedor lançam `NotaFiscalApiException` (com `->codigo` e `->corpo`); documento não suportado lança `OperacaoNaoSuportadaException`. Ambas estendem `NotaFiscalException`.

Exportação de arquivos (XML e TXT)
----------------------------------

[](#exportação-de-arquivos-xml-e-txt)

Reproduz o menu **Download** do Atende.Net (XML IPM / XML Abrasf / TXT). Como o Web Service **não devolve** esses arquivos prontos, o package os **gera localmente** a partir da nota enviada (`NotaServico`) e do retorno da emissão (`NotaEmitida`). Os gateways que suportam isso implementam o contrato `ExportaArquivos`:

```
use DanielBBarcelos\NotasFiscais\Contracts\ExportaArquivos;

$gw = NotaFiscal::driver('ipm-abrasf')->nfse();

if ($gw instanceof ExportaArquivos) {
    $xml = $gw->xmlNota($nota, $emitida);          // XML nativo do provedor
    $txt = $gw->txtExportacao($nota, $emitida);    // arquivo-texto (NT 65/2020)

    Storage::put("nfse/{$emitida->numero}.xml", $xml);
    Storage::put("nfse/{$emitida->numero}.txt", $txt);
}
```

- **`xmlNota()`** — no **ABRASF**, se `$emitida` trouxer o XML oficial assinado pela prefeitura (no `bruto['xml_response']` do retorno de emissão/consulta), ele é devolvido; senão monta o ``/declaração a partir de `$nota`. No **REST**proprietário sempre gera o `` do IPM (esse padrão não tem XML assinado).
- **`txtExportacao()`** — gera o layout posicional do IPM (Nota Técnica 65/2020: registros **10** documento, **20** por item, **30** tomador). Formato único do IPM, idêntico nos dois drivers.

> O **PDF** e a **Impressão** do menu correspondem ao `NotaEmitida::link` (ver abaixo). Já **E-mail**, **Anexos** e **Consulta Nota Nacional** são recursos do portal sem endpoint no Web Service — fora do escopo do package.

Comprovante da NFS-e em PDF
---------------------------

[](#comprovante-da-nfs-e-em-pdf)

O PDF **oficial** do município (link `/ged/r/{hash}` do Atende.Net) é gerado por um endpoint **protegido por captcha** (Cloudflare Turnstile) na via pública — então não é automatizável. Para anexar em e-mail / download, o pacote gera um **comprovante próprio** em PDF a partir dos dados canônicos, sem captcha. O PDF deixa **explícito que não é o documento fiscal oficial** e aponta, no topo, o **link de autenticidade** onde a NFS-e oficial pode ser consultada e impressa (e que reflete o status atual):

```
use DanielBBarcelos\NotasFiscais\Pdf\ComprovanteNfse;

$emitida = NotaFiscal::driver('ipm-abrasf')->nfse()->emitir($nota);

$bytes = ComprovanteNfse::gerar($nota, $emitida);              // bytes do PDF (anexar em e-mail)
ComprovanteNfse::salvar('/tmp/nfse-57.pdf', $nota, $emitida);  // salva em arquivo
```

Se a emissão usou o prestador da config (NotaServico sem prestador), passe-o no 3º argumento. O 4º argumento (`Emitente`) coloca **logo e nome da empresa** no cabeçalho — ambos opcionais (sem eles, o comprovante usa placeholders):

```
use DanielBBarcelos\NotasFiscais\Pdf\Emitente;

ComprovanteNfse::salvar('/tmp/nfse-57.pdf', $nota, $emitida, $prestador, new Emitente(
    nome: 'Minha Empresa LTDA',
    logo: '/caminho/absoluto/logo.png',   // png, jpg, gif ou svg
));
```

Requer **dompdf** (dependência opcional, PHP puro, sem binário):

```
composer require dompdf/dompdf
```

Multi-tenant / SaaS (credenciais em runtime)
--------------------------------------------

[](#multi-tenant--saas-credenciais-em-runtime)

O `.env` é só o default opcional (single-tenant). **Não guarde credenciais de clientes no `.env`/config** num SaaS: a aplicação armazena com segurança (ex.: coluna criptografada por tenant) e injeta em runtime.

```
// Config completa do tenant, sem passar pelo .env (recomendado p/ SaaS):
NotaFiscal::build([
    'driver'   => 'ipm',
    'base_url' => $tenant->ipmUrl,
    'cpf_cnpj' => $tenant->cnpj,
    'senha'    => decrypt($tenant->ipmSenha),
    'cidade'   => $tenant->codigoTom,
])->nfse()->emitir($nota);

// Ou sobrepondo apenas alguns campos sobre a base nomeada:
NotaFiscal::driver('ipm', [
    'cpf_cnpj' => $tenant->cnpj,
    'senha'    => decrypt($tenant->ipmSenha),
])->nfse()->emitir($nota);
```

Instâncias resolvidas em runtime **não são cacheadas pelo nome** (sem vazamento entre tenants), e a sessão (`PHPSESSID`) é isolada por credencial. Para uso fixo single-tenant, `NotaFiscal::nfse()` segue lendo a config nomeada normalmente.

Playground (validação visual)
-----------------------------

[](#playground-validação-visual)

Para validar campos e retornos numa tela, ligue a demo (mantenha **desligada em produção**):

```
NFSE_DEMO=true
```

Isso registra a rota `GET/POST /notas-fiscais/demo` com um formulário que monta o `NotaServico`, mostra o **XML gerado** e o **NotaEmitida** parseado (ou o erro). Um toggle alterna entre:

- **Faked** — devolve os XMLs de exemplo dos docs (sucesso/erro/cancelada); valida o fluxo inteiro sem credenciais nem prefeitura;
- **IPM real** — usa as credenciais do `.env` (com `` por padrão).

Dentro do próprio repositório do pacote, sirva via Testbench:

```
NFSE_DEMO=true vendor/bin/testbench serve
# acesse http://127.0.0.1:8000/notas-fiscais/demo
```

> A demo vive em `routes/demo.php` + `resources/views/demo.blade.php` e só é carregada quando `notas-fiscais.demo` é `true`.

Novos provedores
----------------

[](#novos-provedores)

Implemente `Contracts\Provedor` + `Contracts\NfseGateway` e registre no boot:

```
NotaFiscal::extend('meu-provedor', fn (array $config, string $nome) => new MeuProvedor($config, $nome));
```

Testes
------

[](#testes)

```
composer test
```

Contribuindo
------------

[](#contribuindo)

Issues e pull requests são bem-vindos em [github.com/danielbbarcelos/laravel-notas-fiscais](https://github.com/danielbbarcelos/laravel-notas-fiscais).

Licença
-------

[](#licença)

Distribuído sob a licença MIT. Veja [LICENSE](LICENSE).

###  Health Score

41

—

FairBetter than 87% of packages

Maintenance100

Actively maintained with recent releases

Popularity3

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity47

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

2

Last Release

0d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/14956610?v=4)[Daniel Barcelos](/maintainers/danielbbarcelos)[@danielbbarcelos](https://github.com/danielbbarcelos)

---

Top Contributors

[![danielbbarcelos](https://avatars.githubusercontent.com/u/14956610?v=4)](https://github.com/danielbbarcelos "danielbbarcelos (2 commits)")

---

Tags

laravelnfenota fiscalnfsefiscalatendenetissipm

###  Code Quality

TestsPest

### Embed Badge

![Health badge](/badges/danielbbarcelos-laravel-notas-fiscais/health.svg)

```
[![Health](https://phpackages.com/badges/danielbbarcelos-laravel-notas-fiscais/health.svg)](https://phpackages.com/packages/danielbbarcelos-laravel-notas-fiscais)
```

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

3355.3M345](/packages/psalm-plugin-laravel)[nuwave/lighthouse

A framework for serving GraphQL from Laravel

3.5k11.8M116](/packages/nuwave-lighthouse)[laravel/mcp

Rapidly build MCP servers for your Laravel applications.

77022.3M142](/packages/laravel-mcp)[defstudio/telegraph

A laravel facade to interact with Telegram Bots

815320.5k3](/packages/defstudio-telegraph)[essa/api-tool-kit

set of tools to build an api with laravel

53386.5k](/packages/essa-api-tool-kit)[simplestats-io/laravel-client

Analytics for Laravel. Track visitors, registrations, and payments. Discover which channels actually drive revenue, not just traffic. Server-side, GDPR compliant, ad-blocker proof.

5019.3k](/packages/simplestats-io-laravel-client)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
