Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 20 additions & 70 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,50 +13,39 @@ SPDX-License-Identifier: AGPL-3.0-or-later
[![CI](https://github.com/LibreCodeCoop/nfse-php/actions/workflows/phpunit.yml/badge.svg)](https://github.com/LibreCodeCoop/nfse-php/actions/workflows/phpunit.yml)
[![codecov](https://codecov.io/gh/LibreCodeCoop/nfse-php/branch/main/graph/badge.svg)](https://codecov.io/gh/LibreCodeCoop/nfse-php)

---
## Scope

## Why nfse-php?

Emitting NFS-e in Brazil involves XML signing with ICP-Brasil certificates, SOAP/REST calls to multiple municipal gateways, and safe credential management — all of which most accounting software gets wrong.

**nfse-php** handles all of it correctly:

- **XML signing** with PFX/PKCS#12 certificates (native PHP first, CLI repack fallback for OpenSSL legacy format)
- **Credential isolation** — PFX passwords are never stored in your database; they live in [OpenBao](https://openbao.org/) / HashiCorp Vault KV v2
- **Pluggable secret store** — swap OpenBao for any `SecretStoreInterface` implementation
- **Tier-1 tests always run** via `donatj/mock-webserver` (no real cert required in CI)
- **Strict PHP 8.2+ types** throughout

---

## Requirements

| Dependency | Version |
|---|---|
| PHP | ^8.2 |
| ext-openssl | * |
| ext-dom | * |
| ext-soap | * |

---
- Emit NFS-e (`emit`)
- Query NFS-e (`query`)
- Cancel NFS-e (`cancel`)
- Retrieve DANFSE bytes (`getDanfse`)
- Sign DPS XML with PFX credentials
- Read secrets from OpenBao/Vault or an in-memory store

## Installation

```bash
composer require librecodeoop/nfse-php
```

---

## Quick Start

```php
use LibreCodeCoop\NfsePHP\Config\CertConfig;
use LibreCodeCoop\NfsePHP\Config\EnvironmentConfig;
use LibreCodeCoop\NfsePHP\Dto\DpsData;
use LibreCodeCoop\NfsePHP\Http\NfseClient;
use LibreCodeCoop\NfsePHP\SecretStore\OpenBaoSecretStore;
use LibreCodeCoop\NfsePHP\Dto\DpsData;

$store = new OpenBaoSecretStore(addr: 'http://localhost:8200', token: getenv('VAULT_TOKEN'));
$client = new NfseClient(secretStore: $store, sandboxMode: true);
$env = new EnvironmentConfig(sandboxMode: true);
$cert = new CertConfig(
cnpj: '11222333000181',
pfxPath: '/secure/path/certificate.pfx',
vaultPath: 'pfx/11222333000181',
);

$client = new NfseClient(environment: $env, cert: $cert, secretStore: $store);

$dps = new DpsData(
cnpjPrestador: '11222333000181', // Example only: configure with your provider CNPJ
Expand All @@ -68,13 +57,9 @@ $receipt = $client->emit($dps);
echo $receipt->nfseNumber; // NFS-e number returned by the SEFIN gateway
```

---

## Secret Storage with OpenBao

PFX passwords are **never** persisted in application databases. They are stored in OpenBao (or Vault) KV v2 under a path like `nfse/pfx/{cnpj}`.

The CNPJ values below are **fictitious examples**. Configure your own values through your application settings/environment.
PFX passwords are stored in OpenBao (or Vault) KV v2, for example in `nfse/pfx/{cnpj}`.

```php
use LibreCodeCoop\NfsePHP\SecretStore\OpenBaoSecretStore;
Expand All @@ -93,48 +78,13 @@ $store->put('pfx/11222333000181', ['password' => 'secret']);
$password = $store->get('pfx/11222333000181')['password'];
```

For development/CI without OpenBao, use `NoOpSecretStore` which reads directly from constructor arguments and never touches any server.

---

## Roadmap

- [x] DPS issuance via SEFIN Nacional REST API
- [x] XML signing (PFX, ICP-Brasil)
- [x] OpenBao / Vault KV v2 secret store
- [x] Mock webserver for CI-friendly testing
- [ ] NFS-e query (GET /dps/{id})
- [ ] NFS-e cancellation
- [ ] Webhook / event polling
- [ ] Municipal gateway adapters beyond Niterói

---

## Commercial Support

This library is the foundation of the [akaunting-nfse](https://github.com/LibreCodeCoop/akaunting-nfse) module for [Akaunting](https://akaunting.com/).

Need SLA-backed support, custom municipal adapters, or managed hosting?
Contact us: **comercial@librecodecoop.org.br**

---
For development/CI without OpenBao, use `NoOpSecretStore` (in-memory only, no server calls).

## Contributing

We welcome issues and pull requests. Please read [CONTRIBUTING.md](CONTRIBUTING.md) before opening a PR.

All commits must use [Conventional Commits](https://www.conventionalcommits.org/) and be signed off (`git commit -s`).

---

## Give us a star!

If this library saves you hours of integration pain, please ⭐ the repository.
It helps other developers discover the project and motivates the team to keep improving it.

---

## License

GNU Affero General Public License v3.0 or later — see [LICENSES/AGPL-3.0-or-later.txt](LICENSES/AGPL-3.0-or-later.txt).
© 2026 LibreCode Coop and contributors.
Loading