-
-
Notifications
You must be signed in to change notification settings - Fork 0
Conceitos
Versão canônica no repo: docs/CONCEITOS.md. Esta página da wiki é a porta de entrada; o documento no repo é a fonte única de verdade.
Esta página explica, do zero, o que a biblioteca faz e por que ela existe. Foi escrita para quem nunca ouviu falar de TISS, de hash ou de XML. Cada termo técnico é explicado na primeira vez que aparece.
Se você já conhece o assunto e quer ir direto ao código, pule para Como usar. Para a definição técnica exata do algoritmo, veja o SPEC.
"Biblioteca" (ou "lib") aqui significa um pedaço de código pronto que você coloca dentro do seu programa para reaproveitar, em vez de escrever tudo de novo. Como uma receita pronta que você inclui no seu livro de receitas.
No Brasil, "saúde suplementar" é a parte do sistema de saúde formada pelos planos de saúde privados (as operadoras) e por quem atende os pacientes por esses planos (clínicas, hospitais, laboratórios, chamados de prestadores), funcionando ao lado do SUS público. A ANS (Agência Nacional de Saúde Suplementar) é o órgão do governo que regula esse setor: define as regras que operadoras e prestadores precisam seguir, inclusive como eles trocam informações entre si.
Quando uma clínica atende um paciente de um plano, ela precisa cobrar o plano por aquele atendimento. Para que qualquer clínica consiga "conversar" com qualquer plano sem cada um inventar um formato próprio, a ANS criou o Padrão TISS (Troca de Informações na Saúde Suplementar): um conjunto de regras que padroniza como essas informações (guias de consulta, exames, internações, valores) são escritas e enviadas.
Não citamos aqui o número da versão do Padrão TISS porque a ANS publica uma versão nova quase todo ano. O que esta lib resolve não depende da versão.
Esse padrão define que as informações viajam dentro de arquivos XML. Um "lote TISS" é basicamente um arquivo XML com várias guias de atendimento juntas, que a clínica envia para a operadora.
XML (eXtensible Markup Language) é um jeito de guardar informação em texto puro, organizado de forma que tanto um humano quanto um programa consigam ler. A informação fica dentro de tags (etiquetas): um par de marcas, uma de abertura e uma de fechamento, com o valor no meio. Exemplo mínimo:
<paciente>
<nome>Maria</nome>
<idade>34</idade>
</paciente>O elemento <paciente> contém dois elementos dentro dele, <nome> (valor "Maria") e <idade> (valor "34"). A barra / na marca de fechamento (</nome>) indica onde o elemento termina. Um elemento que só guarda um valor, sem outros elementos dentro, é um elemento-folha (como <nome> e <idade>). Essa noção de "folha" vai ser importante mais adiante.
Para ler um XML, um programa usa um parser (analisador): a parte do código que lê o texto e transforma as tags numa estrutura que o programa entende. Você não precisa escrever um parser, cada linguagem já vem com um.
Um hash é um código calculado a partir de um texto (ou de qualquer dado). Pense nele como uma impressão digital do texto: a partir de um conteúdo grande, gera-se um código curto e de tamanho fixo que o identifica. A propriedade mais importante: se você mudar um único caractere do texto, o hash muda completamente. Isso permite detectar se alguém alterou o conteúdo.
Outra analogia: é como o lacre de uma caixa. Se o lacre ainda bate, ninguém mexeu. Se não bate, algo foi alterado no caminho.
MD5 é uma das fórmulas (algoritmos) que calculam esse hash. Ela sempre devolve um código de 32 caracteres usando apenas os dígitos de 0 a 9 e as letras de a a f (formato hexadecimal, ou "hex"). Exemplo de hash MD5 (do vetor de teste sintético syn_minimal, dados fictícios deste projeto):
3aa0c578c95cdb861a125f480a8a4de5
Conte: são 32 caracteres, todos entre 0-9 e a-f.
O MD5 não é mais considerado seguro contra ataques deliberados modernos e não deve ser usado para senhas. Mas o Padrão TISS exige MD5 para esse campo específico, então a lib usa MD5 por obrigação do padrão, não por escolha de segurança.
Num lote TISS, depois de todas as guias, vem uma parte final chamada epílogo (a "conclusão" do arquivo, como o final de uma carta). Dentro do epílogo existe um campo de hash: o elemento <ans:hash>.
O prefixo
ans:antes do nome do elemento vem de um conceito de XML chamado namespace (espaço de nomes): um rótulo que diz "este elemento pertence ao vocabulário da ANS", evitando confusão caso dois vocabulários diferentes usem o mesmo nome. Para esta página, basta saber que<ans:hash>é o campo onde mora o hash do lote.
Esse campo guarda a impressão digital (o hash MD5) de todo o conteúdo do lote. Quando a clínica envia o arquivo, a operadora e a ANS recalculam o hash do que receberam e comparam com o valor que veio no <ans:hash>. Se baterem, o lote chegou íntegro. Se não baterem, o lote foi alterado (ou gerado errado) e é rejeitado. É o lacre da caixa, aplicado ao arquivo.
Para calcular o hash, o programa precisa transformar o texto do lote em bytes antes de passar pela fórmula MD5. "Byte" é a menor unidade de informação que o computador guarda; transformar texto em bytes se chama encoding (codificação): a regra que diz qual sequência de bytes representa cada letra, acento ou símbolo. Existem várias dessas regras, com nomes como ISO-8859-1 e UTF-8. A letra "ç", por exemplo, vira bytes diferentes dependendo da regra escolhida.
Aqui está a pegadinha:
- O manual oficial do Padrão TISS diz que o encoding é ISO-8859-1.
- Mas, na prática, a ANS valida o hash sobre os bytes em UTF-8.
Quem lê o manual ao pé da letra e calcula o MD5 sobre bytes ISO-8859-1 gera um hash errado, e a ANS rejeita o lote. Como o hash muda completamente quando muda qualquer detalhe da entrada, basta essa diferença de encoding para o código final ficar totalmente diferente do esperado.
Esta lib calcula o hash do jeito que a ANS realmente aceita (bytes UTF-8). Isso não foi adivinhado: foi descoberto comparando com arquivos reais cujo hash a ANS confirmou, até achar a única combinação que reproduz aqueles valores. O detalhe completo está no SPEC, seção 4.
Resumo em uma frase: você entrega o arquivo XML do lote TISS, e a lib devolve o hash MD5 correto do epílogo, aquele que a ANS aceita.
E ela faz isso em 13 linguagens: Python, Rust, C, C++, Node.js, PHP, Java, Go, C#, Kotlin, Delphi/Object Pascal, Dart e WASM. Cada uma vive na sua pasta langs/<linguagem>/. O ponto central: as 13 produzem o mesmo resultado, idêntico byte a byte. Não importa em qual linguagem seu sistema foi escrito, o hash sai igual. O port WASM ainda calcula o hash dentro do próprio navegador do usuário, sem o XML sair da máquina (reforço de privacidade pela LGPD).
Para garantir isso, o projeto tem uma suíte de conformidade com 20 vetores de teste: 18 positivos (entradas válidas com o hash esperado) e 2 negativos (entradas inválidas que a lib deve recusar). Antes de qualquer versão ser liberada, as 13 linguagens precisam passar nos 20 testes, todas dando o mesmo hash. Os dados desses testes são 100% sintéticos (inventados); nenhum dado real de paciente entra no projeto.
Quem desenvolve sistemas que geram e enviam lotes TISS:
- clínicas e consultórios;
- hospitais e laboratórios;
- empresas de software de gestão em saúde (faturamento médico, prontuário, sistemas hospitalares);
- qualquer prestador ou integrador que precise mandar lotes para uma operadora.
Se você está nesse grupo e o seu hash "não bate" com o que a ANS espera, é bem provável que o problema seja exatamente o encoding descrito acima.
Da entrada do XML até os 32 caracteres de saída:
- Arquivo XML entra (os bytes do lote TISS).
- O campo
<ans:hash>é zerado (o hash não entra no cálculo dele mesmo). - Os valores dos elementos-folha são concatenados, na ordem em que aparecem.
- O texto resultante vira bytes UTF-8 (não ISO-8859-1) e aplica-se o MD5.
- Saem 32 caracteres hexadecimais, por exemplo
3aa0c578c95cdb861a125f480a8a4de5. Esse é o valor que a ANS aceita.
A descrição técnica passo a passo está no SPEC, seção 3.
Repositório · Release v0.2.1 · Licença MIT · Esta wiki é a porta de entrada; o detalhe técnico mora no repo.