defmodule Nordigen do
alias Nordigen.Account
alias Nordigen.AccountDetails
alias Nordigen.Bank
alias Nordigen.Link
alias Nordigen.Transactions
alias Nordigen.Balance
@moduledoc """
Documentation for `Nordigen`.
"""
require Logger
@endpoint Application.get_env(:nordigen, :end_point, "https://bankaccountdata.gocardless.com")
@doc """
Get Access Token
Returns `{:ok, [%Binance.HistoricalTrade{}]}` or `{:error, reason}`.
## Example
{:ok, "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUlI1NiJ9.eyJ0b2tlbl90sXBlIjoiYWNjZXNzIiwiZXhwIjoxNjU3NjQwNTczLCJqdGkiOiI3YzcwZDA1MzUwYTA0M2Q4OGVkMGJmNDc5YzlkMWRiNCIsImlkIjoxMzA3NCwic2VjcmV0X2lkIjoiYzFhMDQ0ZjQtOGMyMy00YTQ5LTlkM2MtZWE5OTkzM2ZlZmEwIiwiYWxsb3dlZF9jaWRycyI6WyIwLjAuMC4wLzAiLCI6Oi8wIl19.DZralEAXmHW7NhH0fI6B4NkYgxCwoyPabO3Ryt4u2Tk"}
"""
def get_access_token() do
id = Application.get_env(:nordigen, :id)
key = Application.get_env(:nordigen, :key)
url = "/api/v2/token/new/"
headers = [{"accept", "application/json"}, {"Content-Type", "application/json"}]
payload = "{\"secret_id\":\"#{id}\",\"secret_key\":\"#{key}\"}"
url
|> post_request(headers, payload)
|> format_response(:get_access_token)
end
@doc """
List all Banks by country code.
Returns `{:ok, list}` or `{:error, reason}`.
## Example
{:ok,
[
%Nordigen.Bank{
bic: "AIPTAU32",
countries: ["NO", "SE", "FI", "DK", "EE", "LV", "LT", "GB", "NL", "CZ",
"ES", "PL", "BE", "DE", "AT", "BG", "HR", "CY", "FR", "GR", "HU", "IS",
"IE", "IT", "LI", "LU", "MT", "PT", "RO", "SK", "SI"],
id: "AIRWALLEX_AIPTAU32",
logo: "https://cdn.nordigen.com/ais/AIRWALLEX_AIPTAU32_1.png",
name: "Airwallex",
transaction_total_days: "730"
},...]}
"""
def list_banks(iso, token) do
url = "https://bankaccountdata.gocardless.com/api/v2/institutions/?country=#{iso}"
headers = [{"accept", "application/json"}, {"Authorization", "Bearer #{token}"}]
url
|> get_request(headers)
|> format_response()
end
@doc """
Returns account information by requisition_id
Returns `{:ok, list}` or `{:error, reason}`.
## Example
{:ok,
%Nordigen.Account{
account_selection: false,
accounts: ["7e944232-bda9-40bc-b784-660c7ab5fe78",
"99a0bfe2-0bef-46df-bff2-e9ae0c6c5838"],
agreement: "13c51d1e-5133-4d70-8093-f53caa8dac13",
created: "2022-07-14T10:47:39.912391Z",
id: "61bbe16d-875c-4d26-bd1c-091fb1cd79fb",
institution_id: "SANDBOXFINANCE_SFIN0000",
link: "https://bankaccountdata.gocardless.com/psd2/start/61bbe16d-875c-4d26-bd1c-091fb1cd79fb/SANDBOXFINANCE_SFIN0000",
redirect: "http://localhost:4000/wallets/50",
redirect_immediate: false,
reference: "619db4fc-0362-11ed-87dd-1e00e2346e69",
ssn: nil,
status: "LN",
user_languages: nil
}}
"""
def list_accounts(requisition_id, token) do
url = "https://bankaccountdata.gocardless.com/api/v2/requisitions/#{requisition_id}/"
headers = [{"accept", "application/json"}, {"Authorization", "Bearer #{token}"}]
url
|> get_request(headers)
|> format_response(:list_accounts)
end
@doc """
List account transactions by account_id
Returns `{:ok, list}` or `{:error, reason}`.
## Example
[{:ok,
%Nordigen.Transactions.Booked{
additionalInformation: nil,
balanceAfterTransaction: nil,
bankTransactionCode: "PMNT",
bookingDate: "2022-07-12",
bookingDateTime: nil,
checkId: nil,
creditorAccount: nil,
creditorAgent: nil,
creditorId: nil,
creditorName: nil,
currencyExchange: nil,
debtorAccount: nil,
debtorAgent: nil,
debtorName: nil,
endToEndId: nil,
entryReference: nil,
mandateId: nil,
proprietaryBankTransactionCode: nil,
purposeCode: nil,
remittanceInformationStructured: nil,
remittanceInformationStructuredArray: nil,
remittanceInformationUnstructured: "PAYMENT Alderaan Coffe",
remittanceInformationUnstructuredArray: nil,
transactionAmount: %Nordigen.Transactions.TransactionAmount{
amount: "-15.00",
currency: "EUR"
},
transactionId: "2022071201721808-1",
ultimateCreditor: nil,
ultimateDebtor: nil,
valueDate: "2022-07-12"
},..]
"""
def list_account_transactions(account_id, token) do
url = "https://bankaccountdata.gocardless.com/api/v2/accounts/#{account_id}/transactions/"
headers = [{"accept", "application/json"}, {"Authorization", "Bearer #{token}"}]
url
|> get_request(headers)
|> format_response(:account_transactions)
end
@doc """
List account balances
Returns `{:ok, list}` or `{:error, reason}`.
## Example
{:ok,
%Nordigen.Balance{
balances: [
%Nordigen.Balance.Balances{
balanceAmount: %Nordigen.Balance.Balances.BalanceAmount{
amount: "1913.12",
currency: "EUR"
},
balanceType: "expected",
creditLimitIncluded: nil,
lastChangeDateTime: nil,
lastCommittedTransaction: nil,
referenceDate: "2022-07-14"
},...]}
"""
def get_account_balances(account_id, token) do
url = "https://bankaccountdata.gocardless.com/api/v2/accounts/#{account_id}/balances/"
headers = [{"accept", "application/json"}, {"Authorization", "Bearer #{token}"}]
url
|> get_request(headers)
|> format_response(:account_balances)
end
def get_account_details(account_id, token) do
url = "https://bankaccountdata.gocardless.com/api/v2/accounts/#{account_id}/details/"
headers = [{"accept", "application/json"}, {"Authorization", "Bearer #{token}"}]
url
|> get_request(headers)
|> format_response(:account_details)
end
@doc """
Build Authentication Link
Returns `{:ok, [%Binance.HistoricalTrade{}]}` or `{:error, reason}`.
## Example
{:ok,
%Nordigen.Link{
account_selection: false,
accounts: [],
agreement: "",
created: "2022-07-14T10:47:39.912391Z",
id: "61bbe16d-875c-4d26-bd1c-091fb1cd79fb",
institution_id: "SANDBOXFINANCE_SFIN0000",
link: "https://bankaccountdata.gocardless.com/psd2/start/61bbe16d-875c-4d26-bd1c-091fb1cd79fb/SANDBOXFINANCE_SFIN0000",
redirect: "http://localhost:4000/wallets/50",
redirect_immediate: false,
reference: "619db4fc-0362-11ed-87dd-1e00e2346e69",
ssn: nil,
status: "CR",
user_language: "EN"
}}
"""
def requisition_link(redirect, institution_id, token, language_iso \\ "EN") do
reference = UUID.uuid1()
url = "/api/v2/requisitions/"
headers = [
{"accept", "application/json"},
{"Content-Type", "application/json"},
{"Authorization", "Bearer #{token}"}
]
payload =
"{\"redirect\":\"#{redirect}\",\"institution_id\":\"#{institution_id}\",\"reference\":\"#{reference}\",\"user_language\":\"#{language_iso}\"}"
url
|> post_request(headers, payload)
|> format_response()
end
defp format_response({:ok, %HTTPoison.Response{body: body, status_code: 200}}) do
response_list = Poison.decode!(body)
bank_list =
for bank <- response_list do
Bank.decode(bank)
end
{:ok, bank_list}
end
defp format_response({:ok, %HTTPoison.Response{body: body, status_code: 201}}) do
link =
body
|> Poison.decode!()
|> Link.decode()
{:ok, link}
end
defp format_response(
{:ok, %HTTPoison.Response{body: body, status_code: 200}},
:get_access_token
) do
%{"access" => access} = Poison.decode!(body)
{:ok, access}
end
defp format_response(
{:ok, %HTTPoison.Response{body: body, status_code: 200}},
:list_accounts
) do
requisition = Poison.decode!(body)
account_requisition = Account.decode(requisition)
{:ok, account_requisition}
end
defp format_response(
{:ok, %HTTPoison.Response{body: body, status_code: 200}},
:account_details
) do
%{"account" => account} = Poison.decode!(body)
account_details = AccountDetails.decode(account)
{:ok, account_details}
end
defp format_response(
{:ok, %HTTPoison.Response{body: body, status_code: 200}},
:account_transactions
) do
%{"transactions" => transactions} = Poison.decode!(body)
list = Transactions.decode(transactions)
{:ok, list}
end
defp format_response(
{:ok, %HTTPoison.Response{body: body, status_code: 200}},
:account_balances
) do
balances = Poison.decode!(body)
list = Balance.decode(balances)
{:ok, list}
end
defp format_response({:ok, %HTTPoison.Response{status_code: status_code} = response}, _)
when status_code == 401 do
Logger.warn(~s(Nordigen API response: #{inspect(response)}))
{:error, :invalid_token}
end
defp format_response({:ok, %HTTPoison.Response{status_code: status_code} = response}, _)
when status_code >= 400 do
Logger.warn(~s(Nordigen API response: #{inspect(response)}))
{:error, "Nordigen service could not be reached."}
end
defp format_response({:error, %HTTPoison.Error{reason: reason} = error}, _) do
Logger.warn(~s(
There is an error when fetching access token: #{reason}
response: #{inspect(error)}
))
{:error, reason}
end
defp post_request(url, headers, payload) do
@endpoint
|> URI.merge(url)
|> URI.to_string()
|> HTTPoison.post(payload, headers)
end
defp get_request(url, headers) do
@endpoint
|> URI.merge(url)
|> URI.to_string()
|> HTTPoison.get(headers)
end
end