lib/requests/payments/sources/fawry.ex

defmodule Requests.Payments.Sources.Fawry do
  @type product :: %{
          product_id: String.t(),
          quantity: String.t(),
          price: integer(),
          description: String.t()
        }

  @type t :: %__MODULE__{
          type: String.t(),
          description: String.t(),
          customer_profile_id: String.t(),
          customer_email: String.t(),
          customer_mobile: String.t(),
          expires_on: String.t(),
          products: list(product())
        }

  @enforce_keys [:type, :description, :customer_email, :customer_mobile, :products]
  defstruct [
    :type,
    :description,
    :customer_profile_id,
    :customer_email,
    :customer_mobile,
    :expires_on,
    :products
  ]

  def build(
        %{
          type: type,
          description: description,
          customer_email: customer_email,
          customer_mobile: customer_mobile,
          products: products
        } = params
      ) do
    %{
      type: type,
      description: description,
      customer_profile_id: params[:customer_profile_id],
      customer_email: customer_email,
      customer_mobile: customer_mobile,
      expires_on: params[:expires_on],
      products: build_products(products)
    }
  end

  def build(raw_params) when is_map(raw_params) do
    params =
      Enum.into(raw_params, %{}, fn {key, value} -> {String.to_existing_atom(key), value} end)

    %{
      type: params[:type],
      description: params[:description],
      customer_profile_id: params[:customer_profile_id],
      customer_email: params[:customer_email],
      customer_mobile: params[:customer_mobile],
      expires_on: params[:expires_on],
      products: build_products(params[:products])
    }
  end

  def build(_), do: nil

  defp build_products(products) when is_list(products) do
    Enum.into(products, [], &build_product(&1))
  end

  defp build_products(_), do: nil

  defp build_product(%{
         product_id: product_id,
         quantity: quantity,
         price: price,
         description: description
       }) do
    %{product_id: product_id, quantity: quantity, price: price, description: description}
  end

  defp build_product(_), do: nil
end