Skip to content
Merged
Show file tree
Hide file tree
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
12 changes: 7 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,16 +51,18 @@ O alvo do CGIBS tem uma limitação que vale declarar: `www.cgibs.gov.br` não a

A severidade é por alvo: portal e api-split reprovam o run tanto em divergência quanto em indisponibilidade; o piloto sinaliza sem reprovar, porque é infraestrutura de teste com janela até 31/12/2026 e mudar antes do portal é o comportamento esperado dele.

### O contrato da Plataforma está em v1.1.0; os pacotes ainda geram do v0.0.10
### O contrato da Plataforma está na v1.1.0, e os pacotes acompanham

Em 24/08/2026 o CGIBS publicou o **OpenAPI v1.1.0** da Plataforma Pública, pareado com o Manual de Integração v1.1.0, e moveu o v0.0.10 para "versões anteriores". A mudança quebra o contrato:
Em 24/08/2026 o CGIBS publicou o **OpenAPI v1.1.0** da Plataforma Pública, pareado com o Manual de Integração v1.1.0, e moveu a v0.0.10 para "versões anteriores". Desde a **0.2.0**, `@splitbr/client` e `@splitbr/mock` são gerados desse contrato.

A mudança quebra compatibilidade com a linha 0.1.x:

- os quatro headers obrigatórios (`messageId`, `correlationId`, `tenantId`, `timestamp`) **deixaram de existir**, e entrou a assinatura `X-JWS-Signature` em todas as 43 operações;
- as 12 rotas de stream trocaram `{idPsp}/tributos` por `{cnpjRaizPspRecDir}/transacoes`;
- entraram 3 rotas do Mecanismo de Ocorrências (`/api/v1/moc/*`);
- o header `X-JWS-Signature` virou obrigatório nas 43 operações;
- os schemas foram de 57 para 78, com 33 dos 55 comuns alterados.

O v1.1.0 está vendorado aqui (`vendor/swagger/openapi-v1_1_0.json`), mas **ainda não alimenta o codegen**: `@splitbr/client` e `@splitbr/mock` continuam gerados do v0.0.10 e, portanto, implementam o contrato anterior. Migrar é um major bump nos dois pacotes e tem task própria. Até lá, quem integra a plataforma real deve ler o v1.1.0 como fonte da verdade.
O [guia de migração](https://mozurok.github.io/splitbr/migracao) cobre campo a campo. Sobre a assinatura, a escolha de desenho: **o client canonicaliza, monta o JWS e chama um callback seu para a operação RS256.** A chave privada nunca entra no pacote, e o corpo enviado é byte a byte o que foi assinado, que é o que faz a assinatura validar do outro lado.

**Correção de rota, registrada de propósito**: até 04/09/2026 este README afirmava que não existia fonte pública para esse contrato e que por isso ele não era monitorável. Era falso. O CGIBS publica o OAS numa página aberta, sem login e sem mTLS, e o zip de lá é byte-idêntico ao que já estava vendorado. O custo do engano foi medido: o v1.1.0 ficou 11 dias sem detecção. O detector agora observa aquela página como quarto alvo.

Expand All @@ -78,7 +80,7 @@ Monorepo pnpm: `pnpm install && pnpm -r build && pnpm -r test` (Node >= 22). Con
Engineering notes:

- The official contracts are vendored with a **pinned SHA-256**; a weekly CI diffs four targets against the vendored copies and **opens an issue on drift** instead of updating silently: the three live Calculadora contracts, by normalised content, plus the artifact inventory of the [CGIBS Split Payment page](https://www.cgibs.gov.br/split-payment), which is where the Platform contract itself is published. Severity is per target: the production endpoints fail the run, the pilot one reports without failing (it is test infrastructure and moving ahead is its job). The CGIBS target carries a stated limitation: `www.cgibs.gov.br` refuses connections from the GitHub Actions runner, so in CI it reports unreachable without failing the run, and it only really compares when run from a network that can reach the host. Drift there still fails when reachable.
- **The Platform contract moved to v1.1.0 on 2026-08-24; the packages still generate from v0.0.10.** The new spec renames all 12 stream routes (`{idPsp}/tributos` to `{cnpjRaizPspRecDir}/transacoes`), adds 3 Mechanism-of-Occurrences routes, makes the `X-JWS-Signature` header required on all 43 operations, and grows the schema set from 57 to 78. v1.1.0 is vendored here but does not feed codegen yet: migrating is a major bump for both packages and has its own task. Until then, treat v1.1.0 as the source of truth if you integrate the real platform. Until 2026-09-04 this README claimed that spec had no public endpoint and so could not be monitored; that was false, and the error cost 11 days of undetected drift.
- **The Platform contract moved to v1.1.0 on 2026-08-24, and the packages followed in 0.2.0.** The new spec renames all 12 stream routes (`{idPsp}/tributos` to `{cnpjRaizPspRecDir}/transacoes`), adds 3 Mechanism-of-Occurrences routes, replaces the four mandatory headers with a required `X-JWS-Signature` on all 43 operations, and grows the schema set from 57 to 78. See the [migration guide](https://mozurok.github.io/splitbr/migracao). On signing: the client canonicalises (JCS, RFC 8785), builds the detached JWS, and calls a callback you provide for the RS256 operation, so the private key never enters the package and the bytes sent are exactly the bytes signed. Until 2026-09-04 this README claimed that spec had no public endpoint and so could not be monitored; that was false, and the error cost 11 days of undetected drift.
- Money math is **integer cents only** (BigInt), never floating point, truncated toward zero to match the official rounding.
- The interactive [demo](https://mozurok.github.io/splitbr/) computes every figure with the **same published function the SDK ships**, so it doubles as a live validation of the packages.

Expand Down
1 change: 1 addition & 0 deletions docs/site/.vitepress/config.mts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export default defineConfig({
items: [
{ text: "@splitbr/client", link: "/referencia/client" },
{ text: "@splitbr/mock", link: "/referencia/mock" },
{ text: "Guia de migração", link: "/migracao" },
],
},
{
Expand Down
2 changes: 1 addition & 1 deletion docs/site/estado-regulacao.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ As siglas do quadro: **IBS** (Imposto sobre Bens e Serviços) e **CBS** (Contrib
| IT 2026.001 (tabela de meios de pagamento) | v1.01 | 25/08/2026 | [Portal NF-e, Informes Técnicos](https://www.nfe.fazenda.gov.br/portal/listaConteudo.aspx?tipoConteudo=hXzemuyNHW4=) |
| Resolução CGIBS nº 6/2026 (regulamenta o IBS) | 6/2026 | 30/04/2026 | [PDF na CGIBS](https://www.cgibs.gov.br/upload/arquivos/202604/30084927-res-cgibs-n-6-30-abr-2026-regulamenta-o-ibs.pdf) |

A distribuição offline da Calculadora vendorada no repositório (componente `api-regime-geral` 1.2.4, banco V0039, obtida em 10/07/2026) está atrás da produção desde 31/08/2026. O `@splitbr/client` e o `@splitbr/mock` continuam gerados do OpenAPI **v0.0.10**, não do v1.1.0: a migração é uma quebra de contrato e está em aberto.
A distribuição offline da Calculadora vendorada no repositório (componente `api-regime-geral` 1.2.4, banco V0039, obtida em 10/07/2026) está atrás da produção desde 31/08/2026. Desde a versão 0.2.0, o `@splitbr/client` e o `@splitbr/mock` são gerados do OpenAPI **v1.1.0**. O [guia de migração](/migracao) cobre o que mudou para quem estava na 0.1.x.

## O que ainda não existe

Expand Down
163 changes: 163 additions & 0 deletions docs/site/migracao.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
---
title: Guia de migração
---

# Guia de migração

Registro das mudanças que quebram compatibilidade entre versões do `@splitbr/client` e do `@splitbr/mock`, com o que fazer em cada caso. Mais recente primeiro.

## 0.1.x para 0.2.0: o contrato oficial foi de v0.0.10 para v1.1.0

Em 24/08/2026 o CGIBS publicou o **OpenAPI v1.1.0** da Plataforma Pública de Split Payment, junto com o Manual de Integração v1.1.0, e moveu a v0.0.10 para "versões anteriores". Os pacotes 0.2.0 são gerados desse contrato novo.

**A migração não é opcional se você integra a plataforma real.** O contrato antigo saiu do ar como referência corrente, e as rotas que a 0.1.x chama foram renomeadas.

::: warning Antes de começar
Uma parte do contrato v1.1.0 depende do **Manual de Segurança**, que ainda não foi publicado. É dele que sai o fluxo de JWKS por trás da assinatura, ou seja, como sua chave pública chega à plataforma. O que está aqui cobre o que os artefatos oficiais já definem.
:::

### Resumo das mudanças

| | v0.0.10 (0.1.x) | v1.1.0 (0.2.0) |
|---|---|---|
| Rotas | 32 | 35 |
| Schemas | 57 | 78 |
| Autenticação de mensagem | 4 headers | assinatura `X-JWS-Signature` |
| Rotas de stream | `{idPsp}/tributos/...` | `{cnpjRaizPspRecDir}/transacoes/...` |
| Mecanismo de Ocorrências | não existia | 3 rotas |
| Pix Automático em transação atualizada | existia | removido |

### 1. Os quatro headers obrigatórios deixaram de existir

Esta é a mudança de maior impacto no código, e a mais fácil de aplicar.

O contrato v0.0.10 exigia `messageId`, `correlationId`, `tenantId` e `timestamp` em toda requisição. **Nenhum dos quatro existe no v1.1.0**: eles saíram de `components.parameters` e não são citados uma única vez no Manual de Integração v1.1.0. No lugar entrou um só, o `X-JWS-Signature`, obrigatório nas 43 operações.

```ts
// antes (0.1.x)
const client = createSplitClient({
baseUrl: "https://...",
tenantId: "12345678000199",
});

// depois (0.2.0)
const client = createSplitClient({
baseUrl: "https://...",
kid: "id-da-sua-chave",
assinar: async (bytes) => assinarRS256(bytes),
});
```

`gerarCorrelationId` foi removido, e a validação de `tenantId` junto. `gerarTimestampSplit` **continua exportado**: deixou de ser header, mas o formato segue valendo para `infRequisicao.dtHrMsg`, que é campo de corpo obrigatório.

### 2. A assinatura `X-JWS-Signature`

Toda requisição passa a carregar uma assinatura JWS Compact Detached (RFC 7515) sobre o corpo canonicalizado em JCS (RFC 8785), com sete atributos obrigatórios no protected header: `alg` (RS256), `typ` (JWS), `kid`, `jti` (UUID v4), `iat` (NumericDate), `b64` (false) e `crit` (`["b64"]`).

**O client faz a parte difícil e você fica com a chave.** A canonicalização, o protected header, a montagem do formato detached e a garantia de que o corpo enviado é byte a byte o que foi assinado são responsabilidade do pacote. A operação RS256 sai por um callback:

```ts
import { createSign } from "node:crypto";
import { createSplitClient } from "@splitbr/client";

const client = createSplitClient({
baseUrl: process.env.SPLIT_BASE_URL,
kid: "minha-chave-01",
assinar: (bytes) => {
const s = createSign("RSA-SHA256");
s.update(bytes);
s.end();
return new Uint8Array(s.sign(process.env.CHAVE_PRIVADA_PEM));
},
});
```

Num HSM ou KMS, o callback vira a chamada do serviço e a chave privada nunca entra no processo:

```ts
assinar: async (bytes) => kms.sign({ KeyId, Message: bytes, SigningAlgorithm: "RSASSA_PKCS1_V1_5_SHA_256" }),
```

::: danger O erro que custa caro aqui
O `b64: false` da RFC 7797 significa que a assinatura cobre o **payload cru**, não uma versão em Base64URL. Se você montar a assinatura por fora e serializar o corpo por conta própria, as duas serializações divergem (ordem de chave, formato de número, escaping) e a plataforma rejeita uma assinatura que parece correta. É exatamente por isso que o client não aceita a assinatura pronta: existe uma serialização só, e é a que vai na rede.
:::

Se você precisa das peças isoladas, elas são públicas: `canonicalizarJcs`, `assinarRequisicao`, `montarProtectedHeader`, `montarEntradaDeAssinatura` e `conferirFormaDoHeader`.

### 3. As 12 rotas de stream foram renomeadas

O parâmetro de rota `{idPsp}` virou `{cnpjRaizPspRecDir}` (8 posições, a raiz do CNPJ, não mais um identificador opaco), e o segmento `/tributos/` virou `/transacoes/`.

```diff
- /api/v1/out/boleto/{idPsp}/tributos/stream/start
+ /api/v1/out/boleto/{cnpjRaizPspRecDir}/transacoes/stream/start
```

Vale para os três arranjos com stream (boleto, Pix Automático, Pix Dinâmico), nos modos `out` e `retroativo`, em `start`, consumo por token e `DELETE`.

O corpo da resposta acompanhou: a chave **`tributos` virou `transacoes`**.

Na consulta retroativa, os parâmetros `fromNsu` e `toNsu` viraram **`nsuInicial` e `nsuFinal`**.

### 4. Campos renomeados e tipos alterados

| Onde | Antes | Depois |
|---|---|---|
| Transação iniciada (boleto, Pix Automático, Pix Dinâmico) | `cnpjCpfPagOrig` | `cnpjPagOrig` |
| Finalização da segregação | `valorTotalCbs`, `valorTotalIbs` | `vlTotalCbs`, `vlTotalIbs` |
| Consulta retroativa | `fromNsu`, `toNsu` | `nsuInicial`, `nsuFinal` |
| Super Inteligente | `tributos` | `transacoes` |

**`cnpjCpfPagOrig` para `cnpjPagOrig` não é só um rename.** O campo antigo aceitava CPF de 11 dígitos ou CNPJ de 14; o novo aceita **apenas CNPJ de 14 posições**. Se você informava pagador pessoa física na transação iniciada, esse caminho deixou de existir no contrato.

Dois campos mudaram de tipo, de `integer` para `string`:

- `numIdentcBaixa` (pattern `^\d{1,19}$`)
- `nsuId` (pattern `^\d{1,19}$`)

::: tip Cuidado com comparação de NSU
Como `nsuId` virou string, `a > b` passa a comparar lexicograficamente. `"9" > "10"` é verdadeiro. Converta para número, ou compare com padding, antes de ordenar ou paginar.
:::

Dois campos ganharam formato fixo:

- **`numCodBarras`**: era 1 a 44 dígitos, agora são exatamente 44.
- **`idLote`**: era livre (1 a 16 posições), agora é `idInfSegr` seguido de um sequencial de 6 dígitos, 40 posições. Isso amarra o lote ao informe no próprio identificador.

E um campo novo, obrigatório no Retorno Super Inteligente: **`dtHrDisp`**, o instante em que a plataforma disponibilizou a mensagem para consumo.

### 5. `PATCH /api/v1/pix-automatico` deixou de existir

O Informe de Transação Atualizada passou a contemplar apenas Boleto e Pix Dinâmico. O Pix Automático saiu, e com ele os schemas `InformeDeTransacaoAtualizadaPixAutomaticoRequest` e `InformeDeTransacaoAtualizadaPixAutomaticoTransacao`. O `POST` da mesma rota continua existindo.

### 6. Mecanismo de Ocorrências (MOC): três rotas novas

Recurso novo, nada a migrar. São três rotas:

- `POST /api/v1/moc/solicitacao`, para solicitar estorno
- `POST /api/v1/moc/notificacao`, para notificar
- `GET /api/v1/moc/{cnpjRaizPspRecDir}/ocorrencias`, para consultar as que já receberam resposta da RFB ou do CGIBS

A diferença entre solicitação e notificação é **por presença de campo**, não por rota: a solicitação exige `codMotOcor`, `vlCbsEst` e `vlIbsEst` e proíbe os campos de processo administrativo; a notificação proíbe os três de estorno. Mandar o corpo errado na rota errada dá 400.

O mock simula a resposta da RFB/CGIBS de forma determinística, para o ciclo fechar sem depender da plataforma real.

## Migrando o `@splitbr/mock`

O mock 0.2.0 serve o contrato v1.1.0. Duas mudanças afetam quem já tem teste escrito contra ele:

**Os quatro headers antigos não são mais exigidos.** Ele continua aceitando os quatro e ecoando o `correlationId` na resposta, que é útil para depurar uma jornada, mas nenhum deles é obrigatório. Testes que os enviavam continuam passando; testes que verificavam o 400 por ausência deles precisam mudar.

**A assinatura é conferida na forma, não exigida.** Se o header `X-JWS-Signature` vier, o mock decodifica o protected header e cobra os sete atributos com os valores fixos. Se não vier, deixa passar. Isso mantém o `npx splitbr-mock` utilizável sem par de chaves, e ainda pega o erro mais provável em produção, que é mandar um JWS bem-formado com `b64` errado ou com o payload anexado em vez de detached.

Para o comportamento fiel ao contrato, ligue a exigência:

```ts
buildServer({ exigirAssinatura: true });
```

## Compatibilidade

`^0.1.1` **não** puxa a 0.2.0 automaticamente: em versões `0.x`, o caret do npm não atravessa o minor. Quem depende assim continua na linha antiga até subir de propósito.

A linha 0.1.x segue no npm e implementa a v0.0.10, que a fonte oficial aposentou. Ela serve para quem ainda integra um ambiente preso ao contrato antigo, não para trabalho novo.
6 changes: 6 additions & 0 deletions docs/site/novidades.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ Ordem: mais recente primeiro. Semana sem mudança não gera entrada.

Registro, em ordem cronológica inversa, das mudanças oficiais que afetam o Split Payment e a Reforma Tributária do Consumo: notas técnicas e informes do Portal NF-e, manuais e resoluções do Comitê Gestor do IBS (CGIBS), publicações da Receita Federal e versões da Calculadora de Tributos. Fontes oficiais verificadas toda segunda-feira às 9h (horário de Brasília); semanas sem mudança não geram entrada.

## 2026-09-04: splitbr 0.2.0 acompanha o contrato v1.1.0

- O que mudou: o `@splitbr/client` e o `@splitbr/mock` passaram a ser gerados do OpenAPI **v1.1.0**, publicado pelo CGIBS em 24/08/2026. É quebra de compatibilidade: os quatro headers obrigatórios deixaram de existir e entrou a assinatura `X-JWS-Signature` em todas as operações, as 12 rotas de stream mudaram de caminho, entraram as três rotas do Mecanismo de Ocorrências e vários campos foram renomeados ou mudaram de tipo. O mock agora simula o ciclo do MOC, incluindo a resposta da RFB/CGIBS.
- Impacto: quem está na linha 0.1.x precisa migrar para integrar a plataforma real, porque o contrato antigo saiu da lista de versões correntes. O `^0.1.1` não puxa a 0.2.0 sozinho, então a atualização é deliberada. O passo a passo campo a campo está no [guia de migração](/migracao).
- Fonte: https://www.cgibs.gov.br/split-payment

## 2026-08-31: Calculadora vai para o banco V0043 e sai o Pacote de Liberação 010f

- O que mudou: o endpoint público de versão da Calculadora passou a responder `versaoApp` 1.3.1 e `versaoDb` V0043, datado de 31/08/2026, com a descrição oficial "Ajustes na vigência das tabelas CLASSIF_NBS_INDOP_LC e INDICADOR_OPERACAO_IBS_CBS". A referência anterior registrada aqui era o componente 1.2.4 com o banco V0039. No mesmo dia, o Portal NF-e publicou o Pacote de Liberação 010f (NT 2025.002 v1.50 e NT 2026.007 v1.00) e moveu o 010e v1.02 para a lista de versões em desuso.
Expand Down
Loading
Loading