TD-578: Adds cascade attempt log messages (#65)

* Fixes failure code for cascade trigger
* Refactors failure checks into payproc_errors notation functions
* Bumps payproc_errors
This commit is contained in:
Aleksey Kashapov 2023-04-19 14:32:19 +03:00 committed by GitHub
parent bfccd439a3
commit 0eae6c2455
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 54 additions and 36 deletions

View File

@ -3290,10 +3290,8 @@ merge_change(Change = ?payment_status_changed(?failed(_) = Status), St0, Opts) -
St0,
Opts
),
AttemptLimit = get_routing_attempt_limit(St0),
RouteBlockedFailureCode = genlib_app:env(hellgate, card_blocked_failure, <<"card_blocked">>),
St1 = set_failure_payment_status(St0, Status),
merge_failure_with_new_routing_attempt(St1, Opts, Status, RouteBlockedFailureCode, AttemptLimit);
merge_failure_with_new_routing_attempt(check_if_can_attempt(Status, St1), Opts, St1);
merge_change(Change = ?payment_status_changed({cancelled, _} = Status), #st{payment = Payment} = St, Opts) ->
_ = validate_transition({payment, finalizing_accounter}, Change, St, Opts),
St#st{
@ -3508,19 +3506,13 @@ merge_change(Change = ?session_ev(Target, Event), St = #st{activity = Activity},
St2
end.
merge_failure_with_new_routing_attempt(
#st{routes = AttemptedRoutes, interim_payment_status = InterimPaymentStatus} = St,
Opts,
?failed({failure, #domain_Failure{code = FailureCode}}) = Status,
FailureCode,
AttemptLimit
) when length(AttemptedRoutes) < AttemptLimit ->
merge_failure_with_new_routing_attempt({true, Status}, Opts, #st{interim_payment_status = InterimPaymentStatus} = St) ->
St#st{
interim_payment_status = genlib:define(InterimPaymentStatus, Status),
activity = {payment, routing},
timings = accrue_status_timing(pending_attempt, Opts, St)
};
merge_failure_with_new_routing_attempt(St, Opts, _Status, _FailureCode, _AttemptLimit) ->
merge_failure_with_new_routing_attempt(_Else, Opts, St) ->
St#st{
activity = idle,
failure = undefined,
@ -3548,8 +3540,17 @@ get_routing_attempt_limit(
VS = collect_validation_varset(Party, Shop, get_payment(St), #{}),
Terms = get_merchant_terms(Party, Shop, Revision, CreatedAt, VS),
#domain_TermSet{payments = PaymentTerms} = Terms,
log_payment_terms_attempt_limit(PaymentTerms),
get_routing_attempt_limit_value(PaymentTerms#domain_PaymentsServiceTerms.attempt_limit).
log_payment_terms_attempt_limit(#domain_PaymentsServiceTerms{attempt_limit = AttemptLimit}) ->
_ = logger:log(
info,
"Merchant payment terms' attempt limit: ~p",
[AttemptLimit],
logger:get_process_metadata()
).
get_routing_attempt_limit_value(undefined) ->
1;
get_routing_attempt_limit_value({decisions, _}) ->
@ -4058,3 +4059,30 @@ get_party_client() ->
Client = hg_context:get_party_client(HgContext),
Context = hg_context:get_party_client_context(HgContext),
{Client, Context}.
check_if_can_attempt(?failed({failure, Failure}) = Status, St) ->
ExpectNotation = genlib_app:env(hellgate, card_blocked_failure, <<"preauthorization_failed:card_blocked">>),
payproc_errors:match_notation(Failure, fun
%% Expect exact dynamic error
(Notation) when Notation =:= ExpectNotation -> check_if_within_attempts_limit(Status, St);
(_) -> {false, Status}
end);
check_if_can_attempt(Status, _St) ->
{false, Status}.
check_if_within_attempts_limit(Status, St) ->
AttemptLimit = get_routing_attempt_limit(St),
check_if_within_attempts_limit_(Status, AttemptLimit, St).
check_if_within_attempts_limit_(Status, AttemptLimit, #st{routes = AttemptedRoutes}) when
length(AttemptedRoutes) < AttemptLimit
->
_ = logger:log(
info,
"New cascade attempt, limit: ~p; attempted routes: ~p",
[AttemptLimit, AttemptedRoutes],
logger:get_process_metadata()
),
{true, Status};
check_if_within_attempts_limit_(Status, _AttemptLimit, _St) ->
{false, Status}.

View File

@ -255,10 +255,7 @@ process_payment(
#{<<"always_fail">> := FailureCode, <<"override">> := ProviderCode},
_
) ->
Failure = #domain_Failure{
code = FailureCode,
sub = #domain_SubFailure{code = <<"sub failure by ", ProviderCode/binary>>}
},
Failure = payproc_errors:from_notation(FailureCode, <<"sub failure by ", ProviderCode/binary>>),
result(?finish({failure, Failure}));
process_payment(?processed(), undefined, PaymentInfo, _Ctx, _) ->
case get_payment_info_scenario(PaymentInfo) of

View File

@ -1640,7 +1640,7 @@ routes_ruleset_w_one_failing_route_fixture(Revision) ->
proxy = #domain_Proxy{
ref = ?prx(1),
additional = #{
<<"always_fail">> => <<"card_blocked">>,
<<"always_fail">> => <<"preauthorization_failed:card_blocked">>,
<<"override">> => <<"duckblocker">>
}
},
@ -1678,7 +1678,7 @@ routes_ruleset_w_different_failing_providers_fixture(Revision) ->
proxy = #domain_Proxy{
ref = ?prx(1),
additional = #{
<<"always_fail">> => <<"card_blocked">>,
<<"always_fail">> => <<"preauthorization_failed:card_blocked">>,
<<"override">> => <<"duckblocker">>
}
},
@ -1695,7 +1695,7 @@ routes_ruleset_w_different_failing_providers_fixture(Revision) ->
proxy = #domain_Proxy{
ref = ?prx(1),
additional = #{
<<"always_fail">> => <<"card_blocked">>,
<<"always_fail">> => <<"preauthorization_failed:card_blocked">>,
<<"override">> => <<"duckblocker_younger">>
}
},
@ -1764,7 +1764,7 @@ routes_ruleset_w_failing_provider_fixture(Revision) ->
proxy = #domain_Proxy{
ref = ?prx(1),
additional = #{
<<"always_fail">> => <<"card_blocked">>,
<<"always_fail">> => <<"preauthorization_failed:card_blocked">>,
<<"override">> => <<"duckblocker">>
}
},
@ -5976,7 +5976,7 @@ payment_cascade_success(C) ->
?payment_ev(PaymentID, ?payment_status_changed(?failed({failure, Failure})))
] =
next_changes(InvoiceID, 3, Client),
ok = payproc_errors:match('PreAuthorizationFailure', Failure, fun({card_blocked, _}) -> ok end),
ok = payproc_errors:match('PaymentFailure', Failure, fun({preauthorization_failed, {card_blocked, _}}) -> ok end),
?payment_ev(PaymentID, ?route_changed(Route)) = next_change(InvoiceID, Client),
?assertMatch(#domain_PaymentRoute{provider = ?prv(1)}, Route),
?payment_ev(PaymentID, ?cash_flow_changed(_CashFlow)) = next_change(InvoiceID, Client),
@ -6011,7 +6011,7 @@ payment_cascade_fail_wo_available_attempt_limit(C) ->
?payment_ev(PaymentID, ?payment_status_changed(?failed({failure, Failure})))
] =
next_changes(InvoiceID, 3, Client),
ok = payproc_errors:match('PreAuthorizationFailure', Failure, fun({card_blocked, _}) -> ok end),
ok = payproc_errors:match('PaymentFailure', Failure, fun({preauthorization_failed, {card_blocked, _}}) -> ok end),
?invoice_status_changed(?invoice_cancelled(<<"overdue">>)) = next_change(InvoiceID, Client).
-spec payment_cascade_failures(config()) -> test_return().
@ -6035,11 +6035,7 @@ payment_cascade_failures(C) ->
?payment_ev(PaymentID, ?payment_status_changed(?failed({failure, Failure1})))
] =
next_changes(InvoiceID, 3, Client),
ok = payproc_errors:match(
'PreAuthorizationFailure',
Failure1,
fun({card_blocked, {{unknown_error, <<"sub failure by duckblocker">>}, _}}) -> ok end
),
ok = payproc_errors:match('PaymentFailure', Failure1, fun({preauthorization_failed, {card_blocked, _}}) -> ok end),
?payment_ev(PaymentID, ?route_changed(_Route)) = next_change(InvoiceID, Client),
%% And again
?payment_ev(PaymentID, ?cash_flow_changed(_CashFlow)) = next_change(InvoiceID, Client),
@ -6053,11 +6049,7 @@ payment_cascade_failures(C) ->
?payment_ev(PaymentID, ?payment_status_changed(?failed({failure, Failure2})))
] =
next_changes(InvoiceID, 3, Client),
ok = payproc_errors:match(
'PreAuthorizationFailure',
Failure2,
fun({card_blocked, {{unknown_error, <<"sub failure by duckblocker_younger">>}, _}}) -> ok end
).
ok = payproc_errors:match('PaymentFailure', Failure2, fun({preauthorization_failed, {card_blocked, _}}) -> ok end).
-spec payment_cascade_no_route(config()) -> test_return().
payment_cascade_no_route(C) ->
@ -6081,9 +6073,9 @@ payment_cascade_no_route(C) ->
] =
next_changes(InvoiceID, 3, Client),
ok = payproc_errors:match(
'PreAuthorizationFailure',
'PaymentFailure',
CardBlockedFailure,
fun({card_blocked, {{unknown_error, <<"sub failure by duckblocker">>}, _}}) -> ok end
fun({preauthorization_failed, {card_blocked, _}}) -> ok end
),
?payment_ev(PaymentID, ?route_changed(_Route)) = next_change(InvoiceID, Client),
?payment_ev(PaymentID, ?payment_rollback_started({failure, CardBlockedFailure})) = next_change(InvoiceID, Client).

View File

@ -64,8 +64,9 @@
refunded => no_retry
}},
{inspect_timeout, 3000},
% Payment error code that signals hellgate to attempt another route (default = <<"card_blocked">>)
{card_blocked_failure, <<"card_blocked">>},
% Payment error code that signals hellgate to attempt another route
% (default = <<"preauthorization_failed:card_blocked">>)
{card_blocked_failure, <<"preauthorization_failed:card_blocked">>},
{fault_detector, #{
enabled => true,
% Woody RPC request timeout (ms).

View File

@ -64,7 +64,7 @@
0},
{<<"payproc_errors">>,
{git,"https://github.com/valitydev/payproc-errors-erlang.git",
{ref,"8e58e6dc014283e64293fdc25ac59d9f2fa8e9e5"}},
{ref,"8ae8586239ef68098398acf7eb8363d9ec3b3234"}},
0},
{<<"ranch">>,{pkg,<<"ranch">>,<<"1.8.0">>},2},
{<<"scoper">>,