defmodule Dwolla do @moduledoc """ An HTTP Client for Dwolla. [Dwolla API Docs](https://docsv2.dwolla.com) """ use HTTPoison.Base alias Dwolla.Utils @token_endpoint "token" defmodule MissingClientSecretError do defexception message: """ Client secret is missing. Please add client_id to your config.exs file. config :dwolla, client_id: "your_client_id" """ end defmodule MissingClientIdError do defexception message: """ Client Id is missing. Please add client_secret to your config.exs file. config :dwolla, client_secret: "your_client_secret" """ end defmodule MissingRootUriError do defexception message: """ The root_uri is required to specify the Dwolla environment to which you are making calls, i.e. development or production. Please configure root_uri in your config.exs file. config :dwolla, root_uri: "https://api-sandbox.dwolla.com/" (development) config :dwolla, root_uri: "https://api.dwolla.com/" (production) """ end @doc """ Gets credentials from configuration. """ @spec get_cred() :: map | no_return def get_cred do require_dwolla_credentials() end @doc """ Gets root URI from configuration. """ @spec get_root_uri() :: String.t() | no_return def get_root_uri do require_root_uri() end @doc """ Makes request with token. """ @spec make_request_with_token(atom, String.t(), String.t(), map, map, Keyword.t()) :: {:ok, HTTPoison.Response.t()} | {:error, HTTPoison.Error.t() | {:invalid, binary}} def make_request_with_token(method, endpoint, token, body \\ %{}, headers \\ %{}, options \\ []) do rb = body |> Utils.to_camel_case() |> maybe_encode() rh = token |> get_request_headers() |> Map.merge(headers) |> Map.to_list() options = httpoison_request_options() ++ options request(method, endpoint, rb, rh, options) end defp maybe_encode({:multipart, _} = body), do: body defp maybe_encode(body), do: Poison.encode!(body) @doc """ Makes request to OAuth endpoint with credentials. """ @spec make_oauth_token_request(map, map, list) :: {:ok, HTTPoison.Response.t()} | {:error, HTTPoison.Error.t()} def make_oauth_token_request(params, cred, options \\ []) do rb = Utils.encode_params(params, cred) rh = cred |> get_request_headers() |> Map.to_list() options = httpoison_request_options() ++ options request(:post, @token_endpoint, rb, rh, options) end def process_url(endpoint) do require_root_uri() <> endpoint end def process_response_body(""), do: "" def process_response_body(body) do case Poison.decode(body) do {:ok, parsed_body} -> parsed_body {:error, _} -> {:invalid, body} end end defp get_request_headers(%{client_id: client_id, client_secret: client_secret}) do encoded_auth_params = Base.encode64("#{client_id}:#{client_secret}") Map.new() |> Map.put("Authorization", "Bearer #{encoded_auth_params}") |> Map.put("Content-Type", "application/x-www-form-urlencoded") end defp get_request_headers(access_token) do Map.new() |> Map.put("Authorization", "Bearer #{access_token}") |> Map.put("Accept", "application/vnd.dwolla.v1.hal+json") |> Map.put("Content-Type", "application/vnd.dwolla.v1.hal+json") end defp require_dwolla_credentials do case {get_client_id(), get_client_secret()} do {:not_found, _} -> raise MissingClientIdError {_, :not_found} -> raise MissingClientSecretError {client_id, client_secret} -> %{client_id: client_id, client_secret: client_secret} end end defp require_root_uri do case Application.get_env(:dwolla, :root_uri) || :not_found do :not_found -> raise MissingRootUriError value -> value end end defp httpoison_request_options do Application.get_env(:dwolla, :httpoison_options, []) end defp get_client_id do Application.get_env(:dwolla, :client_id) || :not_found end defp get_client_secret do Application.get_env(:dwolla, :client_secret) || :not_found end end