feat!: migrar client e mock para o contrato oficial v1.1.0 (0.2.0) - #11
Merged
Conversation
BREAKING CHANGE: os quatro headers obrigatorios sairam e entrou a assinatura X-JWS-Signature. createSplitClient troca tenantId por kid e assinar. O v1.1.0 nao declara messageId, correlationId, tenantId nem timestamp: eles sumiram de components.parameters e o Manual de Integracao v1.1.0 nao os cita uma unica vez. No lugar, X-JWS-Signature required nas 43 operacoes. A parte que exigiu decisao foi quem assina. O manual pede JWS Compact Detached sobre o corpo canonicalizado em JCS, com b64=false, e e esse b64=false que resolve a questao: a assinatura cobre os bytes CRUS do payload, entao quem assina precisa ver exatamente o que vai na rede. Se a assinatura viesse pronta de fora, o chamador serializaria por conta propria e as duas serializacoes divergiriam em ordem de chave, formato de numero ou escaping, produzindo uma assinatura valida sobre bytes que ninguem enviou. Entao o client canonicaliza, monta o protected header, calcula a entrada e remonta o detached; a operacao RS256 sai por callback. A chave privada nunca entra no pacote e pode ficar num HSM ou KMS, e existe uma serializacao so. - jcs.ts: RFC 8785. Curto porque o JavaScript ja acerta a maior parte, e o comentario registra por que: o `<` de string compara unidades de codigo UTF-16, que e a ordenacao que a RFC pede, e JSON.stringify ja usa o Number::toString do ES6 para numeros. - assinatura.ts: protected header, entrada de assinatura e conferencia de forma (que o mock reusa). - codegen: pos-processamento para MocOcorrenciaSol/Not. O allOf com not do spec faz o openapi-typescript emitir tipos inconstruiveis, com o discriminador virando o nome do schema e o resto zerado por Record<string, never>. A reescrita mora no gerador de proposito: quando o upstream corrigir, o bloco sai e o gerado continua igual, o que um override manual nao daria. Testes de 27 para 71, incluindo verificacao RS256 real contra par de chaves, os vetores da RFC 8785 e uma prova de tipo do MOC (sem ela, um tipo inconstruivel passa despercebido: nada mais no pacote toca aqueles schemas).
BREAKING CHANGE: rotas de stream renomeadas, os quatro headers antigos deixam
de ser exigidos e campos foram renomeados ou mudaram de tipo.
Spec embarcado e trava de hash agora sao do v1.1.0 (35 rotas).
Mecanismo de Ocorrencias (3.9), tres rotas novas. A resposta da RFB/CGIBS e
simulada de forma deterministica porque sem ela a consulta devolveria 204 para
sempre e a rota seria decoracao: o valor de simular esta em o integrador ver o
ciclo fechar. O MOC nao ganhou matriz M/O/N-E de proposito: o proprio spec ja
codifica a regra por arranjo com oneOf, required e additionalProperties false,
e criar matriz duplicaria a fonte da verdade que o Ajv ja aplica.
Assinatura: o mock confere a FORMA do protected header quando o header vem, e
nao exige que venha. Verificar de verdade obrigaria quem roda `npx
splitbr-mock` a gerar par de chaves antes da primeira resposta, e o valor do
mock e nao precisar de setup; aceitar qualquer string nao ensinaria nada,
porque o erro provavel em producao e mandar um JWS bem-formado com b64 errado
ou com o payload anexado em vez de detached, e a conferencia de forma pega
exatamente isso. `exigirAssinatura` liga o comportamento fiel.
Os quatro headers antigos continuam ACEITOS e o correlationId segue ecoado,
por utilidade de debug, mas nenhum e obrigatorio: exigi-los deixaria o mock
mais estrito que a plataforma real, que e o defeito oposto ao que um mock deve
ter.
Renomes aplicados no codigo e nas matrizes: {idPsp}/tributos para
{cnpjRaizPspRecDir}/transacoes nas 12 rotas de stream, fromNsu/toNsu para
nsuInicial/nsuFinal, cnpjCpfPagOrig para cnpjPagOrig (que passou a aceitar so
CNPJ), valorTotalCbs/Ibs para vlTotalCbs/Ibs. Mudancas de tipo: numIdentcBaixa
e nsuId viraram string, numCodBarras exige 44 digitos e idLote passou a ser
idInfSegr mais sequencial (40 posicoes), o que amarra o lote ao informe no
proprio identificador. dtHrDisp entrou como obrigatorio no Super Inteligente e
e emitido na fronteira HTTP, onde o instante de disponibilizacao existe.
Testes de 105 para 115.
Atualiza as linhas do MANIFEST e o comentario de cobertura do detector: o openapi-v1_1_0.json alimenta o codegen desde a 0.2.0, e o v0.0.10 fica como registro do que gerou ate a 0.1.1. Nenhuma linha sobrescrita, como manda a convencao do arquivo.
Guia novo em docs/site/migracao.md, registrado na navegacao. Formato append-only com um H2 por migracao, espelhando o que novidades.md ja faz: da URL estavel e uma entrada so no menu. O guia cobre campo a campo, mas o trecho que mais evita perda de tempo e o aviso sobre o b64=false: montar a assinatura por fora e serializar o corpo por conta propria produz uma assinatura que parece correta e a plataforma rejeita sem dizer por que. Tambem avisa que nsuId virou string, entao comparar NSU com `>` passou a ordenar lexicograficamente e "9" > "10" e verdadeiro. Tutorial: os quatro headers sairam dos curl e os payloads foram corrigidos para o contrato novo (numCodBarras com 44 digitos, cnpjPagOrig). Verifiquei subindo o mock e rodando o primeiro comando: 201 com 1 item valido. O tutorial de curl sobrevive porque o mock nao exige assinatura por padrao, que e exatamente para isso que esse default existe. READMEs dos dois pacotes (via site-sync), README raiz nas duas metades, quadro de estado da regulacao e uma entrada em novidades.
Pre-1.0, entao a quebra se sinaliza no minor: `^0.1.1` nao atravessa minor em 0.x, e quem depende assim fica protegido sem precisar de um 1.0.0. Subir para 1.0.0 afirmaria uma estabilidade de API que o cenario nao sustenta, com tres manuais da familia ainda nao publicados, dois em minuta, o Manual de Seguranca (que descreve o JWKS por tras da assinatura) inexistente, e a propria plataforma tendo pulado de 0.0.10 para 1.1.0 em dois meses.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Migra os dois pacotes do OpenAPI v0.0.10 para o v1.1.0, publicado pelo CGIBS em 24/08/2026, e entrega o guia de migração. Quebra compatibilidade com a linha 0.1.x.
A decisão que moldou o resto: quem assina
O contrato exige
X-JWS-Signaturenas 43 operações: JWS Compact Detached sobre o corpo canonicalizado em JCS, RS256, sete atributos no protected header.O
b64: false(RFC 7797) decide o desenho. Ele faz a assinatura cobrir os bytes crus do payload, então quem assina precisa ver exatamente o que vai na rede. Se a assinatura viesse pronta de fora, o chamador serializaria por conta própria e as duas serializações divergiriam em ordem de chave, formato de número ou escaping — produzindo uma assinatura válida sobre bytes que ninguém enviou, e um 4xx sem pista.Então o client faz a parte difícil e delega só o RS256:
Existe uma serialização só, e é a que vai na requisição.
O que mudou no contrato
X-JWS-Signature{idPsp}/tributos{cnpjRaizPspRecDir}/transacoesOs quatro headers obrigatórios não existem mais. Sumiram de
components.parameterse o manual v1.1.0 não os cita uma única vez.Campos renomeados:
cnpjCpfPagOrig→cnpjPagOrig(que passou a aceitar só CNPJ, o pagador pessoa física saiu do contrato),valorTotalCbs/Ibs→vlTotalCbs/Ibs,fromNsu/toNsu→nsuInicial/nsuFinal,tributos→transacoes. Tipos:numIdentcBaixaensuIdviraram string. Formatos fixos:numCodBarrascom 44 dígitos,idLote=idInfSegr+ sequencial (40 posições).dtHrDispentrou como obrigatório no Super Inteligente.PATCH /api/v1/pix-automaticodeixou de existir.Três escolhas que valem revisão
1. O pós-processamento do MOC no codegen.
MocOcorrenciaSoleNotusamallOfcomnot, e o openapi-typescript emite tipos inconstruíveis: o discriminador vira o literal com o nome do schema (arrj: "MocOcorrenciaSol"em vez deBOL|PXE|...) e onotviraRecord<string, never>, zerando o resto. Pus a reescrita no gerador, não num override manual, para que ela suma sozinha quando o upstream corrigir. O testemoc-tipos.test.tsexiste porque sem ele isso quebra em silêncio: nada mais no pacote toca aqueles schemas, entãotscsairia 0 com as duas rotas POST inutilizáveis.2. O mock confere a forma da assinatura, sem exigir. Verificar de verdade obrigaria quem roda
npx splitbr-mocka gerar par de chaves antes da primeira resposta, e o valor do mock é não precisar de setup. Aceitar qualquer string não ensinaria nada. Conferir a forma pega o erro provável em produção: JWS bem-formado comb64errado, ou payload anexado em vez de detached.buildServer({ exigirAssinatura: true })liga o modo fiel.3. O MOC não ganhou matriz M/O/N-E. O princípio do repo é que as matrizes são o produto, mas aqui o próprio spec já codifica a regra por arranjo com
oneOf,requiredeadditionalProperties: false, e o Ajv já a aplica. Criar matriz duplicaria a fonte da verdade. Contrariei a convenção de propósito; se preferir a coerência, dá para reverter só isso.Versão: 0.2.0, não 1.0.0
Pré-1.0, a quebra se sinaliza no minor, e
^0.1.1não atravessa minor em0.x— quem depende assim já fica protegido. Subir para 1.0.0 afirmaria uma estabilidade que o cenário não sustenta: três manuais da família não foram publicados, dois são minuta, o Manual de Segurança (que descreve o JWKS por trás da assinatura) não existe, e a plataforma pulou de 0.0.10 para 1.1.0 em dois meses.Verificação
O client saiu de 27 para 71 testes: vetores da RFC 8785 (incluindo o caso do emoji antes do hebraico, que separa ordenação por code unit de code point), verificação RS256 real com par de chaves gerado, teste de adulteração de byte, e a prova de tipo do MOC.
Guia de migração
Novo em
docs/site/migracao.md, na navegação. Além do campo a campo, avisa dois erros que custam tempo: o dob64: falseacima, e quensuIdvirou string, então"9" > "10"é verdadeiro e comparar NSU com>passou a ordenar lexicograficamente.