TD-572: Get payment routes limit values (#79)

* TD-572. impelementation get_limit

* TD-572: add test for get_limits

* TD-572: fix formatting

* TD-572: add test for exception

* TD-572: change limits output format

* TD-572: fix spec

* TD-572: fix naming, add test

---------

Co-authored-by: anatoliy.losev <losto@nix>
This commit is contained in:
ttt161 2023-05-19 17:43:59 +03:00 committed by GitHub
parent e87b1e2ccf
commit 9d46f1fbd7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 113 additions and 25 deletions

1
.gitignore vendored
View File

@ -13,3 +13,4 @@ tags
# make stuff # make stuff
/.image.* /.image.*
Makefile.env Makefile.env
*.iml

View File

@ -241,7 +241,11 @@ handle_function_('Repair', {InvoiceID, Changes, Action, Params}, _Opts) ->
repair(InvoiceID, {changes, Changes, Action, Params}); repair(InvoiceID, {changes, Changes, Action, Params});
handle_function_('RepairWithScenario', {InvoiceID, Scenario}, _Opts) -> handle_function_('RepairWithScenario', {InvoiceID, Scenario}, _Opts) ->
_ = set_invoicing_meta(InvoiceID), _ = set_invoicing_meta(InvoiceID),
repair(InvoiceID, {scenario, Scenario}). repair(InvoiceID, {scenario, Scenario});
handle_function_('GetPaymentRoutesLimitValues', {InvoiceID, PaymentID}, _Opts) ->
_ = set_invoicing_meta(InvoiceID, PaymentID),
St = get_state(InvoiceID),
hg_invoice_payment:get_limit_values(get_payment_session(PaymentID, St), get_payment_opts(St)).
maybe_allocation(undefined, _Cost, _MerchantTerms, _Party, _Shop) -> maybe_allocation(undefined, _Cost, _MerchantTerms, _Party, _Shop) ->
undefined; undefined;

View File

@ -83,6 +83,7 @@
-export([create_session_event_context/3]). -export([create_session_event_context/3]).
-export([add_session/3]). -export([add_session/3]).
-export([accrue_status_timing/3]). -export([accrue_status_timing/3]).
-export([get_limit_values/2]).
%% Machine like %% Machine like
@ -205,6 +206,7 @@
-type recurrent_paytool_service_terms() :: dmsl_domain_thrift:'RecurrentPaytoolsServiceTerms'(). -type recurrent_paytool_service_terms() :: dmsl_domain_thrift:'RecurrentPaytoolsServiceTerms'().
-type session() :: hg_session:t(). -type session() :: hg_session:t().
-type payment_plan_id() :: hg_accounting:plan_id(). -type payment_plan_id() :: hg_accounting:plan_id().
-type route_limit_context() :: dmsl_payproc_thrift:'RouteLimitContext'().
-type opts() :: #{ -type opts() :: #{
party => party(), party => party(),
@ -2243,6 +2245,26 @@ process_risk_score(Action, St) ->
-spec process_routing(action(), st()) -> machine_result(). -spec process_routing(action(), st()) -> machine_result().
process_routing(Action, St) -> process_routing(Action, St) ->
{PaymentInstitution, VS, Revision} = route_args(St),
try
AllRoutes = get_candidates(PaymentInstitution, VS, Revision, St),
AvailableRoutes = filter_out_attempted_routes(AllRoutes, St),
%% Since this is routing step then current attempt is not yet accounted for in `St`.
Iter = get_iter(St) + 1,
Events = handle_gathered_route_result(
filter_limit_overflow_routes(AvailableRoutes, VS, Iter, St),
[hg_routing:to_payment_route(R) || R <- AllRoutes],
[hg_routing:to_payment_route(R) || R <- AvailableRoutes],
Revision,
St
),
{next, {Events, hg_machine_action:set_timeout(0, Action)}}
catch
throw:{no_route_found, Reason} ->
handle_choose_route_error(Reason, [], St, Action)
end.
route_args(St) ->
Opts = get_opts(St), Opts = get_opts(St),
Revision = get_payment_revision(St), Revision = get_payment_revision(St),
Payment = get_payment(St), Payment = get_payment(St),
@ -2253,29 +2275,15 @@ process_routing(Action, St) ->
VS2 = collect_refund_varset(MerchantTerms#domain_PaymentsServiceTerms.refunds, PaymentTool, VS1), VS2 = collect_refund_varset(MerchantTerms#domain_PaymentsServiceTerms.refunds, PaymentTool, VS1),
VS3 = collect_chargeback_varset(MerchantTerms#domain_PaymentsServiceTerms.chargebacks, VS2), VS3 = collect_chargeback_varset(MerchantTerms#domain_PaymentsServiceTerms.chargebacks, VS2),
PaymentInstitution = hg_payment_institution:compute_payment_institution(PaymentInstitutionRef, VS1, Revision), PaymentInstitution = hg_payment_institution:compute_payment_institution(PaymentInstitutionRef, VS1, Revision),
try {PaymentInstitution, VS3, Revision}.
Payer = get_payment_payer(St),
AllRoutes = get_candidates(PaymentInstitution, VS, Revision, St) ->
case get_predefined_route(Payer) of Payer = get_payment_payer(St),
{ok, PaymentRoute} -> case get_predefined_route(Payer) of
[hg_routing:from_payment_route(PaymentRoute)]; {ok, PaymentRoute} ->
undefined -> [hg_routing:from_payment_route(PaymentRoute)];
gather_routes(PaymentInstitution, VS3, Revision, St) undefined ->
end, gather_routes(PaymentInstitution, VS, Revision, St)
AvailableRoutes = filter_out_attempted_routes(AllRoutes, St),
%% Since this is routing step then current attempt is not yet accounted for in `St`.
Iter = get_iter(St) + 1,
Events = handle_gathered_route_result(
filter_limit_overflow_routes(AvailableRoutes, VS3, Iter, St),
[hg_routing:to_payment_route(R) || R <- AllRoutes],
[hg_routing:to_payment_route(R) || R <- AvailableRoutes],
Revision,
St
),
{next, {Events, hg_machine_action:set_timeout(0, Action)}}
catch
throw:{no_route_found, Reason} ->
handle_choose_route_error(Reason, [], St, Action)
end. end.
filter_out_attempted_routes(Routes, #st{routes = AttemptedRoutes}) -> filter_out_attempted_routes(Routes, #st{routes = AttemptedRoutes}) ->
@ -3502,6 +3510,28 @@ accrue_status_timing(Name, Opts, #st{timings = Timings}) ->
EventTime = define_event_timestamp(Opts), EventTime = define_event_timestamp(Opts),
hg_timings:mark(Name, EventTime, hg_timings:accrue(Name, started, EventTime, Timings)). hg_timings:mark(Name, EventTime, hg_timings:accrue(Name, started, EventTime, Timings)).
-spec get_limit_values(st()) -> route_limit_context().
get_limit_values(St) ->
{PaymentInstitution, VS, Revision} = route_args(St),
Routes = get_candidates(PaymentInstitution, VS, Revision, St),
Payment = get_payment(St),
Invoice = get_invoice(get_opts(St)),
lists:foldl(
fun(Route, Acc) ->
PaymentRoute = hg_routing:to_payment_route(Route),
ProviderTerms = hg_routing:get_payment_terms(PaymentRoute, VS, Revision),
TurnoverLimits = get_turnover_limits(ProviderTerms),
TurnoverLimitValues = hg_limiter:get_limit_values(TurnoverLimits, Invoice, Payment, PaymentRoute),
Acc#{PaymentRoute => TurnoverLimitValues}
end,
#{},
Routes
).
-spec get_limit_values(st(), opts()) -> route_limit_context().
get_limit_values(St, Opts) ->
get_limit_values(St#st{opts = Opts}).
try_accrue_waiting_timing(Opts, #st{payment = Payment, timings = Timings}) -> try_accrue_waiting_timing(Opts, #st{payment = Payment, timings = Timings}) ->
case get_payment_flow(Payment) of case get_payment_flow(Payment) of
?invoice_payment_flow_instant() -> ?invoice_payment_flow_instant() ->

View File

@ -1,5 +1,6 @@
-module(hg_limiter). -module(hg_limiter).
-include_lib("damsel/include/dmsl_payproc_thrift.hrl").
-include_lib("damsel/include/dmsl_domain_thrift.hrl"). -include_lib("damsel/include/dmsl_domain_thrift.hrl").
-include_lib("damsel/include/dmsl_base_thrift.hrl"). -include_lib("damsel/include/dmsl_base_thrift.hrl").
-include_lib("limiter_proto/include/limproto_base_thrift.hrl"). -include_lib("limiter_proto/include/limproto_base_thrift.hrl").
@ -14,6 +15,7 @@
-type refund() :: hg_invoice_payment:domain_refund(). -type refund() :: hg_invoice_payment:domain_refund().
-type cash() :: dmsl_domain_thrift:'Cash'(). -type cash() :: dmsl_domain_thrift:'Cash'().
-type handling_flag() :: ignore_business_error. -type handling_flag() :: ignore_business_error.
-type turnover_limit_value() :: dmsl_payproc_thrift:'TurnoverLimitValue'().
-type change_queue() :: [hg_limiter_client:limit_change()]. -type change_queue() :: [hg_limiter_client:limit_change()].
@ -25,6 +27,7 @@
-export([commit_refund_limits/5]). -export([commit_refund_limits/5]).
-export([rollback_payment_limits/6]). -export([rollback_payment_limits/6]).
-export([rollback_refund_limits/5]). -export([rollback_refund_limits/5]).
-export([get_limit_values/4]).
-define(route(ProviderRef, TerminalRef), #domain_PaymentRoute{ -define(route(ProviderRef, TerminalRef), #domain_PaymentRoute{
provider = ProviderRef, provider = ProviderRef,
@ -39,6 +42,23 @@ get_turnover_limits({value, Limits}) ->
get_turnover_limits(Ambiguous) -> get_turnover_limits(Ambiguous) ->
error({misconfiguration, {'Could not reduce selector to a value', Ambiguous}}). error({misconfiguration, {'Could not reduce selector to a value', Ambiguous}}).
-spec get_limit_values([turnover_limit()], invoice(), payment(), route()) -> [turnover_limit_value()].
get_limit_values(TurnoverLimits, Invoice, Payment, Route) ->
Context = gen_limit_context(Invoice, Payment, Route),
lists:foldl(
fun(TurnoverLimit, Acc) ->
#domain_TurnoverLimit{id = LimitID, domain_revision = Version} = TurnoverLimit,
Clock = get_latest_clock(),
Limit = hg_limiter_client:get(LimitID, Version, Clock, Context),
#limiter_Limit{
amount = LimiterAmount
} = Limit,
[#payproc_TurnoverLimitValue{limit = TurnoverLimit, value = LimiterAmount} | Acc]
end,
[],
TurnoverLimits
).
-spec check_limits([turnover_limit()], invoice(), payment(), route()) -> -spec check_limits([turnover_limit()], invoice(), payment(), route()) ->
{ok, [hg_limiter_client:limit()]} {ok, [hg_limiter_client:limit()]}
| {error, {limit_overflow, [binary()]}}. | {error, {limit_overflow, [binary()]}}.

View File

@ -40,6 +40,7 @@
-export([register_payment_customer_payer_success/1]). -export([register_payment_customer_payer_success/1]).
-export([payment_limit_success/1]). -export([payment_limit_success/1]).
-export([payment_routes_limit_values/1]).
-export([register_payment_limit_success/1]). -export([register_payment_limit_success/1]).
-export([payment_limit_other_shop_success/1]). -export([payment_limit_other_shop_success/1]).
-export([payment_limit_overflow/1]). -export([payment_limit_overflow/1]).
@ -383,6 +384,7 @@ groups() ->
{operation_limits, [], [ {operation_limits, [], [
payment_limit_success, payment_limit_success,
payment_routes_limit_values,
register_payment_limit_success, register_payment_limit_success,
payment_limit_other_shop_success, payment_limit_other_shop_success,
payment_limit_overflow, payment_limit_overflow,
@ -1254,6 +1256,30 @@ payment_limit_success(C) ->
[?payment_state(_Payment)] [?payment_state(_Payment)]
) = create_payment(PartyID, ShopID, 10000, Client, ?pmt_sys(<<"visa-ref">>)). ) = create_payment(PartyID, ShopID, 10000, Client, ?pmt_sys(<<"visa-ref">>)).
-spec payment_routes_limit_values(config()) -> test_return().
payment_routes_limit_values(C) ->
RootUrl = cfg(root_url, C),
PartyClient = cfg(party_client, C),
#{party_id := PartyID} = cfg(limits, C),
ShopID = hg_ct_helper:create_shop(PartyID, ?cat(8), <<"RUB">>, ?tmpl(1), ?pinst(1), PartyClient),
Client = hg_client_invoicing:start_link(hg_ct_helper:create_client(RootUrl)),
#payproc_Invoice{
invoice = #domain_Invoice{id = InvoiceId},
payments = [
#payproc_InvoicePayment{payment = #domain_InvoicePayment{id = PaymentId}}
]
} = create_payment(PartyID, ShopID, 10000, Client, ?pmt_sys(<<"visa-ref">>)),
Route = ?route(?prv(5), ?trm(12)),
#{
Route := [
#payproc_TurnoverLimitValue{
limit = #domain_TurnoverLimit{id = ?LIMIT_ID, upper_boundary = ?LIMIT_UPPER_BOUNDARY},
value = 10000
}
]
} = hg_client_invoicing:get_limit_values(InvoiceId, PaymentId, Client).
-spec register_payment_limit_success(config()) -> test_return(). -spec register_payment_limit_success(config()) -> test_return().
register_payment_limit_success(C0) -> register_payment_limit_success(C0) ->
Client = cfg(client, C0), Client = cfg(client, C0),

View File

@ -15,6 +15,7 @@
-export([rescind/3]). -export([rescind/3]).
-export([repair/5]). -export([repair/5]).
-export([repair_scenario/3]). -export([repair_scenario/3]).
-export([get_limit_values/3]).
-export([create_invoice_adjustment/3]). -export([create_invoice_adjustment/3]).
-export([get_invoice_adjustment/3]). -export([get_invoice_adjustment/3]).
@ -98,6 +99,8 @@
-type event_range() :: dmsl_payproc_thrift:'EventRange'(). -type event_range() :: dmsl_payproc_thrift:'EventRange'().
-type party_revision_param() :: dmsl_payproc_thrift:'PartyRevisionParam'(). -type party_revision_param() :: dmsl_payproc_thrift:'PartyRevisionParam'().
-type route_limit_context() :: dmsl_payproc_thrift:'RouteLimitContext'().
-spec start(hg_client_api:t()) -> pid(). -spec start(hg_client_api:t()) -> pid().
start(ApiClient) -> start(ApiClient) ->
start(start, ApiClient). start(start, ApiClient).
@ -150,6 +153,10 @@ repair(InvoiceID, Changes, Action, Params, Client) ->
repair_scenario(InvoiceID, Scenario, Client) -> repair_scenario(InvoiceID, Scenario, Client) ->
map_result_error(gen_server:call(Client, {call, 'RepairWithScenario', [InvoiceID, Scenario]})). map_result_error(gen_server:call(Client, {call, 'RepairWithScenario', [InvoiceID, Scenario]})).
-spec get_limit_values(invoice_id(), payment_id(), pid()) -> route_limit_context() | woody_error:business_error().
get_limit_values(InvoiceID, PaymentID, Client) ->
map_result_error(gen_server:call(Client, {call, 'GetPaymentRoutesLimitValues', [InvoiceID, PaymentID]})).
-spec create_invoice_adjustment(invoice_id(), invoice_adjustment_params(), pid()) -> -spec create_invoice_adjustment(invoice_id(), invoice_adjustment_params(), pid()) ->
invoice_adjustment() | woody_error:business_error(). invoice_adjustment() | woody_error:business_error().
create_invoice_adjustment(InvoiceID, Params, Client) -> create_invoice_adjustment(InvoiceID, Params, Client) ->

View File

@ -17,7 +17,7 @@
{<<"cowlib">>,{pkg,<<"cowlib">>,<<"2.11.0">>},2}, {<<"cowlib">>,{pkg,<<"cowlib">>,<<"2.11.0">>},2},
{<<"damsel">>, {<<"damsel">>,
{git,"https://github.com/valitydev/damsel.git", {git,"https://github.com/valitydev/damsel.git",
{ref,"03bf41075c39b6731c5ed200d5c4b0faaee9d937"}}, {ref,"52dbefd54fd68d0c04f8ded5734b27635e939c60"}},
0}, 0},
{<<"dmt_client">>, {<<"dmt_client">>,
{git,"https://github.com/valitydev/dmt-client.git", {git,"https://github.com/valitydev/dmt-client.git",