defmodule Finicity do use HTTPotion.Base use GenServer def start(args) do GenServer.start(__MODULE__, args, name: Finicity) end def init(args) do {:ok, []} end def handle_cast({:set_token, token}, state) do state = Keyword.put(state, :token, token) {:noreply, state} end def handle_call({:get_token}, caller, state) do {:ok, state[:token], state} end def set_token(token) do GenServer.cast(Finicity, {:set_token, token}) end def get_token do GenServer.call(Finicity, {:get_token}) end def partner_authentication() do partner_authentication([partner_id: partner_id(), partner_secret: partner_secret()]) end def partner_authentication([partner_id: partner_id, partner_secret: partner_secret]) do unless {:ok, token} = get_token do data = {:credentials, nil, [ {:partnerId, nil, partner_id}, {:partnerSecret, nil, partner_secret}]} |> XmlBuilder.generate %{status_code: 200, body: body} = post("/v2/partners/authentication", [body: data, timeout: 20000]) token = body |> Floki.Finder.find("token") |> Floki.FlatText.get end {:ok, token} end def get_institutions(token, params) do %{status_code: 200, body: body} = get("/v1/institutions?" <> URI.encode_query(params), [headers: app_token(token)]) t = body |> Floki.Finder.find("institutions") |> List.first metadata = t |> elem(1) |> Enum.into(%{}) institutions = t |> node_to_map {:ok, %{"metadata" => metadata, "institutions" => institutions}} end def get_institution_form(token, [institution_id: institution_id]) do %{status_code: 200, body: body} = get("/v1/institutions/" <> institution_id <> "/loginForm", [headers: app_token(token)]) fields = body |> Floki.find("loginform") |> List.first |> node_to_map {:ok, %{"fields" => fields}} end def add_all_accounts(token, [customer_id: customer_id, institution_id: institution_id]) do end def discover_customer_accounts(token, [customer_id: customer_id, institution_id: institution_id, credentials: credentials]) do data = {:accounts, nil, [{:credentials, nil, credentials}]} |> XmlBuilder.generate case post("/v1/customers/#{customer_id}/institutions/#{institution_id}/accounts") do %{status_code: 200, body: body} -> IO.inspect body %{status_code: 203, body: body} -> # TODO: handle mfa IO.inspect body _ -> IO.puts "error discovering customer accounts" end end def add_customer(token, [username: username, first_name: first_name, last_name: last_name]) do data = {:customer, nil, [ {:username, nil, username}, {:firstName, nil, first_name}, {:lastName, nil, last_name} ]} |> XmlBuilder.generate %{status_code: 201, body: body} = post("/v1/customers/active", [body: data, timeout: 20000, headers: app_token(token)]) customer = body |> Floki.Finder.find("customer") {:ok, customer} end def add_test_customer(token, [username: username, first_name: first_name, last_name: last_name]) do data = {:customer, nil, [ {:username, nil, username}, {:firstName, nil, first_name}, {:lastName, nil, last_name} ]} |> XmlBuilder.generate %{status_code: 201, body: body} = post("/v1/customers/testing", [body: data, timeout: 20000, headers: app_token(token)]) customer = body |> Floki.Finder.find("customer") {:ok, body} end defp process_url(url) do "https://api.finicity.com/aggregation" <> url end defp process_request_headers(headers) do headers |> Dict.put(:"Content-Type", "application/xml") |> Dict.put(:"Finicity-App-Key", app_key()) end defp process_response_body(body) do body |> Floki.parse end defp app_token(token) do ["Finicity-App-Token": token] end defp app_key do System.get_env("FINICITY_APP_KEY") || Application.get_env(:finicity, :app_key) end defp partner_id do System.get_env("FINICITY_PARTNER_ID") || Application.get_env(:finicity, :partner_id) end defp partner_secret do System.get_env("FINICITY_PARTNER_SECRET") || Application.get_env(:finicity, :partner_secret) end defp node_to_map(node) do node |> elem(2) |> Enum.map(fn(x) -> x |> elem(2) |> Enum.reduce(%{}, fn(x, acc) -> Map.put(acc, elem(x, 0), elem(x, 2) |> List.first) end) end) end end