Cliente HTTP da NFS-e Nacional, autenticado por mTLS com o certificado A1 do emitente.
Todo o estado vive no %Nfse.Client{}: certificado, ambiente, tempo de espera e opções do Req.
Não há processo nem configuração global.
Ambientes
environment | Sefin | ADN |
|---|---|---|
:production | https://sefin.nfse.gov.br/SefinNacional | https://adn.nfse.gov.br |
:restricted | https://sefin.producaorestrita.nfse.gov.br/API/SefinNacional | https://adn.producaorestrita.nfse.gov.br |
Reenvio
Uma escrita (:post) só é repetida quando o pedido nunca saiu da máquina: host que não resolve
(:nxdomain), conexão recusada ou rede inalcançável antes do envio, e as recusas do pool HTTP/2
que ocorrem antes de transmitir. Qualquer outra falha é ambígua, porque a Sefin pode já ter
emitido a nota: timeout, conexão fechada e toda resposta HTTP, 5xx inclusive, voltam como
Nfse.Error para o chamador conferir a DPS em vez de reenviá-la. Repetir nesses casos
duplicaria a nota fiscal.
Leituras (:get e :head) são repetidas em falhas transitórias (408, 429, 500, 502, 503, 504,
timeout, conexão recusada ou fechada, :nxdomain).
req_options não consegue religar o reenvio de uma escrita: a política de :post vale mesmo
que retry: seja informado ali.
Opções do Req
req_options é mesclado por último e vence os padrões. Um finch: [name: MyFinch] ali
substitui o pool inteiro, porque o Req não aceita nome junto com opções de pool. Quem faz isso
precisa iniciar o próprio Finch com o certificado em conn_opts: [transport_opts: [cert: ..., key: ...]],
senão o mTLS não acontece.
O que não está confirmado
- a cadeia do certificado no handshake:
Nfse.Certificatelê a cadeia do.pfx, mas o TLS envia só a folha. Se a Sefin recusar o handshake pedindo a cadeia, enviarcert: [folha | cadeia]emtransport_opts; até a biblioteca fazer isso, quem precisar inicia o próprio Finch assim, como em "Opções do Req"
Summary
Functions
Normaliza um item de erros ou de alertas, em PascalCase ou camelCase, como Nfse.Error.message/0.
Lê as mensagens de erro de um corpo de resposta.
Monta o cliente.
Faz a requisição ao serviço e classifica a resposta.
Valor do campo tpAmb do ambiente, como texto: 1 em produção e 2 em produção restrita.
Types
@type environment() :: :production | :restricted
@type service() :: :sefin | :adn
@type t() :: %Nfse.Client{ certificate: Nfse.Certificate.t(), environment: environment(), receive_timeout: pos_integer(), req_options: keyword() }
Functions
@spec message(map()) :: Nfse.Error.message()
Normaliza um item de erros ou de alertas, em PascalCase ou camelCase, como Nfse.Error.message/0.
@spec messages(map()) :: [Nfse.Error.message()]
Lê as mensagens de erro de um corpo de resposta.
Aceita erros/Erros (lista), erro/Erro (objeto) e, na falta deles, um mensagem de nível
superior. Os campos podem vir em PascalCase ou camelCase.
@spec new(keyword()) :: {:ok, t()} | {:error, Nfse.Error.t()}
Monta o cliente.
Opções: :certificate e :environment (obrigatórias), :receive_timeout (padrão 60_000) e
:req_options (padrão []). Recusa, sem rede, o certificado vencido e o que não serve para
clientAuth.
Uma opção obrigatória ausente ou um :environment fora de :production e :restricted volta
como %Nfse.Error{kind: :validation}, com os erros juntos em details, nos caminhos
certificate e environment (:required ou :inclusion).
@spec request(t(), service(), :get | :head | :post, String.t(), keyword()) :: {:ok, Req.Response.t()} | {:error, Nfse.Error.t()}
Faz a requisição ao serviço e classifica a resposta.
Opções: :json (corpo), :params (query string) e :accept, uma lista de status que voltam
como {:ok, resp} mesmo fora de 2xx, para APIs que respondem 404 com corpo em vez de erro.
Uma resposta que chegou com um JSON que não se decodifica não é falha de transporte: com status
2xx ou aceito, volta como :unexpected com o status e details em {:invalid_json, exceção};
com outro status, volta pelo kind do status, sem mensagens.
Valor do campo tpAmb do ambiente, como texto: 1 em produção e 2 em produção restrita.