mirror of
https://github.com/valitydev/hellgate.git
synced 2024-11-06 02:45:20 +00:00
OPS-375: Add change body event (#102)
* added draft impl * added tests * fixed tests * added review changes * moved on damsel master * fixed * OPS-378: Implements payment amount adjustment (#104) * Adds tracing to invoice tests * Adds cashflow amount change test --------- Co-authored-by: Aleksey Kashapov <nanodirijabl@gmail.com>
This commit is contained in:
parent
84945e0378
commit
42af922405
@ -8,6 +8,9 @@
|
||||
routes = [] :: [hg_routing:payment_route()],
|
||||
candidate_routes :: undefined | [hg_routing:payment_route()],
|
||||
interim_payment_status :: undefined | hg_invoice_payment:payment_status(),
|
||||
new_cash :: undefined | hg_cash:cash(),
|
||||
new_cash_provided :: undefined | boolean(),
|
||||
new_cash_flow :: undefined | hg_cashflow:final_cash_flow(),
|
||||
cash_flow :: undefined | hg_cashflow:final_cash_flow(),
|
||||
partial_cash_flow :: undefined | hg_cashflow:final_cash_flow(),
|
||||
final_cash_flow :: undefined | hg_cashflow:final_cash_flow(),
|
||||
|
@ -58,6 +58,10 @@
|
||||
{invoice_payment_rec_token_acquired, #payproc_InvoicePaymentRecTokenAcquired{token = Token}}
|
||||
).
|
||||
|
||||
-define(cash_changed(OldCash, NewCash),
|
||||
{invoice_payment_cash_changed, #payproc_InvoicePaymentCashChanged{old_cash = OldCash, new_cash = NewCash}}
|
||||
).
|
||||
|
||||
-define(payment_capture_started(Data),
|
||||
{invoice_payment_capture_started, #payproc_InvoicePaymentCaptureStarted{
|
||||
data = Data
|
||||
|
@ -8,6 +8,7 @@
|
||||
-export([sub/2]).
|
||||
|
||||
-type cash() :: dmsl_domain_thrift:'Cash'().
|
||||
-export_type([cash/0]).
|
||||
|
||||
%% Simple arithmetics
|
||||
|
||||
|
@ -270,6 +270,8 @@ get_route(#st{routes = [Route | _AttemptedRoutes]}) ->
|
||||
Route.
|
||||
|
||||
-spec get_iter(st()) -> pos_integer().
|
||||
get_iter(#st{routes = AttemptedRoutes, new_cash_provided = true}) ->
|
||||
length(AttemptedRoutes) * 1000;
|
||||
get_iter(#st{routes = AttemptedRoutes}) ->
|
||||
length(AttemptedRoutes).
|
||||
|
||||
@ -937,24 +939,25 @@ collect_validation_varset(Party, Shop, Payment, VS) ->
|
||||
%%
|
||||
|
||||
-spec construct_payment_plan_id(st()) -> payment_plan_id().
|
||||
construct_payment_plan_id(#st{opts = Opts, payment = Payment, routes = Routes}) ->
|
||||
construct_payment_plan_id(get_invoice(Opts), Payment, Routes, normal).
|
||||
construct_payment_plan_id(St = #st{opts = Opts, payment = Payment}) ->
|
||||
Iter = get_iter(St),
|
||||
construct_payment_plan_id(get_invoice(Opts), Payment, Iter, normal).
|
||||
|
||||
-spec construct_payment_plan_id(st(), legacy | normal) -> payment_plan_id().
|
||||
construct_payment_plan_id(#st{opts = Opts, payment = Payment, routes = Routes}, Mode) ->
|
||||
construct_payment_plan_id(get_invoice(Opts), Payment, Routes, Mode).
|
||||
construct_payment_plan_id(St = #st{opts = Opts, payment = Payment}, Mode) ->
|
||||
Iter = get_iter(St),
|
||||
construct_payment_plan_id(get_invoice(Opts), Payment, Iter, Mode).
|
||||
|
||||
construct_payment_plan_id(Invoice, Payment, _Routes, legacy) ->
|
||||
construct_payment_plan_id(Invoice, Payment, _Iter, legacy) ->
|
||||
hg_utils:construct_complex_id([
|
||||
get_invoice_id(Invoice),
|
||||
get_payment_id(Payment)
|
||||
]);
|
||||
construct_payment_plan_id(Invoice, Payment, Routes, _Mode) ->
|
||||
AttemptNum = length(Routes),
|
||||
construct_payment_plan_id(Invoice, Payment, Iter, _Mode) ->
|
||||
hg_utils:construct_complex_id([
|
||||
get_invoice_id(Invoice),
|
||||
get_payment_id(Payment),
|
||||
integer_to_binary(AttemptNum)
|
||||
integer_to_binary(Iter)
|
||||
]).
|
||||
|
||||
get_selector_value(Name, Selector) ->
|
||||
@ -1497,10 +1500,13 @@ create_cash_flow_adjustment(Timestamp, Params, DomainRevision, St, Opts) ->
|
||||
OldCashFlow = get_final_cashflow(St),
|
||||
VS = collect_validation_varset(St, Opts),
|
||||
Allocation = get_allocation(St),
|
||||
{Payment1, AdditionalEvents} = maybe_inject_new_cost_amount(
|
||||
Payment, Params#payproc_InvoicePaymentAdjustmentParams.scenario
|
||||
),
|
||||
Context = #{
|
||||
provision_terms => get_provider_terminal_terms(Route, VS, NewRevision),
|
||||
route => Route,
|
||||
payment => Payment,
|
||||
payment => Payment1,
|
||||
timestamp => Timestamp,
|
||||
varset => VS,
|
||||
revision => NewRevision,
|
||||
@ -1519,9 +1525,21 @@ create_cash_flow_adjustment(Timestamp, Params, DomainRevision, St, Opts) ->
|
||||
OldCashFlow,
|
||||
NewCashFlow,
|
||||
AdjState,
|
||||
AdditionalEvents,
|
||||
St
|
||||
).
|
||||
|
||||
maybe_inject_new_cost_amount(
|
||||
Payment,
|
||||
{'cash_flow', #domain_InvoicePaymentAdjustmentCashFlow{new_amount = NewAmount}}
|
||||
) when NewAmount =/= undefined ->
|
||||
OldCost = get_payment_cost(Payment),
|
||||
NewCost = OldCost#domain_Cash{amount = NewAmount},
|
||||
Payment1 = Payment#domain_InvoicePayment{cost = NewCost},
|
||||
{Payment1, [?cash_changed(OldCost, NewCost)]};
|
||||
maybe_inject_new_cost_amount(Payment, _AdjustmentScenario) ->
|
||||
{Payment, []}.
|
||||
|
||||
-spec create_status_adjustment(
|
||||
hg_datetime:timestamp(),
|
||||
adjustment_params(),
|
||||
@ -1555,6 +1573,7 @@ create_status_adjustment(Timestamp, Params, Change, St, Opts) ->
|
||||
OldCashFlow,
|
||||
NewCashFlow,
|
||||
AdjState,
|
||||
[],
|
||||
St
|
||||
).
|
||||
|
||||
@ -1667,6 +1686,7 @@ calculate_cashflow(PaymentInstitution, Context = #{route := Route, revision := R
|
||||
OldCashFlow :: final_cash_flow(),
|
||||
NewCashFlow :: final_cash_flow(),
|
||||
State :: adjustment_state(),
|
||||
AdditionalEvents :: events(),
|
||||
St :: st()
|
||||
) -> {adjustment(), result()}.
|
||||
construct_adjustment(
|
||||
@ -1677,6 +1697,7 @@ construct_adjustment(
|
||||
OldCashFlow,
|
||||
NewCashFlow,
|
||||
State,
|
||||
AdditionalEvents,
|
||||
St
|
||||
) ->
|
||||
ID = construct_adjustment_id(St),
|
||||
@ -1691,8 +1712,8 @@ construct_adjustment(
|
||||
new_cash_flow = NewCashFlow,
|
||||
state = State
|
||||
},
|
||||
Event = ?adjustment_ev(ID, ?adjustment_created(Adjustment)),
|
||||
{Adjustment, {[Event], hg_machine_action:instant()}}.
|
||||
Events = [?adjustment_ev(ID, ?adjustment_created(Adjustment)) | AdditionalEvents],
|
||||
{Adjustment, {Events, hg_machine_action:instant()}}.
|
||||
|
||||
construct_adjustment_id(#st{adjustments = As}) ->
|
||||
erlang:integer_to_binary(length(As) + 1).
|
||||
@ -2174,16 +2195,31 @@ process_session(Session0, St = #st{repair_scenario = Scenario}) ->
|
||||
finish_session_processing(get_activity(St), Result, Session3, St).
|
||||
|
||||
-spec finish_session_processing(activity(), result(), hg_session:t(), st()) -> machine_result().
|
||||
finish_session_processing(Activity, {Events0, Action}, Session, St) ->
|
||||
finish_session_processing(Activity, {Events0, Action}, Session, St0) ->
|
||||
Events1 = hg_session:wrap_events(Events0, Session),
|
||||
case {hg_session:status(Session), hg_session:result(Session)} of
|
||||
{finished, ?session_succeeded()} ->
|
||||
TargetType = get_target_type(hg_session:target(Session)),
|
||||
_ = maybe_notify_fault_detector(Activity, TargetType, finish, St),
|
||||
_ = maybe_notify_fault_detector(Activity, TargetType, finish, St0),
|
||||
NewAction = hg_machine_action:set_timeout(0, Action),
|
||||
InvoiceID = get_invoice_id(get_invoice(get_opts(St0))),
|
||||
St1 = collapse_changes(Events1, St0, #{invoice_id => InvoiceID}),
|
||||
_ =
|
||||
case St1 of
|
||||
#st{new_cash_provided = true, activity = {payment, processing_accounter}} ->
|
||||
%% Revert with St0 cause default rollback takes into account new cash
|
||||
%% We need to rollback only current route.
|
||||
%% Previously used routes are supposed to have their limits already rolled back.
|
||||
Route = get_route(St0),
|
||||
Routes = [Route],
|
||||
_ = rollback_payment_limits(Routes, get_iter(St0), St0),
|
||||
_ = rollback_payment_cashflow(St0);
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
{next, {Events1, NewAction}};
|
||||
{finished, ?session_failed(Failure)} ->
|
||||
process_failure(Activity, Events1, Action, Failure, St);
|
||||
process_failure(Activity, Events1, Action, Failure, St0);
|
||||
_ ->
|
||||
{next, {Events1, Action}}
|
||||
end.
|
||||
@ -2218,6 +2254,41 @@ finalize_payment(Action, St) ->
|
||||
process_result(Action, St) ->
|
||||
process_result(get_activity(St), Action, St).
|
||||
|
||||
process_result({payment, processing_accounter}, Action, St0 = #st{new_cash = Cost}) when
|
||||
Cost =/= undefined
|
||||
->
|
||||
%% Rebuild cashflow for new cost
|
||||
Payment0 = get_payment(St0),
|
||||
Payment1 = Payment0#domain_InvoicePayment{cost = Cost},
|
||||
St1 = St0#st{payment = Payment1},
|
||||
Opts = get_opts(St1),
|
||||
Revision = get_payment_revision(St1),
|
||||
Timestamp = get_payment_created_at(Payment0),
|
||||
VS = collect_validation_varset(St1, Opts),
|
||||
MerchantTerms = get_merchant_payments_terms(Opts, Revision, Timestamp, VS),
|
||||
Route = get_route(St1),
|
||||
ProviderTerms = hg_routing:get_payment_terms(Route, VS, Revision),
|
||||
Context = #{
|
||||
provision_terms => ProviderTerms,
|
||||
merchant_terms => MerchantTerms,
|
||||
route => Route,
|
||||
payment => Payment1,
|
||||
timestamp => Timestamp,
|
||||
varset => VS,
|
||||
revision => Revision
|
||||
},
|
||||
FinalCashflow = calculate_cashflow(Context, Opts),
|
||||
%% Hold limits (only for chosen route) for new cashflow
|
||||
{_PaymentInstitution, RouteVS, _Revision} = route_args(St1),
|
||||
Routes = [hg_routing:from_payment_route(Route)],
|
||||
_ = hold_limit_routes(Routes, RouteVS, get_iter(St1), St1),
|
||||
%% Hold cashflow
|
||||
St2 = St1#st{new_cash_flow = FinalCashflow},
|
||||
_Clock = hg_accounting:plan(
|
||||
construct_payment_plan_id(St2),
|
||||
get_cashflow_plan(St2)
|
||||
),
|
||||
{next, {[?cash_flow_changed(FinalCashflow)], hg_machine_action:set_timeout(0, Action)}};
|
||||
process_result({payment, processing_accounter}, Action, St) ->
|
||||
Target = get_target(St),
|
||||
NewAction = get_action(Target, Action, St),
|
||||
@ -2590,6 +2661,26 @@ do_try_with_ids([ID | OtherIDs], Func) when is_function(Func, 1) ->
|
||||
do_try_with_ids(OtherIDs, Func)
|
||||
end.
|
||||
|
||||
get_cashflow_plan(
|
||||
St = #st{
|
||||
partial_cash_flow = PartialCashFlow,
|
||||
new_cash_provided = true,
|
||||
new_cash_flow = NewCashFlow
|
||||
}
|
||||
) when PartialCashFlow =/= undefined ->
|
||||
[
|
||||
{1, get_cashflow(St)},
|
||||
{2, hg_cashflow:revert(get_cashflow(St))},
|
||||
{3, PartialCashFlow},
|
||||
{4, hg_cashflow:revert(PartialCashFlow)},
|
||||
{5, NewCashFlow}
|
||||
];
|
||||
get_cashflow_plan(St = #st{new_cash_provided = true, new_cash_flow = NewCashFlow}) ->
|
||||
[
|
||||
{1, get_cashflow(St)},
|
||||
{2, hg_cashflow:revert(get_cashflow(St))},
|
||||
{3, NewCashFlow}
|
||||
];
|
||||
get_cashflow_plan(St = #st{partial_cash_flow = PartialCashFlow}) when PartialCashFlow =/= undefined ->
|
||||
[
|
||||
{1, get_cashflow(St)},
|
||||
@ -2885,7 +2976,8 @@ merge_change(Change = ?cash_flow_changed(CashFlow), #st{activity = Activity} = S
|
||||
{payment, S}
|
||||
|| S <- [
|
||||
cash_flow_building,
|
||||
processing_capture
|
||||
processing_capture,
|
||||
processing_accounter
|
||||
]
|
||||
],
|
||||
Change,
|
||||
@ -2896,6 +2988,8 @@ merge_change(Change = ?cash_flow_changed(CashFlow), #st{activity = Activity} = S
|
||||
final_cash_flow = CashFlow
|
||||
},
|
||||
case Activity of
|
||||
{payment, processing_accounter} ->
|
||||
St#st{new_cash = undefined, new_cash_flow = CashFlow};
|
||||
{payment, cash_flow_building} ->
|
||||
St#st{
|
||||
cash_flow = CashFlow,
|
||||
@ -2912,6 +3006,16 @@ merge_change(Change = ?cash_flow_changed(CashFlow), #st{activity = Activity} = S
|
||||
merge_change(Change = ?rec_token_acquired(Token), #st{} = St, Opts) ->
|
||||
_ = validate_transition([{payment, processing_session}, {payment, finalizing_session}], Change, St, Opts),
|
||||
St#st{recurrent_token = Token};
|
||||
merge_change(Change = ?cash_changed(_OldCash, NewCash), #st{} = St, Opts) ->
|
||||
_ = validate_transition(
|
||||
[{adjustment_new, latest_adjustment_id(St)}, {payment, processing_session}],
|
||||
Change,
|
||||
St,
|
||||
Opts
|
||||
),
|
||||
Payment0 = get_payment(St),
|
||||
Payment1 = Payment0#domain_InvoicePayment{changed_cost = NewCash},
|
||||
St#st{new_cash = NewCash, new_cash_provided = true, payment = Payment1};
|
||||
merge_change(Change = ?payment_rollback_started(Failure), St, Opts) ->
|
||||
_ = validate_transition(
|
||||
[{payment, cash_flow_building}, {payment, processing_session}],
|
||||
@ -3152,6 +3256,12 @@ merge_change(Change = ?session_ev(Target, Event), St = #st{activity = Activity},
|
||||
St2
|
||||
end.
|
||||
|
||||
latest_adjustment_id(#st{adjustments = []}) ->
|
||||
undefined;
|
||||
latest_adjustment_id(#st{adjustments = Adjustments}) ->
|
||||
Adjustment = lists:last(Adjustments),
|
||||
Adjustment#domain_InvoicePaymentAdjustment.id.
|
||||
|
||||
get_routing_attempt_limit(
|
||||
St = #st{
|
||||
payment = #domain_InvoicePayment{
|
||||
|
@ -329,7 +329,7 @@ handle_proxy_callback_result(
|
||||
apply_result(Result = {Events, _Action}, T) ->
|
||||
{Result, update_state_with(Events, T)}.
|
||||
|
||||
handle_proxy_intent(#proxy_provider_FinishIntent{status = {success, Success}}, Action, _Session) ->
|
||||
handle_proxy_intent(#proxy_provider_FinishIntent{status = {success, Success}}, Action, Session) ->
|
||||
Events0 = [?session_finished(?session_succeeded())],
|
||||
Events1 =
|
||||
case Success of
|
||||
@ -338,7 +338,15 @@ handle_proxy_intent(#proxy_provider_FinishIntent{status = {success, Success}}, A
|
||||
#proxy_provider_Success{token = Token} ->
|
||||
[?rec_token_acquired(Token) | Events0]
|
||||
end,
|
||||
{Events1, Action};
|
||||
Events2 =
|
||||
case Success of
|
||||
#proxy_provider_Success{changed_cost = undefined} ->
|
||||
Events1;
|
||||
#proxy_provider_Success{changed_cost = NewCost} ->
|
||||
OldCost = get_cost_from(payment_info(Session)),
|
||||
[?cash_changed(convert_to_domain_cash(OldCost), convert_to_domain_cash(NewCost)) | Events1]
|
||||
end,
|
||||
{Events2, Action};
|
||||
handle_proxy_intent(#proxy_provider_FinishIntent{status = {failure, Failure}}, Action, _Session) ->
|
||||
Events = [?session_finished(?session_failed({failure, Failure}))],
|
||||
{Events, Action};
|
||||
@ -366,6 +374,8 @@ wrap_events(SessionEvents, Session) ->
|
||||
-spec wrap_event(target(), event()) -> wrapped_event().
|
||||
wrap_event(_Target, Event = ?rec_token_acquired(_Token)) ->
|
||||
Event;
|
||||
wrap_event(_Target, Event = ?cash_changed(_OldCost, _NewCost)) ->
|
||||
Event;
|
||||
wrap_event(Target, SessionEvent) ->
|
||||
?session_ev(Target, SessionEvent).
|
||||
|
||||
@ -408,9 +418,11 @@ apply_event(?interaction_changed(UserInteraction, Status), Session, _Context) ->
|
||||
{UserInteraction, Session1} = maps:take(interaction, Session),
|
||||
Session1
|
||||
end;
|
||||
%% Ignore ?rec_token_acquired event cause it's easiest way to handle this
|
||||
%% Ignore ?rec_token_acquired and ?cash_changed events cause it's easiest way to handle this
|
||||
%% TODO maybe add this token to session state and remove it from payment state?
|
||||
apply_event(?rec_token_acquired(_Token), Session, _Context) ->
|
||||
Session;
|
||||
apply_event(?cash_changed(_OldCost, _NewCost), Session, _Context) ->
|
||||
Session.
|
||||
|
||||
create_session(#{target := Target, route := Route, invoice_id := InvoiceID, payment_id := PaymentID}) ->
|
||||
@ -450,3 +462,14 @@ accrue_timing(Name, Event, #{timestamp := Timestamp}, Session) ->
|
||||
mark_timing_event(Event, #{timestamp := Timestamp}, Session) ->
|
||||
Timings = get_session_timings(Session),
|
||||
set_session_timings(hg_timings:mark(Event, Timestamp, Timings), Session).
|
||||
|
||||
convert_to_domain_cash(#proxy_provider_Cash{
|
||||
amount = Amount,
|
||||
currency = #domain_Currency{symbolic_code = Currency}
|
||||
}) ->
|
||||
#domain_Cash{
|
||||
amount = Amount,
|
||||
currency = #domain_CurrencyRef{symbolic_code = Currency}
|
||||
}.
|
||||
|
||||
get_cost_from(#proxy_provider_PaymentInfo{payment = #proxy_provider_InvoicePayment{cost = Cost}}) -> Cost.
|
||||
|
@ -21,6 +21,7 @@
|
||||
-define(invoice_w_revision(Revision), #domain_Invoice{party_revision = Revision}).
|
||||
-define(payment_w_status(Status), #domain_InvoicePayment{status = Status}).
|
||||
-define(payment_w_status(ID, Status), #domain_InvoicePayment{id = ID, status = Status}).
|
||||
-define(payment_w_changed_cost(ChangedCost), #domain_InvoicePayment{changed_cost = ChangedCost}).
|
||||
-define(invoice_payment_refund(Cash, Status), #domain_InvoicePaymentRefund{cash = Cash, status = Status}).
|
||||
-define(trx_info(ID), #domain_TransactionInfo{id = ID}).
|
||||
-define(trx_info(ID, Extra), #domain_TransactionInfo{id = ID, extra = Extra}).
|
||||
|
@ -76,6 +76,10 @@
|
||||
{success, #proxy_provider_Success{token = Token}}
|
||||
).
|
||||
|
||||
-define(success(Token, ChangedCash),
|
||||
{success, #proxy_provider_Success{token = Token, changed_cost = ChangedCash}}
|
||||
).
|
||||
|
||||
-define(recurrent_token_finish(Token),
|
||||
{finish, #proxy_provider_RecurrentTokenFinishIntent{
|
||||
status = {success, #proxy_provider_RecurrentTokenSuccess{token = Token}}
|
||||
@ -277,6 +281,12 @@ process_payment(?processed(), undefined, PaymentInfo, CtxOpts, _) ->
|
||||
Tag = generate_tag(<<"payment">>),
|
||||
Uri = get_callback_url(),
|
||||
result(?suspend(Tag, Timeout, ?redirect(Uri, #{<<"tag">> => Tag})), <<"suspended">>);
|
||||
change_cash_increase ->
|
||||
%% simple workflow without 3DS
|
||||
result(?sleep(0), <<"sleeping">>);
|
||||
change_cash_decrease ->
|
||||
%% simple workflow without 3DS
|
||||
result(?sleep(0), <<"sleeping">>);
|
||||
no_preauth ->
|
||||
%% simple workflow without 3DS
|
||||
result(?sleep(0), <<"sleeping">>);
|
||||
@ -333,6 +343,10 @@ process_payment(?processed(), undefined, PaymentInfo, CtxOpts, _) ->
|
||||
process_payment(?processed(), <<"sleeping">>, PaymentInfo, CtxOpts, _) ->
|
||||
TrxID = hg_utils:construct_complex_id([get_payment_id(PaymentInfo), get_ctx_opts_override(CtxOpts)]),
|
||||
case get_payment_info_scenario(PaymentInfo) of
|
||||
change_cash_increase ->
|
||||
finish(success(PaymentInfo, get_payment_increased_cost(PaymentInfo)), mk_trx(TrxID, PaymentInfo));
|
||||
change_cash_decrease ->
|
||||
finish(success(PaymentInfo, get_payment_decreased_cost(PaymentInfo)), mk_trx(TrxID, PaymentInfo));
|
||||
unexpected_failure ->
|
||||
error(unexpected_failure);
|
||||
{temporary_unavailability, Scenario} ->
|
||||
@ -554,6 +568,9 @@ respond(Response, CallbackResult) ->
|
||||
}.
|
||||
|
||||
success(PaymentInfo) ->
|
||||
success(PaymentInfo, undefined).
|
||||
|
||||
success(PaymentInfo, ChangedCash) ->
|
||||
#proxy_provider_PaymentInfo{payment = #proxy_provider_InvoicePayment{make_recurrent = MakeRecurrent}} = PaymentInfo,
|
||||
Token =
|
||||
case MakeRecurrent of
|
||||
@ -562,7 +579,7 @@ success(PaymentInfo) ->
|
||||
Other when Other =:= false orelse Other =:= undefined ->
|
||||
undefined
|
||||
end,
|
||||
?success(Token).
|
||||
?success(Token, ChangedCash).
|
||||
|
||||
failure(Code) when is_atom(Code) ->
|
||||
failure(Code, unknown).
|
||||
@ -593,6 +610,19 @@ get_mobile_commerce(#proxy_provider_PaymentInfo{payment = Payment}) ->
|
||||
get_invoice_id(#proxy_provider_PaymentInfo{invoice = Invoice}) ->
|
||||
Invoice#proxy_provider_Invoice.id.
|
||||
|
||||
get_payment_cost(
|
||||
#proxy_provider_PaymentInfo{payment = #proxy_provider_InvoicePayment{cost = Cost}}
|
||||
) ->
|
||||
Cost.
|
||||
|
||||
get_payment_increased_cost(PaymentInfo) ->
|
||||
Cost = #proxy_provider_Cash{amount = Amount} = get_payment_cost(PaymentInfo),
|
||||
Cost#proxy_provider_Cash{amount = Amount * 2}.
|
||||
|
||||
get_payment_decreased_cost(PaymentInfo) ->
|
||||
Cost = #proxy_provider_Cash{amount = Amount} = get_payment_cost(PaymentInfo),
|
||||
Cost#proxy_provider_Cash{amount = Amount div 2}.
|
||||
|
||||
get_payment_info_scenario(
|
||||
#proxy_provider_PaymentInfo{payment = #proxy_provider_InvoicePayment{payment_resource = Resource}}
|
||||
) ->
|
||||
@ -608,6 +638,10 @@ get_recurrent_paytool_scenario(#proxy_provider_RecurrentPaymentTool{payment_reso
|
||||
PaymentTool = get_payment_tool(PaymentResource),
|
||||
get_payment_tool_scenario(PaymentTool).
|
||||
|
||||
get_payment_tool_scenario({'bank_card', #domain_BankCard{token = <<"change_cash_increase">>}}) ->
|
||||
change_cash_increase;
|
||||
get_payment_tool_scenario({'bank_card', #domain_BankCard{token = <<"change_cash_decrease">>}}) ->
|
||||
change_cash_decrease;
|
||||
get_payment_tool_scenario({'bank_card', #domain_BankCard{token = <<"no_preauth">>}}) ->
|
||||
no_preauth;
|
||||
get_payment_tool_scenario({'bank_card', #domain_BankCard{token = <<"no_preauth_timeout">>}}) ->
|
||||
@ -671,6 +705,8 @@ get_payment_tool_scenario(
|
||||
| {mobile_commerce, failure}
|
||||
| {mobile_commerce, success}
|
||||
| preauth_3ds_offsite
|
||||
| change_cash_increase
|
||||
| change_cash_decrease
|
||||
| forbidden
|
||||
| unexpected_failure
|
||||
| unexpected_failure_no_trx
|
||||
@ -691,6 +727,8 @@ make_payment_tool(Code, PSys) when
|
||||
Code =:= no_preauth_timeout_failure orelse
|
||||
Code =:= no_preauth_suspend_default orelse
|
||||
Code =:= preauth_3ds_offsite orelse
|
||||
Code =:= change_cash_increase orelse
|
||||
Code =:= change_cash_decrease orelse
|
||||
Code =:= forbidden orelse
|
||||
Code =:= unexpected_failure orelse
|
||||
Code =:= unexpected_failure_when_suspended orelse
|
||||
|
@ -42,6 +42,7 @@
|
||||
make_wallet_payment_params/1,
|
||||
execute_payment/3,
|
||||
process_payment/3,
|
||||
get_payment_cost/3,
|
||||
make_cash/1,
|
||||
make_cash/2,
|
||||
make_customer_w_rec_tool/4
|
||||
@ -293,6 +294,7 @@ await_payment_capture(InvoiceID, PaymentID, Reason, TrxID, Client) ->
|
||||
next_change(InvoiceID, Client)),
|
||||
await_payment_capture_finish(InvoiceID, PaymentID, Reason, Client).
|
||||
|
||||
-spec get_payment_cost(_, _, _) -> _.
|
||||
get_payment_cost(InvoiceID, PaymentID, Client) ->
|
||||
#payproc_InvoicePayment{
|
||||
payment = #domain_InvoicePayment{cost = Cost}
|
||||
|
@ -59,6 +59,8 @@
|
||||
-export([payments_w_bank_card_issuer_conditions/1]).
|
||||
-export([payments_w_bank_conditions/1]).
|
||||
-export([payment_success_on_second_try/1]).
|
||||
-export([payment_success_with_increased_cost/1]).
|
||||
-export([payment_success_with_decreased_cost/1]).
|
||||
-export([payment_fail_after_silent_callback/1]).
|
||||
-export([invoice_success_on_third_payment/1]).
|
||||
-export([party_revision_check/1]).
|
||||
@ -68,6 +70,7 @@
|
||||
-export([payment_risk_score_check_timeout/1]).
|
||||
-export([invalid_payment_adjustment/1]).
|
||||
-export([payment_adjustment_success/1]).
|
||||
-export([payment_adjustment_w_amount_success/1]).
|
||||
-export([payment_adjustment_refunded_success/1]).
|
||||
-export([payment_adjustment_chargeback_success/1]).
|
||||
-export([payment_adjustment_captured_partial/1]).
|
||||
@ -310,6 +313,8 @@ groups() ->
|
||||
payment_w_another_party_customer,
|
||||
payment_w_deleted_customer,
|
||||
payment_success_on_second_try,
|
||||
payment_success_with_increased_cost,
|
||||
payment_success_with_decreased_cost,
|
||||
payment_fail_after_silent_callback,
|
||||
payment_temporary_unavailability_retry_success,
|
||||
payment_temporary_unavailability_too_many_retries,
|
||||
@ -325,6 +330,7 @@ groups() ->
|
||||
{adjustments, [], [
|
||||
invalid_payment_adjustment,
|
||||
payment_adjustment_success,
|
||||
payment_adjustment_w_amount_success,
|
||||
payment_adjustment_refunded_success,
|
||||
payment_adjustment_chargeback_success,
|
||||
payment_adjustment_captured_partial,
|
||||
@ -642,6 +648,7 @@ end_per_group(_Group, _C) ->
|
||||
-spec init_per_testcase(test_case_name(), config()) -> config().
|
||||
init_per_testcase(Name, C) when
|
||||
Name == payment_adjustment_success;
|
||||
Name == payment_adjustment_w_amount_success;
|
||||
Name == payment_adjustment_refunded_success;
|
||||
Name == payment_adjustment_chargeback_success;
|
||||
Name == payment_adjustment_captured_partial;
|
||||
@ -652,43 +659,43 @@ init_per_testcase(Name, C) when
|
||||
Revision = hg_domain:head(),
|
||||
Fixture = get_payment_adjustment_fixture(Revision),
|
||||
_ = hg_domain:upsert(Fixture),
|
||||
[{original_domain_revision, Revision} | init_per_testcase(C)];
|
||||
init_per_testcase(rounding_cashflow_volume, C) ->
|
||||
override_domain_fixture(fun get_cashflow_rounding_fixture/2, C);
|
||||
init_per_testcase(payments_w_bank_card_issuer_conditions, C) ->
|
||||
override_domain_fixture(fun payments_w_bank_card_issuer_conditions_fixture/2, C);
|
||||
init_per_testcase(payments_w_bank_conditions, C) ->
|
||||
override_domain_fixture(fun payments_w_bank_conditions_fixture/2, C);
|
||||
init_per_testcase(payment_w_misconfigured_routing_failed, C) ->
|
||||
override_domain_fixture(fun payment_w_misconfigured_routing_failed_fixture/2, C);
|
||||
init_per_testcase(ineligible_payment_partial_refund, C) ->
|
||||
override_domain_fixture(fun(_, _) -> construct_term_set_for_refund_eligibility_time(1) end, C);
|
||||
init_per_testcase(invalid_permit_partial_capture_in_service, C) ->
|
||||
override_domain_fixture(fun construct_term_set_for_partial_capture_service_permit/2, C);
|
||||
init_per_testcase(invalid_permit_partial_capture_in_provider, C) ->
|
||||
override_domain_fixture(fun construct_term_set_for_partial_capture_provider_permit/2, C);
|
||||
init_per_testcase(limit_hold_currency_error, C) ->
|
||||
override_domain_fixture(fun patch_limit_config_w_invalid_currency/2, C);
|
||||
init_per_testcase(limit_hold_operation_not_supported, C) ->
|
||||
override_domain_fixture(fun patch_limit_config_for_withdrawal/2, C);
|
||||
init_per_testcase(limit_hold_payment_tool_not_supported, C) ->
|
||||
override_domain_fixture(fun patch_with_unsupported_payment_tool/2, C);
|
||||
init_per_testcase(limit_hold_two_routes_failure, C) ->
|
||||
override_domain_fixture(fun patch_providers_limits_to_fail_and_overflow/2, C);
|
||||
init_per_testcase(repair_fail_routing_succeeded, C) ->
|
||||
[{original_domain_revision, Revision} | init_per_testcase_(Name, C)];
|
||||
init_per_testcase(Name = rounding_cashflow_volume, C) ->
|
||||
override_domain_fixture(fun get_cashflow_rounding_fixture/2, Name, C);
|
||||
init_per_testcase(Name = payments_w_bank_card_issuer_conditions, C) ->
|
||||
override_domain_fixture(fun payments_w_bank_card_issuer_conditions_fixture/2, Name, C);
|
||||
init_per_testcase(Name = payments_w_bank_conditions, C) ->
|
||||
override_domain_fixture(fun payments_w_bank_conditions_fixture/2, Name, C);
|
||||
init_per_testcase(Name = payment_w_misconfigured_routing_failed, C) ->
|
||||
override_domain_fixture(fun payment_w_misconfigured_routing_failed_fixture/2, Name, C);
|
||||
init_per_testcase(Name = ineligible_payment_partial_refund, C) ->
|
||||
override_domain_fixture(fun(_, _) -> construct_term_set_for_refund_eligibility_time(1) end, Name, C);
|
||||
init_per_testcase(Name = invalid_permit_partial_capture_in_service, C) ->
|
||||
override_domain_fixture(fun construct_term_set_for_partial_capture_service_permit/2, Name, C);
|
||||
init_per_testcase(Name = invalid_permit_partial_capture_in_provider, C) ->
|
||||
override_domain_fixture(fun construct_term_set_for_partial_capture_provider_permit/2, Name, C);
|
||||
init_per_testcase(Name = limit_hold_currency_error, C) ->
|
||||
override_domain_fixture(fun patch_limit_config_w_invalid_currency/2, Name, C);
|
||||
init_per_testcase(Name = limit_hold_operation_not_supported, C) ->
|
||||
override_domain_fixture(fun patch_limit_config_for_withdrawal/2, Name, C);
|
||||
init_per_testcase(Name = limit_hold_payment_tool_not_supported, C) ->
|
||||
override_domain_fixture(fun patch_with_unsupported_payment_tool/2, Name, C);
|
||||
init_per_testcase(Name = limit_hold_two_routes_failure, C) ->
|
||||
override_domain_fixture(fun patch_providers_limits_to_fail_and_overflow/2, Name, C);
|
||||
init_per_testcase(Name = repair_fail_routing_succeeded, C) ->
|
||||
meck:expect(
|
||||
hg_limiter,
|
||||
check_limits,
|
||||
fun override_check_limits/4
|
||||
),
|
||||
init_per_testcase(C);
|
||||
init_per_testcase(repair_fail_cash_flow_building_succeeded, C) ->
|
||||
init_per_testcase_(Name, C);
|
||||
init_per_testcase(Name = repair_fail_cash_flow_building_succeeded, C) ->
|
||||
meck:expect(
|
||||
hg_cashflow_utils,
|
||||
collect_cashflow,
|
||||
fun override_collect_cashflow/1
|
||||
),
|
||||
init_per_testcase(C);
|
||||
init_per_testcase_(Name, C);
|
||||
init_per_testcase(Name, C) ->
|
||||
GroupProps = cfg(tc_group_properties, C),
|
||||
C1 =
|
||||
@ -698,7 +705,7 @@ init_per_testcase(Name, C) ->
|
||||
_ ->
|
||||
C
|
||||
end,
|
||||
init_per_testcase(C1).
|
||||
init_per_testcase_(Name, C1).
|
||||
|
||||
override_check_limits(_, _, _, _) -> throw(unknown).
|
||||
-dialyzer({nowarn_function, override_check_limits/4}).
|
||||
@ -709,14 +716,24 @@ override_collect_cashflow(_) -> throw(unknown).
|
||||
override_domain_fixture(Fixture, C) ->
|
||||
Revision = hg_domain:head(),
|
||||
_ = hg_domain:upsert(Fixture(Revision, C)),
|
||||
[{original_domain_revision, Revision} | init_per_testcase(C)].
|
||||
[{original_domain_revision, Revision} | C].
|
||||
|
||||
init_per_testcase(C) ->
|
||||
override_domain_fixture(Fixture, Name, C) ->
|
||||
init_per_testcase_(Name, override_domain_fixture(Fixture, C)).
|
||||
|
||||
init_per_testcase_(Name, C) ->
|
||||
ApiClient = hg_ct_helper:create_client(cfg(root_url, C)),
|
||||
Client = hg_client_invoicing:start_link(ApiClient),
|
||||
ClientTpl = hg_client_invoice_templating:start_link(ApiClient),
|
||||
ok = hg_context:save(hg_context:create()),
|
||||
[{client, Client}, {client_tpl, ClientTpl} | C].
|
||||
[{client, Client}, {client_tpl, ClientTpl} | trace_testcase(Name, C)].
|
||||
|
||||
trace_testcase(Name, C) ->
|
||||
SpanName = iolist_to_binary([atom_to_binary(?MODULE), ":", atom_to_binary(Name), "/1"]),
|
||||
SpanCtx = otel_tracer:start_span(opentelemetry:get_application_tracer(?MODULE), SpanName, #{kind => internal}),
|
||||
%% NOTE This also puts otel context to process dictionary
|
||||
_ = otel_tracer:set_current_span(SpanCtx),
|
||||
[{span_ctx, SpanCtx} | C].
|
||||
|
||||
-spec end_per_testcase(test_case_name(), config()) -> _.
|
||||
end_per_testcase(repair_fail_routing_succeeded, C) ->
|
||||
@ -726,6 +743,7 @@ end_per_testcase(repair_fail_cash_flow_building_succeeded, C) ->
|
||||
meck:unload(hg_cashflow_utils),
|
||||
end_per_testcase(default, C);
|
||||
end_per_testcase(_Name, C) ->
|
||||
ok = maybe_end_trace(C),
|
||||
ok = hg_context:cleanup(),
|
||||
_ =
|
||||
case cfg(original_domain_revision, C) of
|
||||
@ -735,6 +753,15 @@ end_per_testcase(_Name, C) ->
|
||||
ok
|
||||
end.
|
||||
|
||||
maybe_end_trace(C) ->
|
||||
case lists:keyfind(span_ctx, 1, C) of
|
||||
{span_ctx, SpanCtx} ->
|
||||
_ = otel_span:end_span(SpanCtx),
|
||||
ok;
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
|
||||
-spec invoice_creation_idempotency(config()) -> _ | no_return().
|
||||
invoice_creation_idempotency(C) ->
|
||||
Client = cfg(client, C),
|
||||
@ -1864,6 +1891,55 @@ payment_success_on_second_try(C) ->
|
||||
PaymentID = await_payment_process_finish(InvoiceID, PaymentID, Client),
|
||||
PaymentID = await_payment_capture(InvoiceID, PaymentID, Client).
|
||||
|
||||
-spec payment_success_with_increased_cost(config()) -> test_return().
|
||||
payment_success_with_increased_cost(C) ->
|
||||
Client = cfg(client, C),
|
||||
InvoiceID = start_invoice(<<"rubberduck">>, make_due_date(10), 42000, C),
|
||||
{PaymentTool, Session} = hg_dummy_provider:make_payment_tool(change_cash_increase, ?pmt_sys(<<"visa-ref">>)),
|
||||
PaymentParams = make_payment_params(PaymentTool, Session, instant),
|
||||
PaymentID = execute_cash_changed_payment(InvoiceID, PaymentParams, Client),
|
||||
?invoice_state(
|
||||
?invoice_w_status(?invoice_paid()),
|
||||
[?payment_state(State)]
|
||||
) = hg_client_invoicing:get(InvoiceID, Client),
|
||||
?payment_w_status(PaymentID, ?captured()) = State,
|
||||
?payment_w_changed_cost(ChangedCost) = State,
|
||||
?assertEqual(
|
||||
#domain_Cash{amount = 42000 * 2, currency = ?cur(<<"RUB">>)},
|
||||
ChangedCost
|
||||
).
|
||||
|
||||
-spec payment_success_with_decreased_cost(config()) -> test_return().
|
||||
payment_success_with_decreased_cost(C) ->
|
||||
Client = cfg(client, C),
|
||||
InvoiceID = start_invoice(<<"rubberduck">>, make_due_date(10), 42000, C),
|
||||
{PaymentTool, Session} = hg_dummy_provider:make_payment_tool(change_cash_decrease, ?pmt_sys(<<"visa-ref">>)),
|
||||
PaymentParams = make_payment_params(PaymentTool, Session, instant),
|
||||
PaymentID = execute_cash_changed_payment(InvoiceID, PaymentParams, Client),
|
||||
?invoice_state(
|
||||
?invoice_w_status(?invoice_paid()),
|
||||
[?payment_state(State)]
|
||||
) = hg_client_invoicing:get(InvoiceID, Client),
|
||||
?payment_w_status(PaymentID, ?captured()) = State,
|
||||
?payment_w_changed_cost(ChangedCost) = State,
|
||||
?assertEqual(
|
||||
#domain_Cash{amount = 42000 div 2, currency = ?cur(<<"RUB">>)},
|
||||
ChangedCost
|
||||
).
|
||||
|
||||
execute_cash_changed_payment(InvoiceID, PaymentParams, Client) ->
|
||||
PaymentID = hg_invoice_helper:start_payment(InvoiceID, PaymentParams, Client),
|
||||
PaymentID = hg_invoice_helper:await_payment_session_started(InvoiceID, PaymentID, Client, ?processed()),
|
||||
[
|
||||
?payment_ev(PaymentID, ?session_ev(?processed(), ?trx_bound(?trx_info(_)))),
|
||||
?payment_ev(PaymentID, ?cash_changed(_, _)),
|
||||
?payment_ev(PaymentID, ?session_ev(?processed(), ?session_finished(?session_succeeded()))),
|
||||
?payment_ev(PaymentID, ?cash_flow_changed(_)),
|
||||
?payment_ev(PaymentID, ?payment_status_changed(?processed()))
|
||||
] = next_changes(InvoiceID, 5, Client),
|
||||
PaymentID = hg_invoice_helper:await_payment_capture(InvoiceID, PaymentID, Client),
|
||||
PaymentID.
|
||||
|
||||
-spec payment_fail_after_silent_callback(config()) -> _ | no_return().
|
||||
payment_fail_after_silent_callback(C) ->
|
||||
Client = cfg(client, C),
|
||||
@ -2160,6 +2236,94 @@ payment_adjustment_success(C) ->
|
||||
SysDiff = MrcDiff - PrvDiff - 20,
|
||||
SysDiff = maps:get(own_amount, SysAccount2) - maps:get(own_amount, SysAccount1).
|
||||
|
||||
-spec payment_adjustment_w_amount_success(config()) -> test_return().
|
||||
payment_adjustment_w_amount_success(C) ->
|
||||
%% NOTE See share definitions macro
|
||||
%% Old cashflow, `?share(21, 1000, operation_amount))` :
|
||||
%% TO | merch | syst | prov | ext
|
||||
%% FROM---|--------|-------|---------|-----
|
||||
%% merch | 0 | 4500 | -100000 | 0
|
||||
%% syst | -4500 | 0 | 2100 | 0
|
||||
%% prov | 100000 | -2100 | 0 | 0
|
||||
%% ext | 0 | 0 | 0 | 0
|
||||
%%
|
||||
%% DIFF---| 95500 | 2400 | -97900 | 0
|
||||
%%
|
||||
%% New (adjusted) cashflow, `?share(16, 1000, operation_amount))` :
|
||||
%% TO | merch | syst | prov | ext
|
||||
%% FROM---|--------|-------|---------|-----
|
||||
%% merch | 0 | 9000 | -200000 | 0
|
||||
%% syst | -9000 | 0 | 3200 | 20
|
||||
%% prov | 200000 | -3200 | 0 | 0
|
||||
%% ext | 0 | -20 | 0 | 0
|
||||
%%
|
||||
%% DIFF---| 191000 | 5780 | -196800 | 20
|
||||
|
||||
Client = cfg(client, C),
|
||||
|
||||
OriginalAmount = 100000,
|
||||
NewAmount = 200000,
|
||||
|
||||
InvoiceID = start_invoice(<<"rubberduck">>, make_due_date(10), OriginalAmount, C),
|
||||
%% start a healthy man's payment
|
||||
PaymentParams = make_payment_params(?pmt_sys(<<"visa-ref">>)),
|
||||
?payment_state(?payment(PaymentID)) = hg_client_invoicing:start_payment(InvoiceID, PaymentParams, Client),
|
||||
?payment_ev(PaymentID, ?payment_started(?payment_w_status(?pending()))) =
|
||||
next_change(InvoiceID, Client),
|
||||
{CF1, Route} = await_payment_cash_flow(InvoiceID, PaymentID, Client),
|
||||
?payment_ev(PaymentID, ?session_ev(?processed(), ?session_started())) =
|
||||
next_change(InvoiceID, Client),
|
||||
PaymentID = await_payment_process_finish(InvoiceID, PaymentID, Client),
|
||||
PaymentID = await_payment_capture(InvoiceID, PaymentID, Client),
|
||||
CFContext = construct_ta_context(cfg(party_id, C), cfg(shop_id, C), Route),
|
||||
PrvAccount1 = get_deprecated_cashflow_account({provider, settlement}, CF1, CFContext),
|
||||
SysAccount1 = get_deprecated_cashflow_account({system, settlement}, CF1, CFContext),
|
||||
MrcAccount1 = get_deprecated_cashflow_account({merchant, settlement}, CF1, CFContext),
|
||||
|
||||
?payment_state(#domain_InvoicePayment{cost = OriginalCost}) =
|
||||
hg_client_invoicing:get_payment(InvoiceID, PaymentID, Client),
|
||||
?assertEqual(?cash(OriginalAmount, <<"RUB">>), OriginalCost),
|
||||
|
||||
%% update terminal cashflow
|
||||
ok = update_payment_terms_cashflow(?prv(100), get_payment_adjustment_provider_cashflow(actual)),
|
||||
|
||||
%% make an adjustment
|
||||
Params = make_adjustment_params(Reason = <<"imdrunk">>, undefined, NewAmount),
|
||||
?adjustment(AdjustmentID, ?adjustment_pending()) =
|
||||
Adjustment =
|
||||
hg_client_invoicing:create_payment_adjustment(InvoiceID, PaymentID, Params, Client),
|
||||
Adjustment =
|
||||
#domain_InvoicePaymentAdjustment{id = AdjustmentID, reason = Reason} =
|
||||
hg_client_invoicing:get_payment_adjustment(InvoiceID, PaymentID, AdjustmentID, Client),
|
||||
?payment_ev(PaymentID, ?adjustment_ev(AdjustmentID, ?adjustment_created(Adjustment))) =
|
||||
next_change(InvoiceID, Client),
|
||||
[
|
||||
?payment_ev(PaymentID, ?cash_changed(?cash(OriginalAmount, <<"RUB">>), ?cash(NewAmount, <<"RUB">>))),
|
||||
?payment_ev(PaymentID, ?adjustment_ev(AdjustmentID, ?adjustment_status_changed(?adjustment_processed()))),
|
||||
?payment_ev(PaymentID, ?adjustment_ev(AdjustmentID, ?adjustment_status_changed(?adjustment_captured(_))))
|
||||
] = next_changes(InvoiceID, 3, Client),
|
||||
%% verify that cash deposited correctly everywhere
|
||||
#domain_InvoicePaymentAdjustment{new_cash_flow = DCF2} = Adjustment,
|
||||
PrvAccount2 = get_deprecated_cashflow_account({provider, settlement}, DCF2, CFContext),
|
||||
SysAccount2 = get_deprecated_cashflow_account({system, settlement}, DCF2, CFContext),
|
||||
MrcAccount2 = get_deprecated_cashflow_account({merchant, settlement}, DCF2, CFContext),
|
||||
|
||||
%% NOTE See cashflow table
|
||||
ZeroShare = ?share(0, 100, operation_amount),
|
||||
{OpDiffMrc, OpDiffSys, OpDiffPrv} = compute_operation_amount_diffs(
|
||||
OriginalAmount, ?merchant_to_system_share_1, ?system_to_provider_share_initial, ZeroShare
|
||||
),
|
||||
{NewOpDiffMrc, NewOpDiffSys, NewOpDiffPrv} = compute_operation_amount_diffs(
|
||||
NewAmount, ?merchant_to_system_share_1, ?system_to_provider_share_actual, ?system_to_external_fixed
|
||||
),
|
||||
?assertEqual(NewOpDiffMrc - OpDiffMrc, maps:get(own_amount, MrcAccount2) - maps:get(own_amount, MrcAccount1)),
|
||||
?assertEqual(NewOpDiffPrv - OpDiffPrv, maps:get(own_amount, PrvAccount2) - maps:get(own_amount, PrvAccount1)),
|
||||
?assertEqual(NewOpDiffSys - OpDiffSys, maps:get(own_amount, SysAccount2) - maps:get(own_amount, SysAccount1)),
|
||||
|
||||
?payment_state(#domain_InvoicePayment{cost = OriginalCost, changed_cost = NewCost}) =
|
||||
hg_client_invoicing:get_payment(InvoiceID, PaymentID, Client),
|
||||
?assertEqual(?cash(NewAmount, <<"RUB">>), NewCost).
|
||||
|
||||
-spec payment_adjustment_refunded_success(config()) -> test_return().
|
||||
payment_adjustment_refunded_success(C) ->
|
||||
Client = cfg(client, C),
|
||||
@ -2569,6 +2733,17 @@ update_payment_terms_cashflow(ProviderRef, CashFlow) ->
|
||||
),
|
||||
ok.
|
||||
|
||||
compute_operation_amount_share(Amount, Share) ->
|
||||
Context = #{operation_amount => ?cash(Amount, <<"RUB">>)},
|
||||
#domain_Cash{amount = ResultAmount} = hg_cashflow:compute_volume(Share, Context),
|
||||
ResultAmount.
|
||||
|
||||
compute_operation_amount_diffs(Amount, MrcSysShare, SysPrvShare, SysExtShare) ->
|
||||
MrcSys = compute_operation_amount_share(Amount, MrcSysShare),
|
||||
SysExt = compute_operation_amount_share(Amount, SysExtShare),
|
||||
SysPrv = compute_operation_amount_share(Amount, SysPrvShare),
|
||||
{Amount - MrcSys, MrcSys - SysPrv - SysExt, SysPrv - Amount}.
|
||||
|
||||
construct_ta_context(Party, Shop, Route) ->
|
||||
hg_invoice_helper:construct_ta_context(Party, Shop, Route).
|
||||
|
||||
@ -5734,7 +5909,7 @@ cascade_fixture(Revision, C) ->
|
||||
init_route_cascading_group(C1) ->
|
||||
PartyID = cfg(party_id, C1),
|
||||
PartyClient = cfg(party_client, C1),
|
||||
_ = override_domain_fixture(fun cascade_fixture_pre_shop_create/2, C1),
|
||||
_ = override_domain_fixture(fun cascade_fixture_pre_shop_create/2, undefined, C1),
|
||||
C2 = [
|
||||
{
|
||||
{shop_id, ?PAYMENT_CASCADE_SUCCESS_ID},
|
||||
@ -5802,7 +5977,7 @@ init_route_cascading_group(C1) ->
|
||||
}
|
||||
| C1
|
||||
],
|
||||
override_domain_fixture(fun cascade_fixture/2, C2).
|
||||
override_domain_fixture(fun cascade_fixture/2, undefined, C2).
|
||||
|
||||
init_per_cascade_case(payment_cascade_success, C) ->
|
||||
ShopID = cfg({shop_id, ?PAYMENT_CASCADE_SUCCESS_ID}, C),
|
||||
@ -6997,14 +7172,15 @@ make_adjustment_params() ->
|
||||
make_adjustment_params(<<>>).
|
||||
|
||||
make_adjustment_params(Reason) ->
|
||||
make_adjustment_params(Reason, undefined).
|
||||
make_adjustment_params(Reason, undefined, undefined).
|
||||
|
||||
make_adjustment_params(Reason, Revision) ->
|
||||
make_adjustment_params(Reason, Revision, Amount) ->
|
||||
#payproc_InvoicePaymentAdjustmentParams{
|
||||
reason = Reason,
|
||||
scenario =
|
||||
{cash_flow, #domain_InvoicePaymentAdjustmentCashFlow{
|
||||
domain_revision = Revision
|
||||
domain_revision = Revision,
|
||||
new_amount = Amount
|
||||
}}
|
||||
}.
|
||||
|
||||
|
@ -116,11 +116,11 @@ stop(Client) ->
|
||||
|
||||
-spec create(invoice_params(), pid()) -> invoice_state() | woody_error:business_error().
|
||||
create(InvoiceParams, Client) ->
|
||||
map_result_error(gen_server:call(Client, {call, 'Create', [InvoiceParams]})).
|
||||
map_result_error(gen_server:call(Client, {call, 'Create', [InvoiceParams], otel_ctx:get_current()})).
|
||||
|
||||
-spec create_with_tpl(invoice_params_tpl(), pid()) -> invoice_state() | woody_error:business_error().
|
||||
create_with_tpl(Params, Client) ->
|
||||
map_result_error(gen_server:call(Client, {call, 'CreateWithTemplate', [Params]})).
|
||||
map_result_error(gen_server:call(Client, {call, 'CreateWithTemplate', [Params], otel_ctx:get_current()})).
|
||||
|
||||
-spec get(invoice_id(), pid()) -> invoice_state() | woody_error:business_error().
|
||||
get(InvoiceID, Client) ->
|
||||
@ -128,44 +128,56 @@ get(InvoiceID, Client) ->
|
||||
|
||||
-spec get(invoice_id(), pid(), event_range()) -> invoice_state() | woody_error:business_error().
|
||||
get(InvoiceID, Client, EventRange) ->
|
||||
map_result_error(gen_server:call(Client, {call, 'Get', [InvoiceID, EventRange]})).
|
||||
map_result_error(gen_server:call(Client, {call, 'Get', [InvoiceID, EventRange], otel_ctx:get_current()})).
|
||||
|
||||
-spec fulfill(invoice_id(), binary(), pid()) -> ok | woody_error:business_error().
|
||||
fulfill(InvoiceID, Reason, Client) ->
|
||||
map_result_error(gen_server:call(Client, {call, 'Fulfill', [InvoiceID, Reason]})).
|
||||
map_result_error(gen_server:call(Client, {call, 'Fulfill', [InvoiceID, Reason], otel_ctx:get_current()})).
|
||||
|
||||
-spec rescind(invoice_id(), binary(), pid()) -> ok | woody_error:business_error().
|
||||
rescind(InvoiceID, Reason, Client) ->
|
||||
map_result_error(gen_server:call(Client, {call, 'Rescind', [InvoiceID, Reason]})).
|
||||
map_result_error(gen_server:call(Client, {call, 'Rescind', [InvoiceID, Reason], otel_ctx:get_current()})).
|
||||
|
||||
-spec repair(invoice_id(), [tuple()], tuple() | undefined, tuple() | undefined, pid()) ->
|
||||
ok | woody_error:business_error().
|
||||
repair(InvoiceID, Changes, Action, Params, Client) ->
|
||||
map_result_error(gen_server:call(Client, {call, 'Repair', [InvoiceID, Changes, Action, Params]})).
|
||||
map_result_error(
|
||||
gen_server:call(Client, {call, 'Repair', [InvoiceID, Changes, Action, Params], otel_ctx:get_current()})
|
||||
).
|
||||
|
||||
-spec repair_scenario(invoice_id(), hg_invoice_repair:scenario(), pid()) -> ok | woody_error:business_error().
|
||||
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], otel_ctx:get_current()})
|
||||
).
|
||||
|
||||
-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]})).
|
||||
map_result_error(
|
||||
gen_server:call(Client, {call, 'GetPaymentRoutesLimitValues', [InvoiceID, PaymentID], otel_ctx:get_current()})
|
||||
).
|
||||
|
||||
-spec start_payment(invoice_id(), payment_params(), pid()) -> payment_st() | woody_error:business_error().
|
||||
start_payment(InvoiceID, PaymentParams, Client) ->
|
||||
map_result_error(gen_server:call(Client, {call, 'StartPayment', [InvoiceID, PaymentParams]})).
|
||||
map_result_error(
|
||||
gen_server:call(Client, {call, 'StartPayment', [InvoiceID, PaymentParams], otel_ctx:get_current()})
|
||||
).
|
||||
|
||||
-spec register_payment(invoice_id(), register_payment_params(), pid()) -> payment() | woody_error:business_error().
|
||||
register_payment(InvoiceID, RegisterPaymentParams, Client) ->
|
||||
map_result_error(gen_server:call(Client, {call, 'RegisterPayment', [InvoiceID, RegisterPaymentParams]})).
|
||||
map_result_error(
|
||||
gen_server:call(Client, {call, 'RegisterPayment', [InvoiceID, RegisterPaymentParams], otel_ctx:get_current()})
|
||||
).
|
||||
|
||||
-spec get_payment(invoice_id(), payment_id(), pid()) -> payment_st() | woody_error:business_error().
|
||||
get_payment(InvoiceID, PaymentID, Client) ->
|
||||
map_result_error(gen_server:call(Client, {call, 'GetPayment', [InvoiceID, PaymentID]})).
|
||||
map_result_error(gen_server:call(Client, {call, 'GetPayment', [InvoiceID, PaymentID], otel_ctx:get_current()})).
|
||||
|
||||
-spec cancel_payment(invoice_id(), payment_id(), binary(), pid()) -> ok | woody_error:business_error().
|
||||
cancel_payment(InvoiceID, PaymentID, Reason, Client) ->
|
||||
map_result_error(gen_server:call(Client, {call, 'CancelPayment', [InvoiceID, PaymentID, Reason]})).
|
||||
map_result_error(
|
||||
gen_server:call(Client, {call, 'CancelPayment', [InvoiceID, PaymentID, Reason], otel_ctx:get_current()})
|
||||
).
|
||||
|
||||
-spec capture_payment(invoice_id(), payment_id(), binary(), pid()) -> ok | woody_error:business_error().
|
||||
capture_payment(InvoiceID, PaymentID, Reason, Client) ->
|
||||
@ -194,68 +206,99 @@ capture_payment(InvoiceID, PaymentID, Reason, Cash, Cart, Allocation, Client) ->
|
||||
cart = Cart,
|
||||
allocation = Allocation
|
||||
}
|
||||
]
|
||||
],
|
||||
otel_ctx:get_current()
|
||||
},
|
||||
map_result_error(gen_server:call(Client, Call)).
|
||||
|
||||
-spec create_chargeback(invoice_id(), payment_id(), chargeback_params(), pid()) ->
|
||||
chargeback() | woody_error:business_error().
|
||||
create_chargeback(InvoiceID, PaymentID, Params, Client) ->
|
||||
map_result_error(gen_server:call(Client, {call, 'CreateChargeback', [InvoiceID, PaymentID, Params]})).
|
||||
map_result_error(
|
||||
gen_server:call(Client, {call, 'CreateChargeback', [InvoiceID, PaymentID, Params], otel_ctx:get_current()})
|
||||
).
|
||||
|
||||
-spec cancel_chargeback(invoice_id(), payment_id(), chargeback_id(), chargeback_cancel_params(), pid()) ->
|
||||
chargeback() | woody_error:business_error().
|
||||
cancel_chargeback(InvoiceID, PaymentID, ChargebackID, Params, Client) ->
|
||||
map_result_error(gen_server:call(Client, {call, 'CancelChargeback', [InvoiceID, PaymentID, ChargebackID, Params]})).
|
||||
map_result_error(
|
||||
gen_server:call(
|
||||
Client, {call, 'CancelChargeback', [InvoiceID, PaymentID, ChargebackID, Params], otel_ctx:get_current()}
|
||||
)
|
||||
).
|
||||
|
||||
-spec reject_chargeback(invoice_id(), payment_id(), chargeback_id(), chargeback_reject_params(), pid()) ->
|
||||
chargeback() | woody_error:business_error().
|
||||
reject_chargeback(InvoiceID, PaymentID, ChargebackID, Params, Client) ->
|
||||
map_result_error(gen_server:call(Client, {call, 'RejectChargeback', [InvoiceID, PaymentID, ChargebackID, Params]})).
|
||||
map_result_error(
|
||||
gen_server:call(
|
||||
Client, {call, 'RejectChargeback', [InvoiceID, PaymentID, ChargebackID, Params], otel_ctx:get_current()}
|
||||
)
|
||||
).
|
||||
|
||||
-spec accept_chargeback(invoice_id(), payment_id(), chargeback_id(), chargeback_accept_params(), pid()) ->
|
||||
chargeback() | woody_error:business_error().
|
||||
accept_chargeback(InvoiceID, PaymentID, ChargebackID, Params, Client) ->
|
||||
map_result_error(gen_server:call(Client, {call, 'AcceptChargeback', [InvoiceID, PaymentID, ChargebackID, Params]})).
|
||||
map_result_error(
|
||||
gen_server:call(
|
||||
Client, {call, 'AcceptChargeback', [InvoiceID, PaymentID, ChargebackID, Params], otel_ctx:get_current()}
|
||||
)
|
||||
).
|
||||
|
||||
-spec reopen_chargeback(invoice_id(), payment_id(), chargeback_id(), chargeback_reopen_params(), pid()) ->
|
||||
chargeback() | woody_error:business_error().
|
||||
reopen_chargeback(InvoiceID, PaymentID, ChargebackID, Params, Client) ->
|
||||
map_result_error(gen_server:call(Client, {call, 'ReopenChargeback', [InvoiceID, PaymentID, ChargebackID, Params]})).
|
||||
map_result_error(
|
||||
gen_server:call(
|
||||
Client, {call, 'ReopenChargeback', [InvoiceID, PaymentID, ChargebackID, Params], otel_ctx:get_current()}
|
||||
)
|
||||
).
|
||||
|
||||
-spec get_payment_chargeback(invoice_id(), payment_id(), chargeback_id(), pid()) ->
|
||||
refund() | woody_error:business_error().
|
||||
get_payment_chargeback(InvoiceID, PaymentID, ChargebackID, Client) ->
|
||||
map_result_error(gen_server:call(Client, {call, 'GetPaymentChargeback', [InvoiceID, PaymentID, ChargebackID]})).
|
||||
map_result_error(
|
||||
gen_server:call(
|
||||
Client, {call, 'GetPaymentChargeback', [InvoiceID, PaymentID, ChargebackID], otel_ctx:get_current()}
|
||||
)
|
||||
).
|
||||
|
||||
-spec refund_payment(invoice_id(), payment_id(), refund_params(), pid()) -> refund() | woody_error:business_error().
|
||||
refund_payment(InvoiceID, PaymentID, Params, Client) ->
|
||||
map_result_error(gen_server:call(Client, {call, 'RefundPayment', [InvoiceID, PaymentID, Params]})).
|
||||
map_result_error(
|
||||
gen_server:call(Client, {call, 'RefundPayment', [InvoiceID, PaymentID, Params], otel_ctx:get_current()})
|
||||
).
|
||||
|
||||
-spec refund_payment_manual(invoice_id(), payment_id(), refund_params(), pid()) ->
|
||||
refund() | woody_error:business_error().
|
||||
refund_payment_manual(InvoiceID, PaymentID, Params, Client) ->
|
||||
map_result_error(gen_server:call(Client, {call, 'CreateManualRefund', [InvoiceID, PaymentID, Params]})).
|
||||
map_result_error(
|
||||
gen_server:call(Client, {call, 'CreateManualRefund', [InvoiceID, PaymentID, Params], otel_ctx:get_current()})
|
||||
).
|
||||
|
||||
-spec get_payment_refund(invoice_id(), payment_id(), refund_id(), pid()) -> refund() | woody_error:business_error().
|
||||
get_payment_refund(InvoiceID, PaymentID, RefundID, Client) ->
|
||||
map_result_error(gen_server:call(Client, {call, 'GetPaymentRefund', [InvoiceID, PaymentID, RefundID]})).
|
||||
map_result_error(
|
||||
gen_server:call(Client, {call, 'GetPaymentRefund', [InvoiceID, PaymentID, RefundID], otel_ctx:get_current()})
|
||||
).
|
||||
|
||||
-spec create_payment_adjustment(invoice_id(), payment_id(), payment_adjustment_params(), pid()) ->
|
||||
payment_adjustment() | woody_error:business_error().
|
||||
create_payment_adjustment(InvoiceID, PaymentID, ID, Client) ->
|
||||
Args = [InvoiceID, PaymentID, ID],
|
||||
map_result_error(gen_server:call(Client, {call, 'CreatePaymentAdjustment', Args})).
|
||||
map_result_error(gen_server:call(Client, {call, 'CreatePaymentAdjustment', Args, otel_ctx:get_current()})).
|
||||
|
||||
-spec get_payment_adjustment(invoice_id(), payment_id(), payment_adjustment_id(), pid()) ->
|
||||
payment_adjustment() | woody_error:business_error().
|
||||
get_payment_adjustment(InvoiceID, PaymentID, Params, Client) ->
|
||||
Args = [InvoiceID, PaymentID, Params],
|
||||
map_result_error(gen_server:call(Client, {call, 'GetPaymentAdjustment', Args})).
|
||||
map_result_error(gen_server:call(Client, {call, 'GetPaymentAdjustment', Args, otel_ctx:get_current()})).
|
||||
|
||||
-spec compute_terms(invoice_id(), party_revision_param(), pid()) -> term_set().
|
||||
compute_terms(InvoiceID, PartyRevision, Client) ->
|
||||
map_result_error(gen_server:call(Client, {call, 'ComputeTerms', [InvoiceID, PartyRevision]})).
|
||||
map_result_error(
|
||||
gen_server:call(Client, {call, 'ComputeTerms', [InvoiceID, PartyRevision], otel_ctx:get_current()})
|
||||
).
|
||||
|
||||
-define(DEFAULT_NEXT_EVENT_TIMEOUT, 5000).
|
||||
|
||||
@ -267,7 +310,7 @@ pull_event(InvoiceID, Client) ->
|
||||
-spec pull_event(invoice_id(), timeout(), pid()) ->
|
||||
tuple() | timeout | woody_error:business_error().
|
||||
pull_event(InvoiceID, Timeout, Client) ->
|
||||
gen_server:call(Client, {pull_event, InvoiceID, Timeout}, infinity).
|
||||
gen_server:call(Client, {pull_event, InvoiceID, Timeout, otel_ctx:get_current()}, infinity).
|
||||
|
||||
-spec pull_change(invoice_id(), fun((_Elem) -> boolean() | {'true', _Value}), timeout(), pid()) ->
|
||||
tuple() | timeout | woody_error:business_error().
|
||||
@ -278,7 +321,7 @@ pull_change(InvoiceID, FilterMapFun, PullTimeout, Client) ->
|
||||
pull_change_(InvoiceID, FilterMapFun, Deadline, Client) ->
|
||||
case erlang:monotonic_time(millisecond) of
|
||||
Time when Time < Deadline ->
|
||||
case gen_server:call(Client, {pull_change, InvoiceID, Deadline - Time}, infinity) of
|
||||
case gen_server:call(Client, {pull_change, InvoiceID, Deadline - Time, otel_ctx:get_current()}, infinity) of
|
||||
{ok, Change} ->
|
||||
case FilterMapFun(Change) of
|
||||
true ->
|
||||
@ -321,13 +364,16 @@ init(ApiClient) ->
|
||||
{ok, #state{pollers = #{}, client = ApiClient}}.
|
||||
|
||||
-spec handle_call(term(), callref(), state()) -> {reply, term(), state()} | {noreply, state()}.
|
||||
handle_call({call, Function, Args}, _From, St = #state{client = Client}) ->
|
||||
handle_call({call, Function, Args, OtelCtx}, _From, St = #state{client = Client}) ->
|
||||
_ = otel_ctx:attach(OtelCtx),
|
||||
{Result, ClientNext} = hg_client_api:call(invoicing, Function, Args, Client),
|
||||
{reply, Result, St#state{client = ClientNext}};
|
||||
handle_call({pull_event, InvoiceID, Timeout}, _From, St) ->
|
||||
handle_call({pull_event, InvoiceID, Timeout, OtelCtx}, _From, St) ->
|
||||
_ = otel_ctx:attach(OtelCtx),
|
||||
{Result, StNext} = handle_pull_event(InvoiceID, Timeout, St),
|
||||
{reply, Result, StNext};
|
||||
handle_call({pull_change, InvoiceID, Timeout}, _From, St) ->
|
||||
handle_call({pull_change, InvoiceID, Timeout, OtelCtx}, _From, St) ->
|
||||
_ = otel_ctx:attach(OtelCtx),
|
||||
{Result, StNext} = handle_pull_change(InvoiceID, Timeout, St),
|
||||
{reply, Result, StNext};
|
||||
handle_call(Call, _From, State) ->
|
||||
|
@ -1,5 +1,26 @@
|
||||
services:
|
||||
|
||||
dominant:
|
||||
environment: &otlp_enabled
|
||||
OTEL_TRACES_EXPORTER: otlp
|
||||
OTEL_TRACES_SAMPLER: parentbased_always_off
|
||||
OTEL_EXPORTER_OTLP_PROTOCOL: http_protobuf
|
||||
OTEL_EXPORTER_OTLP_ENDPOINT: http://jaeger:4318
|
||||
|
||||
bender:
|
||||
environment: *otlp_enabled
|
||||
|
||||
limiter:
|
||||
environment: *otlp_enabled
|
||||
|
||||
party-management:
|
||||
environment: *otlp_enabled
|
||||
|
||||
testrunner:
|
||||
environment:
|
||||
<<: *otlp_enabled
|
||||
OTEL_SERVICE_NAME: hellgate_testrunner
|
||||
OTEL_TRACES_SAMPLER: parentbased_always_on
|
||||
depends_on:
|
||||
jaeger:
|
||||
condition: service_healthy
|
||||
|
@ -20,7 +20,7 @@
|
||||
{<<"ctx">>,{pkg,<<"ctx">>,<<"0.6.0">>},2},
|
||||
{<<"damsel">>,
|
||||
{git,"https://github.com/valitydev/damsel.git",
|
||||
{ref,"caf49f8fe73b35da695726292755309238143f76"}},
|
||||
{ref,"5b69a5bb59529a22f2bf0bbd78b7539773db3562"}},
|
||||
0},
|
||||
{<<"dmt_client">>,
|
||||
{git,"https://github.com/valitydev/dmt-client.git",
|
||||
|
Loading…
Reference in New Issue
Block a user