lib/dwolla/transfer.ex

defmodule Dwolla.Transfer do
  @moduledoc """
  Functions for `transfers` endpoint.
  """

  alias Dwolla.Utils

  defstruct id: nil, created: nil, status: nil, amount: nil, metadata: nil,
            source_resource: nil, source_resource_id: nil,
            source_funding_source_id: nil, dest_resource: nil,
            dest_resource_id: nil, can_cancel: false

  @type t :: %__MODULE__{id: String.t,
                         created: String.t,
                         status: String.t, # "pending" | "processed" | "cancelled" | "failed" | "reclaimed"
                         amount: Dwolla.Transfer.Amount.t,
                         metadata: Dwolla.Transfer.Metadata.t,
                         source_resource: String.t,
                         source_resource_id: String.t,
                         source_funding_source_id: String.t,
                         dest_resource: String.t,
                         dest_resource_id: String.t,
                         can_cancel: boolean
                        }

  @type token :: String.t
  @type id :: String.t
  @type params :: %{required(atom) => any}
  @type error :: HTTPoison.Error.t | Dwolla.Errors.t | tuple
  @type location :: %{id: String.t}

  @endpoint "transfers"

  defmodule Amount do
    @moduledoc """
    Dwolla Transfer Amount data structure.
    """

    defstruct value: nil, currency: nil
    @type t :: %__MODULE__{value: String.t, currency: String.t}
  end

  defmodule Metadata do
    @moduledoc """
    Dwolla Transfer Metatdata data structure.
    """

    defstruct vendor: nil, origin_trans_id: nil, title: nil, note: nil
    @type t :: %__MODULE__{vendor: String.t,
                           origin_trans_id: String.t,
                           title: String.t,
                           note: String.t
                          }
  end

  defmodule Failure do
    @moduledoc """
    Dwolla Transfer Failure data structure.
    """

    defstruct code: nil, description: nil
    @type t :: %__MODULE__{code: String.t, description: String.t}
  end

  @doc """
  Initiates a transfer.

  The parameters are verbose because of the many options available to the user
  for setting the source and destination of the funds in the `href` field.

  Parameters
  ```
  %{
    _links: %{
      source: %{
        href: "https://api-sandbox.dwolla.com/funding-sources/..."
      },
      destination: %{
        href: "https://api-sandbox.dwolla.com/funding-sources/..."
      }
    },
    amount: %{
      value: 100.00,
      currency: "USD"
    },
    metadata: %{
      vendor: "Acme Inc.",
      note: "Invoice #12314"
    }
  }
  ```
  """
  @spec initiate(token, params, any | nil) :: {:ok, location} | {:error, error}
  def initiate(token, params, idempotency_key \\ nil) do
    headers = Utils.idempotency_header(idempotency_key || params)
    Dwolla.make_request_with_token(:post, @endpoint, token, params, headers)
    |> Utils.handle_resp(:transfer)
  end

  @doc """
  Gets a transfer by id.
  """
  @spec get(token, id) :: {:ok, Dwolla.Transfer.t} | {:error, error}
  def get(token, id) do
    endpoint = @endpoint <> "/#{id}"
    Dwolla.make_request_with_token(:get, endpoint, token)
    |> Utils.handle_resp(:transfer)
  end

  @doc """
  Gets reason for a transfer's failure.
  """
  @spec get_transfer_failure_reason(token, id) :: {:ok, Dwolla.Transfer.Failure} | {:error, error}
  def get_transfer_failure_reason(token, id) do
    endpoint = @endpoint <> "/#{id}/failure"
    Dwolla.make_request_with_token(:get, endpoint, token)
    |> Utils.handle_resp(:failure)
  end

  @doc """
  Cancels a transfer.
  """
  @spec cancel(token, id) :: {:ok, Dwolla.Transfer.t} | {:error, error}
  def cancel(token, id) do
    endpoint = @endpoint <> "/#{id}"
    params = %{status: "cancelled"}
    headers = Utils.idempotency_header(params)
    Dwolla.make_request_with_token(:post, endpoint, token, params, headers)
    |> Utils.handle_resp(:transfer)
  end

end