Current section
Files
Jump to
Current section
Files
lib/command/initiate_payment.ex
defmodule FinTex.Command.InitiatePayment do
@moduledoc false
alias FinTex.Command.AbstractCommand
alias FinTex.Command.Synchronization
alias FinTex.Command.Sequencer
alias FinTex.Model.Account
alias FinTex.Model.Bank
alias FinTex.Model.Challenge
alias FinTex.Model.Payment
alias FinTex.Model.TANScheme
alias FinTex.Segment.HITAN
alias FinTex.Segment.HITAB
alias FinTex.Segment.HKCCS
alias FinTex.Segment.HKTAB
alias FinTex.Segment.HKTAN
alias FinTex.Segment.HNHBK
alias FinTex.Segment.HNHBS
alias FinTex.Segment.HNSHA
alias FinTex.Segment.HNSHK
use AbstractCommand
@type login :: String.t
@type client_id :: String.t
@type pin :: String.t
@type options :: [timeout: timeout]
@spec initiate_payment(Bank.t, login, client_id, pin, Payment.t, (Challenge.t -> any), options) :: binary
def initiate_payment(bank, login, client_id, pin, payment, callback_fn, options)
when is_function(callback_fn) and is_binary(login) and is_binary(client_id) and is_binary(pin) do
{seq, accounts} = Synchronization.initialize_dialog(bank, login, client_id, pin, payment.tan_scheme.sec_func)
sender_account = accounts
|> Dict.values
|> Enum.find(&find_account(payment.sender_account, &1))
if sender_account do
payment = %Payment{payment | sender_account: sender_account}
else
raise ArgumentError,
"could not find sender account: #{inspect payment.sender_account}"
end
unless payment.sender_account.supported_transactions |> Enum.into(HashSet.new) |> Set.member?("HKCCS") do
raise ArgumentError,
"could not find \"HKCCS\" in sender account's supported transactions: #{inspect payment.sender_account.supported_transactions}"
end
# filter out valid HKTAN/HITANS segment version based on given sec_func
valid_tan_schemes = (seq |> Sequencer.dialog).supported_tan_schemes
|> Enum.filter(fn %TANScheme{:sec_func => sec_func} -> sec_func == payment.tan_scheme.sec_func end)
if valid_tan_schemes |> Enum.count == 0 do
raise ArgumentError,
"could not find supported TAN scheme for sec_func: #{inspect payment.tan_scheme.sec_func}"
end
# find maximum version of supported TAN schemes
tan_scheme = valid_tan_schemes
|> Enum.max_by(fn %TANScheme{:v => v} -> v end)
tan_medium_required = tan_scheme.medium_name == :required &&
(seq |> Sequencer.dialog).pintan
|> Dict.get("HKTAB" |> control_structure_to_bpd)
if tan_medium_required do
request_segments = [
%HNHBK{},
%HNSHK{},
%HKTAB{},
%HNSHA{},
%HNHBS{}
]
{:ok, response} = seq |> Sequencer.call_http(request_segments)
tan_scheme = response[:HITAB] |> Enum.at(0) |> HITAB.to_tan_scheme(tan_scheme)
seq = seq |> Sequencer.inc
end
request_segments = [
%HNHBK{},
%HNSHK{},
%HKCCS{payment: payment},
%HKTAN{v: tan_scheme.v, process: 4, medium_name: tan_scheme.medium_name},
%HNSHA{},
%HNHBS{}
]
{:ok, response} = seq |> Sequencer.call_http(request_segments)
challenge = response[:HITAN]
|> Enum.at(0)
|> HITAN.to_challenge(tan_scheme)
response_string = callback_fn.(challenge)
request_segments = [
%HNHBK{},
%HNSHK{},
%HKTAN{v: tan_scheme.v, process: 2, ref: challenge.ref},
%HNSHA{response: response_string},
%HNHBS{}
]
seq = seq |> Sequencer.inc
{:ok, response} = seq |> Sequencer.call_http(request_segments)
seq = seq |> Sequencer.inc
%{} = Task.async(fn -> seq |> Synchronization.terminate_dialog end)
Stream.concat(response[:HIRMG], response[:HIRMS])
|> messages
|> Stream.map(fn [code, _ref, text | params] -> "#{code} #{text} #{Enum.join(params, ", ")}" end)
|> Enum.join(", ")
end
def find_account(
%Account{iban: iban},
%Account{iban: iban}
) when iban != nil
do
true
end
def find_account(
%Account{account_number: account_number, subaccount_id: subaccount_id},
%Account{account_number: account_number, subaccount_id: subaccount_id}
) when account_number != nil
do
true
end
def find_account(_, _), do: false
end