HG-190: Abuse persistent timers in the invoice machine (#84)

* HG-190: Cut down on extraneous mg calls a little

* HG-190: Switch to rbkmoney/damsel@4018c41

* HG-190: Abuse persistent timers in the invoice machine

* HG-190: Drop merchant proxy logic altogether

* HG-190: Add one more test on invoice timings

* HG-190: Fix confusing timer management

* HG-190: Oops

* HG-190: Switch to damsel upstream, bump to rbkmoney/machinegun@a5a8653

* HG-190: Bump to rbkmoney/machinegun@707c2f8
This commit is contained in:
Andrew Mayorov 2017-05-04 17:58:54 +03:00 committed by GitHub
parent a0ba38625e
commit 5824c62847
9 changed files with 82 additions and 309 deletions

View File

@ -8,7 +8,6 @@
%%% - state collapsing (?)
%%% - simpler flow control (?)
%%% - event publishing (?)
%%% - timer preservation on calls (?)
%%% - unify somehow with operability assertions from hg_party
%%% - should party blocking / suspension be version-locked? probably _not_
%%% - if someone has access to a party then it has access to an invoice
@ -51,8 +50,7 @@
sequence = 0 :: 0 | sequence(),
pending = invoice ::
invoice |
{payment, payment_id()} |
{session, session()}
{payment, payment_id()}
}).
-type st() :: #st{}.
@ -82,24 +80,23 @@ handle_function_('Create', [UserInfo, InvoiceParams], _Opts) ->
handle_function_('Get', [UserInfo, InvoiceID], _Opts) ->
_ = set_invoicing_meta(InvoiceID, UserInfo),
ok = validate_invoice_access(UserInfo, InvoiceID),
get_invoice_state(get_state(InvoiceID));
St = validate_invoice_access(UserInfo, get_state(InvoiceID)),
get_invoice_state(St);
handle_function_('GetEvents', [UserInfo, InvoiceID, Range], _Opts) ->
_ = set_invoicing_meta(InvoiceID, UserInfo),
ok = validate_invoice_access(UserInfo, InvoiceID),
_ = validate_invoice_access(UserInfo, get_initial_state(InvoiceID)),
get_public_history(InvoiceID, Range);
handle_function_('StartPayment', [UserInfo, InvoiceID, PaymentParams], _Opts) ->
_ = set_invoicing_meta(InvoiceID, UserInfo),
ok = validate_invoice_access(UserInfo, InvoiceID),
ok = validate_operability(get_initial_state(InvoiceID)),
St = validate_invoice_access(UserInfo, get_initial_state(InvoiceID)),
ok = validate_operability(St),
call(InvoiceID, {start_payment, PaymentParams});
handle_function_('GetPayment', [UserInfo, InvoiceID, PaymentID], _Opts) ->
_ = set_invoicing_meta(InvoiceID, UserInfo),
ok = validate_invoice_access(UserInfo, InvoiceID),
St = get_state(InvoiceID),
St = validate_invoice_access(UserInfo, get_state(InvoiceID)),
case get_payment_session(PaymentID, St) of
PaymentSession when PaymentSession /= undefined ->
hg_invoice_payment:get_payment(PaymentSession);
@ -109,14 +106,14 @@ handle_function_('GetPayment', [UserInfo, InvoiceID, PaymentID], _Opts) ->
handle_function_('Fulfill', [UserInfo, InvoiceID, Reason], _Opts) ->
_ = set_invoicing_meta(InvoiceID, UserInfo),
ok = validate_invoice_access(UserInfo, InvoiceID),
ok = validate_operability(get_initial_state(InvoiceID)),
St = validate_invoice_access(UserInfo, get_initial_state(InvoiceID)),
ok = validate_operability(St),
call(InvoiceID, {fulfill, Reason});
handle_function_('Rescind', [UserInfo, InvoiceID, Reason], _Opts) ->
_ = set_invoicing_meta(InvoiceID, UserInfo),
ok = validate_invoice_access(UserInfo, InvoiceID),
ok = validate_operability(get_initial_state(InvoiceID)),
St = validate_invoice_access(UserInfo, get_initial_state(InvoiceID)),
ok = validate_operability(St),
call(InvoiceID, {rescind, Reason}).
validate_operability(St) ->
@ -230,35 +227,18 @@ map_start_error({error, Reason}) ->
-type invoice() :: dmsl_domain_thrift:'Invoice'().
-type invoice_id() :: dmsl_domain_thrift:'InvoiceID'().
-type invoice_status() :: dmsl_domain_thrift:'InvoiceStatus'().
-type user_info() :: dmsl_payment_processing_thrift:'UserInfo'().
-type invoice_params() :: dmsl_payment_processing_thrift:'InvoiceParams'().
-type payment_params() :: dmsl_payment_processing_thrift:'InvoicePaymentParams'().
-type payment_id() :: dmsl_domain_thrift:'InvoicePaymentID'().
-type payment_st() :: hg_invoice_payment:st().
-type sequence() :: pos_integer().
-type proxy_state() :: dmsl_proxy_thrift:'ProxyState'().
-type ev() ::
{public, sequence(), dmsl_payment_processing_thrift:'EventPayload'()} |
{private, sequence(), private_event()}.
-type private_event() ::
session_event(). %% TODO hg_invoice_payment:private_event() ?
-type session_event() ::
{session_event,
{started, invoice_status()} |
finished |
{proxy_state_changed, proxy_state()}
}.
-type session() :: #{
status => invoice_status(),
proxy_state => undefined | proxy_state()
}.
-define(session_ev(E), {session_event, E}).
none(). %% TODO hg_invoice_payment:private_event() ?
-include("invoice_events.hrl").
@ -306,15 +286,12 @@ handle_signal(timeout, St = #st{pending = {payment, PaymentID}}) ->
% there's a payment pending
PaymentSession = get_payment_session(PaymentID, St),
process_payment_signal(timeout, PaymentID, PaymentSession, St);
handle_signal(timeout, St = #st{pending = {session, Session}}) ->
% there's a session pending
process_session_signal(timeout, Session, St);
handle_signal(timeout, St = #st{pending = invoice}) ->
% invoice is expired
handle_expiration(St);
handle_signal({repair, _}, St) ->
ok([], St, restore_timer(St)).
ok([], St).
handle_expiration(St) ->
Event = {public, ?invoice_ev(?invoice_status_changed(?cancelled(format_reason(overdue))))},
@ -323,9 +300,9 @@ handle_expiration(St) ->
%%
-type call() ::
{start_payment, payment_params(), user_info()} |
{fulfill, binary(), user_info()} |
{rescind, binary(), user_info()} |
{start_payment, payment_params()} |
{fulfill, binary()} |
{rescind, binary()} |
{callback, callback()}.
-type response() ::
@ -338,7 +315,7 @@ process_call(Call, History) ->
St = collapse_history(History),
try handle_call(Call, St) catch
{exception, Exception} ->
{{exception, Exception}, {[], restore_timer(St)}}
{{exception, Exception}, {[], hg_machine_action:new()}}
end.
-spec raise(term()) -> no_return().
@ -360,7 +337,7 @@ handle_call({rescind, Reason}, St) ->
_ = assert_invoice_status(unpaid, St),
_ = assert_no_pending_payment(St),
Event = {public, ?invoice_ev(?invoice_status_changed(?cancelled(format_reason(Reason))))},
respond(ok, Event, St);
respond(ok, Event, St, hg_machine_action:unset_timer());
handle_call({callback, Callback}, St) ->
dispatch_callback(Callback, St).
@ -383,17 +360,6 @@ assert_no_pending_payment(#st{pending = {payment, PaymentID}}) ->
assert_no_pending_payment(_) ->
ok.
restore_timer(St = #st{invoice = #domain_Invoice{status = Status}, pending = Pending}) ->
case Pending of
invoice when Status == ?unpaid() ->
set_invoice_timer(St);
invoice ->
hg_machine_action:new();
_ ->
% TODO how to restore timer properly then, magic number for now
hg_machine_action:set_timeout(10)
end.
set_invoice_timer(#st{invoice = #domain_Invoice{due = Due}}) ->
hg_machine_action:set_deadline(Due).
@ -403,6 +369,7 @@ start_payment(PaymentParams, St) ->
PaymentID = create_payment_id(St),
Party = checkout_party(St),
Opts = get_payment_opts(Party, St),
% TODO make timer reset explicit here
{Events1, _} = hg_invoice_payment:init(PaymentID, PaymentParams, Opts),
{Events2, Action} = hg_invoice_payment:start_session(?processed()),
Events = wrap_payment_events(PaymentID, Events1 ++ Events2),
@ -431,8 +398,8 @@ handle_payment_result(Result, PaymentID, PaymentSession, Party, St) ->
{Events2, Action} = hg_invoice_payment:start_session(?captured()),
ok(wrap_payment_events(PaymentID, Events1 ++ Events2), St, Action);
?captured() ->
{Events2, Action} = start_session(?paid(), Party, St),
ok(wrap_payment_events(PaymentID, Events1) ++ Events2, St, Action);
Events2 = [{public, ?invoice_ev(?invoice_status_changed(?paid()))}],
ok(wrap_payment_events(PaymentID, Events1) ++ Events2, St);
?failed(_) ->
ok(wrap_payment_events(PaymentID, Events1), St, set_invoice_timer(St))
end
@ -454,113 +421,6 @@ get_payment_opts(Party, #st{invoice = Invoice}) ->
%%
start_session(Status, Party, St) ->
Shop = get_party_shop(get_shop_id(St), Party),
case Shop#domain_Shop.proxy of
Proxy when Proxy /= undefined ->
Event = {private, ?session_ev({started, Status})},
{[Event], hg_machine_action:instant()};
undefined ->
finalize_session(Status, St)
end.
process_session_signal(timeout, Session, St) ->
Party = checkout_party(St),
Revision = hg_domain:head(),
ProxyContext = construct_proxy_context(Session, Party, St, Revision),
{ok, ProxyResult} = issue_handle_event_call(ProxyContext, Party, St, Revision),
{Events, Action} = handle_proxy_result(ProxyResult, Session, St),
ok(Events, St, Action).
finalize_session(Status, _St) ->
Event = {public, ?invoice_ev(?invoice_status_changed(Status))},
% TODO ideally we should restore timer if there was any
% as of right now there's no timeout possibility
{[Event], hg_machine_action:new()}.
%%
-include_lib("dmsl/include/dmsl_proxy_merchant_thrift.hrl").
handle_proxy_result(#prxmerch_ProxyResult{intent = {_, Intent}, next_state = ProxyState}, Session, St) ->
handle_proxy_intent(Intent, ProxyState, Session, St).
handle_proxy_intent(#prxmerch_FinishIntent{}, _ProxyState, #{status := Status}, St) ->
Event = {private, ?session_ev(finished)},
{Events, Action} = finalize_session(Status, St),
{[Event | Events], Action};
handle_proxy_intent(#prxmerch_SleepIntent{timer = Timer}, ProxyState, _Session, _St) ->
Event = {private, ?session_ev({proxy_state_changed, ProxyState})},
{[Event], hg_machine_action:set_timer(Timer)}.
construct_proxy_context(Session, Party, St, Revision) ->
Shop = hg_party:get_shop(get_shop_id(St), Party),
Proxy = Shop#domain_Shop.proxy,
#prxmerch_Context{
session = construct_proxy_session(Session),
invoice = collect_invoice_info(Party, Shop, St#st.invoice, Revision),
options = collect_proxy_options(Proxy, Revision)
}.
construct_proxy_session(#{status := Status, proxy_state := ProxyState}) ->
#prxmerch_Session{
event = {status_changed, #prxmerch_InvoiceStatusChanged{status = construct_proxy_status(Status)}},
state = ProxyState
}.
construct_proxy_status(?paid()) ->
{paid, #prxmerch_InvoicePaid{}}.
collect_invoice_info(Party, Shop, Invoice, Revision) ->
Cost = Invoice#domain_Invoice.cost,
#prxmerch_InvoiceInfo{
party = #prxmerch_Party{
id = Party#domain_Party.id
},
shop = #prxmerch_Shop{
id = Shop#domain_Shop.id,
details = Shop#domain_Shop.details
},
invoice = #prxmerch_Invoice{
id = Invoice#domain_Invoice.id,
created_at = Invoice#domain_Invoice.created_at,
due = Invoice#domain_Invoice.due,
details = Invoice#domain_Invoice.details,
context = Invoice#domain_Invoice.context,
cost = #prxmerch_Cash{
amount = Cost#domain_Cash.amount,
currency = hg_domain:get(Revision, {currency, Cost#domain_Cash.currency})
}
}
}.
collect_proxy_options(Proxy, Revision) ->
ProxyDef = hg_domain:get(Revision, {proxy, Proxy#domain_Proxy.ref}),
maps:merge(
Proxy#domain_Proxy.additional,
ProxyDef#domain_ProxyDefinition.options
).
issue_handle_event_call(ProxyContext, Party, St, Revision) ->
CallOpts = get_call_options(Party, St, Revision),
issue_call('HandleInvoiceEvent', [ProxyContext], CallOpts).
issue_call(Func, Args, CallOpts) ->
try
hg_woody_wrapper:call('MerchantProxy', Func, Args, CallOpts)
catch
{exception, Exception} ->
error({proxy_failure, Exception})
end.
get_call_options(Party, St, Revision) ->
Shop = hg_party:get_shop(get_shop_id(St), Party),
Proxy = Shop#domain_Shop.proxy,
hg_proxy:get_call_options(Proxy, Revision).
%%
checkout_party(St = #st{invoice = #domain_Invoice{created_at = CreationTimestamp}}) ->
PartyID = get_party_id(St),
hg_party_machine:checkout(PartyID, CreationTimestamp).
@ -625,8 +485,6 @@ collapse_history(History) ->
merge_event(?invoice_ev(Event), St) ->
merge_invoice_event(Event, St);
merge_event(?session_ev(Event), St) ->
merge_session_event(Event, St);
merge_event({{payment, PaymentID}, Event}, St) ->
PaymentSession = get_payment_session(PaymentID, St),
PaymentSession1 = hg_invoice_payment:merge_event(Event, PaymentSession),
@ -648,13 +506,6 @@ merge_invoice_event(?invoice_status_changed(Status), St = #st{invoice = I}) ->
get_party_id(#st{invoice = #domain_Invoice{owner_id = PartyID}}) ->
PartyID.
merge_session_event({started, Status}, St = #st{}) ->
St#st{pending = {session, #{status => Status, proxy_state => undefined}}};
merge_session_event(finished, St = #st{pending = {session, _}}) ->
St#st{pending = invoice};
merge_session_event({proxy_state_changed, ProxyState}, St = #st{pending = {session, Session}}) ->
St#st{pending = {session, Session#{proxy_state := ProxyState}}}.
get_shop_id(#st{invoice = #domain_Invoice{shop_id = ShopID}}) ->
ShopID.
@ -679,9 +530,6 @@ format_reason(V) ->
%%
get_party_shop(ID, #domain_Party{shops = Shops}) ->
maps:get(ID, Shops, undefined).
get_shop_currency(#domain_Shop{account = #domain_ShopAccount{currency = Currency}}) ->
Currency.
@ -733,10 +581,10 @@ validate_currency(Currency, Currency) ->
validate_currency(_, _) ->
throw(#'InvalidRequest'{errors = [<<"Invalid currency">>]}).
validate_invoice_access(UserInfo, InvoiceID) ->
St = get_initial_state(InvoiceID),
validate_invoice_access(UserInfo, St = #st{}) ->
PartyID = get_party_id(St),
validate_party_access(UserInfo, PartyID).
ok = validate_party_access(UserInfo, PartyID),
St.
validate_party_access(UserInfo, PartyID) ->
case hg_access_control:check_user_info(UserInfo, PartyID) of

View File

@ -292,12 +292,13 @@ start_session(Target) ->
{next | done, hg_machine:result()}.
process_signal(timeout, St, Options) ->
Action = hg_machine_action:new(),
hg_log_scope:scope(payment, fun() ->
case get_status(St) of
active ->
process(St, Options);
process(Action, St, Options);
suspended ->
fail(construct_failure(<<"provider_timeout">>), St)
fail(construct_failure(<<"provider_timeout">>), Action, St)
end
end, get_st_meta(St)).
@ -308,33 +309,37 @@ process_call({callback, Payload}, St, Options) ->
hg_log_scope:scope(payment, fun() ->
case get_status(St) of
suspended ->
handle_callback(Payload, St, Options);
Action = hg_machine_action:unset_timer(),
handle_callback(Payload, Action, St, Options);
active ->
% there's ultimately no way how we could end up here
error(invalid_session_status)
end
end, get_st_meta(St)).
process(St, Options) ->
process(Action0, St, Options) ->
ProxyContext = construct_proxy_context(St, Options),
{ok, ProxyResult} = issue_process_call(ProxyContext, Options, St),
handle_proxy_result(ProxyResult, St, Options).
{ok, ProxyResult} = issue_process_call(ProxyContext, St, Options),
handle_proxy_result(ProxyResult, Action0, St, Options).
handle_callback(Payload, St, Options) ->
handle_callback(Payload, Action0, St, Options) ->
ProxyContext = construct_proxy_context(St, Options),
{ok, CallbackResult} = issue_callback_call(Payload, ProxyContext, Options, St),
handle_callback_result(CallbackResult, Options, St).
{ok, CallbackResult} = issue_callback_call(Payload, ProxyContext, St, Options),
handle_callback_result(CallbackResult, Action0, St, Options).
handle_callback_result(#prxprv_CallbackResult{result = ProxyResult, response = Response}, Options, St) ->
{What, {Events, Action}} = handle_proxy_result(ProxyResult, St, Options),
handle_callback_result(
#prxprv_CallbackResult{result = ProxyResult, response = Response},
Action0, St, Options
) ->
{What, {Events, Action}} = handle_proxy_result(ProxyResult, Action0, St, Options),
{Response, {What, {[?session_ev(activated) | Events], Action}}}.
handle_proxy_result(
#prxprv_ProxyResult{intent = {_, Intent}, trx = Trx, next_state = ProxyState},
St, Options
Action0, St, Options
) ->
Events1 = bind_transaction(Trx, St),
{What, {Events2, Action}} = handle_proxy_intent(Intent, ProxyState, St, Options),
{What, {Events2, Action}} = handle_proxy_intent(Intent, ProxyState, Action0, St, Options),
{What, {Events1 ++ Events2, Action}}.
bind_transaction(undefined, _St) ->
@ -356,7 +361,7 @@ bind_transaction(Trx, #st{payment = #domain_InvoicePayment{id = PaymentID, trx =
error(proxy_contract_violated)
end.
handle_proxy_intent(#'FinishIntent'{status = {success, _}}, _ProxyState, St, Options) ->
handle_proxy_intent(#'FinishIntent'{status = {success, _}}, _ProxyState, Action, St, Options) ->
PaymentID = get_payment_id(St),
Target = get_target(St),
case get_target(St) of
@ -368,23 +373,22 @@ handle_proxy_intent(#'FinishIntent'{status = {success, _}}, _ProxyState, St, Opt
ok
end,
Events = [?payment_ev(?payment_status_changed(PaymentID, Target))],
Action = hg_machine_action:new(),
{done, {Events, Action}};
handle_proxy_intent(#'FinishIntent'{status = {failure, Failure}}, _ProxyState, St, Options) ->
handle_proxy_intent(#'FinishIntent'{status = {failure, Failure}}, _ProxyState, Action0, St, Options) ->
_AccountsState = rollback_plan(St, Options),
fail(convert_failure(Failure), St);
fail(convert_failure(Failure), Action0, St);
handle_proxy_intent(#'SleepIntent'{timer = Timer}, ProxyState, _St, _Options) ->
Action = hg_machine_action:set_timer(Timer),
handle_proxy_intent(#'SleepIntent'{timer = Timer}, ProxyState, Action0, _St, _Options) ->
Action = hg_machine_action:set_timer(Timer, Action0),
Events = [?session_ev({proxy_state_changed, ProxyState})],
{next, {Events, Action}};
handle_proxy_intent(
#'SuspendIntent'{tag = Tag, timeout = Timer, user_interaction = UserInteraction},
ProxyState, St, _Options
ProxyState, Action0, St, _Options
) ->
Action = try_set_timer(Timer, hg_machine_action:set_tag(Tag)),
Action = try_set_timer(Timer, hg_machine_action:set_tag(Tag, Action0)),
Events = [
?session_ev({proxy_state_changed, ProxyState}),
?session_ev(suspended)
@ -402,9 +406,8 @@ try_emit_interaction_event(undefined, _St) ->
try_emit_interaction_event(UserInteraction, St) ->
[?payment_ev(?payment_interaction_requested(get_payment_id(St), UserInteraction))].
fail(Error, St) ->
fail(Error, Action, St) ->
Events = [?payment_ev(?payment_status_changed(get_payment_id(St), ?failed(Error)))],
Action = hg_machine_action:new(),
{done, {Events, Action}}.
commit_plan(St, Options) ->
@ -599,13 +602,13 @@ create_session(Target) ->
%%
issue_process_call(ProxyContext, Opts, St) ->
issue_call('ProcessPayment', [ProxyContext], Opts, St).
issue_process_call(ProxyContext, St, Opts) ->
issue_call('ProcessPayment', [ProxyContext], St, Opts).
issue_callback_call(Payload, ProxyContext, Opts, St) ->
issue_call('HandlePaymentCallback', [Payload, ProxyContext], Opts, St).
issue_callback_call(Payload, ProxyContext, St, Opts) ->
issue_call('HandlePaymentCallback', [Payload, ProxyContext], St, Opts).
issue_call(Func, Args, Opts, St) ->
issue_call(Func, Args, St, Opts) ->
CallOpts = get_call_options(St, Opts),
hg_woody_wrapper:call('ProviderProxy', Func, Args, CallOpts).

View File

@ -8,6 +8,8 @@
-export([set_deadline/2]).
-export([set_timer/1]).
-export([set_timer/2]).
-export([unset_timer/0]).
-export([unset_timer/1]).
-export([set_tag/1]).
-export([set_tag/2]).
@ -65,7 +67,18 @@ set_timer(Timer) ->
-spec set_timer(timer(), t()) -> t().
set_timer(Timer, Action = #'ComplexAction'{}) ->
Action#'ComplexAction'{set_timer = #'SetTimerAction'{timer = Timer}}.
% TODO pass range and processing timeout explicitly too
Action#'ComplexAction'{timer = {set_timer, #'SetTimerAction'{timer = Timer}}}.
-spec unset_timer() -> t().
unset_timer() ->
unset_timer(new()).
-spec unset_timer(t()) -> t().
unset_timer(Action = #'ComplexAction'{}) ->
Action#'ComplexAction'{timer = {unset_timer, #'UnsetTimerAction'{}}}.
-spec set_tag(tag()) -> t().

View File

@ -8,7 +8,6 @@
-export([create_contract/2]).
-export([create_shop/4]).
-export([create_shop/5]).
-export([set_shop_proxy/4]).
-export([get_first_contract_id/1]).
-export([get_first_battle_ready_contract_id/1]).
-export([get_first_payout_tool_id/2]).
@ -123,8 +122,6 @@ start_apps(Apps) ->
-type category() :: dmsl_domain_thrift:'CategoryRef'().
-type cost() :: integer() | {integer(), binary()}.
-type invoice_params() :: dmsl_payment_processing_thrift:'InvoiceParams'().
-type proxy_ref() :: dmsl_domain_thrift:'ProxyRef'().
-type proxy_options() :: dmsl_domain_thrift:'ProxyOptions'().
-type timestamp() :: integer().
-spec create_party_and_shop(Client :: pid()) ->
@ -178,16 +175,6 @@ create_shop(ContractID, Category, Name, Description, Client) ->
ok = flush_events(Client),
ShopID.
-spec set_shop_proxy(shop_id(), proxy_ref(), proxy_options(), Client :: pid()) ->
ok.
set_shop_proxy(ShopID, ProxyRef, ProxyOptions, Client) ->
Proxy = #domain_Proxy{ref = ProxyRef, additional = ProxyOptions},
Update = #payproc_ShopUpdate{proxy = Proxy},
#payproc_ClaimResult{status = ?accepted(_)} = hg_client_party:update_shop(ShopID, Update, Client),
ok = flush_events(Client),
ok.
flush_events(Client) ->
case hg_client_party:pull_event(500, Client) of
timeout ->

View File

@ -1,50 +0,0 @@
-module(hg_dummy_merchant).
-behaviour(hg_woody_wrapper).
-export([handle_function/3]).
-behaviour(hg_test_proxy).
-export([get_service_spec/0]).
-spec get_service_spec() ->
hg_proto:service_spec().
get_service_spec() ->
{"/test/proxy/merchant/dummy", {dmsl_proxy_merchant_thrift, 'MerchantProxy'}}.
%%
-include_lib("dmsl/include/dmsl_proxy_merchant_thrift.hrl").
-include_lib("hellgate/include/invoice_events.hrl").
-spec handle_function(woody:func(), woody:args(), hg_woody_wrapper:handler_opts()) ->
term() | no_return().
handle_function(
'HandleInvoiceEvent',
[#prxmerch_Context{
session = #prxmerch_Session{event = Event, state = State},
invoice = InvoiceInfo,
options = _
}],
Opts
) ->
handle_invoice_event(Event, State, InvoiceInfo, Opts).
handle_invoice_event(_Event, undefined, _InvoiceInfo, _Opts) ->
sleep(1, <<"sleeping">>);
handle_invoice_event(_Event, <<"sleeping">>, _InvoiceInfo, _Opts) ->
finish().
finish() ->
#prxmerch_ProxyResult{
intent = {finish, #prxmerch_FinishIntent{}},
next_state = <<>>
}.
sleep(Timeout, State) ->
#prxmerch_ProxyResult{
intent = {sleep, #prxmerch_SleepIntent{timer = {timeout, Timeout}}},
next_state = State
}.

View File

@ -15,11 +15,10 @@
-export([invalid_party_status/1]).
-export([invalid_shop_status/1]).
-export([invoice_cancellation/1]).
-export([overdue_invoice_cancelled/1]).
-export([invoice_cancelled_after_payment_timeout/1]).
-export([overdue_invoice_cancellation/1]).
-export([invoice_cancellation_after_payment_timeout/1]).
-export([invalid_payment_amount/1]).
-export([payment_success/1]).
-export([payment_success_w_merchant_callback/1]).
-export([payment_success_on_second_try/1]).
-export([invoice_success_on_third_payment/1]).
-export([payment_risk_score_check/1]).
@ -59,11 +58,10 @@ all() ->
invalid_party_status,
invalid_shop_status,
invoice_cancellation,
overdue_invoice_cancelled,
invoice_cancelled_after_payment_timeout,
overdue_invoice_cancellation,
invoice_cancellation_after_payment_timeout,
invalid_payment_amount,
payment_success,
payment_success_w_merchant_callback,
payment_success_on_second_try,
invoice_success_on_third_payment,
@ -204,20 +202,20 @@ invoice_cancellation(C) ->
?invalid_invoice_status(_) = hg_client_invoicing:fulfill(InvoiceID, <<"perfect">>, Client),
ok = hg_client_invoicing:rescind(InvoiceID, <<"whynot">>, Client).
-spec overdue_invoice_cancelled(config()) -> _ | no_return().
-spec overdue_invoice_cancellation(config()) -> _ | no_return().
overdue_invoice_cancelled(C) ->
overdue_invoice_cancellation(C) ->
Client = ?c(client, C),
InvoiceID = start_invoice(<<"rubberduck">>, make_due_date(1), 10000, C),
?invoice_status_changed(?cancelled(<<"overdue">>)) = next_event(InvoiceID, Client).
-spec invoice_cancelled_after_payment_timeout(config()) -> _ | no_return().
-spec invoice_cancellation_after_payment_timeout(config()) -> _ | no_return().
invoice_cancelled_after_payment_timeout(C) ->
invoice_cancellation_after_payment_timeout(C) ->
Client = ?c(client, C),
ok = start_proxy(hg_dummy_provider, 1, C),
ok = start_proxy(hg_dummy_inspector, 2, C),
InvoiceID = start_invoice(<<"rubberdusk">>, make_due_date(7), 1000, C),
InvoiceID = start_invoice(<<"rubberdusk">>, make_due_date(3), 1000, C),
PaymentParams = make_tds_payment_params(),
PaymentID = attach_payment(InvoiceID, PaymentParams, Client),
?payment_interaction_requested(PaymentID, _) = next_event(InvoiceID, Client),
@ -255,29 +253,6 @@ payment_success(C) ->
[?payment_w_status(PaymentID, ?captured())]
) = hg_client_invoicing:get(InvoiceID, Client).
-spec payment_success_w_merchant_callback(config()) -> _ | no_return().
payment_success_w_merchant_callback(C) ->
Client = ?c(client, C),
PartyClient = ?c(party_client, C),
ContractParams = hg_ct_helper:make_battle_ready_contract_params(),
ContractID = hg_ct_helper:create_contract(ContractParams, PartyClient),
ShopID = hg_ct_helper:create_shop(ContractID, ?cat(3), <<"Callback Shop">>, PartyClient),
ok = start_proxy(hg_dummy_provider, 1, C),
ok = start_proxy(hg_dummy_inspector, 2, C),
MerchantProxy = construct_proxy(3, start_service_handler(hg_dummy_merchant, C, #{}), #{}),
ok = hg_domain:upsert(MerchantProxy),
ok = hg_ct_helper:set_shop_proxy(ShopID, get_proxy_ref(MerchantProxy), #{}, PartyClient),
InvoiceID = start_invoice(ShopID, <<"rubberduck">>, make_due_date(10), 42000, C),
PaymentParams = make_payment_params(),
PaymentID = attach_payment(InvoiceID, PaymentParams, Client),
?payment_status_changed(PaymentID, ?captured()) = next_event(InvoiceID, Client),
?invoice_status_changed(?paid()) = next_event(InvoiceID, Client),
?invoice_state(
?invoice_w_status(?paid()),
[?payment_w_status(PaymentID, ?captured())]
) = hg_client_invoicing:get(InvoiceID, Client).
-spec payment_success_on_second_try(config()) -> _ | no_return().
payment_success_on_second_try(C) ->
@ -467,9 +442,6 @@ construct_proxy(ID, Url, Options) ->
}
}}.
get_proxy_ref({proxy, #domain_ProxyObject{ref = Ref}}) ->
Ref.
%%
make_userinfo(PartyID) ->
@ -691,7 +663,7 @@ construct_domain_fixture() ->
}
]},
default_contract_template = ?tmpl(2),
common_merchant_proxy = ?prx(3),
common_merchant_proxy = ?prx(3), % FIXME
inspector = {decisions, [
#domain_InspectorDecision{
if_ = {condition, {currency_is, ?cur(<<"RUB">>)}},

View File

@ -1039,7 +1039,7 @@ construct_domain_fixture() ->
system_account_set = {value, ?sas(1)},
external_account_set = {value, ?eas(1)},
default_contract_template = ?tmpl(2),
common_merchant_proxy = ?prx(1),
common_merchant_proxy = ?prx(1), % FIXME
inspector = {value, ?insp(1)}
}
}},

View File

@ -24,7 +24,7 @@ services:
condition: service_healthy
machinegun:
image: dr.rbkmoney.com/rbkmoney/machinegun:e04e529f4c5682b527d12d73a13a3cf9eb296d4d
image: dr.rbkmoney.com/rbkmoney/machinegun:707c2f8015f21de8dd9aa51a748532fe384c3a60
command: /opt/machinegun/bin/machinegun foreground
volumes:
- ./test/machinegun/sys.config:/opt/machinegun/releases/0.1.0/sys.config

View File

@ -3,7 +3,7 @@
{<<"cowlib">>,{pkg,<<"cowlib">>,<<"1.0.2">>},2},
{<<"dmsl">>,
{git,"git@github.com:rbkmoney/damsel_erlang.git",
{ref,"52018a4684f48bae501860113790d4f535c4213c"}},
{ref,"56fb48e6a23471e49a88c0d646680fba14e74d35"}},
0},
{<<"dmt">>,
{git,"git@github.com:rbkmoney/dmt_core.git",