diff --git a/Dockerfile b/Dockerfile index d3bf13c..ec0732d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,7 +6,6 @@ SHELL ["/bin/bash", "-o", "pipefail", "-c"] # Install thrift compiler ARG THRIFT_VERSION - ARG TARGETARCH RUN wget -q -O- "https://github.com/valitydev/thrift/releases/download/${THRIFT_VERSION}/thrift-${THRIFT_VERSION}-linux-${TARGETARCH}.tar.gz" \ | tar -xvz -C /usr/local/bin/ diff --git a/Dockerfile.dev b/Dockerfile.dev index 2cec51f..e4cfa53 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -3,12 +3,10 @@ ARG OTP_VERSION FROM docker.io/library/erlang:${OTP_VERSION} SHELL ["/bin/bash", "-o", "pipefail", "-c"] -ARG BUILDARCH - # Install thrift compiler ARG THRIFT_VERSION - -RUN wget -q -O- "https://github.com/valitydev/thrift/releases/download/${THRIFT_VERSION}/thrift-${THRIFT_VERSION}-linux-${BUILDARCH}.tar.gz" \ +ARG TARGETARCH +RUN wget -q -O- "https://github.com/valitydev/thrift/releases/download/${THRIFT_VERSION}/thrift-${THRIFT_VERSION}-linux-${TARGETARCH}.tar.gz" \ | tar -xvz -C /usr/local/bin/ # Set env diff --git a/apps/ff_cth/include/ct_domain.hrl b/apps/ff_cth/include/ct_domain.hrl index 765314a..5f822ff 100644 --- a/apps/ff_cth/include/ct_domain.hrl +++ b/apps/ff_cth/include/ct_domain.hrl @@ -16,8 +16,6 @@ -define(prx(ID), #domain_ProxyRef{id = ID}). -define(prv(ID), #domain_ProviderRef{id = ID}). -define(trm(ID), #domain_TerminalRef{id = ID}). --define(prv_trm(ID), #domain_ProviderTerminalRef{id = ID}). --define(prv_trm(ID, P), #domain_ProviderTerminalRef{id = ID, priority = P}). -define(tmpl(ID), #domain_ContractTemplateRef{id = ID}). -define(trms(ID), #domain_TermSetHierarchyRef{id = ID}). -define(sas(ID), #domain_SystemAccountSetRef{id = ID}). diff --git a/apps/ff_cth/src/ct_domain.erl b/apps/ff_cth/src/ct_domain.erl index 924ab30..6abd0f6 100644 --- a/apps/ff_cth/src/ct_domain.erl +++ b/apps/ff_cth/src/ct_domain.erl @@ -23,8 +23,9 @@ -export([term_set_hierarchy/3]). -export([timed_term_set/1]). -export([globals/2]). --export([withdrawal_provider/3]). --export([withdrawal_terminal/1]). +-export([withdrawal_provider/4]). +-export([withdrawal_terminal/2]). +-export([withdrawal_terminal/3]). %% @@ -36,214 +37,45 @@ -type object() :: dmsl_domain_thrift:'DomainObject'(). --spec withdrawal_provider(?DTP('ProviderRef'), ?DTP('ProxyRef'), binary()) -> object(). -withdrawal_provider(?prv(16) = Ref, ProxyRef, IdentityID) -> +-spec withdrawal_provider( + ?DTP('ProviderRef'), + ?DTP('ProxyRef'), + binary(), + ?DTP('ProvisionTermSet') | undefined +) -> object(). +withdrawal_provider(?prv(ID) = Ref, ProxyRef, IdentityID, TermSet) -> {ok, AccountID} = ct_helper:create_account(<<"RUB">>), {provider, #domain_ProviderObject{ ref = Ref, data = #domain_Provider{ - name = <<"WithdrawalProvider">>, - description = <<"Withdrawal provider">>, + name = genlib:format("Withdrawal provider #~B", [ID]), + description = <<>>, proxy = #domain_Proxy{ref = ProxyRef, additional = #{}}, identity = IdentityID, - terms = undefined, + terms = TermSet, accounts = #{ ?cur(<<"RUB">>) => #domain_ProviderAccount{settlement = AccountID} - }, - terminal = - {decisions, [ - #domain_TerminalDecision{ - if_ = {constant, true}, - then_ = {value, [?prv_trm(6)]} - } - ]} - } - }}; -withdrawal_provider(Ref, ProxyRef, IdentityID) -> - {ok, AccountID} = ct_helper:create_account(<<"RUB">>), - {provider, #domain_ProviderObject{ - ref = Ref, - data = #domain_Provider{ - name = <<"WithdrawalProvider">>, - description = <<"Withdrawal provider">>, - proxy = #domain_Proxy{ref = ProxyRef, additional = #{}}, - identity = IdentityID, - terms = #domain_ProvisionTermSet{ - wallet = #domain_WalletProvisionTerms{ - withdrawals = #domain_WithdrawalProvisionTerms{ - currencies = {value, ?ordset([?cur(<<"RUB">>)])}, - payout_methods = {value, ?ordset([])}, - cash_limit = - {value, - ?cashrng( - {inclusive, ?cash(0, <<"RUB">>)}, - {exclusive, ?cash(10000000, <<"RUB">>)} - )}, - cash_flow = - {decisions, [ - #domain_CashFlowDecision{ - if_ = {condition, {currency_is, ?cur(<<"RUB">>)}}, - then_ = - {value, [ - ?cfpost( - {system, settlement}, - {provider, settlement}, - {product, - {min_of, - ?ordset([ - ?fixed(10, <<"RUB">>), - ?share(5, 100, operation_amount, round_half_towards_zero) - ])}} - ) - ]} - } - ]} - } - } - }, - accounts = #{ - ?cur(<<"RUB">>) => #domain_ProviderAccount{settlement = AccountID} - }, - terminal = - case Ref of - ?prv(9) -> - {decisions, [ - #domain_TerminalDecision{ - if_ = {constant, true}, - then_ = {value, [?prv_trm(1, 500)]} - } - ]}; - ?prv(10) -> - {decisions, [ - #domain_TerminalDecision{ - if_ = {constant, true}, - then_ = {value, [?prv_trm(1)]} - } - ]}; - ?prv(11) -> - {decisions, [ - #domain_TerminalDecision{ - if_ = {constant, true}, - then_ = {value, [?prv_trm(1)]} - } - ]}; - ?prv(17) -> - {decisions, [ - #domain_TerminalDecision{ - if_ = - {condition, - {cost_in, - ?cashrng( - {inclusive, ?cash(300, <<"RUB">>)}, - {inclusive, ?cash(300, <<"RUB">>)} - )}}, - then_ = {value, [?prv_trm(1)]} - }, - #domain_TerminalDecision{ - if_ = - {condition, - {cost_in, - ?cashrng( - {inclusive, ?cash(301, <<"RUB">>)}, - {inclusive, ?cash(301, <<"RUB">>)} - )}}, - then_ = {value, [?prv_trm(8)]} - } - ]}; - _ -> - {decisions, [ - #domain_TerminalDecision{ - if_ = - {condition, - {cost_in, - ?cashrng( - {inclusive, ?cash(0, <<"RUB">>)}, - {exclusive, ?cash(1000000, <<"RUB">>)} - )}}, - then_ = {value, [?prv_trm(1)]} - }, - #domain_TerminalDecision{ - if_ = - {condition, - {cost_in, - ?cashrng( - {inclusive, ?cash(3000000, <<"RUB">>)}, - {exclusive, ?cash(10000000, <<"RUB">>)} - )}}, - then_ = {value, [?prv_trm(7)]} - } - ]} - end + } } }}. --spec withdrawal_terminal(?DTP('TerminalRef')) -> object(). -withdrawal_terminal(?trm(N) = Ref) when N > 0, N < 6 -> +-spec withdrawal_terminal(?DTP('TerminalRef'), ?DTP('ProviderRef')) -> object(). +withdrawal_terminal(Ref, ProviderRef) -> + withdrawal_terminal(Ref, ProviderRef, undefined). + +-spec withdrawal_terminal( + ?DTP('TerminalRef'), + ?DTP('ProviderRef'), + ?DTP('ProvisionTermSet') | undefined +) -> object(). +withdrawal_terminal(?trm(ID) = Ref, ?prv(ProviderID) = ProviderRef, TermSet) -> {terminal, #domain_TerminalObject{ ref = Ref, data = #domain_Terminal{ - name = <<"WithdrawalTerminal">>, - description = <<"Withdrawal terminal">>, - provider_ref = ?prv(1) - } - }}; -withdrawal_terminal(?trm(6) = Ref) -> - {terminal, #domain_TerminalObject{ - ref = Ref, - data = #domain_Terminal{ - name = <<"WithdrawalTerminal">>, - description = <<"Withdrawal terminal">> - } - }}; -withdrawal_terminal(?trm(7) = Ref) -> - {terminal, #domain_TerminalObject{ - ref = Ref, - data = #domain_Terminal{ - name = <<"Terminal7">>, - description = <<"Withdrawal terminal">>, - terms = #domain_ProvisionTermSet{ - wallet = #domain_WalletProvisionTerms{ - withdrawals = #domain_WithdrawalProvisionTerms{ - currencies = {value, ?ordset([?cur(<<"BTC">>)])}, - payout_methods = {value, ?ordset([])}, - cash_limit = - {value, - ?cashrng( - {inclusive, ?cash(1000000, <<"BTC">>)}, - {exclusive, ?cash(10000000, <<"BTC">>)} - )}, - cash_flow = {value, ?ordset([])} - } - } - } - } - }}; -withdrawal_terminal(?trm(8) = Ref) -> - {terminal, #domain_TerminalObject{ - ref = Ref, - data = #domain_Terminal{ - name = <<"Terminal8">>, - description = <<"Override provider cashflow">>, - terms = #domain_ProvisionTermSet{ - wallet = #domain_WalletProvisionTerms{ - withdrawals = #domain_WithdrawalProvisionTerms{ - cash_flow = - {decisions, [ - #domain_CashFlowDecision{ - if_ = {constant, true}, - then_ = - {value, [ - ?cfpost( - {system, settlement}, - {provider, settlement}, - ?fixed(16, <<"RUB">>) - ) - ]} - } - ]} - } - } - } + name = genlib:format("Withdrawal Terminal #~B", [ID]), + description = genlib:format("Withdrawal Terminal @ Provider #~B", [ProviderID]), + provider_ref = ProviderRef, + terms = TermSet } }}. diff --git a/apps/ff_cth/src/ct_payment_system.erl b/apps/ff_cth/src/ct_payment_system.erl index 1345a14..ca60274 100644 --- a/apps/ff_cth/src/ct_payment_system.erl +++ b/apps/ff_cth/src/ct_payment_system.erl @@ -239,6 +239,14 @@ services(Options) -> -include_lib("ff_cth/include/ct_domain.hrl"). +% NOTE +% Allocate those domain object identifiers at least 100 apart from each other. +% This space might be used to define additional object in place (see below). +-define(EMPTY_ROUTING_RULESET, 0). +-define(PAYINST1_ROUTING_POLICIES, 100). +-define(PAYINST1_ROUTING_PROHIBITIONS, 200). +-define(PAYINST2_ROUTING_POLICIES, 300). + payment_inst_identity_id(Options) -> maps:get(payment_inst_identity_id, Options). @@ -252,40 +260,268 @@ dummy_provider_identity_id(Options) -> maps:get(dummy_provider_identity_id, Options). domain_config(Options) -> - WithdrawalDecision1 = - {delegates, [ - delegate(condition(party, <<"12345">>), ?ruleset(2)), - delegate(condition(party, <<"67890">>), ?ruleset(4)) - ]}, - WithdrawalDecision2 = - {delegates, [ - delegate(condition(cost_in, {0, 1000, <<"RUB">>}), ?ruleset(3)) - ]}, - WithdrawalDecision3 = - {candidates, [ - candidate({constant, true}, ?trm(1)), - candidate({constant, true}, ?trm(2)) - ]}, - WithdrawalDecision4 = - {candidates, [ - candidate({constant, true}, ?trm(3)), - candidate({constant, true}, ?trm(4)), - candidate({constant, true}, ?trm(5)) - ]}, - WithdrawalDecision5 = - {candidates, [ - candidate({constant, true}, ?trm(4)) - ]}, - + ProviderTermSet = #domain_ProvisionTermSet{ + wallet = #domain_WalletProvisionTerms{ + withdrawals = #domain_WithdrawalProvisionTerms{ + currencies = {value, ?ordset([?cur(<<"RUB">>)])}, + payout_methods = {value, ?ordset([])}, + cash_limit = + {value, + ?cashrng( + {inclusive, ?cash(0, <<"RUB">>)}, + {exclusive, ?cash(10000000, <<"RUB">>)} + )}, + cash_flow = + {decisions, [ + #domain_CashFlowDecision{ + if_ = {condition, {currency_is, ?cur(<<"RUB">>)}}, + then_ = + {value, [ + ?cfpost( + {system, settlement}, + {provider, settlement}, + {product, + {min_of, + ?ordset([ + ?fixed(10, <<"RUB">>), + ?share(5, 100, operation_amount, round_half_towards_zero) + ])}} + ) + ]} + } + ]} + } + } + }, Default = [ ct_domain:globals(?eas(1), [?payinst(1)]), ct_domain:external_account_set(?eas(1), <<"Default">>, ?cur(<<"RUB">>)), - routing_ruleset(?ruleset(1), <<"WithdrawalRuleset#1">>, WithdrawalDecision1), - routing_ruleset(?ruleset(2), <<"WithdrawalRuleset#2">>, WithdrawalDecision2), - routing_ruleset(?ruleset(3), <<"WithdrawalRuleset#3">>, WithdrawalDecision3), - routing_ruleset(?ruleset(4), <<"WithdrawalRuleset#4">>, WithdrawalDecision4), - routing_ruleset(?ruleset(5), <<"WithdrawalRuleset#5">>, WithdrawalDecision5), + routing_ruleset( + ?ruleset(?EMPTY_ROUTING_RULESET), + <<"Empty Ruleset">>, + {candidates, []} + ), + + routing_ruleset( + ?ruleset(?PAYINST1_ROUTING_POLICIES), + <<"PayInst1 Withdrawal Ruleset">>, + {delegates, [ + delegate(condition(party, <<"12345">>), ?ruleset(?PAYINST1_ROUTING_POLICIES + 1)), + delegate(condition(party, <<"67890">>), ?ruleset(?PAYINST1_ROUTING_POLICIES + 3)), + delegate({constant, true}, ?ruleset(?PAYINST1_ROUTING_POLICIES + 4)) + ]} + ), + + routing_ruleset( + ?ruleset(?PAYINST1_ROUTING_POLICIES + 1), + {delegates, [ + delegate(condition(cost_in, {0, 1000, <<"RUB">>}), ?ruleset(?PAYINST1_ROUTING_POLICIES + 2)) + ]} + ), + + routing_ruleset( + ?ruleset(?PAYINST1_ROUTING_POLICIES + 2), + {candidates, [ + candidate({constant, true}, ?trm(1)), + candidate({constant, true}, ?trm(2)) + ]} + ), + + routing_ruleset( + ?ruleset(?PAYINST1_ROUTING_POLICIES + 3), + {candidates, [ + candidate({constant, true}, ?trm(3)), + candidate({constant, true}, ?trm(4)), + candidate({constant, true}, ?trm(5)) + ]} + ), + + routing_ruleset( + ?ruleset(?PAYINST1_ROUTING_POLICIES + 4), + {delegates, [ + delegate( + condition(cost_in, {300, 302, <<"RUB">>}), + ?ruleset(?PAYINST1_ROUTING_POLICIES + 5) + ), + delegate( + condition(cost_in, {123123, <<"RUB">>}), + ?ruleset(?PAYINST1_ROUTING_POLICIES + 6) + ), + delegate( + condition(cost_in, {100500, <<"RUB">>}), + ?ruleset(?PAYINST1_ROUTING_POLICIES + 10) + ), + delegate( + condition(cost_in, {500100, <<"RUB">>}), + ?ruleset(?PAYINST1_ROUTING_POLICIES + 11) + ), + delegate( + condition(cost_in, {500500, <<"RUB">>}), + ?ruleset(?PAYINST1_ROUTING_POLICIES + 12) + ), + delegate( + condition(cost_in, {700700, <<"RUB">>}), + ?ruleset(?PAYINST1_ROUTING_POLICIES + 13) + ), + delegate( + {condition, + {payment_tool, + {bank_card, #domain_BankCardCondition{ + definition = {issuer_country_is, 'rus'} + }}}}, + ?ruleset(?PAYINST1_ROUTING_POLICIES + 14) + ), + delegate( + {condition, {payment_tool, {crypto_currency, #domain_CryptoCurrencyCondition{}}}}, + ?ruleset(?PAYINST1_ROUTING_POLICIES + 15) + ), + delegate( + {condition, + {payment_tool, + {generic, + {payment_service_is, #domain_PaymentServiceRef{ + id = <<"IND">> + }}}}}, + ?ruleset(?PAYINST1_ROUTING_POLICIES + 15) + ) + ]} + ), + + routing_ruleset( + ?ruleset(?PAYINST1_ROUTING_POLICIES + 5), + {candidates, [ + candidate(condition(cost_in, {300, <<"RUB">>}), ?trm(1701)), + candidate(condition(cost_in, {301, <<"RUB">>}), ?trm(1708)) + ]} + ), + + routing_ruleset( + ?ruleset(?PAYINST1_ROUTING_POLICIES + 6), + {candidates, [ + candidate({constant, true}, ?trm(6)) + ]} + ), + + routing_ruleset( + ?ruleset(?PAYINST1_ROUTING_POLICIES + 10), + {candidates, [ + % provider 4 will be discarded by proxy 6 + candidate({constant, true}, ?trm(401)), + candidate({constant, true}, ?trm(501)) + ]} + ), + + routing_ruleset( + ?ruleset(?PAYINST1_ROUTING_POLICIES + 11), + {candidates, [ + candidate({constant, true}, ?trm(401)), + candidate({constant, true}, ?trm(601)), + candidate({constant, true}, ?trm(701)), + candidate({constant, true}, ?trm(801)) + ]} + ), + + routing_ruleset( + ?ruleset(?PAYINST1_ROUTING_POLICIES + 12), + {candidates, [ + candidate({constant, true}, ?trm(901), 500), + candidate({constant, true}, ?trm(1001), 1000) + ]} + ), + + routing_ruleset( + ?ruleset(?PAYINST1_ROUTING_POLICIES + 13), + {candidates, [ + candidate({constant, true}, ?trm(1101)) + ]} + ), + + routing_ruleset( + ?ruleset(?PAYINST1_ROUTING_POLICIES + 14), + {candidates, [ + candidate( + condition(cost_in, {0, 1000000, <<"RUB">>}), + ?trm(1) + ), + candidate( + condition(cost_in, {3000000, 10000000, <<"RUB">>}), + ?trm(307) + ) + ]} + ), + + routing_ruleset( + ?ruleset(?PAYINST1_ROUTING_POLICIES + 15), + {candidates, [ + candidate( + condition(cost_in, {0, 1000000, <<"RUB">>}), + ?trm(201) + ), + candidate( + condition(cost_in, {3000000, 10000000, <<"RUB">>}), + ?trm(307) + ) + ]} + ), + + routing_ruleset( + ?ruleset(?PAYINST1_ROUTING_PROHIBITIONS), + <<"PayInst1 Withdrawal Prohibitions">>, + {candidates, [ + candidate({constant, true}, ?trm(4)) + ]} + ), + + routing_ruleset( + ?ruleset(?PAYINST2_ROUTING_POLICIES), + <<"PayInst2 Withdrawal Ruleset">>, + {delegates, [ + delegate( + condition(cost_in, {123, <<"RUB">>}), + ?ruleset(?PAYINST2_ROUTING_POLICIES + 1) + ), + delegate( + {condition, + {payment_tool, + {crypto_currency, #domain_CryptoCurrencyCondition{ + definition = {crypto_currency_is_deprecated, litecoin} + }}}}, + ?ruleset(?PAYINST2_ROUTING_POLICIES + 1) + ), + delegate( + {condition, + {payment_tool, + {digital_wallet, #domain_DigitalWalletCondition{ + definition = {payment_service_is, ?pmtsrv(<<"webmoney">>)} + }}}}, + ?ruleset(?PAYINST2_ROUTING_POLICIES + 1) + ), + delegate( + {condition, + {payment_tool, + {bank_card, #domain_BankCardCondition{ + definition = {issuer_country_is, 'rus'} + }}}}, + ?ruleset(?PAYINST2_ROUTING_POLICIES + 1) + ) + ]} + ), + + routing_ruleset( + ?ruleset(?PAYINST2_ROUTING_POLICIES + 1), + <<"PayInst2 Withdrawal Ruleset #1">>, + {candidates, [ + candidate( + condition(cost_in, {0, 1000000, <<"RUB">>}), + ?trm(301) + ), + candidate( + condition(cost_in, {3000000, 10000000, <<"RUB">>}), + ?trm(307) + ) + ]} + ), {payment_institution, #domain_PaymentInstitutionObject{ ref = ?payinst(1), @@ -295,134 +531,14 @@ domain_config(Options) -> default_contract_template = {value, ?tmpl(1)}, providers = {value, ?ordset([])}, withdrawal_routing_rules = #domain_RoutingRules{ - policies = ?ruleset(1), - prohibitions = ?ruleset(5) + policies = ?ruleset(?PAYINST1_ROUTING_POLICIES), + prohibitions = ?ruleset(?PAYINST1_ROUTING_PROHIBITIONS) }, inspector = {value, ?insp(1)}, residences = ['rus'], realm = live, wallet_system_account_set = {value, ?sas(1)}, identity = payment_inst_identity_id(Options), - withdrawal_providers = - {decisions, [ - #domain_ProviderDecision{ - if_ = - {condition, - {cost_in, - ?cashrng( - {inclusive, ?cash(300, <<"RUB">>)}, - {inclusive, ?cash(301, <<"RUB">>)} - )}}, - then_ = {value, [?prv(17)]} - }, - #domain_ProviderDecision{ - if_ = - {condition, - {cost_in, - ?cashrng( - {inclusive, ?cash(123123, <<"RUB">>)}, - {inclusive, ?cash(123123, <<"RUB">>)} - )}}, - then_ = {value, [?prv(16)]} - }, - #domain_ProviderDecision{ - if_ = - {condition, - {cost_in, #domain_CashRange{ - upper = - {inclusive, #domain_Cash{ - amount = 100500, - currency = #domain_CurrencyRef{symbolic_code = <<"RUB">>} - }}, - lower = - {inclusive, #domain_Cash{ - amount = 100500, - currency = #domain_CurrencyRef{symbolic_code = <<"RUB">>} - }} - }}}, - % provider 4 will be discarded by proxy 6 - then_ = {value, [?prv(4), ?prv(5)]} - }, - #domain_ProviderDecision{ - if_ = - {condition, - {cost_in, #domain_CashRange{ - upper = - {inclusive, #domain_Cash{ - amount = 500100, - currency = #domain_CurrencyRef{symbolic_code = <<"RUB">>} - }}, - lower = - {inclusive, #domain_Cash{ - amount = 500100, - currency = #domain_CurrencyRef{symbolic_code = <<"RUB">>} - }} - }}}, - then_ = {value, [?prv(4), ?prv(6), ?prv(7), ?prv(8)]} - }, - #domain_ProviderDecision{ - if_ = - {condition, - {cost_in, #domain_CashRange{ - upper = - {inclusive, #domain_Cash{ - amount = 500500, - currency = #domain_CurrencyRef{symbolic_code = <<"RUB">>} - }}, - lower = - {inclusive, #domain_Cash{ - amount = 500500, - currency = #domain_CurrencyRef{symbolic_code = <<"RUB">>} - }} - }}}, - then_ = {value, [?prv(9), ?prv(10)]} - }, - #domain_ProviderDecision{ - if_ = - {condition, - {cost_in, #domain_CashRange{ - upper = - {inclusive, #domain_Cash{ - amount = 700700, - currency = #domain_CurrencyRef{symbolic_code = <<"RUB">>} - }}, - lower = - {inclusive, #domain_Cash{ - amount = 700700, - currency = #domain_CurrencyRef{symbolic_code = <<"RUB">>} - }} - }}}, - then_ = {value, [?prv(11)]} - }, - #domain_ProviderDecision{ - if_ = { - condition, - {payment_tool, - {bank_card, #domain_BankCardCondition{ - definition = {issuer_country_is, 'rus'} - }}} - }, - then_ = {value, [?prv(1)]} - }, - #domain_ProviderDecision{ - if_ = {condition, {payment_tool, {crypto_currency, #domain_CryptoCurrencyCondition{}}}}, - then_ = {value, [?prv(2)]} - }, - #domain_ProviderDecision{ - if_ = - {condition, - {payment_tool, - {generic, - {payment_service_is, #domain_PaymentServiceRef{ - id = <<"IND">> - }}}}}, - then_ = {value, [?prv(2)]} - }, - #domain_ProviderDecision{ - if_ = {constant, true}, - then_ = {value, []} - } - ]}, payment_system = {decisions, [ #domain_PaymentSystemDecision{ @@ -470,54 +586,10 @@ domain_config(Options) -> realm = live, wallet_system_account_set = {value, ?sas(1)}, identity = dummy_payment_inst_identity_id(Options), - withdrawal_providers = - {decisions, [ - #domain_ProviderDecision{ - if_ = - {condition, - {cost_in, #domain_CashRange{ - upper = - {inclusive, #domain_Cash{ - amount = 123, - currency = #domain_CurrencyRef{symbolic_code = <<"RUB">>} - }}, - lower = - {inclusive, #domain_Cash{ - amount = 123, - currency = #domain_CurrencyRef{symbolic_code = <<"RUB">>} - }} - }}}, - then_ = {value, [?prv(3)]} - }, - #domain_ProviderDecision{ - if_ = - {condition, - {payment_tool, - {crypto_currency, #domain_CryptoCurrencyCondition{ - definition = {crypto_currency_is_deprecated, litecoin} - }}}}, - then_ = {value, [?prv(3)]} - }, - #domain_ProviderDecision{ - if_ = - {condition, - {payment_tool, - {digital_wallet, #domain_DigitalWalletCondition{ - definition = {payment_service_is, ?pmtsrv(<<"webmoney">>)} - }}}}, - then_ = {value, [?prv(3)]} - }, - #domain_ProviderDecision{ - if_ = { - condition, - {payment_tool, - {bank_card, #domain_BankCardCondition{ - definition = {issuer_country_is, 'rus'} - }}} - }, - then_ = {value, [?prv(3)]} - } - ]}, + withdrawal_routing_rules = #domain_RoutingRules{ + policies = ?ruleset(?PAYINST2_ROUTING_POLICIES), + prohibitions = ?ruleset(?EMPTY_ROUTING_RULESET) + }, payment_system = {decisions, [ #domain_PaymentSystemDecision{ @@ -563,34 +635,90 @@ domain_config(Options) -> ct_domain:proxy(?prx(7), <<"Another down proxy">>, <<"http://localhost:8222/downbank2">>), ct_domain:proxy(?prx(8), <<"Sleep proxy">>, <<"http://localhost:8222/sleepybank">>), - ct_domain:withdrawal_provider(?prv(1), ?prx(2), provider_identity_id(Options)), - ct_domain:withdrawal_provider(?prv(2), ?prx(2), provider_identity_id(Options)), - ct_domain:withdrawal_provider(?prv(3), ?prx(3), dummy_provider_identity_id(Options)), - ct_domain:withdrawal_provider(?prv(4), ?prx(6), provider_identity_id(Options)), - ct_domain:withdrawal_provider(?prv(5), ?prx(2), provider_identity_id(Options)), - ct_domain:withdrawal_provider(?prv(6), ?prx(6), provider_identity_id(Options)), - ct_domain:withdrawal_provider(?prv(7), ?prx(6), provider_identity_id(Options)), - ct_domain:withdrawal_provider(?prv(8), ?prx(2), provider_identity_id(Options)), - ct_domain:withdrawal_provider(?prv(9), ?prx(7), provider_identity_id(Options)), - ct_domain:withdrawal_provider(?prv(10), ?prx(6), provider_identity_id(Options)), - ct_domain:withdrawal_provider(?prv(11), ?prx(8), provider_identity_id(Options)), - ct_domain:withdrawal_provider(?prv(16), ?prx(2), provider_identity_id(Options)), - ct_domain:withdrawal_provider(?prv(17), ?prx(2), provider_identity_id(Options)), + ct_domain:withdrawal_provider(?prv(1), ?prx(2), provider_identity_id(Options), ProviderTermSet), + ct_domain:withdrawal_provider(?prv(2), ?prx(2), provider_identity_id(Options), ProviderTermSet), + ct_domain:withdrawal_provider(?prv(3), ?prx(3), dummy_provider_identity_id(Options), ProviderTermSet), + ct_domain:withdrawal_provider(?prv(4), ?prx(6), provider_identity_id(Options), ProviderTermSet), + ct_domain:withdrawal_provider(?prv(5), ?prx(2), provider_identity_id(Options), ProviderTermSet), + ct_domain:withdrawal_provider(?prv(6), ?prx(6), provider_identity_id(Options), ProviderTermSet), + ct_domain:withdrawal_provider(?prv(7), ?prx(6), provider_identity_id(Options), ProviderTermSet), + ct_domain:withdrawal_provider(?prv(8), ?prx(2), provider_identity_id(Options), ProviderTermSet), + ct_domain:withdrawal_provider(?prv(9), ?prx(7), provider_identity_id(Options), ProviderTermSet), + ct_domain:withdrawal_provider(?prv(10), ?prx(6), provider_identity_id(Options), ProviderTermSet), + ct_domain:withdrawal_provider(?prv(11), ?prx(8), provider_identity_id(Options), ProviderTermSet), + ct_domain:withdrawal_provider(?prv(16), ?prx(2), provider_identity_id(Options), undefined), + ct_domain:withdrawal_provider(?prv(17), ?prx(2), provider_identity_id(Options), ProviderTermSet), ct_domain:contract_template(?tmpl(1), ?trms(1)), ct_domain:term_set_hierarchy(?trms(1), [ct_domain:timed_term_set(default_termset(Options))]), ct_domain:contract_template(?tmpl(2), ?trms(2)), ct_domain:term_set_hierarchy(?trms(2), [ct_domain:timed_term_set(company_termset(Options))]), - ct_domain:withdrawal_terminal(?trm(1)), - ct_domain:withdrawal_terminal(?trm(2)), - ct_domain:withdrawal_terminal(?trm(3)), - ct_domain:withdrawal_terminal(?trm(4)), - ct_domain:withdrawal_terminal(?trm(5)), - ct_domain:withdrawal_terminal(?trm(6)), - ct_domain:withdrawal_terminal(?trm(7)), - % Provider 17 satellite - ct_domain:withdrawal_terminal(?trm(8)), + ct_domain:withdrawal_terminal(?trm(1), ?prv(1)), + ct_domain:withdrawal_terminal(?trm(2), ?prv(1)), + ct_domain:withdrawal_terminal(?trm(3), ?prv(1)), + ct_domain:withdrawal_terminal(?trm(4), ?prv(1)), + ct_domain:withdrawal_terminal(?trm(5), ?prv(1)), + + ct_domain:withdrawal_terminal(?trm(6), ?prv(16)), + + ct_domain:withdrawal_terminal(?trm(201), ?prv(2)), + + ct_domain:withdrawal_terminal(?trm(301), ?prv(3)), + ct_domain:withdrawal_terminal( + ?trm(307), + ?prv(3), + #domain_ProvisionTermSet{ + wallet = #domain_WalletProvisionTerms{ + withdrawals = #domain_WithdrawalProvisionTerms{ + currencies = {value, ?ordset([?cur(<<"BTC">>)])}, + payout_methods = {value, ?ordset([])}, + cash_limit = + {value, + ?cashrng( + {inclusive, ?cash(1000000, <<"BTC">>)}, + {exclusive, ?cash(10000000, <<"BTC">>)} + )}, + cash_flow = {value, ?ordset([])} + } + } + } + ), + + ct_domain:withdrawal_terminal(?trm(401), ?prv(4)), + ct_domain:withdrawal_terminal(?trm(501), ?prv(5)), + ct_domain:withdrawal_terminal(?trm(601), ?prv(6)), + ct_domain:withdrawal_terminal(?trm(701), ?prv(7)), + ct_domain:withdrawal_terminal(?trm(801), ?prv(8)), + ct_domain:withdrawal_terminal(?trm(901), ?prv(9)), + ct_domain:withdrawal_terminal(?trm(1001), ?prv(10)), + ct_domain:withdrawal_terminal(?trm(1101), ?prv(11)), + + ct_domain:withdrawal_terminal(?trm(1701), ?prv(17)), + ct_domain:withdrawal_terminal( + ?trm(1708), + ?prv(17), + #domain_ProvisionTermSet{ + wallet = #domain_WalletProvisionTerms{ + withdrawals = #domain_WithdrawalProvisionTerms{ + cash_flow = + {decisions, [ + #domain_CashFlowDecision{ + if_ = {constant, true}, + then_ = + {value, [ + ?cfpost( + {system, settlement}, + {provider, settlement}, + ?fixed(16, <<"RUB">>) + ) + ]} + } + ]} + } + } + } + ), ct_domain:currency(?cur(<<"RUB">>)), ct_domain:currency(?cur(<<"USD">>)), @@ -1036,6 +1164,9 @@ company_termset(Options) -> }, maps:get(company_termset, Options, Default). +routing_ruleset(?ruleset(ID) = Ref, Decisions) -> + routing_ruleset(Ref, genlib:format("Withdrawal Ruleset #~B", [ID]), Decisions). + routing_ruleset(Ref, Name, Decisions) -> {routing_rules, #domain_RoutingRulesObject{ ref = Ref, @@ -1045,26 +1176,38 @@ routing_ruleset(Ref, Name, Decisions) -> } }}. -condition(cost_in, {Min, Max, Cur}) -> +condition(cost_in, {CostExact, Currency}) -> {condition, {cost_in, ?cashrng( - {inclusive, ?cash(Min, Cur)}, - {exclusive, ?cash(Max, Cur)} + {inclusive, ?cash(CostExact, Currency)}, + {inclusive, ?cash(CostExact, Currency)} + )}}; +condition(cost_in, {Min, Max, Currency}) -> + {condition, + {cost_in, + ?cashrng( + {inclusive, ?cash(Min, Currency)}, + {exclusive, ?cash(Max, Currency)} )}}; condition(party, ID) -> {condition, {party, #domain_PartyCondition{id = ID}}}. delegate(Allowed, RuleSetRef) -> #domain_RoutingDelegate{ - description = <<"Delagate description">>, allowed = Allowed, ruleset = RuleSetRef }. candidate(Allowed, Terminal) -> #domain_RoutingCandidate{ - description = <<"Candidate description">>, allowed = Allowed, terminal = Terminal }. + +candidate(Allowed, Terminal, Prio) -> + #domain_RoutingCandidate{ + allowed = Allowed, + terminal = Terminal, + priority = Prio + }. diff --git a/apps/ff_server/src/ff_identity_handler.erl b/apps/ff_server/src/ff_identity_handler.erl index 8e8fc8f..b140849 100644 --- a/apps/ff_server/src/ff_identity_handler.erl +++ b/apps/ff_server/src/ff_identity_handler.erl @@ -34,7 +34,7 @@ handle_function_('Create', {IdentityParams, Context}, Opts) -> woody_error:raise(business, #fistful_ProviderNotFound{}); {error, {party, notfound}} -> woody_error:raise(business, #fistful_PartyNotFound{}); - {error, {inaccessible, _}} -> + {error, {party, {inaccessible, _}}} -> woody_error:raise(business, #fistful_PartyInaccessible{}); {error, exists} -> handle_function_('Get', {IdentityID, #'fistful_base_EventRange'{}}, Opts); diff --git a/apps/ff_transfer/src/ff_withdrawal.erl b/apps/ff_transfer/src/ff_withdrawal.erl index fcc0704..f7790a3 100644 --- a/apps/ff_transfer/src/ff_withdrawal.erl +++ b/apps/ff_transfer/src/ff_withdrawal.erl @@ -1043,18 +1043,7 @@ compute_fees(Route, VS, DomainRevision) -> compute_provider_terminal_terms(#{provider_id := ProviderID, terminal_id := TerminalID}, VS, DomainRevision) -> ProviderRef = ff_payouts_provider:ref(ProviderID), TerminalRef = ff_payouts_terminal:ref(TerminalID), - ff_party:compute_provider_terminal_terms(ProviderRef, TerminalRef, VS, DomainRevision); -% Backward compatibility legacy case for old withrawals without terminals -compute_provider_terminal_terms(#{provider_id := ProviderID}, VS, DomainRevision) -> - ProviderRef = ff_payouts_provider:ref(ProviderID), - case ff_party:compute_provider(ProviderRef, VS, DomainRevision) of - {ok, #domain_Provider{ - terms = Terms - }} -> - {ok, Terms}; - {error, Error} -> - {error, Error} - end. + ff_party:compute_provider_terminal_terms(ProviderRef, TerminalRef, VS, DomainRevision). cash_flow_postings(CashFlowSelector) -> case CashFlowSelector of diff --git a/apps/ff_transfer/src/ff_withdrawal_routing.erl b/apps/ff_transfer/src/ff_withdrawal_routing.erl index 8d99de6..4b6b64f 100644 --- a/apps/ff_transfer/src/ff_withdrawal_routing.erl +++ b/apps/ff_transfer/src/ff_withdrawal_routing.erl @@ -29,7 +29,6 @@ -type terminal_ref() :: ff_payouts_terminal:terminal_ref(). -type terminal_id() :: ff_payouts_terminal:id(). --type terminal_priority() :: ff_payouts_terminal:terminal_priority(). -type routing_rule_route() :: ff_routing_rule:route(). -type reject_context() :: ff_routing_rule:reject_context(). @@ -52,21 +51,11 @@ prepare_routes(PartyVarset, Identity, DomainRevision) -> ), {ValidatedRoutes, RejectContext1} = filter_valid_routes(Routes, RejectContext0, PartyVarset, DomainRevision), case ValidatedRoutes of + [_ | _] -> + {ok, ValidatedRoutes}; [] -> ff_routing_rule:log_reject_context(RejectContext1), - logger:log(info, "Fallback to legacy method of routes gathering"), - case ff_payment_institution:withdrawal_providers(PaymentInstitution) of - {ok, Providers} -> - filter_routes_legacy(Providers, PartyVarset, DomainRevision); - {error, {misconfiguration, _Details} = Error} -> - %% TODO: Do not interpret such error as an empty route list. - %% The current implementation is made for compatibility reasons. - %% Try to remove and follow the tests. - _ = logger:warning("Route search failed: ~p", [Error]), - {error, route_not_found} - end; - _ -> - {ok, ValidatedRoutes} + {error, route_not_found} end. -spec make_route(provider_id(), terminal_id() | undefined) -> route(). @@ -159,71 +148,6 @@ filter_valid_routes_([Route | Rest], PartyVarset, {Acc0, RejectContext0}, Domain end, filter_valid_routes_(Rest, PartyVarset, {Acc, RejectContext}, DomainRevision). --spec filter_routes_legacy([provider_id()], party_varset(), domain_revision()) -> - {ok, [route()]} | {error, route_not_found}. -filter_routes_legacy(Providers, PartyVarset, DomainRevision) -> - do(fun() -> - unwrap(filter_routes_legacy_(Providers, PartyVarset, DomainRevision, #{})) - end). - -filter_routes_legacy_([], _PartyVarset, _DomainRevision, Acc) when map_size(Acc) == 0 -> - {error, route_not_found}; -filter_routes_legacy_([], _PartyVarset, _DomainRevision, Acc) -> - {ok, convert_to_route(Acc)}; -filter_routes_legacy_([ProviderID | Rest], PartyVarset, DomainRevision, Acc0) -> - ProviderRef = ff_payouts_provider:ref(ProviderID), - {ok, TerminalsWithPriority} = compute_withdrawal_terminals_with_priority(ProviderRef, PartyVarset, DomainRevision), - Acc = - case get_valid_terminals_with_priority(TerminalsWithPriority, ProviderRef, PartyVarset, DomainRevision, []) of - [] -> - Acc0; - TPL -> - lists:foldl( - fun({TerminalID, Priority}, Acc1) -> - Terms = maps:get(Priority, Acc1, []), - maps:put(Priority, [{ProviderID, TerminalID} | Terms], Acc1) - end, - Acc0, - TPL - ) - end, - filter_routes_legacy_(Rest, PartyVarset, DomainRevision, Acc). - --spec compute_withdrawal_terminals_with_priority(provider_ref(), party_varset(), domain_revision()) -> - {ok, [{terminal_id(), terminal_priority()}]} | {error, term()}. -compute_withdrawal_terminals_with_priority(ProviderRef, VS, DomainRevision) -> - case ff_party:compute_provider(ProviderRef, VS, DomainRevision) of - {ok, Provider} -> - case Provider of - #domain_Provider{ - terminal = {value, Terminals} - } -> - {ok, [ - {TerminalID, Priority} - || #domain_ProviderTerminalRef{id = TerminalID, priority = Priority} <- Terminals - ]}; - _ -> - Error = {misconfiguration, {missing, terminal_selector}}, - _ = logger:warning("Provider terminal search failed: ~p", [Error]), - {ok, []} - end; - {error, Error} -> - {error, Error} - end. - -get_valid_terminals_with_priority([], _ProviderRef, _PartyVarset, _DomainRevision, Acc) -> - Acc; -get_valid_terminals_with_priority([{TerminalID, Priority} | Rest], ProviderRef, PartyVarset, DomainRevision, Acc0) -> - TerminalRef = ff_payouts_terminal:ref(TerminalID), - Acc = - case validate_terms(ProviderRef, TerminalRef, PartyVarset, DomainRevision) of - {ok, valid} -> - [{TerminalID, Priority} | Acc0]; - {error, _Error} -> - Acc0 - end, - get_valid_terminals_with_priority(Rest, ProviderRef, PartyVarset, DomainRevision, Acc). - -spec validate_terms(provider_ref(), terminal_ref(), party_varset(), domain_revision()) -> {ok, valid} | {error, Error :: term()}. diff --git a/apps/ff_transfer/test/ff_transfer_SUITE.erl b/apps/ff_transfer/test/ff_transfer_SUITE.erl index 532aaa5..536d91b 100644 --- a/apps/ff_transfer/test/ff_transfer_SUITE.erl +++ b/apps/ff_transfer/test/ff_transfer_SUITE.erl @@ -2,7 +2,6 @@ -include_lib("fistful_proto/include/ff_proto_fistful_admin_thrift.hrl"). -include_lib("fistful_proto/include/ff_proto_withdrawal_thrift.hrl"). --include_lib("damsel/include/dmsl_domain_thrift.hrl"). -export([all/0]). -export([groups/0]). @@ -380,7 +379,7 @@ deposit_quote_withdrawal_ok(C) -> created_at => <<"2016-03-22T06:12:27Z">>, expires_on => <<"2016-03-22T06:12:27Z">>, quote_data => #{<<"test">> => <<"test">>}, - route => ff_withdrawal_routing:make_route(3, 1), + route => ff_withdrawal_routing:make_route(3, 301), domain_revision => DomainRevision, party_revision => PartyRevision } diff --git a/apps/ff_transfer/test/ff_withdrawal_SUITE.erl b/apps/ff_transfer/test/ff_withdrawal_SUITE.erl index b1ade29..815d621 100644 --- a/apps/ff_transfer/test/ff_withdrawal_SUITE.erl +++ b/apps/ff_transfer/test/ff_withdrawal_SUITE.erl @@ -179,7 +179,7 @@ session_fail_test(C) -> cash_to => {2120, <<"USD">>}, created_at => <<"2016-03-22T06:12:27Z">>, expires_on => <<"2016-03-22T06:12:27Z">>, - route => ff_withdrawal_routing:make_route(3, 1), + route => ff_withdrawal_routing:make_route(3, 301), quote_data => #{<<"test">> => <<"error">>}, operation_timestamp => ff_time:now() } @@ -704,7 +704,7 @@ session_repair_test(C) -> cash_to => {700700, <<"RUB">>}, created_at => <<"2016-03-22T06:12:27Z">>, expires_on => <<"2016-03-22T06:12:27Z">>, - route => ff_withdrawal_routing:make_route(11, 1), + route => ff_withdrawal_routing:make_route(11, 1101), quote_data => #{<<"test">> => <<"fatal">>}, operation_timestamp => ff_time:now() } @@ -748,8 +748,8 @@ provider_terminal_terms_merging_test(C) -> end, {Route1, VolumeEntries1} = ProduceWithdrawal({300, <<"RUB">>}), {Route2, VolumeEntries2} = ProduceWithdrawal({301, <<"RUB">>}), - ?assertMatch(#{provider_id := 17, terminal_id := 1}, Route1), - ?assertMatch(#{provider_id := 17, terminal_id := 8}, Route2), + ?assertMatch(#{provider_id := 17, terminal_id := 1701}, Route1), + ?assertMatch(#{provider_id := 17, terminal_id := 1708}, Route2), ?assertEqual([300, 30, 30, 10], VolumeEntries1), ?assertEqual([301, 30, 30, 16], VolumeEntries2). diff --git a/apps/ff_transfer/test/ff_withdrawal_routing_SUITE.erl b/apps/ff_transfer/test/ff_withdrawal_routing_SUITE.erl index 1cc2800..613e472 100644 --- a/apps/ff_transfer/test/ff_withdrawal_routing_SUITE.erl +++ b/apps/ff_transfer/test/ff_withdrawal_routing_SUITE.erl @@ -186,7 +186,7 @@ adapter_unreachable_quote_test(C) -> cash_to => {2120, <<"USD">>}, created_at => <<"2020-03-22T06:12:27Z">>, expires_on => <<"2020-03-22T06:12:27Z">>, - route => ff_withdrawal_routing:make_route(4, 1), + route => ff_withdrawal_routing:make_route(4, 401), quote_data => #{<<"test">> => <<"test">>}, operation_timestamp => ff_time:now() } diff --git a/apps/fistful/src/ff_context.erl b/apps/fistful/src/ff_context.erl index 356607b..4af3aec 100644 --- a/apps/fistful/src/ff_context.erl +++ b/apps/fistful/src/ff_context.erl @@ -7,26 +7,19 @@ -export([cleanup/0]). -export([get_woody_context/1]). --export([set_woody_context/2]). --export([get_user_identity/1]). --export([set_user_identity/2]). -export([get_party_client_context/1]). --export([set_party_client_context/2]). -export([get_party_client/1]). --export([set_party_client/2]). -opaque context() :: #{ woody_context := woody_context(), party_client_context := party_client_context(), - party_client => party_client(), - user_identity => user_identity() + party_client => party_client() }. -type options() :: #{ - party_client => party_client(), - user_identity => user_identity(), woody_context => woody_context(), - party_client_context => party_client_context() + party_client_context => party_client_context(), + party_client => party_client() }. -export_type([context/0]). @@ -34,7 +27,6 @@ %% Internal types --type user_identity() :: woody_user_identity:user_identity(). -type woody_context() :: woody_context:ctx(). -type party_client() :: party_client:client(). -type party_client_context() :: party_client:context(). @@ -73,48 +65,19 @@ cleanup() -> ok. -spec get_woody_context(context()) -> woody_context(). -get_woody_context(Context) -> - #{woody_context := WoodyContext} = ensure_woody_user_info_set(Context), +get_woody_context(#{woody_context := WoodyContext}) -> WoodyContext. --spec set_woody_context(woody_context(), context()) -> context(). -set_woody_context(WoodyContext, #{party_client_context := PartyContext0} = Context) -> - PartyContext1 = party_client_context:set_woody_context(WoodyContext, PartyContext0), - Context#{ - woody_context => WoodyContext, - party_client_context => PartyContext1 - }. - -spec get_party_client(context()) -> party_client(). get_party_client(#{party_client := PartyClient}) -> PartyClient; get_party_client(Context) -> error(no_party_client, [Context]). --spec set_party_client(party_client(), context()) -> context(). -set_party_client(PartyClient, Context) -> - Context#{party_client => PartyClient}. - -spec get_party_client_context(context()) -> party_client_context(). -get_party_client_context(Context) -> - #{party_client_context := PartyContext} = ensure_party_user_info_set(Context), +get_party_client_context(#{party_client_context := PartyContext}) -> PartyContext. --spec set_party_client_context(party_client_context(), context()) -> context(). -set_party_client_context(PartyContext, Context) -> - Context#{party_client_context := PartyContext}. - --spec get_user_identity(context()) -> user_identity() | no_return(). -get_user_identity(#{user_identity := Identity}) -> - Identity; -get_user_identity(Context) -> - WoodyContext = get_woody_context(Context), - woody_user_identity:get(WoodyContext). - --spec set_user_identity(user_identity(), context()) -> context(). -set_user_identity(Identity, Context) -> - Context#{user_identity => Identity}. - %% Internal functions -spec ensure_woody_context_exists(options()) -> options(). @@ -128,17 +91,3 @@ ensure_party_context_exists(#{party_client_context := _PartyContext} = Options) Options; ensure_party_context_exists(#{woody_context := WoodyContext} = Options) -> Options#{party_client_context => party_client:create_context(#{woody_context => WoodyContext})}. - --spec ensure_woody_user_info_set(context()) -> context(). -ensure_woody_user_info_set(#{user_identity := Identity, woody_context := WoodyContext} = Context) -> - NewWoodyContext = woody_user_identity:put(Identity, WoodyContext), - Context#{woody_context := NewWoodyContext}; -ensure_woody_user_info_set(Context) -> - Context. - --spec ensure_party_user_info_set(context()) -> context(). -ensure_party_user_info_set(#{user_identity := Identity, party_client_context := PartyContext} = Context) -> - NewPartyContext = party_client_context:set_user_info(Identity, PartyContext), - Context#{party_client_context := NewPartyContext}; -ensure_party_user_info_set(Context) -> - Context. diff --git a/apps/fistful/src/ff_identity.erl b/apps/fistful/src/ff_identity.erl index a0d8f06..48bfa24 100644 --- a/apps/fistful/src/ff_identity.erl +++ b/apps/fistful/src/ff_identity.erl @@ -73,8 +73,7 @@ -type create_error() :: {provider, notfound} - | {party, notfound} - | ff_party:inaccessibility() + | {party, notfound | ff_party:inaccessibility()} | invalid. -type get_terms_params() :: #{ diff --git a/apps/fistful/src/ff_party.erl b/apps/fistful/src/ff_party.erl index 8c746af..0f3e429 100644 --- a/apps/fistful/src/ff_party.erl +++ b/apps/fistful/src/ff_party.erl @@ -87,7 +87,6 @@ -export([get_contract_terms/6]). -export([compute_payment_institution/3]). -export([compute_routing_ruleset/3]). --export([compute_provider/3]). -export([compute_provider_terminal_terms/4]). -export([get_withdrawal_cash_flow_plan/1]). -export([get_w2w_cash_flow_plan/1]). @@ -114,7 +113,6 @@ -type provider_ref() :: dmsl_domain_thrift:'ProviderRef'(). -type terminal_ref() :: dmsl_domain_thrift:'TerminalRef'(). -type method_ref() :: dmsl_domain_thrift:'PaymentMethodRef'(). --type provider() :: dmsl_domain_thrift:'Provider'(). -type provision_term_set() :: dmsl_domain_thrift:'ProvisionTermSet'(). -type bound_type() :: 'exclusive' | 'inclusive'. -type cash_range() :: {{bound_type(), cash()}, {bound_type(), cash()}}. @@ -165,7 +163,8 @@ create(ID, Params) -> -spec is_accessible(id()) -> {ok, accessible} - | {error, inaccessibility()}. + | {error, inaccessibility()} + | {error, notfound}. is_accessible(ID) -> case do_get_party(ID) of #domain_Party{blocking = {blocked, _}} -> @@ -185,9 +184,7 @@ get_revision(ID) -> {ok, Revision} -> {ok, Revision}; {error, #payproc_PartyNotFound{}} -> - {error, {party_not_found, ID}}; - {error, Unexpected} -> - error(Unexpected) + {error, {party_not_found, ID}} end. %% @@ -271,9 +268,7 @@ get_contract_terms(PartyID, ContractID, Varset, Timestamp, PartyRevision, Domain {error, #payproc_ContractNotFound{}} -> {error, {contract_not_found, ContractID}}; {error, #payproc_PartyNotExistsYet{}} -> - {error, {party_not_exists_yet, PartyID}}; - {error, Unexpected} -> - erlang:error({unexpected, Unexpected}) + {error, {party_not_exists_yet, PartyID}} end. -spec compute_payment_institution(PaymentInstitutionRef, Varset, DomainRevision) -> Result when @@ -320,28 +315,6 @@ compute_routing_ruleset(RoutingRulesetRef, Varset, DomainRevision) -> {error, ruleset_not_found} end. --spec compute_provider(ProviderRef, Varset, DomainRevision) -> Result when - ProviderRef :: provider_ref(), - Varset :: ff_varset:varset(), - DomainRevision :: domain_revision(), - Result :: {ok, provider()} | {error, provider_not_found}. -compute_provider(ProviderRef, Varset, DomainRevision) -> - DomainVarset = ff_varset:encode(Varset), - {Client, Context} = get_party_client(), - Result = party_client_thrift:compute_provider( - ProviderRef, - DomainRevision, - DomainVarset, - Client, - Context - ), - case Result of - {ok, Provider} -> - {ok, Provider}; - {error, #payproc_ProviderNotFound{}} -> - {error, provider_not_found} - end. - -spec compute_provider_terminal_terms(ProviderRef, TerminalRef, Varset, DomainRevision) -> Result when ProviderRef :: provider_ref(), TerminalRef :: terminal_ref(), @@ -484,9 +457,7 @@ do_create_party(ID, Params) -> ok -> ok; {error, #payproc_PartyExists{}} -> - {error, exists}; - {error, Unexpected} -> - error(Unexpected) + {error, exists} end. do_get_party(ID) -> @@ -499,9 +470,7 @@ do_get_party(ID) -> {ok, Party} -> Party; {error, #payproc_PartyNotFound{} = Reason} -> - Reason; - {error, Unexpected} -> - error(Unexpected) + Reason end. do_get_contract(ID, ContractID) -> @@ -512,9 +481,7 @@ do_get_contract(ID, ContractID) -> {error, #payproc_PartyNotFound{}} -> {error, {party_not_found, ID}}; {error, #payproc_ContractNotFound{}} -> - {error, {contract_not_found, ContractID}}; - {error, Unexpected} -> - error(Unexpected) + {error, {contract_not_found, ContractID}} end. do_create_claim(ID, Changeset) -> @@ -527,9 +494,7 @@ do_create_claim(ID, Changeset) -> }} -> {error, invalid}; {error, #payproc_InvalidPartyStatus{status = Status}} -> - {error, construct_inaccessibilty(Status)}; - {error, Unexpected} -> - error(Unexpected) + {error, construct_inaccessibilty(Status)} end. do_accept_claim(ID, Claim) -> @@ -543,31 +508,15 @@ do_accept_claim(ID, Claim) -> ok -> accepted; {error, #payproc_InvalidClaimStatus{status = {accepted, _}}} -> - accepted; - {error, Unexpected} -> - error(Unexpected) + accepted end. get_party_client() -> - % TODO - % - Move auth logic from hellgate to capi the same way as it works - % in wapi & fistful. Then the following dirty user_identity hack - % will not be necessary anymore. - Context0 = ff_context:load(), - WoodyContextWithoutMeta = maps:without([meta], ff_context:get_woody_context(Context0)), - Context1 = ff_context:set_woody_context(WoodyContextWithoutMeta, Context0), - Context2 = ff_context:set_user_identity(construct_user_identity(), Context1), - Client = ff_context:get_party_client(Context2), - ClientContext = ff_context:get_party_client_context(Context2), + Context = ff_context:load(), + Client = ff_context:get_party_client(Context), + ClientContext = ff_context:get_party_client_context(Context), {Client, ClientContext}. --spec construct_user_identity() -> woody_user_identity:user_identity(). -construct_user_identity() -> - #{ - id => <<"fistful">>, - realm => <<"service">> - }. - construct_inaccessibilty({blocking, _}) -> {inaccessible, blocked}; construct_inaccessibilty({suspension, _}) -> @@ -583,10 +532,6 @@ construct_inaccessibilty({suspension, _}) -> {contract_modification, #payproc_ContractModificationUnit{id = ID, modification = Mod}} ). --define(WALLET_MOD(ID, Mod), - {wallet_modification, #payproc_WalletModificationUnit{id = ID, modification = Mod}} -). - construct_party_params(#{email := Email}) -> #payproc_PartyParams{ contact_info = #domain_PartyContactInfo{ diff --git a/apps/fistful/src/ff_payment_institution.erl b/apps/fistful/src/ff_payment_institution.erl index ab4e204..355a483 100644 --- a/apps/fistful/src/ff_payment_institution.erl +++ b/apps/fistful/src/ff_payment_institution.erl @@ -11,7 +11,6 @@ id := id(), system_accounts := dmsl_domain_thrift:'SystemAccountSetSelector'(), identity := binary(), - withdrawal_providers := dmsl_domain_thrift:'ProviderSelector'(), withdrawal_routing_rules := dmsl_domain_thrift:'RoutingRules'(), payment_system => dmsl_domain_thrift:'PaymentSystemSelector'() }. @@ -35,7 +34,6 @@ -export([ref/1]). -export([get/3]). --export([withdrawal_providers/1]). -export([system_accounts/2]). -export([payment_system/1]). @@ -86,17 +84,6 @@ payment_system(#{payment_system := PaymentSystem}) -> payment_system(_PaymentInstitution) -> {ok, undefined}. --spec withdrawal_providers(payment_institution()) -> - {ok, [ff_payouts_provider:id()]} - | {error, term()}. -withdrawal_providers(#{withdrawal_providers := ProvidersSelector}) -> - case get_selector_value(withdrawal_providers, ProvidersSelector) of - {ok, Providers} -> - {ok, [ProviderID || #domain_ProviderRef{id = ProviderID} <- Providers]}; - {error, Error} -> - {error, Error} - end. - -spec system_accounts(payment_institution(), domain_revision()) -> {ok, system_accounts()} | {error, term()}. @@ -116,7 +103,6 @@ system_accounts(PaymentInstitution, DomainRevision) -> decode(ID, #domain_PaymentInstitution{ wallet_system_account_set = SystemAccounts, identity = Identity, - withdrawal_providers = WithdrawalProviders, withdrawal_routing_rules = WithdrawalRoutingRules, payment_system = PaymentSystem }) -> @@ -124,7 +110,6 @@ decode(ID, #domain_PaymentInstitution{ id => ID, system_accounts => SystemAccounts, identity => Identity, - withdrawal_providers => WithdrawalProviders, withdrawal_routing_rules => WithdrawalRoutingRules, payment_system => PaymentSystem }). diff --git a/apps/fistful/src/ff_payouts_provider.erl b/apps/fistful/src/ff_payouts_provider.erl index d64a285..209acf9 100644 --- a/apps/fistful/src/ff_payouts_provider.erl +++ b/apps/fistful/src/ff_payouts_provider.erl @@ -8,8 +8,7 @@ terms => dmsl_domain_thrift:'ProvisionTermSet'(), accounts := accounts(), adapter := ff_adapter:adapter(), - adapter_opts := map(), - terminal => dmsl_domain_thrift:'TerminalSelector'() + adapter_opts := map() }. -type id() :: dmsl_domain_thrift:'ObjectID'(). @@ -98,8 +97,7 @@ decode(ID, #domain_Provider{ proxy = Proxy, identity = Identity, terms = Terms, - accounts = Accounts, - terminal = TerminalSelector + accounts = Accounts }) -> genlib_map:compact( maps:merge( @@ -107,8 +105,7 @@ decode(ID, #domain_Provider{ id => ID, identity => Identity, terms => Terms, - accounts => decode_accounts(Identity, Accounts), - terminal => TerminalSelector + accounts => decode_accounts(Identity, Accounts) }, decode_adapter(Proxy) ) diff --git a/apps/fistful/src/ff_routing_rule.erl b/apps/fistful/src/ff_routing_rule.erl index dc6e79f..8169ee4 100644 --- a/apps/fistful/src/ff_routing_rule.erl +++ b/apps/fistful/src/ff_routing_rule.erl @@ -65,26 +65,16 @@ gather_routes(PaymentInstitution, RoutingRuleTag, VS, Revision) -> | {error, misconfiguration}. do_gather_routes(PaymentInstitution, RoutingRuleTag, VS, Revision) -> do(fun() -> - case maps:get(RoutingRuleTag, PaymentInstitution, undefined) of - undefined -> - logger:log( - warning, - "RoutingRules ~p is undefined, PaymentInstitution: ~p", - [RoutingRuleTag, PaymentInstitution] - ), - {[], []}; - RoutingRules -> - Policies = RoutingRules#domain_RoutingRules.policies, - Prohibitions = RoutingRules#domain_RoutingRules.prohibitions, - PermitCandidates = unwrap(compute_routing_ruleset(Policies, VS, Revision)), - DenyCandidates = unwrap(compute_routing_ruleset(Prohibitions, VS, Revision)), - {AcceptedRoutes, RejectedRoutes} = prohibited_candidates_filter( - PermitCandidates, - DenyCandidates, - Revision - ), - {AcceptedRoutes, RejectedRoutes} - end + RoutingRules = maps:get(RoutingRuleTag, PaymentInstitution), + Policies = RoutingRules#domain_RoutingRules.policies, + Prohibitions = RoutingRules#domain_RoutingRules.prohibitions, + PermitCandidates = unwrap(compute_routing_ruleset(Policies, VS, Revision)), + DenyCandidates = unwrap(compute_routing_ruleset(Prohibitions, VS, Revision)), + filter_prohibited_candidates( + PermitCandidates, + DenyCandidates, + Revision + ) end). -spec compute_routing_ruleset(routing_ruleset_ref(), varset(), revision()) -> @@ -115,8 +105,8 @@ check_ruleset_computing({candidates, Candidates}) -> {error, misconfiguration} end. --spec prohibited_candidates_filter([candidate()], [candidate()], revision()) -> {[route()], [rejected_route()]}. -prohibited_candidates_filter(Candidates, ProhibitedCandidates, Revision) -> +-spec filter_prohibited_candidates([candidate()], [candidate()], revision()) -> {[route()], [rejected_route()]}. +filter_prohibited_candidates(Candidates, ProhibitedCandidates, Revision) -> ProhibitionTable = lists:foldl( fun(C, Acc) -> Acc#{get_terminal_ref(C) => get_description(C)} diff --git a/apps/fistful/src/fistful.app.src b/apps/fistful/src/fistful.app.src index 6e163ed..f3ce4bb 100644 --- a/apps/fistful/src/fistful.app.src +++ b/apps/fistful/src/fistful.app.src @@ -11,7 +11,6 @@ machinery, machinery_extra, woody, - woody_user_identity, uuid, damsel, dmt_client, diff --git a/apps/fistful/src/fistful.erl b/apps/fistful/src/fistful.erl index 9201bba..b262afc 100644 --- a/apps/fistful/src/fistful.erl +++ b/apps/fistful/src/fistful.erl @@ -8,7 +8,7 @@ -behaviour(machinery_backend). -type namespace() :: machinery:namespace(). --type backend() :: machinery:backend(machinery:backend(_)). +-type backend() :: machinery:backend(_). -type options() :: #{ handler := machinery:modopts(_), diff --git a/apps/fistful/test/ff_identity_SUITE.erl b/apps/fistful/test/ff_identity_SUITE.erl index cc26545..e64fdc4 100644 --- a/apps/fistful/test/ff_identity_SUITE.erl +++ b/apps/fistful/test/ff_identity_SUITE.erl @@ -7,7 +7,9 @@ -export([end_per_testcase/2]). -export([get_missing_fails/1]). --export([create_missing_fails/1]). +-export([create_missing_party_fails/1]). +-export([create_inaccessible_party_fails/1]). +-export([create_missing_provider_fails/1]). -export([create_ok/1]). %% @@ -23,12 +25,16 @@ all() -> [ get_missing_fails, - create_missing_fails, + create_missing_party_fails, + create_inaccessible_party_fails, + create_missing_provider_fails, create_ok ]. -spec get_missing_fails(config()) -> test_return(). --spec create_missing_fails(config()) -> test_return(). +-spec create_missing_party_fails(config()) -> test_return(). +-spec create_inaccessible_party_fails(config()) -> test_return(). +-spec create_missing_provider_fails(config()) -> test_return(). -spec create_ok(config()) -> test_return(). -spec init_per_suite(config()) -> config(). @@ -65,7 +71,36 @@ get_missing_fails(_C) -> ID = genlib:unique(), {error, notfound} = ff_identity_machine:get(ID). -create_missing_fails(C) -> +create_missing_party_fails(_C) -> + ID = genlib:unique(), + NonexistentParty = genlib:bsuuid(), + Name = <<"Identity Name">>, + {error, {party, notfound}} = ff_identity_machine:create( + #{ + id => ID, + name => Name, + party => NonexistentParty, + provider => <<"good-one">> + }, + #{<<"dev.vality.wapi">> => #{<<"name">> => Name}} + ). + +create_inaccessible_party_fails(C) -> + ID = genlib:unique(), + PartyID = create_party(C), + ok = block_party(PartyID, genlib:to_binary(?FUNCTION_NAME)), + Name = <<"Identity Name">>, + {error, {party, {inaccessible, blocked}}} = ff_identity_machine:create( + #{ + id => ID, + name => Name, + party => PartyID, + provider => <<"good-one">> + }, + #{<<"dev.vality.wapi">> => #{<<"name">> => Name}} + ). + +create_missing_provider_fails(C) -> ID = genlib:unique(), Party = create_party(C), Name = <<"Identity Name">>, @@ -76,7 +111,7 @@ create_missing_fails(C) -> party => Party, provider => <<"who">> }, - #{<<"com.rbkmoney.wapi">> => #{<<"name">> => Name}} + #{<<"dev.vality.wapi">> => #{<<"name">> => Name}} ). create_ok(C) -> @@ -90,7 +125,7 @@ create_ok(C) -> party => Party, provider => <<"good-one">> }, - #{<<"com.rbkmoney.wapi">> => #{<<"name">> => Name}} + #{<<"dev.vality.wapi">> => #{<<"name">> => Name}} ), I1 = ff_identity_machine:identity(unwrap(ff_identity_machine:get(ID))), {ok, accessible} = ff_identity:is_accessible(I1), @@ -100,3 +135,9 @@ create_party(_C) -> ID = genlib:bsuuid(), _ = ff_party:create(ID), ID. + +block_party(ID, Reason) -> + Context = ff_context:load(), + Client = ff_context:get_party_client(Context), + ClientContext = ff_context:get_party_client_context(Context), + party_client_thrift:block(ID, Reason, Client, ClientContext). diff --git a/apps/fistful/test/ff_routing_rule_SUITE.erl b/apps/fistful/test/ff_routing_rule_SUITE.erl index 2bbbb02..3be8191 100644 --- a/apps/fistful/test/ff_routing_rule_SUITE.erl +++ b/apps/fistful/test/ff_routing_rule_SUITE.erl @@ -1,7 +1,6 @@ -module(ff_routing_rule_SUITE). -include_lib("stdlib/include/assert.hrl"). --include_lib("damsel/include/dmsl_domain_thrift.hrl"). -include_lib("ff_cth/include/ct_domain.hrl"). %% Common test API @@ -21,7 +20,6 @@ -export([withdrawal_no_routes_found_test/1]). -export([withdrawal_rejected_by_prohibitions_table_test/1]). -export([withdrawal_ruleset_misconfig_test/1]). --export([withdrawal_rules_not_found_test/1]). %% Internal types @@ -47,8 +45,7 @@ groups() -> withdrawal_routes_found_test, withdrawal_no_routes_found_test, withdrawal_rejected_by_prohibitions_table_test, - withdrawal_ruleset_misconfig_test, - withdrawal_rules_not_found_test + withdrawal_ruleset_misconfig_test ]} ]. @@ -129,7 +126,7 @@ withdrawal_rejected_by_prohibitions_table_test(_C) -> ], #{ rejected_routes := [ - {_, ?trm(4), {'RoutingRule', <<"Candidate description">>}} + {_, ?trm(4), {'RoutingRule', undefined}} ] } }, @@ -148,18 +145,6 @@ withdrawal_ruleset_misconfig_test(_C) -> gather_routes(withdrawal_routing_rules, PaymentInstitutionID, VS) ). --spec withdrawal_rules_not_found_test(config()) -> test_return(). -withdrawal_rules_not_found_test(_C) -> - VS = make_varset(?cash(1000, <<"RUB">>), <<"12345">>), - PaymentInstitutionID = 2, - ?assertMatch( - { - [], - #{rejected_routes := []} - }, - gather_routes(withdrawal_routing_rules, PaymentInstitutionID, VS) - ). - %% make_varset(Cash, PartyID) -> diff --git a/docker-compose.yml b/docker-compose.yml index 829a685..c8a699c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -69,7 +69,7 @@ services: retries: 20 party-management: - image: ghcr.io/valitydev/party-management:sha-e456e24 + image: ghcr.io/valitydev/party-management:sha-f757b79 command: /opt/party-management/bin/party-management foreground depends_on: machinegun: diff --git a/rebar.config b/rebar.config index eb0d92c..7b84ec6 100644 --- a/rebar.config +++ b/rebar.config @@ -33,7 +33,6 @@ {scoper, {git, "https://github.com/valitydev/scoper.git", {branch, "master"}}}, {thrift, {git, "https://github.com/valitydev/thrift_erlang.git", {branch, "master"}}}, {woody, {git, "https://github.com/valitydev/woody_erlang.git", {branch, "master"}}}, - {woody_user_identity, {git, "https://github.com/valitydev/woody_erlang_user_identity.git", {branch, "master"}}}, {erl_health, {git, "https://github.com/valitydev/erlang-health.git", {branch, "master"}}}, {machinery, {git, "https://github.com/valitydev/machinery.git", {branch, "master"}}}, {damsel, {git, "https://github.com/valitydev/damsel.git", {branch, "master"}}}, diff --git a/rebar.lock b/rebar.lock index 1b12ce0..e3de393 100644 --- a/rebar.lock +++ b/rebar.lock @@ -53,7 +53,7 @@ {<<"parse_trans">>,{pkg,<<"parse_trans">>,<<"3.3.1">>},2}, {<<"party_client">>, {git,"https://github.com/valitydev/party-client-erlang.git", - {ref,"8fc5595c4c61c0fe3d2dc29a61f48ba94e9bdef7"}}, + {ref,"31850a63f6c00da7e10897b23298ad38f9bf448d"}}, 0}, {<<"prometheus">>,{pkg,<<"prometheus">>,<<"4.8.1">>},0}, {<<"prometheus_cowboy">>,{pkg,<<"prometheus_cowboy">>,<<"0.1.8">>},0}, @@ -85,10 +85,6 @@ {<<"woody">>, {git,"https://github.com/valitydev/woody_erlang.git", {ref,"6f818c57e3b19f96260b1f968115c9bc5bcad4d2"}}, - 0}, - {<<"woody_user_identity">>, - {git,"https://github.com/valitydev/woody_erlang_user_identity.git", - {ref,"a480762fea8d7c08f105fb39ca809482b6cb042e"}}, 0}]}. [ {pkg_hash,[