mirror of
https://github.com/valitydev/hellgate.git
synced 2024-11-06 10:55:22 +00:00
Implement ad-hoc repairs w/ the ability to push arbitrary changes (#179)
* Make misbehaving testcase revert domain config alterations at the end
This commit is contained in:
parent
0791b44eee
commit
c658f1f4fd
@ -213,7 +213,13 @@ handle_function_('ComputeTerms', [UserInfo, InvoiceID], _Opts) ->
|
||||
ShopTerms = hg_invoice_utils:compute_shop_terms(UserInfo, PartyID, ShopID, Timestamp),
|
||||
Revision = hg_domain:head(),
|
||||
Cash = get_cost(St),
|
||||
hg_party:reduce_terms(ShopTerms, #{cost => Cash}, Revision).
|
||||
hg_party:reduce_terms(ShopTerms, #{cost => Cash}, Revision);
|
||||
|
||||
handle_function_('Repair', [UserInfo, InvoiceID, Changes], _Opts) ->
|
||||
ok = assume_user_identity(UserInfo),
|
||||
_ = set_invoicing_meta(InvoiceID),
|
||||
_ = assert_invoice_accessible(get_initial_state(InvoiceID)),
|
||||
repair(InvoiceID, {changes, Changes}).
|
||||
|
||||
assert_invoice_operable(St) ->
|
||||
% FIXME do not lose party here
|
||||
@ -304,6 +310,9 @@ start(ID, Args) ->
|
||||
call(ID, Args) ->
|
||||
map_error(hg_machine:call(?NS, ID, Args)).
|
||||
|
||||
repair(ID, Args) ->
|
||||
map_repair_error(hg_machine:repair(?NS, ID, Args)).
|
||||
|
||||
map_error({ok, CallResult}) ->
|
||||
case CallResult of
|
||||
{ok, Result} ->
|
||||
@ -326,6 +335,16 @@ map_start_error({ok, _}) ->
|
||||
map_start_error({error, Reason}) ->
|
||||
error(Reason).
|
||||
|
||||
map_repair_error({ok, _}) ->
|
||||
ok;
|
||||
map_repair_error({error, notfound}) ->
|
||||
throw(#payproc_InvoiceNotFound{});
|
||||
map_repair_error({error, working}) ->
|
||||
% TODO
|
||||
throw(#'InvalidRequest'{errors = [<<"No need to repair">>]});
|
||||
map_repair_error({error, Reason}) ->
|
||||
error(Reason).
|
||||
|
||||
%%
|
||||
|
||||
-type invoice() :: dmsl_domain_thrift:'Invoice'().
|
||||
@ -388,8 +407,14 @@ handle_signal(timeout, St = #st{activity = invoice}) ->
|
||||
% invoice is expired
|
||||
handle_expiration(St);
|
||||
|
||||
handle_signal({repair, _}, St) ->
|
||||
#{
|
||||
handle_signal({repair, {changes, Changes}}, St) ->
|
||||
Result = case Changes of
|
||||
[_ | _] ->
|
||||
#{changes => Changes};
|
||||
[] ->
|
||||
#{}
|
||||
end,
|
||||
Result#{
|
||||
state => St
|
||||
}.
|
||||
|
||||
|
@ -17,6 +17,8 @@
|
||||
%%% - think about safe clamping of timers returned by some proxy
|
||||
%%% - why don't user interaction events imprint anything on the state?
|
||||
%%% - adjustments look and behave very much like claims over payments
|
||||
%%% - payment status transition are caused by the fact that some session
|
||||
%%% finishes, which could have happened in the past, not just now
|
||||
|
||||
-module(hg_invoice_payment).
|
||||
-include_lib("dmsl/include/dmsl_proxy_provider_thrift.hrl").
|
||||
|
@ -64,6 +64,7 @@
|
||||
|
||||
-export([start/3]).
|
||||
-export([call/3]).
|
||||
-export([repair/3]).
|
||||
-export([get_history/2]).
|
||||
-export([get_history/4]).
|
||||
|
||||
@ -108,6 +109,13 @@ call(Ns, Ref, Args) ->
|
||||
Error
|
||||
end.
|
||||
|
||||
-spec repair(ns(), ref(), term()) ->
|
||||
{ok, term()} | {error, notfound | failed | working} | no_return().
|
||||
|
||||
repair(Ns, Ref, Args) ->
|
||||
Descriptor = prepare_descriptor(Ns, Ref, #'HistoryRange'{}),
|
||||
call_automaton('Repair', [Descriptor, wrap_args(Args)]).
|
||||
|
||||
-spec get_history(ns(), ref()) ->
|
||||
{ok, history()} | {error, notfound} | no_return().
|
||||
|
||||
@ -140,7 +148,9 @@ call_automaton(Function, Args) ->
|
||||
{exception, #'MachineNotFound'{}} ->
|
||||
{error, notfound};
|
||||
{exception, #'MachineFailed'{}} ->
|
||||
{error, failed}
|
||||
{error, failed};
|
||||
{exception, #'MachineAlreadyWorking'{}} ->
|
||||
{error, working}
|
||||
end.
|
||||
|
||||
%%
|
||||
|
@ -139,6 +139,8 @@ generate_token(undefined, #prxprv_RecurrentTokenInfo{payment_tool = RecurrentPay
|
||||
#prxprv_RecurrentTokenProxyResult{
|
||||
intent = ?recurrent_token_finish_w_failure(#'Failure'{code = <<"forbidden">>})
|
||||
};
|
||||
unexpected_failure ->
|
||||
error(unexpected_failure);
|
||||
_ ->
|
||||
token_sleep(1, <<"sleeping">>)
|
||||
end;
|
||||
@ -240,10 +242,17 @@ process_payment(?processed(), undefined, PaymentInfo, _) ->
|
||||
sleep(1, <<"sleeping">>);
|
||||
recurrent ->
|
||||
%% simple workflow without 3DS
|
||||
sleep(1, <<"sleeping">>)
|
||||
sleep(1, <<"sleeping">>);
|
||||
unexpected_failure ->
|
||||
sleep(1, <<"sleeping">>, undefined, get_payment_id(PaymentInfo))
|
||||
end;
|
||||
process_payment(?processed(), <<"sleeping">>, PaymentInfo, _) ->
|
||||
finish(?success(), get_payment_id(PaymentInfo));
|
||||
case get_payment_info_scenario(PaymentInfo) of
|
||||
unexpected_failure ->
|
||||
error(unexpected_failure);
|
||||
_ ->
|
||||
finish(?success(), get_payment_id(PaymentInfo))
|
||||
end;
|
||||
process_payment(?processed(), <<"sleeping_with_user_interaction">>, PaymentInfo, _) ->
|
||||
Key = {get_invoice_id(PaymentInfo), get_payment_id(PaymentInfo)},
|
||||
case get_transaction_state(Key) of
|
||||
@ -300,6 +309,13 @@ sleep(Timeout, State, UserInteraction) ->
|
||||
next_state = State
|
||||
}.
|
||||
|
||||
sleep(Timeout, State, UserInteraction, TrxID) ->
|
||||
#prxprv_PaymentProxyResult{
|
||||
intent = ?sleep(Timeout, UserInteraction),
|
||||
trx = #domain_TransactionInfo{id = TrxID, extra = #{}},
|
||||
next_state = State
|
||||
}.
|
||||
|
||||
suspend(Tag, Timeout, State, UserInteraction) ->
|
||||
#prxprv_PaymentProxyResult{
|
||||
intent = ?suspend(Tag, Timeout, UserInteraction),
|
||||
@ -344,6 +360,8 @@ get_payment_tool_scenario({'bank_card', #domain_BankCard{token = <<"preauth_3ds_
|
||||
preauth_3ds_offsite;
|
||||
get_payment_tool_scenario({'bank_card', #domain_BankCard{token = <<"forbidden">>}}) ->
|
||||
forbidden;
|
||||
get_payment_tool_scenario({'bank_card', #domain_BankCard{token = <<"unexpected_failure">>}}) ->
|
||||
unexpected_failure;
|
||||
get_payment_tool_scenario({'payment_terminal', #domain_PaymentTerminal{terminal_type = euroset}}) ->
|
||||
terminal;
|
||||
get_payment_tool_scenario({'digital_wallet', #domain_DigitalWallet{provider = qiwi}}) ->
|
||||
@ -359,6 +377,8 @@ make_payment_tool(preauth_3ds_offsite) ->
|
||||
make_simple_payment_tool(<<"preauth_3ds_offsite">>, jcb);
|
||||
make_payment_tool(forbidden) ->
|
||||
make_simple_payment_tool(<<"forbidden">>, visa);
|
||||
make_payment_tool(unexpected_failure) ->
|
||||
make_simple_payment_tool(<<"unexpected_failure">>, visa);
|
||||
make_payment_tool(terminal) ->
|
||||
{
|
||||
{payment_terminal, #domain_PaymentTerminal{
|
||||
|
@ -53,6 +53,10 @@
|
||||
-export([payment_with_offsite_preauth_success/1]).
|
||||
-export([payment_with_offsite_preauth_failed/1]).
|
||||
-export([terms_retrieval/1]).
|
||||
|
||||
-export([adhoc_repair_working_failed/1]).
|
||||
-export([adhoc_repair_failed_succeeded/1]).
|
||||
|
||||
-export([consistent_history/1]).
|
||||
|
||||
%%
|
||||
@ -120,6 +124,9 @@ all() ->
|
||||
|
||||
terms_retrieval,
|
||||
|
||||
adhoc_repair_working_failed,
|
||||
adhoc_repair_failed_succeeded,
|
||||
|
||||
consistent_history
|
||||
].
|
||||
|
||||
@ -1106,11 +1113,47 @@ terms_retrieval(C) ->
|
||||
?pmt(payment_terminal, euroset)
|
||||
]}
|
||||
}} = TermSet1,
|
||||
Revision = hg_domain:head(),
|
||||
ok = hg_domain:update(construct_term_set_for_cost(1000, 2000)),
|
||||
TermSet2 = hg_client_invoicing:compute_terms(InvoiceID, Client),
|
||||
#domain_TermSet{payments = #domain_PaymentsServiceTerms{
|
||||
payment_methods = {value, [?pmt(bank_card, visa)]}
|
||||
}} = TermSet2.
|
||||
}} = TermSet2,
|
||||
ok = hg_domain:reset(Revision).
|
||||
|
||||
%%
|
||||
|
||||
-spec adhoc_repair_working_failed(config()) -> _ | no_return().
|
||||
|
||||
adhoc_repair_working_failed(C) ->
|
||||
Client = cfg(client, C),
|
||||
InvoiceID = start_invoice(<<"rubbercrack">>, make_due_date(10), 42000, C),
|
||||
PaymentParams = make_payment_params(),
|
||||
PaymentID = start_payment(InvoiceID, PaymentParams, Client),
|
||||
{exception, #'InvalidRequest'{}} = repair_invoice(InvoiceID, [], Client),
|
||||
PaymentID = await_payment_process_finish(InvoiceID, PaymentID, Client),
|
||||
PaymentID = await_payment_capture(InvoiceID, PaymentID, Client).
|
||||
|
||||
-spec adhoc_repair_failed_succeeded(config()) -> _ | no_return().
|
||||
|
||||
adhoc_repair_failed_succeeded(C) ->
|
||||
Client = cfg(client, C),
|
||||
InvoiceID = start_invoice(<<"rubbercrack">>, make_due_date(10), 42000, C),
|
||||
{PaymentTool, Session} = hg_dummy_provider:make_payment_tool(unexpected_failure),
|
||||
PaymentParams = make_payment_params(PaymentTool, Session),
|
||||
PaymentID = start_payment(InvoiceID, PaymentParams, Client),
|
||||
[
|
||||
?payment_ev(PaymentID, ?session_ev(?processed(), ?trx_bound(?trx_info(PaymentID))))
|
||||
] = next_event(InvoiceID, Client),
|
||||
% assume no more events here since machine is FUBAR already
|
||||
timeout = next_event(InvoiceID, 2000, Client),
|
||||
Changes = [
|
||||
?payment_ev(PaymentID, ?session_ev(?processed(), ?session_finished(?session_succeeded()))),
|
||||
?payment_ev(PaymentID, ?payment_status_changed(?processed()))
|
||||
],
|
||||
ok = repair_invoice(InvoiceID, Changes, Client),
|
||||
Changes = next_event(InvoiceID, Client),
|
||||
PaymentID = await_payment_capture(InvoiceID, PaymentID, Client).
|
||||
|
||||
-spec payment_with_offsite_preauth_success(config()) -> test_return().
|
||||
|
||||
@ -1118,7 +1161,7 @@ payment_with_offsite_preauth_success(C) ->
|
||||
Client = cfg(client, C),
|
||||
InvoiceID = start_invoice(<<"rubberduck">>, make_due_date(10), 42000, C),
|
||||
{PaymentTool, Session} = hg_dummy_provider:make_payment_tool(preauth_3ds_offsite),
|
||||
PaymentParams = make_payment_params(PaymentTool, Session, instant),
|
||||
PaymentParams = make_payment_params(PaymentTool, Session),
|
||||
PaymentID = start_payment(InvoiceID, PaymentParams, Client),
|
||||
UserInteraction = await_payment_process_interaction(InvoiceID, PaymentID, Client),
|
||||
timer:sleep(2000),
|
||||
@ -1325,6 +1368,9 @@ make_payment_params(FlowType) ->
|
||||
{PaymentTool, Session} = hg_dummy_provider:make_payment_tool(no_preauth),
|
||||
make_payment_params(PaymentTool, Session, FlowType).
|
||||
|
||||
make_payment_params(PaymentTool, Session) ->
|
||||
make_payment_params(PaymentTool, Session, instant).
|
||||
|
||||
make_payment_params(PaymentTool, Session, FlowType) ->
|
||||
Flow = case FlowType of
|
||||
instant ->
|
||||
@ -1368,6 +1414,9 @@ create_invoice(InvoiceParams, Client) ->
|
||||
?invoice_state(?invoice(InvoiceID)) = hg_client_invoicing:create(InvoiceParams, Client),
|
||||
InvoiceID.
|
||||
|
||||
repair_invoice(InvoiceID, Changes, Client) ->
|
||||
hg_client_invoicing:repair(InvoiceID, Changes, Client).
|
||||
|
||||
start_invoice(Product, Due, Amount, C) ->
|
||||
start_invoice(cfg(shop_id, C), Product, Due, Amount, C).
|
||||
|
||||
|
@ -11,6 +11,7 @@
|
||||
-export([get/2]).
|
||||
-export([fulfill/3]).
|
||||
-export([rescind/3]).
|
||||
-export([repair/3]).
|
||||
|
||||
-export([start_payment/3]).
|
||||
-export([get_payment/3]).
|
||||
@ -115,6 +116,12 @@ fulfill(InvoiceID, Reason, Client) ->
|
||||
rescind(InvoiceID, Reason, Client) ->
|
||||
map_result_error(gen_server:call(Client, {call, 'Rescind', [InvoiceID, Reason]})).
|
||||
|
||||
-spec repair(invoice_id(), [tuple()], pid()) ->
|
||||
ok | woody_error:business_error().
|
||||
|
||||
repair(InvoiceID, Changes, Client) ->
|
||||
map_result_error(gen_server:call(Client, {call, 'Repair', [InvoiceID, Changes]})).
|
||||
|
||||
-spec start_payment(invoice_id(), payment_params(), pid()) ->
|
||||
payment() | woody_error:business_error().
|
||||
|
||||
|
@ -39,7 +39,7 @@
|
||||
{branch, "master"}
|
||||
}
|
||||
},
|
||||
{dmsl , {git, "git@github.com:rbkmoney/damsel.git", {branch, "release/erlang/master"}}},
|
||||
{dmsl , {git, "git@github.com:keynslug/damsel.git", {branch, "release"}}},
|
||||
{mg_proto , {git, "git@github.com:rbkmoney/machinegun_proto.git", {branch, "master"}}},
|
||||
{dmt_client , {git, "git@github.com:rbkmoney/dmt_client.git", {branch, "master"}}},
|
||||
{scoper , {git, "git@github.com:rbkmoney/scoper.git", {branch, "master"}}}
|
||||
|
@ -3,8 +3,8 @@
|
||||
{<<"cowboy">>,{pkg,<<"cowboy">>,<<"1.0.4">>},1},
|
||||
{<<"cowlib">>,{pkg,<<"cowlib">>,<<"1.0.2">>},2},
|
||||
{<<"dmsl">>,
|
||||
{git,"git@github.com:rbkmoney/damsel.git",
|
||||
{ref,"4ca2329c564b3730dbd742ae370be9919905b9de"}},
|
||||
{git,"git@github.com:keynslug/damsel.git",
|
||||
{ref,"cdef6d34947d03610faaa094925c1ff38892fdfe"}},
|
||||
0},
|
||||
{<<"dmt_client">>,
|
||||
{git,"git@github.com:rbkmoney/dmt_client.git",
|
||||
|
Loading…
Reference in New Issue
Block a user