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