lib/requests/instruments/create_token.ex

defmodule Requests.Instruments.CreateToken do
    @moduledoc false
  alias Customers.{BillingAddress, Phone}

  @derive Jason.Encoder

  @type t :: %__MODULE__{
          type: String.t(),
          token: String.t(),
          account_holder: %{
            first_name: String.t(),
            last_name: String.t(),
            billing_address: BillingAddress.t(),
            phone: Phone.t()
          },
          customer: %{
            id: String.t(),
            default: true | false
          }
        }

  @enforce_keys [:type, :token]
  defstruct [
    :type,
    :token,
    :account_holder,
    :customer
  ]

  def build(%{type: type, token: token} = params) when is_binary(type) and is_binary(token) do
    %__MODULE__{
      type: type,
      token: token,
      account_holder: build_account_holder(params[:account_holder]),
      customer: build_customer(params[:customer])
    }
  end

  def build(params) when is_map(params) do
    {:error, "Token must have a type and a token"}
  end

  def build(_), do: nil

  defp build_account_holder(params) when is_map(params) do
    %{
      first_name: params[:first_name],
      last_name: params[:last_name],
      billing_address: BillingAddress.build(params[:billing_address]),
      phone: Phone.build(params[:phone])
    }
  end

  defp build_account_holder(_), do: nil

  defp build_customer(params) when is_map(params) do
    %{
      default: params[:default],
      email: params[:email],
      id: params[:id],
      name: params[:name]
    }
  end

  defp build_customer(_), do: nil
end