TD-550: Limit hold exception to reject route (#64)

* Refactor `hg_routing:to_rejected_route/2`
* Adds new limiter hold exceptions handling
* Adds rejected routes log message on limit overflow
* Adds testcase for limit hold fail with multiple route candidates
This commit is contained in:
Aleksey Kashapov 2023-04-17 11:30:24 +03:00 committed by GitHub
parent de0c01cfe4
commit bfccd439a3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 316 additions and 73 deletions

View File

@ -21,6 +21,8 @@
-include_lib("damsel/include/dmsl_payproc_thrift.hrl").
-include_lib("damsel/include/dmsl_payproc_error_thrift.hrl").
-include_lib("limiter_proto/include/limproto_limiter_thrift.hrl").
-include_lib("hellgate/include/domain.hrl").
-include_lib("hellgate/include/allocation.hrl").
@ -839,6 +841,22 @@ log_rejected_routes(no_route_found, RejectedRoutes, Varset) ->
logger:get_process_metadata()
),
ok;
log_rejected_routes(limit_hold_reject, RejectedRoutes, _Varset) ->
_ = logger:log(
warning,
"Limiter hold error caused route candidates to be rejected: ~p",
[RejectedRoutes],
logger:get_process_metadata()
),
ok;
log_rejected_routes(limit_overflow_reject, RejectedRoutes, _Varset) ->
_ = logger:log(
info,
"Limit overflow caused route candidates to be rejected: ~p",
[RejectedRoutes],
logger:get_process_metadata()
),
ok;
log_rejected_routes(rejected_route_found, RejectedRoutes, Varset) ->
_ = logger:log(
info,
@ -2221,15 +2239,8 @@ process_routing(Action, St) ->
CreatedAt = get_payment_created_at(Payment),
PaymentInstitutionRef = get_payment_institution_ref(Opts),
MerchantTerms = get_merchant_payments_terms(Opts, Revision, CreatedAt, VS1),
VS2 = collect_refund_varset(
MerchantTerms#domain_PaymentsServiceTerms.refunds,
PaymentTool,
VS1
),
VS3 = collect_chargeback_varset(
MerchantTerms#domain_PaymentsServiceTerms.chargebacks,
VS2
),
VS2 = collect_refund_varset(MerchantTerms#domain_PaymentsServiceTerms.refunds, PaymentTool, VS1),
VS3 = collect_chargeback_varset(MerchantTerms#domain_PaymentsServiceTerms.chargebacks, VS2),
PaymentInstitution = hg_payment_institution:compute_payment_institution(PaymentInstitutionRef, VS1, Revision),
try
Payer = get_payment_payer(St),
@ -2243,8 +2254,6 @@ process_routing(Action, 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,
%% TODO Investigate necessity of so many `hg_routing:to_payment_route/1` calls.
%% Here, in `filter_out_attempted_routes/2` and in `filter_limit_overflow_routes/3`.
Events = handle_gathered_route_result(
filter_limit_overflow_routes(AvailableRoutes, VS3, Iter, St),
[hg_routing:to_payment_route(R) || R <- AllRoutes],
@ -2525,7 +2534,7 @@ process_result({payment, processing_accounter}, Action, St) ->
process_result({payment, routing_failure}, Action, St = #st{failure = Failure}) ->
NewAction = hg_machine_action:set_timeout(0, Action),
Routes = get_candidate_routes(St),
_ = rollback_payment_limits(Routes, get_iter(St), St),
_ = rollback_payment_limits(Routes, get_iter(St), St, [ignore_business_error]),
{done, {[?payment_status_changed(?failed(Failure))], NewAction}};
process_result({payment, processing_failure}, Action, St = #st{failure = Failure}) ->
NewAction = hg_machine_action:set_timeout(0, Action),
@ -2745,55 +2754,80 @@ get_provider_terms(St, Revision) ->
hg_routing:get_payment_terms(Route, VS1, Revision).
filter_limit_overflow_routes(Routes, VS, Iter, St) ->
ok = hold_limit_routes(Routes, VS, Iter, St),
RejectedContext = #{rejected_routes => []},
case get_limit_overflow_routes(Routes, VS, St, RejectedContext) of
{UsableRoutes, _HoldRejectedRoutes} = hold_limit_routes(Routes, VS, Iter, St),
case get_limit_overflow_routes(UsableRoutes, VS, St) of
{[], _RejectedRoutesOut} ->
{error, not_found};
{RoutesNoOverflow, _} ->
{ok, RoutesNoOverflow}
end.
get_limit_overflow_routes(Routes, VS, St, RejectedRoutes) ->
get_limit_overflow_routes(Routes, VS, St) ->
Opts = get_opts(St),
Revision = get_payment_revision(St),
Payment = get_payment(St),
Invoice = get_invoice(Opts),
lists:foldl(
fun(Route, {RoutesNoOverflowIn, RejectedIn}) ->
{_Routes, RejectedRoutes} =
Result = lists:foldl(
fun(Route, {RoutesNoOverflowIn, RejectedIn}) ->
PaymentRoute = hg_routing:to_payment_route(Route),
ProviderTerms = hg_routing:get_payment_terms(PaymentRoute, VS, Revision),
TurnoverLimits = get_turnover_limits(ProviderTerms),
case hg_limiter:check_limits(TurnoverLimits, Invoice, Payment, PaymentRoute) of
{ok, _} ->
{[Route | RoutesNoOverflowIn], RejectedIn};
{error, {limit_overflow, IDs}} ->
RejectedRoute = hg_routing:to_rejected_route(Route, {'LimitOverflow', IDs}),
{RoutesNoOverflowIn, [RejectedRoute | RejectedIn]}
end
end,
{[], []},
Routes
),
erlang:length(RejectedRoutes) > 0 andalso
log_rejected_routes(limit_overflow_reject, RejectedRoutes, VS),
Result.
-spec hold_limit_routes([hg_routing:route()], hg_varset:varset(), pos_integer(), st()) ->
{[hg_routing:route()], [hg_routing:rejected_route()]}.
hold_limit_routes(Routes0, VS, Iter, St) ->
Opts = get_opts(St),
Revision = get_payment_revision(St),
Payment = get_payment(St),
Invoice = get_invoice(Opts),
{Routes1, Rejected} = lists:foldl(
fun(Route, {LimitHeldRoutes, RejectedRoutes} = Acc) ->
PaymentRoute = hg_routing:to_payment_route(Route),
ProviderTerms = hg_routing:get_payment_terms(PaymentRoute, VS, Revision),
TurnoverLimits = get_turnover_limits(ProviderTerms),
case hg_limiter:check_limits(TurnoverLimits, Invoice, Payment, PaymentRoute) of
{ok, _} ->
{[Route | RoutesNoOverflowIn], RejectedIn};
{error, {limit_overflow, IDs}} ->
PRef = hg_routing:provider_ref(Route),
TRef = hg_routing:terminal_ref(Route),
RejectedOut = [{PRef, TRef, {'LimitOverflow', IDs}} | RejectedIn],
{RoutesNoOverflowIn, RejectedOut}
try
ok = hg_limiter:hold_payment_limits(TurnoverLimits, PaymentRoute, Iter, Invoice, Payment),
{[Route | LimitHeldRoutes], RejectedRoutes}
catch
error:(#limiter_InvalidOperationCurrency{} = LimiterError) ->
do_reject_route(LimiterError, Route, TurnoverLimits, Acc);
error:(#limiter_OperationContextNotSupported{} = LimiterError) ->
do_reject_route(LimiterError, Route, TurnoverLimits, Acc);
error:(#limiter_PaymentToolNotSupported{} = LimiterError) ->
do_reject_route(LimiterError, Route, TurnoverLimits, Acc)
end
end,
{[], RejectedRoutes},
Routes
).
{[], []},
Routes0
),
erlang:length(Rejected) > 0 andalso
log_rejected_routes(limit_hold_reject, Rejected, VS),
{lists:reverse(Routes1), Rejected}.
hold_limit_routes(Routes, VS, Iter, St) ->
Opts = get_opts(St),
Revision = get_payment_revision(St),
Payment = get_payment(St),
Invoice = get_invoice(Opts),
lists:foreach(
fun(Route) ->
PaymentRoute = hg_routing:to_payment_route(Route),
ProviderTerms = hg_routing:get_payment_terms(PaymentRoute, VS, Revision),
TurnoverLimits = get_turnover_limits(ProviderTerms),
ok = hg_limiter:hold_payment_limits(TurnoverLimits, PaymentRoute, Iter, Invoice, Payment)
end,
Routes
).
do_reject_route(LimiterError, Route, TurnoverLimits, {LimitHeldRoutes, RejectedRoutes}) ->
Reason = {'LimitHoldError', [T#domain_TurnoverLimit.id || T <- TurnoverLimits], LimiterError},
RejectedRoute = hg_routing:to_rejected_route(Route, Reason),
{LimitHeldRoutes, [RejectedRoute | RejectedRoutes]}.
rollback_payment_limits(Routes, Iter, St) ->
rollback_payment_limits(Routes, Iter, St, []).
rollback_payment_limits(Routes, Iter, St, Flags) ->
Opts = get_opts(St),
Revision = get_payment_revision(St),
Payment = get_payment(St),
@ -2803,7 +2837,7 @@ rollback_payment_limits(Routes, Iter, St) ->
fun(Route) ->
ProviderTerms = hg_routing:get_payment_terms(Route, VS, Revision),
TurnoverLimits = get_turnover_limits(ProviderTerms),
ok = hg_limiter:rollback_payment_limits(TurnoverLimits, Route, Iter, Invoice, Payment)
ok = hg_limiter:rollback_payment_limits(TurnoverLimits, Route, Iter, Invoice, Payment, Flags)
end,
Routes
).
@ -2812,7 +2846,7 @@ rollback_unused_payment_limits(St) ->
Route = get_route(St),
Routes = get_candidate_routes(St),
UnUsedRoutes = Routes -- [Route],
rollback_payment_limits(UnUsedRoutes, get_iter(St), St).
rollback_payment_limits(UnUsedRoutes, get_iter(St), St, [ignore_business_error]).
get_turnover_limits(ProviderTerms) ->
TurnoverLimitSelector = ProviderTerms#domain_PaymentsProvisionTerms.turnover_limits,

View File

@ -13,6 +13,7 @@
-type route() :: hg_routing:payment_route().
-type refund() :: hg_invoice_payment:domain_refund().
-type cash() :: dmsl_domain_thrift:'Cash'().
-type handling_flag() :: ignore_business_error.
-type change_queue() :: [hg_limiter_client:limit_change()].
@ -22,7 +23,7 @@
-export([hold_refund_limits/5]).
-export([commit_payment_limits/6]).
-export([commit_refund_limits/5]).
-export([rollback_payment_limits/5]).
-export([rollback_payment_limits/6]).
-export([rollback_refund_limits/5]).
-define(route(ProviderRef, TerminalRef), #domain_PaymentRoute{
@ -104,55 +105,64 @@ commit_refund_limits(TurnoverLimits, Invoice, Payment, Refund, Route) ->
Context = gen_limit_refund_context(Invoice, Payment, Refund, Route),
commit(LimitChanges, get_latest_clock(), Context).
-spec rollback_payment_limits([turnover_limit()], route(), pos_integer(), invoice(), payment()) -> ok.
rollback_payment_limits(TurnoverLimits, Route, Iter, Invoice, Payment) ->
-spec rollback_payment_limits([turnover_limit()], route(), pos_integer(), invoice(), payment(), [handling_flag()]) ->
ok.
rollback_payment_limits(TurnoverLimits, Route, Iter, Invoice, Payment, Flags) ->
ChangeIDs = [
construct_payment_change_id(Route, Iter, Invoice, Payment),
construct_payment_change_id(Route, Iter, Invoice, Payment, legacy)
],
LimitChanges = gen_limit_changes(TurnoverLimits, ChangeIDs),
Context = gen_limit_context(Invoice, Payment, Route),
rollback(LimitChanges, get_latest_clock(), Context).
rollback(LimitChanges, get_latest_clock(), Context, Flags).
-spec rollback_refund_limits([turnover_limit()], invoice(), payment(), refund(), route()) -> ok.
rollback_refund_limits(TurnoverLimits, Invoice, Payment, Refund, Route) ->
ChangeIDs = [construct_refund_change_id(Invoice, Payment, Refund)],
LimitChanges = gen_limit_changes(TurnoverLimits, ChangeIDs),
Context = gen_limit_refund_context(Invoice, Payment, Refund, Route),
rollback(LimitChanges, get_latest_clock(), Context).
rollback(LimitChanges, get_latest_clock(), Context, []).
-spec hold([change_queue()], hg_limiter_client:clock(), hg_limiter_client:context()) -> ok.
hold(LimitChanges, Clock, Context) ->
process_changes(LimitChanges, fun hg_limiter_client:hold/3, Clock, Context).
process_changes(LimitChanges, fun hg_limiter_client:hold/3, Clock, Context, []).
-spec commit([change_queue()], hg_limiter_client:clock(), hg_limiter_client:context()) -> ok.
commit(LimitChanges, Clock, Context) ->
process_changes(LimitChanges, fun hg_limiter_client:commit/3, Clock, Context).
process_changes(LimitChanges, fun hg_limiter_client:commit/3, Clock, Context, []).
-spec rollback([change_queue()], hg_limiter_client:clock(), hg_limiter_client:context()) -> ok.
rollback(LimitChanges, Clock, Context) ->
process_changes(LimitChanges, fun hg_limiter_client:rollback/3, Clock, Context).
-spec rollback([change_queue()], hg_limiter_client:clock(), hg_limiter_client:context(), [handling_flag()]) -> ok.
rollback(LimitChanges, Clock, Context, Flags) ->
process_changes(LimitChanges, fun hg_limiter_client:rollback/3, Clock, Context, Flags).
process_changes(LimitChangesQueues, WithFun, Clock, Context) ->
process_changes(LimitChangesQueues, WithFun, Clock, Context, Flags) ->
lists:foreach(
fun(LimitChangesQueue) ->
process_changes_try_wrap(LimitChangesQueue, WithFun, Clock, Context)
process_changes_try_wrap(LimitChangesQueue, WithFun, Clock, Context, Flags)
end,
LimitChangesQueues
).
process_changes_try_wrap([LimitChange], WithFun, Clock, Context) ->
process_changes_try_wrap([LimitChange], WithFun, Clock, Context, _Flags) ->
WithFun(LimitChange, Clock, Context);
process_changes_try_wrap([LimitChange | OtherLimitChanges], WithFun, Clock, Context) ->
process_changes_try_wrap([LimitChange | OtherLimitChanges], WithFun, Clock, Context, Flags) ->
IgnoreBusinessError = lists:member(ignore_business_error, Flags),
#limiter_LimitChange{change_id = ChangeID} = LimitChange,
try
WithFun(LimitChange, Clock, Context)
catch
%% Very specific error to crutch around
error:#base_InvalidRequest{errors = [<<"Posting plan not found: ", ChangeID/binary>>]} ->
process_changes_try_wrap(OtherLimitChanges, WithFun, Clock, Context)
process_changes_try_wrap(OtherLimitChanges, WithFun, Clock, Context, Flags);
Class:Reason:Stacktrace ->
handle_caught_exception(Class, Reason, Stacktrace, IgnoreBusinessError)
end.
handle_caught_exception(error, #limiter_InvalidOperationCurrency{}, _Stacktrace, true) -> ok;
handle_caught_exception(error, #limiter_OperationContextNotSupported{}, _Stacktrace, true) -> ok;
handle_caught_exception(error, #limiter_PaymentToolNotSupported{}, _Stacktrace, true) -> ok;
handle_caught_exception(Class, Reason, Stacktrace, _IgnoreBusinessError) -> erlang:raise(Class, Reason, Stacktrace).
gen_limit_context(Invoice, Payment, Route) ->
gen_limit_context(Invoice, Payment, Route, undefined).

View File

@ -17,6 +17,7 @@
-export([new/4]).
-export([new/5]).
-export([to_payment_route/1]).
-export([to_rejected_route/2]).
-export([provider_ref/1]).
-export([terminal_ref/1]).
@ -113,6 +114,7 @@
-export_type([route/0]).
-export_type([payment_route/0]).
-export_type([rejected_route/0]).
-export_type([route_predestination/0]).
%% Route accessors
@ -171,6 +173,10 @@ from_payment_route(Route) ->
to_payment_route(#route{} = Route) ->
?route(provider_ref(Route), terminal_ref(Route)).
-spec to_rejected_route(route(), term()) -> rejected_route().
to_rejected_route(Route, Reason) ->
{provider_ref(Route), terminal_ref(Route), Reason}.
-spec set_weight(integer(), route()) -> route().
set_weight(Weight, Route) ->
Route#route{weight = Weight}.

View File

@ -47,6 +47,10 @@
-export([payment_partial_capture_limit_success/1]).
-export([switch_provider_after_limit_overflow/1]).
-export([limit_not_found/1]).
-export([limit_hold_currency_error/1]).
-export([limit_hold_operation_not_supported/1]).
-export([limit_hold_payment_tool_not_supported/1]).
-export([limit_hold_two_routes_failure/1]).
-export([processing_deadline_reached_test/1]).
-export([payment_success_empty_cvv/1]).
@ -381,7 +385,11 @@ groups() ->
payment_partial_capture_limit_success,
switch_provider_after_limit_overflow,
limit_not_found,
refund_limit_success
refund_limit_success,
limit_hold_currency_error,
limit_hold_operation_not_supported,
limit_hold_payment_tool_not_supported,
limit_hold_two_routes_failure
]},
{refunds, [], [
@ -686,6 +694,14 @@ init_per_testcase(payment_cascade_fail_wo_available_attempt_limit, C) ->
override_domain_fixture(fun merchant_payments_service_terms_wo_attempt_limit/1, C);
init_per_testcase(payment_cascade_success, C) ->
override_domain_fixture(fun routes_ruleset_w_failing_provider_fixture/1, C);
init_per_testcase(limit_hold_currency_error, C) ->
override_domain_fixture(fun patch_limit_config_w_invalid_currency/1, C);
init_per_testcase(limit_hold_operation_not_supported, C) ->
override_domain_fixture(fun patch_limit_config_for_withdrawal/1, C);
init_per_testcase(limit_hold_payment_tool_not_supported, C) ->
override_domain_fixture(fun patch_with_unsupported_payment_tool/1, C);
init_per_testcase(limit_hold_two_routes_failure, C) ->
override_domain_fixture(fun patch_providers_limits_to_fail_and_overflow/1, C);
init_per_testcase(_Name, C) ->
init_per_testcase(C).
@ -1272,6 +1288,51 @@ payment_limit_overflow(C) ->
fun({no_route_found, {forbidden, _}}) -> ok end
).
-spec limit_hold_currency_error(config()) -> test_return().
limit_hold_currency_error(C) ->
payment_route_not_found(C).
-spec limit_hold_operation_not_supported(config()) -> test_return().
limit_hold_operation_not_supported(C) ->
payment_route_not_found(C).
-spec limit_hold_payment_tool_not_supported(config()) -> test_return().
limit_hold_payment_tool_not_supported(C) ->
{PaymentTool, Session} = hg_dummy_provider:make_payment_tool(crypto_currency, ?crypta(<<"bitcoin-ref">>)),
payment_route_not_found(PaymentTool, Session, C).
-spec limit_hold_two_routes_failure(config()) -> test_return().
limit_hold_two_routes_failure(C) ->
payment_route_not_found(C).
payment_route_not_found(C) ->
PmtSys = ?pmt_sys(<<"visa-ref">>),
{PaymentTool, Session} = hg_dummy_provider:make_payment_tool(no_preauth, PmtSys),
payment_route_not_found(PaymentTool, Session, C).
payment_route_not_found(PaymentTool, Session, 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)),
Cash = make_cash(10000, <<"RUB">>),
InvoiceParams = make_invoice_params(PartyID, ShopID, <<"rubberduck">>, make_due_date(10), Cash),
InvoiceID = create_invoice(InvoiceParams, Client),
?invoice_created(?invoice_w_status(?invoice_unpaid())) = next_change(InvoiceID, Client),
PaymentParams = make_payment_params(PaymentTool, Session, instant),
?payment_state(?payment(PaymentID)) = hg_client_invoicing:start_payment(InvoiceID, PaymentParams, Client),
_ = start_payment_ev(InvoiceID, Client),
?payment_ev(PaymentID, ?payment_rollback_started({failure, Failure})) =
next_change(InvoiceID, Client),
ok = payproc_errors:match(
'PaymentFailure',
Failure,
fun({no_route_found, _}) -> ok end
).
-spec switch_provider_after_limit_overflow(config()) -> test_return().
switch_provider_after_limit_overflow(C) ->
PmtSys = ?pmt_sys(<<"visa-ref">>),
@ -1749,6 +1810,113 @@ merchant_payments_service_terms_wo_attempt_limit(Revision) ->
routes_ruleset_w_failing_provider_fixture(Revision)
]).
patch_limit_config_w_invalid_currency(Revision) ->
NewRevision = hg_domain:update({limit_config, hg_limiter_helper:mk_config_object(?LIMIT_ID, <<"KEK">>)}),
[
change_terms_limit_config_version(Revision, NewRevision)
].
patch_limit_config_for_withdrawal(Revision) ->
NewRevision = hg_domain:update(
{limit_config,
hg_limiter_helper:mk_config_object(?LIMIT_ID, <<"RUB">>, hg_limiter_helper:mk_context_type(withdrawal))}
),
[
change_terms_limit_config_version(Revision, NewRevision)
].
patch_with_unsupported_payment_tool(Revision) ->
NewRevision = hg_domain:update(
{limit_config,
hg_limiter_helper:mk_config_object(
?LIMIT_ID,
<<"RUB">>,
hg_limiter_helper:mk_context_type(payment),
hg_limiter_helper:mk_scopes([shop, payment_tool])
)}
),
[
change_provider_payments_provision_terms(?prv(5), Revision, fun(PaymentsProvisionTerms) ->
PaymentsProvisionTerms#domain_PaymentsProvisionTerms{
turnover_limits =
{value, [
#domain_TurnoverLimit{
id = ?LIMIT_ID,
upper_boundary = ?LIMIT_UPPER_BOUNDARY,
domain_revision = NewRevision
}
]},
payment_methods =
{value,
?ordset([
?pmt(crypto_currency, ?crypta(<<"bitcoin-ref">>))
])}
}
end)
].
patch_providers_limits_to_fail_and_overflow(Revision) ->
%% 1. Must have two routes to different providers.
%% 2. Each provider must have different turnover limit.
%% 3. First of those turnover limits must fail on hold operation with business error.
%% 4. Second must get rejected due limit overflow.
NewRevision = hg_domain:update([
{limit_config,
hg_limiter_helper:mk_config_object(?LIMIT_ID, <<"RUB">>, hg_limiter_helper:mk_context_type(withdrawal))}
]),
[
hg_ct_fixture:construct_payment_routing_ruleset(
?ruleset(4),
<<"SubMain">>,
{candidates, [
%% Provider = ?prv(5)
?candidate({constant, true}, ?trm(12)),
%% Provider = ?prv(6)
?candidate({constant, true}, ?trm(13))
]}
),
change_terms_limit_config_version(Revision, ?prv(5), [
#domain_TurnoverLimit{
id = ?LIMIT_ID,
upper_boundary = ?LIMIT_UPPER_BOUNDARY,
domain_revision = NewRevision
}
]),
change_terms_limit_config_version(Revision, ?prv(6), [
#domain_TurnoverLimit{
id = ?LIMIT_ID2,
%% Every op will overflow!
upper_boundary = 0,
domain_revision = NewRevision
}
])
].
change_terms_limit_config_version(Revision, LimitConfigRevision) ->
change_terms_limit_config_version(Revision, ?prv(5), [
#domain_TurnoverLimit{
id = ?LIMIT_ID,
upper_boundary = ?LIMIT_UPPER_BOUNDARY,
domain_revision = LimitConfigRevision
}
]).
change_terms_limit_config_version(Revision, ProviderRef, TurnoverLimits) ->
change_provider_payments_provision_terms(ProviderRef, Revision, fun(PaymentsProvisionTerms) ->
PaymentsProvisionTerms#domain_PaymentsProvisionTerms{turnover_limits = {value, TurnoverLimits}}
end).
change_provider_payments_provision_terms(ProviderID, Revision, Changer) when is_function(Changer, 1) ->
Provider = #domain_Provider{terms = Terms} = hg_domain:get(Revision, {provider, ProviderID}),
Terms1 =
Terms#domain_ProvisionTermSet{
payments = Changer(Terms#domain_ProvisionTermSet.payments)
},
{provider, #domain_ProviderObject{
ref = ProviderID,
data = Provider#domain_Provider{terms = Terms1}
}}.
-spec payment_capture_failed(config()) -> test_return().
payment_capture_failed(C) ->
Client = cfg(client, C),

View File

@ -13,6 +13,9 @@
-export([assert_payment_limit_amount/3]).
-export([assert_payment_limit_amount/4]).
-export([get_payment_limit_amount/4]).
-export([mk_config_object/2, mk_config_object/3, mk_config_object/4]).
-export([mk_context_type/1]).
-export([mk_scopes/1]).
-type config() :: ct_suite:ct_config().
@ -23,10 +26,10 @@
-spec init_per_suite(config()) -> _.
init_per_suite(_Config) ->
_ = dmt_client:upsert({limit_config, limiter_mk_config_object(?LIMIT_ID)}),
_ = dmt_client:upsert({limit_config, limiter_mk_config_object(?LIMIT_ID2)}),
_ = dmt_client:upsert({limit_config, limiter_mk_config_object(?LIMIT_ID3)}),
_ = dmt_client:upsert({limit_config, limiter_mk_config_object(?LIMIT_ID4)}).
_ = dmt_client:upsert({limit_config, mk_config_object(?LIMIT_ID)}),
_ = dmt_client:upsert({limit_config, mk_config_object(?LIMIT_ID2)}),
_ = dmt_client:upsert({limit_config, mk_config_object(?LIMIT_ID3)}),
_ = dmt_client:upsert({limit_config, mk_config_object(?LIMIT_ID4)}).
-spec get_amount(_) -> pos_integer().
get_amount(#limiter_Limit{amount = Amount}) ->
@ -59,7 +62,19 @@ get_payment_limit_amount(LimitId, Version, Payment, Invoice) ->
},
hg_dummy_limiter:get(LimitId, Version, Context, hg_dummy_limiter:new()).
limiter_mk_config_object(LimitID) ->
mk_config_object(LimitID) ->
mk_config_object(LimitID, <<"RUB">>).
-spec mk_config_object(_, _) -> _.
mk_config_object(LimitID, Currency) ->
mk_config_object(LimitID, Currency, mk_context_type(payment)).
-spec mk_config_object(_, _, _) -> _.
mk_config_object(LimitID, Currency, ContextType) ->
mk_config_object(LimitID, Currency, ContextType, mk_scopes([shop])).
-spec mk_config_object(_, _, _, _) -> _.
mk_config_object(LimitID, Currency, ContextType, Scopes) ->
#domain_LimitConfigObject{
ref = #domain_LimitConfigRef{id = LimitID},
data = #limiter_config_LimitConfig{
@ -68,15 +83,25 @@ limiter_mk_config_object(LimitID) ->
started_at = <<"2000-01-01T00:00:00Z">>,
shard_size = 12,
time_range_type = {calendar, {month, #limiter_config_TimeRangeTypeCalendarMonth{}}},
context_type = {payment_processing, #limiter_config_LimitContextTypePaymentProcessing{}},
context_type = ContextType,
type =
{turnover, #limiter_config_LimitTypeTurnover{
metric = {amount, #limiter_config_LimitTurnoverAmount{currency = <<"RUB">>}}
metric = {amount, #limiter_config_LimitTurnoverAmount{currency = Currency}}
}},
scopes = [{shop, #limiter_config_LimitScopeEmptyDetails{}}],
scopes = Scopes,
description = <<"description">>,
op_behaviour = #limiter_config_OperationLimitBehaviour{
invoice_payment_refund = {subtraction, #limiter_config_Subtraction{}}
}
}
}.
-spec mk_context_type(payment | withdrawal) -> _.
mk_context_type(withdrawal) ->
{withdrawal_processing, #limiter_config_LimitContextTypeWithdrawalProcessing{}};
mk_context_type(payment) ->
{payment_processing, #limiter_config_LimitContextTypePaymentProcessing{}}.
-spec mk_scopes(_) -> _.
mk_scopes(ScopeTags) ->
ordsets:from_list([{Tag, #limiter_config_LimitScopeEmptyDetails{}} || Tag <- ScopeTags]).

View File

@ -39,7 +39,7 @@ services:
retries: 10
machinegun:
image: ghcr.io/valitydev/machinegun:sha-fb7fbf9
image: ghcr.io/valitydev/machinegun:sha-058bada
command: /opt/machinegun/bin/machinegun foreground
volumes:
- ./test/machinegun/config.yaml:/opt/machinegun/etc/config.yaml
@ -63,7 +63,7 @@ services:
retries: 10
limiter:
image: ghcr.io/valitydev/limiter:sha-3eff7dd
image: ghcr.io/valitydev/limiter:sha-2b8723b
command: /opt/limiter/bin/limiter foreground
depends_on:
machinegun:

View File

@ -17,7 +17,7 @@
{<<"cowlib">>,{pkg,<<"cowlib">>,<<"2.11.0">>},2},
{<<"damsel">>,
{git,"https://github.com/valitydev/damsel.git",
{ref,"cc95eab778addb9b4cb86b648c60dc87d2cec645"}},
{ref,"698c7d275fbe6a8f06f209638ede093f3134fc9b"}},
0},
{<<"dmt_client">>,
{git,"https://github.com/valitydev/dmt-client.git",
@ -45,7 +45,7 @@
{<<"jsx">>,{pkg,<<"jsx">>,<<"3.1.0">>},1},
{<<"limiter_proto">>,
{git,"https://github.com/valitydev/limiter-proto.git",
{ref,"9b76200a957c0e91bcdf6f16dfbab90d38a3f173"}},
{ref,"bbd2c0dce044dd5b4e424fc8e38a0023a1685a22"}},
0},
{<<"metrics">>,{pkg,<<"metrics">>,<<"1.0.1">>},2},
{<<"mg_proto">>,