mirror of
https://github.com/valitydev/hellgate.git
synced 2024-11-06 10:55:22 +00:00
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:
parent
a0ba38625e
commit
5824c62847
@ -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
|
||||
|
@ -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).
|
||||
|
||||
|
@ -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().
|
||||
|
||||
|
@ -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 ->
|
||||
|
@ -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
|
||||
}.
|
@ -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">>)}},
|
||||
|
@ -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)}
|
||||
}
|
||||
}},
|
||||
|
@ -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
|
||||
|
@ -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",
|
||||
|
Loading…
Reference in New Issue
Block a user