lib/requests/payments/bank_payout.ex

defmodule Requests.Payments.BankPayout do
    @moduledoc false
  alias Requests.Payments.{Destinations, Senders}
  alias Requests.Payments.Destinations.{BankAccount, Id}
  alias Requests.Payments.Senders.{Corporate, Government, Individual}

  @derive Jason.Encoder

  @type instruction :: %{
          purpose: String.t(),
          charge_bearer: String.t(),
          repair: String.t(),
          scheme: String.t(),
          quote_id: String.t()
        }

  @type t :: %__MODULE__{
          source: %{amount: String.t(), type: String.t(), id: String.t()},
          destination: Id.t() | BankAccount.t(),
          amount: integer(),
          currency: String.t(),
          reference: String.t(),
          billing_descriptor: %{reference: String.t()},
          sender: Corporate.t() | Government.t() | Individual.t(),
          instruction: instruction(),
          processing_channel_id: String.t()
        }

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

  def build(
        %{
          destination: destination,
          source: source,
          amount: amount,
          currency: currency,
          billing_descriptor: %{reference: reference}
        } = params
      )
      when is_map(destination) and is_map(source) and is_integer(amount) and is_binary(currency) and
             is_binary(reference) do
    %__MODULE__{
      source: build_source(params[:source]),
      destination: Destinations.build_bank_payout_destination(params[:destination]),
      amount: params[:amount],
      currency: params[:currency],
      reference: params[:reference],
      billing_descriptor: build_billing_descriptor(%{reference: reference}),
      sender: Senders.build_bank_payout_sender(params[:sender]),
      instruction: build_instruction(params[:instruction]),
      processing_channel_id: params[:processing_channel_id]
    }
  end

  def build(params) when is_map(params),
    do:
      {:error,
       "Bank payout must have destination, source, amount, currency and billing_descriptor"}

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

  defp build_billing_descriptor(%{reference: reference}) when reference not in [nil, ""],
    do: %{reference: reference}

  defp build_billing_descriptor(_), do: nil

  defp build_instruction(params) when is_map(params) do
    %{
      purpose: params[:purpose],
      charge_bearer: params[:charge_bearer],
      repair: params[:repair],
      scheme: params[:scheme],
      quote_id: params[:quote_id]
    }
  end

  defp build_instruction(_), do: nil

  defp build_source(%{amount: _amount, type: _type, id: _id} = source), do: source

  defp build_source(_), do: {:error, "BankPayout source must have amount, type, and id"}
end