@alijunior/acbr-api-sdk-node 2.3.4 → 2.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +153 -26
- package/dist/index.d.mts +777 -112
- package/dist/index.d.ts +777 -112
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -10,19 +10,29 @@
|
|
|
10
10
|
<img alt="license" src="https://img.shields.io/npm/l/@alijunior/acbr-api-sdk-node.svg">
|
|
11
11
|
|
|
12
12
|
</p>
|
|
13
|
+
<h3>Veja também o SDK para a Nuvem Fiscal
|
|
14
|
+
<a href="https://github.com/alijuniorbr/nuvemfiscal-sdk-node">
|
|
15
|
+
@alijunior/nuvemfiscal-sdk-node
|
|
16
|
+
</a>
|
|
17
|
+
</h3>
|
|
18
|
+
<br />
|
|
13
19
|
|
|
14
20
|
SDK em **TypeScript/Node.js** para integração com a **Acbr API**, com tipos gerados a partir do OpenAPI e client HTTP enxuto (fetch/axios).
|
|
15
21
|
|
|
16
22
|
> ✅ Destaques
|
|
17
23
|
>
|
|
18
24
|
> - Tipagem forte (params, body e response) **direto do OpenAPI**
|
|
19
|
-
> - Client simples: `
|
|
25
|
+
> - Client simples: `AcbrApi`
|
|
20
26
|
> - Adapters: `FetchHttpClient` (nativo) e `AxiosHttpClient` (opcional)
|
|
21
|
-
> - **
|
|
27
|
+
> - **Helpers de token**:
|
|
22
28
|
> - `getClientCredentialsToken` (objeto completo do token)
|
|
23
29
|
> - `getAccessTokenString` (só a string do token; wrapper)
|
|
24
30
|
> - `getAcbrApiTokenForBrowser` (para **testes no browser** — não usar em produção)
|
|
25
31
|
|
|
32
|
+
> 🤖 **Usando com assistentes de IA?** Este pacote inclui um arquivo [`llms.txt`](./llms.txt) com contexto pronto sobre como aplicar o SDK no backend e no browser. Aponte seu assistente para ele.
|
|
33
|
+
|
|
34
|
+
> 💡 Este SDK é **gêmeo** do [`@alijunior/nuvemfiscal-sdk-node`](https://github.com/alijuniorbr/nuvemfiscal-sdk-node): mesma interface, mesmos adapters e mesmos helpers de token. Se você já usa um, o outro funciona do mesmo jeito — trocando apenas o nome do client (`AcbrApi` ⇆ `NuvemFiscalApi`), o `tokenUrl` e o `API_BASE`.
|
|
35
|
+
|
|
26
36
|
---
|
|
27
37
|
|
|
28
38
|
## Instalação
|
|
@@ -62,6 +72,8 @@ API_BASE=https://prod.acbr.api.br/
|
|
|
62
72
|
SCOPE=empresa conta cnpj cep nfce nfe
|
|
63
73
|
```
|
|
64
74
|
|
|
75
|
+
> A autenticação da Acbr API usa **Keycloak** (`auth.acbr.api.br`). O endpoint de token é o padrão OpenID Connect: `.../realms/ACBrAPI/protocol/openid-connect/token`.
|
|
76
|
+
|
|
65
77
|
---
|
|
66
78
|
|
|
67
79
|
## Helpers de Token
|
|
@@ -81,7 +93,7 @@ const input: ClientCredentialsInput = {
|
|
|
81
93
|
tokenUrl: process.env.TOKEN_URL!,
|
|
82
94
|
clientId: process.env.CLIENT_ID!,
|
|
83
95
|
clientSecret: process.env.CLIENT_SECRET!,
|
|
84
|
-
scope: process.env.SCOPE,
|
|
96
|
+
scope: process.env.SCOPE, // opcional
|
|
85
97
|
authStyle: "auto", // tenta basic e cai para Body em 400/401
|
|
86
98
|
};
|
|
87
99
|
|
|
@@ -100,13 +112,17 @@ const accessToken = await getAccessTokenString({
|
|
|
100
112
|
tokenUrl: process.env.TOKEN_URL!,
|
|
101
113
|
clientId: process.env.CLIENT_ID!,
|
|
102
114
|
clientSecret: process.env.CLIENT_SECRET!,
|
|
103
|
-
scope: process.env.SCOPE
|
|
115
|
+
scope: process.env.SCOPE, // opcional
|
|
104
116
|
});
|
|
105
117
|
```
|
|
106
118
|
|
|
107
119
|
### 3) `getAcbrApiTokenForBrowser` (Browser – **somente testes**)
|
|
108
120
|
|
|
109
|
-
Útil para provar fluxo
|
|
121
|
+
Útil para provar o fluxo **localmente**. **Não use em produção** (expõe o `client_secret`).
|
|
122
|
+
|
|
123
|
+
Assinatura: `getAcbrApiTokenForBrowser(clientId, clientSecret, tokenUrl?, scope?)`. Os parâmetros `tokenUrl` e `scope` são **opcionais** e posicionais.
|
|
124
|
+
|
|
125
|
+
> ⚠️ **Atenção (CORS):** o servidor de autenticação Keycloak da Acbr API normalmente **não libera CORS** para origens de browser. Na prática, mesmo em testes, a obtenção de token direto do navegador pode ser bloqueada. O caminho confiável é obter o token **no backend** e expô-lo ao front (veja a seção sobre browser em produção, mais abaixo).
|
|
110
126
|
|
|
111
127
|
```ts
|
|
112
128
|
import { getAcbrApiTokenForBrowser } from "@alijunior/acbr-api-sdk-node";
|
|
@@ -114,8 +130,8 @@ import { getAcbrApiTokenForBrowser } from "@alijunior/acbr-api-sdk-node";
|
|
|
114
130
|
const accessToken = await getAcbrApiTokenForBrowser(
|
|
115
131
|
"CLIENT_ID",
|
|
116
132
|
"CLIENT_SECRET",
|
|
117
|
-
"https://auth.acbr.api.br/
|
|
118
|
-
"empresa conta cnpj cep nfce nfe"
|
|
133
|
+
"https://auth.acbr.api.br/realms/ACBrAPI/protocol/openid-connect/token",
|
|
134
|
+
"empresa conta cnpj cep nfce nfe"
|
|
119
135
|
);
|
|
120
136
|
```
|
|
121
137
|
|
|
@@ -127,7 +143,7 @@ const accessToken = await getAcbrApiTokenForBrowser(
|
|
|
127
143
|
|
|
128
144
|
```ts
|
|
129
145
|
import {
|
|
130
|
-
|
|
146
|
+
AcbrApi,
|
|
131
147
|
FetchHttpClient,
|
|
132
148
|
getAccessTokenString,
|
|
133
149
|
} from "@alijunior/acbr-api-sdk-node";
|
|
@@ -136,14 +152,14 @@ const accessToken = await getAccessTokenString({
|
|
|
136
152
|
tokenUrl: process.env.TOKEN_URL!,
|
|
137
153
|
clientId: process.env.CLIENT_ID!,
|
|
138
154
|
clientSecret: process.env.CLIENT_SECRET!,
|
|
139
|
-
scope: process.env.SCOPE
|
|
155
|
+
scope: process.env.SCOPE,
|
|
140
156
|
});
|
|
141
157
|
|
|
142
158
|
const http = new FetchHttpClient({
|
|
143
159
|
headers: { Authorization: `Bearer ${accessToken}` },
|
|
144
160
|
});
|
|
145
161
|
|
|
146
|
-
const api = new
|
|
162
|
+
const api = new AcbrApi(http, process.env.API_BASE!);
|
|
147
163
|
|
|
148
164
|
// CEP
|
|
149
165
|
const cep = await api.consultarCep({ Cep: "04513010" });
|
|
@@ -163,10 +179,11 @@ console.log("CNPJ:", cnpjList);
|
|
|
163
179
|
|
|
164
180
|
### Com `axios` (opcional)
|
|
165
181
|
|
|
182
|
+
O `AxiosHttpClient` recebe um objeto de configuração. Você pode passar apenas o `accessToken` (o adapter cria a instância axios internamente) **ou** fornecer uma `axiosInstance` própria.
|
|
183
|
+
|
|
166
184
|
```ts
|
|
167
|
-
import axios from "axios";
|
|
168
185
|
import {
|
|
169
|
-
|
|
186
|
+
AcbrApi,
|
|
170
187
|
AxiosHttpClient,
|
|
171
188
|
getAccessTokenString,
|
|
172
189
|
} from "@alijunior/acbr-api-sdk-node";
|
|
@@ -177,17 +194,114 @@ const accessToken = await getAccessTokenString({
|
|
|
177
194
|
clientSecret: process.env.CLIENT_SECRET!,
|
|
178
195
|
});
|
|
179
196
|
|
|
180
|
-
const
|
|
181
|
-
|
|
197
|
+
const http = new AxiosHttpClient({
|
|
198
|
+
accessToken,
|
|
199
|
+
baseURL: process.env.API_BASE!,
|
|
182
200
|
});
|
|
183
201
|
|
|
184
|
-
const
|
|
185
|
-
const api = new AcbrApiClient(http, process.env.API_BASE!);
|
|
202
|
+
const api = new AcbrApi(http, process.env.API_BASE!);
|
|
186
203
|
|
|
187
204
|
// exemplo
|
|
188
205
|
const cep = await api.consultarCep({ Cep: "01001000" });
|
|
189
206
|
```
|
|
190
207
|
|
|
208
|
+
Caso prefira controlar a instância axios (interceptors, timeout, etc.), passe-a via `axiosInstance`:
|
|
209
|
+
|
|
210
|
+
```ts
|
|
211
|
+
import axios from "axios";
|
|
212
|
+
import { AxiosHttpClient } from "@alijunior/acbr-api-sdk-node";
|
|
213
|
+
|
|
214
|
+
const axiosInstance = axios.create({
|
|
215
|
+
baseURL: process.env.API_BASE!,
|
|
216
|
+
headers: { Authorization: `Bearer ${accessToken}` },
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
const http = new AxiosHttpClient({ axiosInstance });
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
> **Opções do construtor do `AxiosHttpClient`:** `{ accessToken?, axiosInstance?, baseURL?, headers?, timeoutMs? }`. Se `accessToken` e `axiosInstance` forem informados simultaneamente, a `axiosInstance` prevalece.
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
226
|
+
## Exemplo completo: empresa, certificado e emissão
|
|
227
|
+
|
|
228
|
+
Fluxo típico: cadastrar a empresa emitente, enviar o certificado digital, configurar a NFC-e e emitir.
|
|
229
|
+
|
|
230
|
+
```ts
|
|
231
|
+
import {
|
|
232
|
+
AcbrApi,
|
|
233
|
+
FetchHttpClient,
|
|
234
|
+
getAccessTokenString,
|
|
235
|
+
type Empresa,
|
|
236
|
+
} from "@alijunior/acbr-api-sdk-node";
|
|
237
|
+
|
|
238
|
+
const accessToken = await getAccessTokenString({
|
|
239
|
+
tokenUrl: process.env.TOKEN_URL!,
|
|
240
|
+
clientId: process.env.CLIENT_ID!,
|
|
241
|
+
clientSecret: process.env.CLIENT_SECRET!,
|
|
242
|
+
scope: process.env.SCOPE,
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
const http = new FetchHttpClient({
|
|
246
|
+
headers: { Authorization: `Bearer ${accessToken}` },
|
|
247
|
+
});
|
|
248
|
+
const api = new AcbrApi(http, process.env.API_BASE!);
|
|
249
|
+
|
|
250
|
+
// 1) Cadastrar a empresa emitente
|
|
251
|
+
const empresa: Empresa = {
|
|
252
|
+
cpf_cnpj: "00000000000000", // sem máscara
|
|
253
|
+
nome_razao_social: "Minha Empresa LTDA",
|
|
254
|
+
nome_fantasia: "Minha Empresa",
|
|
255
|
+
email: "contato@minhaempresa.com.br",
|
|
256
|
+
inscricao_estadual: "ISENTO",
|
|
257
|
+
endereco: {
|
|
258
|
+
logradouro: "Rua Exemplo",
|
|
259
|
+
numero: "100",
|
|
260
|
+
bairro: "Centro",
|
|
261
|
+
codigo_municipio: "3550308", // código IBGE
|
|
262
|
+
uf: "SP",
|
|
263
|
+
cep: "01001000", // sem máscara
|
|
264
|
+
},
|
|
265
|
+
};
|
|
266
|
+
|
|
267
|
+
// criarEmpresa cria ou, se já existir, normalmente retorna conflito —
|
|
268
|
+
// trate o erro e use atualizarEmpresa quando necessário (veja Tratamento de erros).
|
|
269
|
+
await api.criarEmpresa(empresa);
|
|
270
|
+
|
|
271
|
+
// 2) Cadastrar o certificado digital (.pfx/.p12 em base64)
|
|
272
|
+
await api.cadastrarCertificadoEmpresa(
|
|
273
|
+
{ cpf_cnpj: empresa.cpf_cnpj },
|
|
274
|
+
{
|
|
275
|
+
certificado: "BASE64_DO_PFX",
|
|
276
|
+
password: "senha-do-certificado",
|
|
277
|
+
}
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
// 3) Configurar a NFC-e (CRT + CSC da SEFAZ)
|
|
281
|
+
// CRT: 1=Simples Nacional, 2=SN excesso de sublimite, 3=Regime Normal, 4=MEI.
|
|
282
|
+
await api.alterarConfigNfce(
|
|
283
|
+
{ cpf_cnpj: empresa.cpf_cnpj },
|
|
284
|
+
{
|
|
285
|
+
CRT: 1,
|
|
286
|
+
ambiente: "homologacao", // ou "producao"
|
|
287
|
+
sefaz: {
|
|
288
|
+
id_csc: 1,
|
|
289
|
+
csc: "CODIGO_CSC_DA_SEFAZ",
|
|
290
|
+
},
|
|
291
|
+
}
|
|
292
|
+
);
|
|
293
|
+
|
|
294
|
+
// 4) Consultar a empresa
|
|
295
|
+
const cadastrada = await api.consultarEmpresa({ cpf_cnpj: empresa.cpf_cnpj });
|
|
296
|
+
console.log("Empresa cadastrada:", cadastrada.nome_razao_social);
|
|
297
|
+
|
|
298
|
+
// 5) Emitir uma NFC-e (monte o pedido conforme o tipo NfePedidoEmissao)
|
|
299
|
+
// const nfce = await api.emitirNfce(pedido);
|
|
300
|
+
// console.log("Status:", nfce.status, "Chave:", nfce.chave);
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
> Operações de exclusão (ex.: `excluirEmpresa`, `excluirCertificadoEmpresa`) retornam `void`: a API responde `204 No Content` e o adapter resolve sem corpo.
|
|
304
|
+
|
|
191
305
|
---
|
|
192
306
|
|
|
193
307
|
## Tratamento de erros (exemplo)
|
|
@@ -196,10 +310,11 @@ Os adapters levantam `Error` com `status` e, se presente, `body`.
|
|
|
196
310
|
|
|
197
311
|
```ts
|
|
198
312
|
function printHttpError(e: unknown, ctx?: string) {
|
|
199
|
-
const status = (e as
|
|
313
|
+
const status = (e as { status?: number }).status;
|
|
314
|
+
const body = (e as { body?: { error?: { message?: string }; message?: string } }).body;
|
|
200
315
|
const msg =
|
|
201
|
-
|
|
202
|
-
|
|
316
|
+
body?.error?.message ??
|
|
317
|
+
body?.message ??
|
|
203
318
|
(e as Error)?.message ??
|
|
204
319
|
String(e);
|
|
205
320
|
|
|
@@ -217,6 +332,10 @@ try {
|
|
|
217
332
|
}
|
|
218
333
|
```
|
|
219
334
|
|
|
335
|
+
> Em adapters baseados em `fetch`/`axios` deste SDK, o erro expõe `status` e `body` diretamente (ex.: `err.status`), e **não** `err.response.status` (formato do axios cru). Trate uma consulta `404` como "registro não encontrado".
|
|
336
|
+
|
|
337
|
+
> **Empresa já cadastrada:** ao tentar `criarEmpresa` para um CNPJ existente, a Acbr API pode retornar um erro com código do tipo `EmpresaAlreadyExists`. Nesse caso, faça o fallback para `atualizarEmpresa`. A detecção mais segura é verificar `status === 409` **ou** `/AlreadyExists/i.test(code)`, já que o status retornado pode variar.
|
|
338
|
+
|
|
220
339
|
---
|
|
221
340
|
|
|
222
341
|
## Tipos úteis (reexportados)
|
|
@@ -243,20 +362,20 @@ import type {
|
|
|
243
362
|
|
|
244
363
|
---
|
|
245
364
|
|
|
246
|
-
## Download de
|
|
365
|
+
## Download de binários
|
|
366
|
+
|
|
367
|
+
Métodos como `baixarPdfNfe` retornam `ArrayBuffer`.
|
|
247
368
|
|
|
248
369
|
```typescript
|
|
370
|
+
// Node: salvar em disco
|
|
249
371
|
import { writeFile } from "node:fs/promises";
|
|
250
|
-
import { arrayBufferToNodeBuffer } from "../types/acbr-api.httpclient";
|
|
251
|
-
|
|
252
|
-
// Node: para salvar o arquivo
|
|
253
372
|
|
|
254
373
|
const pdf = await api.baixarPdfNfe({ id: "..." });
|
|
255
|
-
await writeFile("nota.pdf",
|
|
374
|
+
await writeFile("nota.pdf", Buffer.from(pdf));
|
|
256
375
|
```
|
|
257
376
|
|
|
258
377
|
```typescript
|
|
259
|
-
// Browser:
|
|
378
|
+
// Browser: forçar download
|
|
260
379
|
|
|
261
380
|
const ab = await api.baixarPdfNfe({ id: "..." });
|
|
262
381
|
const blob = new Blob([ab], { type: "application/pdf" });
|
|
@@ -270,6 +389,14 @@ URL.revokeObjectURL(url);
|
|
|
270
389
|
|
|
271
390
|
---
|
|
272
391
|
|
|
392
|
+
## Uso com assistentes de IA (`llms.txt`)
|
|
393
|
+
|
|
394
|
+
O pacote inclui um arquivo [`llms.txt`](./llms.txt) na raiz, seguindo a convenção [llmstxt.org](https://llmstxt.org/). Ele descreve, em formato otimizado para LLMs, as assinaturas verificadas dos helpers de token e adapters, os fluxos de uso em **backend** e **browser** (incluindo a restrição de CORS do Keycloak), e as armadilhas mais comuns.
|
|
395
|
+
|
|
396
|
+
Ao gerar código que usa este SDK com um assistente, forneça o conteúdo de `llms.txt` como contexto para obter exemplos corretos.
|
|
397
|
+
|
|
398
|
+
---
|
|
399
|
+
|
|
273
400
|
## Desenvolvimento
|
|
274
401
|
|
|
275
402
|
```bash
|
|
@@ -292,4 +419,4 @@ pnpm build
|
|
|
292
419
|
|
|
293
420
|
## Licença
|
|
294
421
|
|
|
295
|
-
[MIT](./LICENSE)
|
|
422
|
+
[MIT](./LICENSE)
|