From 9d46f1fbd7cca5d5796246509f91e547256d10b0 Mon Sep 17 00:00:00 2001 From: ttt161 <45654208+ttt161@users.noreply.github.com> Date: Fri, 19 May 2023 17:43:59 +0300 Subject: [PATCH] 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 --- .gitignore | 1 + apps/hellgate/src/hg_invoice.erl | 6 +- apps/hellgate/src/hg_invoice_payment.erl | 76 +++++++++++++------ apps/hellgate/src/hg_limiter.erl | 20 +++++ apps/hellgate/test/hg_invoice_tests_SUITE.erl | 26 +++++++ apps/hg_client/src/hg_client_invoicing.erl | 7 ++ rebar.lock | 2 +- 7 files changed, 113 insertions(+), 25 deletions(-) diff --git a/.gitignore b/.gitignore index e1ec754..8c9860c 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ tags # make stuff /.image.* Makefile.env +*.iml diff --git a/apps/hellgate/src/hg_invoice.erl b/apps/hellgate/src/hg_invoice.erl index bb07436..5f186b6 100644 --- a/apps/hellgate/src/hg_invoice.erl +++ b/apps/hellgate/src/hg_invoice.erl @@ -241,7 +241,11 @@ handle_function_('Repair', {InvoiceID, Changes, Action, Params}, _Opts) -> repair(InvoiceID, {changes, Changes, Action, Params}); handle_function_('RepairWithScenario', {InvoiceID, Scenario}, _Opts) -> _ = 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) -> undefined; diff --git a/apps/hellgate/src/hg_invoice_payment.erl b/apps/hellgate/src/hg_invoice_payment.erl index 029e056..c2d887e 100644 --- a/apps/hellgate/src/hg_invoice_payment.erl +++ b/apps/hellgate/src/hg_invoice_payment.erl @@ -83,6 +83,7 @@ -export([create_session_event_context/3]). -export([add_session/3]). -export([accrue_status_timing/3]). +-export([get_limit_values/2]). %% Machine like @@ -205,6 +206,7 @@ -type recurrent_paytool_service_terms() :: dmsl_domain_thrift:'RecurrentPaytoolsServiceTerms'(). -type session() :: hg_session:t(). -type payment_plan_id() :: hg_accounting:plan_id(). +-type route_limit_context() :: dmsl_payproc_thrift:'RouteLimitContext'(). -type opts() :: #{ party => party(), @@ -2243,6 +2245,26 @@ process_risk_score(Action, St) -> -spec process_routing(action(), st()) -> machine_result(). 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), Revision = get_payment_revision(St), Payment = get_payment(St), @@ -2253,29 +2275,15 @@ process_routing(Action, St) -> 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), - AllRoutes = - case get_predefined_route(Payer) of - {ok, PaymentRoute} -> - [hg_routing:from_payment_route(PaymentRoute)]; - undefined -> - gather_routes(PaymentInstitution, VS3, Revision, St) - end, - 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) + {PaymentInstitution, VS3, Revision}. + +get_candidates(PaymentInstitution, VS, Revision, St) -> + Payer = get_payment_payer(St), + case get_predefined_route(Payer) of + {ok, PaymentRoute} -> + [hg_routing:from_payment_route(PaymentRoute)]; + undefined -> + gather_routes(PaymentInstitution, VS, Revision, St) end. 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), 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}) -> case get_payment_flow(Payment) of ?invoice_payment_flow_instant() -> diff --git a/apps/hellgate/src/hg_limiter.erl b/apps/hellgate/src/hg_limiter.erl index f245f89..87e6b12 100644 --- a/apps/hellgate/src/hg_limiter.erl +++ b/apps/hellgate/src/hg_limiter.erl @@ -1,5 +1,6 @@ -module(hg_limiter). +-include_lib("damsel/include/dmsl_payproc_thrift.hrl"). -include_lib("damsel/include/dmsl_domain_thrift.hrl"). -include_lib("damsel/include/dmsl_base_thrift.hrl"). -include_lib("limiter_proto/include/limproto_base_thrift.hrl"). @@ -14,6 +15,7 @@ -type refund() :: hg_invoice_payment:domain_refund(). -type cash() :: dmsl_domain_thrift:'Cash'(). -type handling_flag() :: ignore_business_error. +-type turnover_limit_value() :: dmsl_payproc_thrift:'TurnoverLimitValue'(). -type change_queue() :: [hg_limiter_client:limit_change()]. @@ -25,6 +27,7 @@ -export([commit_refund_limits/5]). -export([rollback_payment_limits/6]). -export([rollback_refund_limits/5]). +-export([get_limit_values/4]). -define(route(ProviderRef, TerminalRef), #domain_PaymentRoute{ provider = ProviderRef, @@ -39,6 +42,23 @@ get_turnover_limits({value, Limits}) -> get_turnover_limits(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()) -> {ok, [hg_limiter_client:limit()]} | {error, {limit_overflow, [binary()]}}. diff --git a/apps/hellgate/test/hg_invoice_tests_SUITE.erl b/apps/hellgate/test/hg_invoice_tests_SUITE.erl index 6a82da8..a3678d7 100644 --- a/apps/hellgate/test/hg_invoice_tests_SUITE.erl +++ b/apps/hellgate/test/hg_invoice_tests_SUITE.erl @@ -40,6 +40,7 @@ -export([register_payment_customer_payer_success/1]). -export([payment_limit_success/1]). +-export([payment_routes_limit_values/1]). -export([register_payment_limit_success/1]). -export([payment_limit_other_shop_success/1]). -export([payment_limit_overflow/1]). @@ -383,6 +384,7 @@ groups() -> {operation_limits, [], [ payment_limit_success, + payment_routes_limit_values, register_payment_limit_success, payment_limit_other_shop_success, payment_limit_overflow, @@ -1254,6 +1256,30 @@ payment_limit_success(C) -> [?payment_state(_Payment)] ) = 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(). register_payment_limit_success(C0) -> Client = cfg(client, C0), diff --git a/apps/hg_client/src/hg_client_invoicing.erl b/apps/hg_client/src/hg_client_invoicing.erl index e9d1442..eb23ef9 100644 --- a/apps/hg_client/src/hg_client_invoicing.erl +++ b/apps/hg_client/src/hg_client_invoicing.erl @@ -15,6 +15,7 @@ -export([rescind/3]). -export([repair/5]). -export([repair_scenario/3]). +-export([get_limit_values/3]). -export([create_invoice_adjustment/3]). -export([get_invoice_adjustment/3]). @@ -98,6 +99,8 @@ -type event_range() :: dmsl_payproc_thrift:'EventRange'(). -type party_revision_param() :: dmsl_payproc_thrift:'PartyRevisionParam'(). +-type route_limit_context() :: dmsl_payproc_thrift:'RouteLimitContext'(). + -spec start(hg_client_api:t()) -> pid(). start(ApiClient) -> start(start, ApiClient). @@ -150,6 +153,10 @@ repair(InvoiceID, Changes, Action, Params, Client) -> repair_scenario(InvoiceID, Scenario, Client) -> 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()) -> invoice_adjustment() | woody_error:business_error(). create_invoice_adjustment(InvoiceID, Params, Client) -> diff --git a/rebar.lock b/rebar.lock index 0e6832a..2caafaa 100644 --- a/rebar.lock +++ b/rebar.lock @@ -17,7 +17,7 @@ {<<"cowlib">>,{pkg,<<"cowlib">>,<<"2.11.0">>},2}, {<<"damsel">>, {git,"https://github.com/valitydev/damsel.git", - {ref,"03bf41075c39b6731c5ed200d5c4b0faaee9d937"}}, + {ref,"52dbefd54fd68d0c04f8ded5734b27635e939c60"}}, 0}, {<<"dmt_client">>, {git,"https://github.com/valitydev/dmt-client.git",