defmodule Payeezy.HTTP do @moduledoc """ Base client for all server interaction, used by all endpoint specific modules. This request wrapper coordinates the remote server, headers, authorization and SSL options. This uses `HTTPoison.Base`, so all of the typical HTTP verbs are available. Using `Payeezy.HTTP` requires the presence of four config values: * apikey - Payeezy API key * apisecret - Payeezy API secret * token - Payeezy token * endpoint - Payeezy endpoint URL All four must have values set or a `Payeezy.ConfigError` will be raised at runtime. """ require Logger use HTTPoison.Base @headers [ {"Accept", "application/json"}, {"User-Agent", "Payeezy Elixir/0.1"}, {"Accept-Encoding", "gzip"}, {"Content-type", "application/json"} ] def request(method, url, body, _headers \\ [], options \\ []) do super(method, url, body, header_authorization(body), options) end ## HTTPoison Callbacks @doc false def process_url(path) do endpoint = Payeezy.get_env(:endpoint) endpoint <> "/" <> path end @doc false def process_request_headers(headers) do headers ++ @headers end @doc false def process_request_body(body) when body == "" or body == %{}, do: "" def process_request_body(body) do Poison.encode!(body) end @doc false def process_response_body(body) do Poison.Parser.parse!(body) rescue _ -> inspect(body) end defp header_authorization(body) do apikey = Payeezy.get_env(:apikey) token = Payeezy.get_env(:token) apisecret = Payeezy.get_env(:apisecret) epoch_timestamp = timestamp() nonce = generate_nonce() payload = Poison.encode!(body) string_timestamp = Integer.to_string(epoch_timestamp) data = apikey <> nonce <> string_timestamp <> token <> payload authorization = generate_hmac(apisecret, data) [ {"apikey", apikey}, {"token", token}, {"apisecret", apisecret}, {"Authorization", authorization}, {"nonce", nonce}, {"timestamp", epoch_timestamp} ] end defp generate_hmac(key, data) do if Code.ensure_loaded?(:crypto) and function_exported?(:crypto, :mac, 4) do Base.encode64(Base.encode16(:crypto.mac(:hmac, :sha256, key, "#{data}"), case: :lower)) else Base.encode64(Base.encode16(:crypto.hmac(:sha256, key, "#{data}"), case: :lower)) end end defp generate_nonce do :rand.uniform() |> to_string |> String.replace("0.", "") end defp timestamp do :os.system_time(:milli_seconds) end end