Codec das mensagens ISO 20022 do catálogo do SPI (Pix).

encode/2 e decode/1 são genéricos: despacham pelo tipo do modelo na ida e pelo namespace do XML na volta, então você não precisa saber de antemão qual mensagem está em jogo. O schema vem dos XSDs publicados pelo Banco Central, que não são redistribuídos aqui (ver mix catalog.gen).

envelope = %Isox.Envelope{
  header: %Isox.AppHdr{...},
  message: %Isox.Pacs008{...}
}

{:ok, xml} = Isox.encode(envelope, :v1_16)
{:ok, %Isox.Envelope{message: %Isox.Pacs008{}}, :v1_16} = Isox.decode(xml)

encode/2 recebe um Isox.Envelope, que é o par cabeçalho e mensagem, mais a versão do schema, e devolve o XML pronto e já validado. decode/1 faz o caminho inverso e devolve os dois de volta dentro de um envelope.

Cada mensagem também tem seu módulo de baixo nível, com a mesma forma de API mas sem o despacho automático. Serve para quando você já sabe o que está manipulando:

{:ok, xml} = Isox.Pacs008.encode(mensagem, cabecalho, :v1_16)
{:ok, mensagem, :v1_16} = Isox.Pacs008.decode(xml)

O que cada campo quer dizer

A pergunta que mais aparece integrando com o SPI é o que é um campo. O Isox.Dictionary responde, sem sair do editor:

iex> {:ok, entrada} = Isox.Dictionary.field(Isox.Pacs008, :dbtr_acct_id)
iex> entrada.name_br
"contaUsuarioPagador"

explain/2 mostra o campo inteiro, com caminho no XML, regra de preenchimento e valores aceitos. search/1 procura por qualquer termo em todas as mensagens. A documentação de cada mensagem traz a mesma informação em tabela.

Assinatura digital

sign/4, verify/2 e generate_test_certificate/0 cobrem o dia a dia: assinar o que sua aplicação envia, verificar o que ela recebe e gerar certificado descartável para testar.

Por baixo, quem faz isso é Isox.Xmldsig, um módulo à parte que não conhece estrutura de mensagem nenhuma. Para esta camada de codec, <Sgntr> é sempre opaco. Vá direto lá só se precisar de controle mais fino, como um perfil com outro número de referências.

# Os dois lados usam chaves opostas. Para assinar o que você manda,
# é a sua chave privada com o seu certificado. Para verificar o que
# você recebe, é o certificado público de quem assinou. Nenhum dos
# lados precisa da chave privada do outro. Ver a seção de
# certificados no README.
signature_xml = Isox.sign(app_hdr_xml, document_xml, minha_chave_privada_der, meu_certificado_der)
:ok = Isox.verify(envelope_recebido_xml, certificado_de_quem_enviou_der)

Summary

Functions

Decodifica um XML de qualquer mensagem do catálogo, identificando sozinho o tipo e a versão a partir do namespace. É o ponto de entrada para quem recebe XML sem saber de antemão que mensagem é, no lugar de Isox.Registry.decode/1.

Codifica um envelope (cabeçalho + modelo da mensagem) para XML, na versão de schema dada. Despacha pelo tipo do modelo em envelope.message para o módulo de baixo nível certo: se for um Isox.Pacs008, chama Isox.Pacs008.encode/3.

O campo explicado em texto corrido, para ler no IEx.

Todos os campos de uma mensagem, com nome no catálogo, caminho no XML, obrigatoriedade e descrição. Encaminha para Isox.Dictionary.fields/1.

Gera, em memória, um par de chave privada + certificado autoassinado para testes. Nunca em produção. Ver Isox.Xmldsig.TestCA.

Procura um termo nos campos de todas as mensagens, ignorando acento e caixa. Encaminha para Isox.Dictionary.search/1.

Assina app_hdr_xml + document_xml no perfil de três <ds:Reference> do Manual de Segurança do SFN. Ambos precisam já vir em forma canônica exclusiva. Esta função só encaminha para Isox.Xmldsig.Signer.sign/4.

Verifica a assinatura de envelope_xml contra certificate_der, o certificado que você confia para quem assinou. Encaminha para Isox.Xmldsig.Verifier.verify/2.

Functions

decode(xml)

@spec decode(binary()) :: {:ok, Isox.Envelope.t(), atom()} | {:error, term()}

Decodifica um XML de qualquer mensagem do catálogo, identificando sozinho o tipo e a versão a partir do namespace. É o ponto de entrada para quem recebe XML sem saber de antemão que mensagem é, no lugar de Isox.Registry.decode/1.

encode(envelope, version)

@spec encode(Isox.Envelope.t(), atom()) :: {:ok, binary()} | {:error, term()}

Codifica um envelope (cabeçalho + modelo da mensagem) para XML, na versão de schema dada. Despacha pelo tipo do modelo em envelope.message para o módulo de baixo nível certo: se for um Isox.Pacs008, chama Isox.Pacs008.encode/3.

explain(message, field)

@spec explain(module(), atom()) :: String.t()

O campo explicado em texto corrido, para ler no IEx.

Isox.Pacs008 |> Isox.explain(:purp_cd) |> IO.puts()

Encaminha para Isox.Dictionary.explain/2.

fields(message)

@spec fields(module()) :: [Isox.Dictionary.Entry.t()]

Todos os campos de uma mensagem, com nome no catálogo, caminho no XML, obrigatoriedade e descrição. Encaminha para Isox.Dictionary.fields/1.

generate_test_certificate()

@spec generate_test_certificate() :: Isox.Xmldsig.TestCA.ca()

Gera, em memória, um par de chave privada + certificado autoassinado para testes. Nunca em produção. Ver Isox.Xmldsig.TestCA.

search(term)

@spec search(String.t()) :: [{module(), Isox.Dictionary.Entry.t()}]

Procura um termo nos campos de todas as mensagens, ignorando acento e caixa. Encaminha para Isox.Dictionary.search/1.

sign(app_hdr_xml, document_xml, private_key_der, certificate_der)

@spec sign(binary(), binary(), binary(), binary()) :: binary()

Assina app_hdr_xml + document_xml no perfil de três <ds:Reference> do Manual de Segurança do SFN. Ambos precisam já vir em forma canônica exclusiva. Esta função só encaminha para Isox.Xmldsig.Signer.sign/4.

verify(envelope_xml, certificate_der)

@spec verify(binary(), binary()) :: :ok | {:error, Isox.Xmldsig.Verifier.error()}

Verifica a assinatura de envelope_xml contra certificate_der, o certificado que você confia para quem assinou. Encaminha para Isox.Xmldsig.Verifier.verify/2.