lib/requests/payments/card_payout.ex

defmodule Requests.Payments.CardPayout do
    @moduledoc false
  import Utilities, only: [atomize_keys: 1]

  alias Requests.Payments.{Destinations, Senders}

  @derive Jason.Encoder

  defmodule Instruction do
    @moduledoc false
    @type t :: %__MODULE__{
            funds_transfer_type: String.t(),
            purpose: String.t()
          }

    @enforce_keys [:funds_transfer_type]
    defstruct [:funds_transfer_type, :purpose]

    def build(params) when is_map(params) do
      %{
        funds_transfer_type: params[:funds_transfer_type],
        purpose: params[:purpose]
      }
    end

    def build(_), do: nil
  end

  defmodule Source do
    @moduledoc false
    @type t :: %__MODULE__{
            type: String.t(),
            id: String.t(),
            amount: integer()
          }

    @enforce_keys [:id, :type]
    defstruct [
      :amount,
      :id,
      :type
    ]

    def build(params) when is_map(params) do
      %{
        amount: params[:amount],
        id: params[:id],
        type: params[:type]
      }
    end

    def build(_), do: nil
  end

  @type t :: %__MODULE__{
          source: Source.t(),
          destination: map(),
          amount: integer(),
          currency: String.t(),
          instruction: Instruction.t(),
          processing_channel_id: String.t(),
          sender: map(),
          reference: String.t(),
          metadata: map()
        }

  @enforce_keys [:currency, :destination, :instruction, :processing_channel_id, :source]
  defstruct [
    :source,
    :destination,
    :amount,
    :currency,
    :instruction,
    :processing_channel_id,
    :sender,
    :reference,
    :metadata
  ]

  def build(
        %{source: source, destination: destination, currency: currency, instruction: instruction} =
          params
      )
      when is_map(source) and is_map(destination) and is_binary(currency) and is_map(instruction) do
    %__MODULE__{
      source: Source.build(source),
      destination: Destinations.build(destination),
      amount: params[:amount],
      currency: currency,
      instruction: Instruction.build(instruction),
      processing_channel_id: params[:processing_channel_id],
      sender: Senders.build(params[:sender]),
      reference: params[:reference],
      metadata: atomize_keys(params[:metadata])
    }
  end

  def build(params) when is_map(params),
    do: {:error, "Card payout must have a source, destination, currency, and instruction"}

  def build(_), do: {:error, "params must be a map"}
end