Skip to main content

lib/sat/cfdi/descarga/masiva/internal/http.ex

defmodule Sat.Cfdi.Descarga.Masiva.Internal.Http do
  @moduledoc false

  @default_timeout 30_000

  @doc """
  POST a un endpoint SOAP. Headers obligatorios:
    * Content-Type: text/xml; charset=UTF-8
    * SOAPAction: <action>
    * Authorization: WRAP access_token="<token>" — solo si se pasa `:token`
  """
  @spec post_soap(String.t(), String.t(), iodata(), keyword()) ::
          {:ok, %{status: integer(), body: binary(), headers: list()}}
          | {:error, term()}
  def post_soap(url, soap_action, body, opts \\ [])
      when is_binary(url) and is_binary(soap_action) do
    headers =
      [
        {"content-type", "text/xml; charset=UTF-8"},
        {"soapaction", soap_action}
      ] ++ auth_header(opts[:token])

    timeout = opts[:timeout] || @default_timeout

    case Req.post(url,
           body: body,
           headers: headers,
           receive_timeout: timeout,
           connect_options: [timeout: timeout],
           retry: false,
           decode_body: false
         ) do
      {:ok, %Req.Response{status: status, body: body, headers: headers}} ->
        {:ok, %{status: status, body: body_to_binary(body), headers: headers}}

      {:error, reason} ->
        {:error, {:network_error, reason}}
    end
  rescue
    e -> {:error, {:exception, e}}
  end

  defp auth_header(nil), do: []

  defp auth_header(token) when is_binary(token) do
    [{"authorization", ~s|WRAP access_token="#{token}"|}]
  end

  defp body_to_binary(body) when is_binary(body), do: body
  defp body_to_binary(body) when is_list(body), do: IO.iodata_to_binary(body)
  defp body_to_binary(other), do: to_string(other)
end