ED-40: reduce party reading (#544)

* reduce party reading

* asserts do not change state anymore

* reduced getting party calls

Co-authored-by: Andrey Fadeev <me@ciiol.net>
This commit is contained in:
yuri-bukhalenkov 2021-02-26 20:58:29 +03:00 committed by GitHub
parent 12f5f18711
commit 75ed95a9bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -15,6 +15,9 @@
-module(hg_invoice). -module(hg_invoice).
-include_lib("damsel/include/dmsl_payment_processing_thrift.hrl"). -include_lib("damsel/include/dmsl_payment_processing_thrift.hrl").
-include("payment_events.hrl").
-include("invoice_events.hrl").
-include("domain.hrl").
-define(NS, <<"invoice">>). -define(NS, <<"invoice">>).
@ -53,13 +56,23 @@
-export([fail/1]). -export([fail/1]).
-import(hg_invoice_utils, [
assert_party_accessible/1,
assert_party_operable/1,
assert_shop_operable/1,
assert_shop_exists/1
]).
%% Internal types %% Internal types
-define(invalid_invoice_status(Status), #payproc_InvalidInvoiceStatus{status = Status}).
-record(st, { -record(st, {
activity :: undefined | activity(), activity :: undefined | activity(),
invoice :: undefined | invoice(), invoice :: undefined | invoice(),
payments = [] :: [{payment_id(), payment_st()}], payments = [] :: [{payment_id(), payment_st()}],
adjustments = [] :: [adjustment()] adjustments = [] :: [adjustment()],
party :: undefined | party()
}). }).
-type st() :: #st{}. -type st() :: #st{}.
@ -105,11 +118,17 @@ get_payment_tags(PaymentSession) ->
hg_invoice_payment:get_tags(PaymentSession). hg_invoice_payment:get_tags(PaymentSession).
-spec get_payment_opts(st()) -> hg_invoice_payment:opts(). -spec get_payment_opts(st()) -> hg_invoice_payment:opts().
get_payment_opts(St = #st{invoice = Invoice}) -> get_payment_opts(St = #st{invoice = Invoice, party = undefined}) ->
#{ #{
party => hg_party:get_party(get_party_id(St)), party => hg_party:get_party(get_party_id(St)),
invoice => Invoice, invoice => Invoice,
timestamp => hg_datetime:format_now() timestamp => hg_datetime:format_now()
};
get_payment_opts(#st{invoice = Invoice, party = Party}) ->
#{
party => Party,
invoice => Invoice,
timestamp => hg_datetime:format_now()
}. }.
-spec get_payment_opts(hg_party:party_revision() | undefined, hg_datetime:timestamp(), st()) -> -spec get_payment_opts(hg_party:party_revision() | undefined, hg_datetime:timestamp(), st()) ->
@ -138,7 +157,6 @@ handle_function(Func, Args, Opts) ->
-spec handle_function_(woody:func(), woody:args(), hg_woody_wrapper:handler_opts()) -> term() | no_return(). -spec handle_function_(woody:func(), woody:args(), hg_woody_wrapper:handler_opts()) -> term() | no_return().
handle_function_('Create', {UserInfo, InvoiceParams}, _Opts) -> handle_function_('Create', {UserInfo, InvoiceParams}, _Opts) ->
TimestampNow = hg_datetime:format_now(),
DomainRevision = hg_domain:head(), DomainRevision = hg_domain:head(),
InvoiceID = hg_utils:uid(InvoiceParams#payproc_InvoiceParams.id), InvoiceID = hg_utils:uid(InvoiceParams#payproc_InvoiceParams.id),
ok = assume_user_identity(UserInfo), ok = assume_user_identity(UserInfo),
@ -149,19 +167,18 @@ handle_function_('Create', {UserInfo, InvoiceParams}, _Opts) ->
Party = hg_party:get_party(PartyID), Party = hg_party:get_party(PartyID),
Shop = assert_shop_exists(hg_party:get_shop(ShopID, Party)), Shop = assert_shop_exists(hg_party:get_shop(ShopID, Party)),
_ = assert_party_shop_operable(Shop, Party), _ = assert_party_shop_operable(Shop, Party),
MerchantTerms = get_merchant_terms(Party, DomainRevision, Shop, TimestampNow), MerchantTerms = get_merchant_terms(Party, DomainRevision, Shop, hg_datetime:format_now()),
ok = validate_invoice_params(InvoiceParams, Party, Shop, MerchantTerms, DomainRevision), ok = validate_invoice_params(InvoiceParams, Party, Shop, MerchantTerms, DomainRevision),
ok = ensure_started(InvoiceID, {undefined, Party#domain_Party.revision, InvoiceParams}), ok = ensure_started(InvoiceID, {undefined, Party#domain_Party.revision, InvoiceParams}),
get_invoice_state(get_state(InvoiceID)); get_invoice_state(get_state(InvoiceID));
handle_function_('CreateWithTemplate', {UserInfo, Params}, _Opts) -> handle_function_('CreateWithTemplate', {UserInfo, Params}, _Opts) ->
TimestampNow = hg_datetime:format_now(),
DomainRevision = hg_domain:head(), DomainRevision = hg_domain:head(),
InvoiceID = hg_utils:uid(Params#payproc_InvoiceWithTemplateParams.id), InvoiceID = hg_utils:uid(Params#payproc_InvoiceWithTemplateParams.id),
ok = assume_user_identity(UserInfo), ok = assume_user_identity(UserInfo),
_ = set_invoicing_meta(InvoiceID), _ = set_invoicing_meta(InvoiceID),
TplID = Params#payproc_InvoiceWithTemplateParams.template_id, TplID = Params#payproc_InvoiceWithTemplateParams.template_id,
{Party, Shop, InvoiceParams} = make_invoice_params(Params), {Party, Shop, InvoiceParams} = make_invoice_params(Params),
MerchantTerms = get_merchant_terms(Party, DomainRevision, Shop, TimestampNow), MerchantTerms = get_merchant_terms(Party, DomainRevision, Shop, hg_datetime:format_now()),
ok = validate_invoice_params(InvoiceParams, Party, Shop, MerchantTerms, DomainRevision), ok = validate_invoice_params(InvoiceParams, Party, Shop, MerchantTerms, DomainRevision),
ok = ensure_started(InvoiceID, {TplID, Party#domain_Party.revision, InvoiceParams}), ok = ensure_started(InvoiceID, {TplID, Party#domain_Party.revision, InvoiceParams}),
get_invoice_state(get_state(InvoiceID)); get_invoice_state(get_state(InvoiceID));
@ -170,49 +187,57 @@ handle_function_('CapturePaymentNew', Args, Opts) ->
handle_function_('Get', {UserInfo, InvoiceID, #payproc_EventRange{'after' = AfterID, limit = Limit}}, _Opts) -> handle_function_('Get', {UserInfo, InvoiceID, #payproc_EventRange{'after' = AfterID, limit = Limit}}, _Opts) ->
ok = assume_user_identity(UserInfo), ok = assume_user_identity(UserInfo),
_ = set_invoicing_meta(InvoiceID), _ = set_invoicing_meta(InvoiceID),
St = assert_invoice_accessible(get_state(InvoiceID, AfterID, Limit)), St = get_state(InvoiceID, AfterID, Limit),
_ = assert_invoice(accessible, St),
get_invoice_state(St); get_invoice_state(St);
%% TODO Удалить после перехода на новый протокол %% TODO Удалить после перехода на новый протокол
handle_function_('Get', {UserInfo, InvoiceID, undefined}, _Opts) -> handle_function_('Get', {UserInfo, InvoiceID, undefined}, _Opts) ->
ok = assume_user_identity(UserInfo), ok = assume_user_identity(UserInfo),
_ = set_invoicing_meta(InvoiceID), _ = set_invoicing_meta(InvoiceID),
St = assert_invoice_accessible(get_state(InvoiceID)), St = get_state(InvoiceID),
_ = assert_invoice(accessible, St),
get_invoice_state(St); get_invoice_state(St);
handle_function_('GetEvents', {UserInfo, InvoiceID, Range}, _Opts) -> handle_function_('GetEvents', {UserInfo, InvoiceID, Range}, _Opts) ->
ok = assume_user_identity(UserInfo), ok = assume_user_identity(UserInfo),
_ = set_invoicing_meta(InvoiceID), _ = set_invoicing_meta(InvoiceID),
_ = assert_invoice_accessible(get_initial_state(InvoiceID)), _ = assert_invoice(accessible, get_initial_state(InvoiceID)),
get_public_history(InvoiceID, Range); get_public_history(InvoiceID, Range);
handle_function_('GetInvoiceAdjustment', {UserInfo, InvoiceID, ID}, _Opts) -> handle_function_('GetInvoiceAdjustment', {UserInfo, InvoiceID, ID}, _Opts) ->
St = assert_invoice_accessible(get_state(InvoiceID)), St = get_state(InvoiceID),
_ = assert_invoice(accessible, St),
ok = assume_user_identity(UserInfo), ok = assume_user_identity(UserInfo),
ok = set_invoicing_meta(InvoiceID), ok = set_invoicing_meta(InvoiceID),
get_adjustment(ID, St); get_adjustment(ID, St);
handle_function_('GetPayment', {UserInfo, InvoiceID, PaymentID}, _Opts) -> handle_function_('GetPayment', {UserInfo, InvoiceID, PaymentID}, _Opts) ->
ok = assume_user_identity(UserInfo), ok = assume_user_identity(UserInfo),
_ = set_invoicing_meta(InvoiceID, PaymentID), _ = set_invoicing_meta(InvoiceID, PaymentID),
St = assert_invoice_accessible(get_state(InvoiceID)), St = get_state(InvoiceID),
_ = assert_invoice(accessible, St),
get_payment_state(get_payment_session(PaymentID, St)); get_payment_state(get_payment_session(PaymentID, St));
handle_function_('GetPaymentRefund', {UserInfo, InvoiceID, PaymentID, ID}, _Opts) -> handle_function_('GetPaymentRefund', {UserInfo, InvoiceID, PaymentID, ID}, _Opts) ->
ok = assume_user_identity(UserInfo), ok = assume_user_identity(UserInfo),
_ = set_invoicing_meta(InvoiceID, PaymentID), _ = set_invoicing_meta(InvoiceID, PaymentID),
St = assert_invoice_accessible(get_state(InvoiceID)), St = get_state(InvoiceID),
_ = assert_invoice(accessible, St),
hg_invoice_payment:get_refund(ID, get_payment_session(PaymentID, St)); hg_invoice_payment:get_refund(ID, get_payment_session(PaymentID, St));
handle_function_('GetPaymentChargeback', {UserInfo, InvoiceID, PaymentID, ID}, _Opts) -> handle_function_('GetPaymentChargeback', {UserInfo, InvoiceID, PaymentID, ID}, _Opts) ->
ok = assume_user_identity(UserInfo), ok = assume_user_identity(UserInfo),
_ = set_invoicing_meta(InvoiceID, PaymentID), _ = set_invoicing_meta(InvoiceID, PaymentID),
St = assert_invoice_accessible(get_state(InvoiceID)), St = get_state(InvoiceID),
_ = assert_invoice(accessible, St),
CBSt = hg_invoice_payment:get_chargeback_state(ID, get_payment_session(PaymentID, St)), CBSt = hg_invoice_payment:get_chargeback_state(ID, get_payment_session(PaymentID, St)),
hg_invoice_payment_chargeback:get(CBSt); hg_invoice_payment_chargeback:get(CBSt);
handle_function_('GetPaymentAdjustment', {UserInfo, InvoiceID, PaymentID, ID}, _Opts) -> handle_function_('GetPaymentAdjustment', {UserInfo, InvoiceID, PaymentID, ID}, _Opts) ->
ok = assume_user_identity(UserInfo), ok = assume_user_identity(UserInfo),
_ = set_invoicing_meta(InvoiceID, PaymentID), _ = set_invoicing_meta(InvoiceID, PaymentID),
St = assert_invoice_accessible(get_state(InvoiceID)), St = get_state(InvoiceID),
_ = assert_invoice(accessible, St),
hg_invoice_payment:get_adjustment(ID, get_payment_session(PaymentID, St)); hg_invoice_payment:get_adjustment(ID, get_payment_session(PaymentID, St));
handle_function_('ComputeTerms', {UserInfo, InvoiceID, PartyRevision0}, _Opts) -> handle_function_('ComputeTerms', {UserInfo, InvoiceID, PartyRevision0}, _Opts) ->
ok = assume_user_identity(UserInfo), ok = assume_user_identity(UserInfo),
_ = set_invoicing_meta(InvoiceID), _ = set_invoicing_meta(InvoiceID),
St = assert_invoice_accessible(get_state(InvoiceID)), St = get_state(InvoiceID),
_ = assert_invoice(accessible, St),
ShopID = get_shop_id(St), ShopID = get_shop_id(St),
PartyID = get_party_id(St), PartyID = get_party_id(St),
Timestamp = get_created_at(St), Timestamp = get_created_at(St),
@ -264,23 +289,35 @@ handle_function_(Fun, Args, _Opts) when
handle_function_('Repair', {UserInfo, InvoiceID, Changes, Action, Params}, _Opts) -> handle_function_('Repair', {UserInfo, InvoiceID, Changes, Action, Params}, _Opts) ->
ok = assume_user_identity(UserInfo), ok = assume_user_identity(UserInfo),
_ = set_invoicing_meta(InvoiceID), _ = set_invoicing_meta(InvoiceID),
_ = assert_invoice_accessible(get_initial_state(InvoiceID)), _ = assert_invoice(accessible, get_initial_state(InvoiceID)),
repair(InvoiceID, {changes, Changes, Action, Params}); repair(InvoiceID, {changes, Changes, Action, Params});
handle_function_('RepairWithScenario', {UserInfo, InvoiceID, Scenario}, _Opts) -> handle_function_('RepairWithScenario', {UserInfo, InvoiceID, Scenario}, _Opts) ->
ok = assume_user_identity(UserInfo), ok = assume_user_identity(UserInfo),
_ = set_invoicing_meta(InvoiceID), _ = set_invoicing_meta(InvoiceID),
_ = assert_invoice_accessible(get_initial_state(InvoiceID)), _ = assert_invoice(accessible, get_initial_state(InvoiceID)),
repair(InvoiceID, {scenario, Scenario}). repair(InvoiceID, {scenario, Scenario}).
assert_invoice_operable(St) -> %%----------------- invoice asserts
% FIXME do not lose party here assert_invoice(Checks, #st{} = St) when is_list(Checks) ->
Party = hg_party:get_party(get_party_id(St)), lists:foldl(fun assert_invoice/2, St, Checks);
Shop = hg_party:get_shop(get_shop_id(St), Party), assert_invoice(operable, #st{party = Party} = St) when Party =/= undefined ->
assert_party_shop_operable(Shop, Party). assert_party_shop_operable(
hg_party:get_shop(get_shop_id(St), Party),
Party
),
St;
assert_invoice(accessible, #st{} = St) ->
assert_party_accessible(get_party_id(St)),
St;
assert_invoice({status, Status}, #st{invoice = #domain_Invoice{status = {Status, _}}} = St) ->
St;
assert_invoice({status, _Status}, #st{invoice = #domain_Invoice{status = Invalid}}) ->
throw(?invalid_invoice_status(Invalid)).
assert_party_shop_operable(Shop, Party) -> assert_party_shop_operable(Shop, Party) ->
_ = assert_party_operable(Party), _ = assert_party_operable(Party),
assert_shop_operable(Shop). _ = assert_shop_operable(Shop),
ok.
get_invoice_state(#st{invoice = Invoice, payments = Payments}) -> get_invoice_state(#st{invoice = Invoice, payments = Payments}) ->
#payproc_Invoice{ #payproc_Invoice{
@ -351,8 +388,6 @@ fail(Ref) ->
%% %%
-include("invoice_events.hrl").
get_history(Ref) -> get_history(Ref) ->
History = hg_machine:get_history(?NS, Ref), History = hg_machine:get_history(?NS, Ref),
unmarshal_history(map_history_error(History)). unmarshal_history(map_history_error(History)).
@ -383,57 +418,38 @@ publish_invoice_event(InvoiceID, {ID, Dt, Event}) ->
ensure_started(ID, {TemplateID, PartyRevision, Params}) -> ensure_started(ID, {TemplateID, PartyRevision, Params}) ->
SerializedArgs = {TemplateID, PartyRevision, marshal_invoice_params(Params)}, SerializedArgs = {TemplateID, PartyRevision, marshal_invoice_params(Params)},
map_start_error(do_start(ID, SerializedArgs)). case hg_machine:start(?NS, ID, SerializedArgs) of
{ok, _} -> ok;
do_start(ID, Args) -> {error, exists} -> ok;
hg_machine:start(?NS, ID, Args). {error, Reason} -> erlang:error(Reason)
end.
call(ID, Function, Args) -> call(ID, Function, Args) ->
case hg_machine:thrift_call(?NS, ID, invoicing, {'Invoicing', Function}, Args) of case hg_machine:thrift_call(?NS, ID, invoicing, {'Invoicing', Function}, Args) of
ok -> ok -> ok;
ok; {ok, Reply} -> Reply;
{ok, Reply} -> {exception, Exception} -> erlang:throw(Exception);
Reply; {error, notfound} -> erlang:throw(#payproc_InvoiceNotFound{});
{exception, Exception} -> {error, Error} -> erlang:error(Error)
erlang:throw(Exception);
{error, Error} ->
map_error(Error)
end. end.
repair(ID, Args) -> repair(ID, Args) ->
map_repair_error(hg_machine:repair(?NS, ID, Args)). case hg_machine:repair(?NS, ID, Args) of
{ok, _Result} -> ok;
-spec map_error(notfound | any()) -> no_return(). {error, notfound} -> erlang:throw(#payproc_InvoiceNotFound{});
map_error(notfound) -> {error, working} -> erlang:throw(#'InvalidRequest'{errors = [<<"No need to repair">>]});
erlang:throw(#payproc_InvoiceNotFound{}); {error, Reason} -> erlang:error(Reason)
map_error(Reason) -> end.
erlang:error(Reason).
map_history_error({ok, Result}) -> map_history_error({ok, Result}) ->
Result; Result;
map_history_error({error, notfound}) -> map_history_error({error, notfound}) ->
throw(#payproc_InvoiceNotFound{}). throw(#payproc_InvoiceNotFound{}).
map_start_error({ok, _}) ->
ok;
map_start_error({error, exists}) ->
ok;
map_start_error({error, Reason}) ->
error(Reason).
map_repair_error({ok, _Result}) ->
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'(). -type invoice() :: dmsl_domain_thrift:'Invoice'().
-type party() :: dmsl_domain_thrift:'Party'().
-type invoice_id() :: dmsl_domain_thrift:'InvoiceID'(). -type invoice_id() :: dmsl_domain_thrift:'InvoiceID'().
-type invoice_tpl_id() :: dmsl_domain_thrift:'InvoiceTemplateID'(). -type invoice_tpl_id() :: dmsl_domain_thrift:'InvoiceTemplateID'().
-type invoice_params() :: dmsl_payment_processing_thrift:'InvoiceParams'(). -type invoice_params() :: dmsl_payment_processing_thrift:'InvoiceParams'().
@ -443,7 +459,6 @@ map_repair_error({error, Reason}) ->
-type payment_id() :: dmsl_domain_thrift:'InvoicePaymentID'(). -type payment_id() :: dmsl_domain_thrift:'InvoicePaymentID'().
-type payment_st() :: hg_invoice_payment:st(). -type payment_st() :: hg_invoice_payment:st().
-define(invalid_invoice_status(Status), #payproc_InvalidInvoiceStatus{status = Status}).
-define(payment_pending(PaymentID), #payproc_InvoicePaymentPending{id = PaymentID}). -define(payment_pending(PaymentID), #payproc_InvoicePaymentPending{id = PaymentID}).
-define(adjustment_target_status(Status), #domain_InvoiceAdjustment{ -define(adjustment_target_status(Status), #domain_InvoiceAdjustment{
state = state =
@ -578,15 +593,14 @@ process_call(Call, #{history := History}) ->
end. end.
-spec handle_call(call(), st()) -> call_result(). -spec handle_call(call(), st()) -> call_result().
handle_call({{'Invoicing', 'StartPayment'}, {_UserInfo, _InvoiceID, PaymentParams}}, St) -> handle_call({{'Invoicing', 'StartPayment'}, {_UserInfo, _InvoiceID, PaymentParams}}, St0) ->
% TODO consolidate these assertions somehow St = St0#st{party = hg_party:get_party(get_party_id(St0))},
_ = assert_invoice_accessible(St), _ = assert_invoice([accessible, operable], St),
_ = assert_invoice_operable(St),
_ = assert_all_adjustments_finalised(St), _ = assert_all_adjustments_finalised(St),
start_payment(PaymentParams, St); start_payment(PaymentParams, St);
handle_call({{'Invoicing', 'CapturePayment'}, {_UserInfo, _InvoiceID, PaymentID, Params}}, St) -> handle_call({{'Invoicing', 'CapturePayment'}, {_UserInfo, _InvoiceID, PaymentID, Params}}, St0) ->
_ = assert_invoice_accessible(St), St = St0#st{party = hg_party:get_party(get_party_id(St0))},
_ = assert_invoice_operable(St), _ = assert_invoice([accessible, operable], St),
#payproc_InvoicePaymentCaptureParams{ #payproc_InvoicePaymentCaptureParams{
reason = Reason, reason = Reason,
cash = Cash, cash = Cash,
@ -601,31 +615,28 @@ handle_call({{'Invoicing', 'CapturePayment'}, {_UserInfo, _InvoiceID, PaymentID,
action => Action, action => Action,
state => St state => St
}; };
handle_call({{'Invoicing', 'CancelPayment'}, {_UserInfo, _InvoiceID, PaymentID, Reason}}, St) -> handle_call({{'Invoicing', 'CancelPayment'}, {_UserInfo, _InvoiceID, PaymentID, Reason}}, St0) ->
_ = assert_invoice_accessible(St), St = St0#st{party = hg_party:get_party(get_party_id(St0))},
_ = assert_invoice_operable(St), _ = assert_invoice([accessible, operable], St),
PaymentSession = get_payment_session(PaymentID, St), PaymentSession = get_payment_session(PaymentID, St),
#{timestamp := OccurredAt} = get_payment_opts(St),
{ok, {Changes, Action}} = hg_invoice_payment:cancel(PaymentSession, Reason), {ok, {Changes, Action}} = hg_invoice_payment:cancel(PaymentSession, Reason),
#{ #{
response => ok, response => ok,
changes => wrap_payment_changes(PaymentID, Changes, OccurredAt), changes => wrap_payment_changes(PaymentID, Changes, hg_datetime:format_now()),
action => Action, action => Action,
state => St state => St
}; };
handle_call({{'Invoicing', 'Fulfill'}, {_UserInfo, _InvoiceID, Reason}}, St) -> handle_call({{'Invoicing', 'Fulfill'}, {_UserInfo, _InvoiceID, Reason}}, St0) ->
_ = assert_invoice_accessible(St), St = St0#st{party = hg_party:get_party(get_party_id(St0))},
_ = assert_invoice_operable(St), _ = assert_invoice([accessible, operable, {status, paid}], St),
_ = assert_invoice_status(paid, St),
#{ #{
response => ok, response => ok,
changes => [?invoice_status_changed(?invoice_fulfilled(hg_utils:format_reason(Reason)))], changes => [?invoice_status_changed(?invoice_fulfilled(hg_utils:format_reason(Reason)))],
state => St state => St
}; };
handle_call({{'Invoicing', 'Rescind'}, {_UserInfo, _InvoiceID, Reason}}, St) -> handle_call({{'Invoicing', 'Rescind'}, {_UserInfo, _InvoiceID, Reason}}, St0) ->
_ = assert_invoice_accessible(St), St = St0#st{party = hg_party:get_party(get_party_id(St0))},
_ = assert_invoice_operable(St), _ = assert_invoice([accessible, operable, {status, unpaid}], St),
_ = assert_invoice_status(unpaid, St),
_ = assert_no_pending_payment(St), _ = assert_no_pending_payment(St),
#{ #{
response => ok, response => ok,
@ -634,8 +645,8 @@ handle_call({{'Invoicing', 'Rescind'}, {_UserInfo, _InvoiceID, Reason}}, St) ->
state => St state => St
}; };
handle_call({{'Invoicing', 'CreateInvoiceAdjustment'}, {_UserInfo, _InvoiceID, Params}}, St) -> handle_call({{'Invoicing', 'CreateInvoiceAdjustment'}, {_UserInfo, _InvoiceID, Params}}, St) ->
_ = assert_invoice(accessible, St),
ID = create_adjustment_id(St), ID = create_adjustment_id(St),
St = assert_invoice_accessible(St),
TargetStatus = get_adjustment_params_target_status(Params), TargetStatus = get_adjustment_params_target_status(Params),
InvoiceStatus = get_invoice_status(St), InvoiceStatus = get_invoice_status(St),
ok = assert_no_pending_payment(St), ok = assert_no_pending_payment(St),
@ -644,7 +655,7 @@ handle_call({{'Invoicing', 'CreateInvoiceAdjustment'}, {_UserInfo, _InvoiceID, P
OccurredAt = hg_datetime:format_now(), OccurredAt = hg_datetime:format_now(),
wrap_adjustment_impact(ID, hg_invoice_adjustment:create(ID, Params, OccurredAt), St, OccurredAt); wrap_adjustment_impact(ID, hg_invoice_adjustment:create(ID, Params, OccurredAt), St, OccurredAt);
handle_call({{'Invoicing', 'CaptureAdjustment'}, {_UserInfo, _InvoiceID, ID}}, St) -> handle_call({{'Invoicing', 'CaptureAdjustment'}, {_UserInfo, _InvoiceID, ID}}, St) ->
_ = assert_invoice_accessible(St), _ = assert_invoice(accessible, St),
_ = assert_adjustment_processed(ID, St), _ = assert_adjustment_processed(ID, St),
OccurredAt = hg_datetime:format_now(), OccurredAt = hg_datetime:format_now(),
?adjustment_target_status(Status) = get_adjustment(ID, St), ?adjustment_target_status(Status) = get_adjustment(ID, St),
@ -656,7 +667,7 @@ handle_call({{'Invoicing', 'CaptureAdjustment'}, {_UserInfo, _InvoiceID, ID}}, S
state => St state => St
}; };
handle_call({{'Invoicing', 'CancelAdjustment'}, {_UserInfo, _InvoiceID, ID}}, St) -> handle_call({{'Invoicing', 'CancelAdjustment'}, {_UserInfo, _InvoiceID, ID}}, St) ->
_ = assert_invoice_accessible(St), _ = assert_invoice(accessible, St),
_ = assert_adjustment_processed(ID, St), _ = assert_adjustment_processed(ID, St),
OccurredAt = hg_datetime:format_now(), OccurredAt = hg_datetime:format_now(),
Status = get_invoice_status(St), Status = get_invoice_status(St),
@ -667,57 +678,56 @@ handle_call({{'Invoicing', 'CancelAdjustment'}, {_UserInfo, _InvoiceID, ID}}, St
action => set_invoice_timer(Status, Action, St), action => set_invoice_timer(Status, Action, St),
state => St state => St
}; };
handle_call({{'Invoicing', 'RefundPayment'}, {_UserInfo, _InvoiceID, PaymentID, Params}}, St) -> handle_call({{'Invoicing', 'RefundPayment'}, {_UserInfo, _InvoiceID, PaymentID, Params}}, St0) ->
_ = assert_invoice_accessible(St), St = St0#st{party = hg_party:get_party(get_party_id(St0))},
_ = assert_invoice_operable(St), _ = assert_invoice([accessible, operable], St),
PaymentSession = get_payment_session(PaymentID, St), PaymentSession = get_payment_session(PaymentID, St),
start_refund(refund, Params, PaymentID, PaymentSession, St); start_refund(refund, Params, PaymentID, PaymentSession, St);
handle_call({{'Invoicing', 'CreateManualRefund'}, {_UserInfo, _InvoiceID, PaymentID, Params}}, St) -> handle_call({{'Invoicing', 'CreateManualRefund'}, {_UserInfo, _InvoiceID, PaymentID, Params}}, St0) ->
_ = assert_invoice_accessible(St), St = St0#st{party = hg_party:get_party(get_party_id(St0))},
_ = assert_invoice_operable(St), _ = assert_invoice([accessible, operable], St),
PaymentSession = get_payment_session(PaymentID, St), PaymentSession = get_payment_session(PaymentID, St),
start_refund(manual_refund, Params, PaymentID, PaymentSession, St); start_refund(manual_refund, Params, PaymentID, PaymentSession, St);
handle_call({{'Invoicing', 'CreateChargeback'}, {_UserInfo, _InvoiceID, PaymentID, Params}}, St) -> handle_call({{'Invoicing', 'CreateChargeback'}, {_UserInfo, _InvoiceID, PaymentID, Params}}, St) ->
_ = assert_invoice_accessible(St), _ = assert_invoice(accessible, St),
PaymentSession = get_payment_session(PaymentID, St), PaymentSession = get_payment_session(PaymentID, St),
PaymentOpts = get_payment_opts(St), PaymentOpts = get_payment_opts(St),
start_chargeback(Params, PaymentID, PaymentSession, PaymentOpts, St); start_chargeback(Params, PaymentID, PaymentSession, PaymentOpts, St);
handle_call({{'Invoicing', 'CancelChargeback'}, {_UserInfo, _InvoiceID, PaymentID, ChargebackID, Params}}, St) -> handle_call({{'Invoicing', 'CancelChargeback'}, {_UserInfo, _InvoiceID, PaymentID, ChargebackID, Params}}, St) ->
#payproc_InvoicePaymentChargebackCancelParams{occurred_at = OccurredAt} = Params, #payproc_InvoicePaymentChargebackCancelParams{occurred_at = OccurredAt} = Params,
_ = assert_invoice_accessible(St), _ = assert_invoice(accessible, St),
PaymentSession = get_payment_session(PaymentID, St), PaymentSession = get_payment_session(PaymentID, St),
CancelResult = hg_invoice_payment:cancel_chargeback(ChargebackID, PaymentSession, Params), CancelResult = hg_invoice_payment:cancel_chargeback(ChargebackID, PaymentSession, Params),
wrap_payment_impact(PaymentID, CancelResult, St, OccurredAt); wrap_payment_impact(PaymentID, CancelResult, St, OccurredAt);
handle_call({{'Invoicing', 'RejectChargeback'}, {_UserInfo, _InvoiceID, PaymentID, ChargebackID, Params}}, St) -> handle_call({{'Invoicing', 'RejectChargeback'}, {_UserInfo, _InvoiceID, PaymentID, ChargebackID, Params}}, St) ->
#payproc_InvoicePaymentChargebackRejectParams{occurred_at = OccurredAt} = Params, #payproc_InvoicePaymentChargebackRejectParams{occurred_at = OccurredAt} = Params,
_ = assert_invoice_accessible(St), _ = assert_invoice(accessible, St),
PaymentSession = get_payment_session(PaymentID, St), PaymentSession = get_payment_session(PaymentID, St),
RejectResult = hg_invoice_payment:reject_chargeback(ChargebackID, PaymentSession, Params), RejectResult = hg_invoice_payment:reject_chargeback(ChargebackID, PaymentSession, Params),
wrap_payment_impact(PaymentID, RejectResult, St, OccurredAt); wrap_payment_impact(PaymentID, RejectResult, St, OccurredAt);
handle_call({{'Invoicing', 'AcceptChargeback'}, {_UserInfo, _InvoiceID, PaymentID, ChargebackID, Params}}, St) -> handle_call({{'Invoicing', 'AcceptChargeback'}, {_UserInfo, _InvoiceID, PaymentID, ChargebackID, Params}}, St) ->
#payproc_InvoicePaymentChargebackAcceptParams{occurred_at = OccurredAt} = Params, #payproc_InvoicePaymentChargebackAcceptParams{occurred_at = OccurredAt} = Params,
_ = assert_invoice_accessible(St), _ = assert_invoice(accessible, St),
PaymentSession = get_payment_session(PaymentID, St), PaymentSession = get_payment_session(PaymentID, St),
AcceptResult = hg_invoice_payment:accept_chargeback(ChargebackID, PaymentSession, Params), AcceptResult = hg_invoice_payment:accept_chargeback(ChargebackID, PaymentSession, Params),
wrap_payment_impact(PaymentID, AcceptResult, St, OccurredAt); wrap_payment_impact(PaymentID, AcceptResult, St, OccurredAt);
handle_call({{'Invoicing', 'ReopenChargeback'}, {_UserInfo, _InvoiceID, PaymentID, ChargebackID, Params}}, St) -> handle_call({{'Invoicing', 'ReopenChargeback'}, {_UserInfo, _InvoiceID, PaymentID, ChargebackID, Params}}, St) ->
#payproc_InvoicePaymentChargebackReopenParams{occurred_at = OccurredAt} = Params, #payproc_InvoicePaymentChargebackReopenParams{occurred_at = OccurredAt} = Params,
_ = assert_invoice_accessible(St), _ = assert_invoice(accessible, St),
PaymentSession = get_payment_session(PaymentID, St), PaymentSession = get_payment_session(PaymentID, St),
ReopenResult = hg_invoice_payment:reopen_chargeback(ChargebackID, PaymentSession, Params), ReopenResult = hg_invoice_payment:reopen_chargeback(ChargebackID, PaymentSession, Params),
wrap_payment_impact(PaymentID, ReopenResult, St, OccurredAt); wrap_payment_impact(PaymentID, ReopenResult, St, OccurredAt);
handle_call({{'Invoicing', 'CreatePaymentAdjustment'}, {_UserInfo, _InvoiceID, PaymentID, Params}}, St) -> handle_call({{'Invoicing', 'CreatePaymentAdjustment'}, {_UserInfo, _InvoiceID, PaymentID, Params}}, St) ->
_ = assert_invoice_accessible(St), _ = assert_invoice(accessible, St),
PaymentSession = get_payment_session(PaymentID, St), PaymentSession = get_payment_session(PaymentID, St),
Timestamp = hg_datetime:format_now(), Opts = #{timestamp := Timestamp} = get_payment_opts(St),
PaymentOpts = get_payment_opts(St),
wrap_payment_impact( wrap_payment_impact(
PaymentID, PaymentID,
hg_invoice_payment:create_adjustment(Timestamp, Params, PaymentSession, PaymentOpts), hg_invoice_payment:create_adjustment(Timestamp, Params, PaymentSession, Opts),
St St
); );
handle_call({{'Invoicing', 'CapturePaymentAdjustment'}, {_UserInfo, _InvoiceID, PaymentID, ID}}, St) -> handle_call({{'Invoicing', 'CapturePaymentAdjustment'}, {_UserInfo, _InvoiceID, PaymentID, ID}}, St) ->
_ = assert_invoice_accessible(St), _ = assert_invoice(accessible, St),
PaymentSession = get_payment_session(PaymentID, St), PaymentSession = get_payment_session(PaymentID, St),
Adjustment = hg_invoice_payment:get_adjustment(ID, PaymentSession), Adjustment = hg_invoice_payment:get_adjustment(ID, PaymentSession),
PaymentOpts = get_payment_opts( PaymentOpts = get_payment_opts(
@ -728,7 +738,7 @@ handle_call({{'Invoicing', 'CapturePaymentAdjustment'}, {_UserInfo, _InvoiceID,
Impact = hg_invoice_payment:capture_adjustment(ID, PaymentSession, PaymentOpts), Impact = hg_invoice_payment:capture_adjustment(ID, PaymentSession, PaymentOpts),
wrap_payment_impact(PaymentID, Impact, St); wrap_payment_impact(PaymentID, Impact, St);
handle_call({{'Invoicing', 'CancelPaymentAdjustment'}, {_UserInfo, _InvoiceID, PaymentID, ID}}, St) -> handle_call({{'Invoicing', 'CancelPaymentAdjustment'}, {_UserInfo, _InvoiceID, PaymentID, ID}}, St) ->
_ = assert_invoice_accessible(St), _ = assert_invoice(accessible, St),
PaymentSession = get_payment_session(PaymentID, St), PaymentSession = get_payment_session(PaymentID, St),
Adjustment = hg_invoice_payment:get_adjustment(ID, PaymentSession), Adjustment = hg_invoice_payment:get_adjustment(ID, PaymentSession),
PaymentOpts = get_payment_opts( PaymentOpts = get_payment_opts(
@ -751,13 +761,6 @@ dispatch_callback(Tag, {provider, Payload}, St = #st{activity = {payment, Paymen
dispatch_callback(_Tag, _Callback, _St) -> dispatch_callback(_Tag, _Callback, _St) ->
throw(invalid_callback). throw(invalid_callback).
assert_invoice_status(Status, #st{invoice = Invoice}) ->
assert_invoice_status(Status, Invoice);
assert_invoice_status(Status, #domain_Invoice{status = {Status, _}}) ->
ok;
assert_invoice_status(_Status, #domain_Invoice{status = Invalid}) ->
throw(?invalid_invoice_status(Invalid)).
assert_no_pending_payment(#st{activity = {payment, PaymentID}}) -> assert_no_pending_payment(#st{activity = {payment, PaymentID}}) ->
throw(?payment_pending(PaymentID)); throw(?payment_pending(PaymentID));
assert_no_pending_payment(_) -> assert_no_pending_payment(_) ->
@ -779,8 +782,6 @@ capture_payment(PaymentSession, Reason, Cash, Cart, Opts) ->
%% %%
-include("payment_events.hrl").
start_payment(#payproc_InvoicePaymentParams{id = undefined} = PaymentParams, St) -> start_payment(#payproc_InvoicePaymentParams{id = undefined} = PaymentParams, St) ->
PaymentID = create_payment_id(St), PaymentID = create_payment_id(St),
do_start_payment(PaymentID, PaymentParams, St); do_start_payment(PaymentID, PaymentParams, St);
@ -796,7 +797,7 @@ start_payment(#payproc_InvoicePaymentParams{id = PaymentID} = PaymentParams, St)
end. end.
do_start_payment(PaymentID, PaymentParams, St) -> do_start_payment(PaymentID, PaymentParams, St) ->
_ = assert_invoice_status(unpaid, St), _ = assert_invoice({status, unpaid}, St),
_ = assert_no_pending_payment(St), _ = assert_no_pending_payment(St),
Opts = #{timestamp := OccurredAt} = get_payment_opts(St), Opts = #{timestamp := OccurredAt} = get_payment_opts(St),
% TODO make timer reset explicit here % TODO make timer reset explicit here
@ -1009,9 +1010,13 @@ start_new_refund(RefundType, PaymentID, Params, PaymentSession, St) when
%% %%
start_chargeback(Params, PaymentID, PaymentSession, PaymentOpts, St) -> start_chargeback(Params, PaymentID, PaymentSession, PaymentOpts, St) ->
case get_chargeback_state(get_chargeback_id(Params), PaymentSession) of #payproc_InvoicePaymentChargebackParams{id = ID} = Params,
case get_chargeback_state(ID, PaymentSession) of
undefined -> undefined ->
start_new_chargeback(PaymentID, Params, PaymentSession, PaymentOpts, St); #payproc_InvoicePaymentChargebackParams{occurred_at = OccurredAt} = Params,
CreateResult = hg_invoice_payment:create_chargeback(PaymentSession, PaymentOpts, Params),
wrap_payment_impact(PaymentID, CreateResult, St, OccurredAt);
ChargebackState -> ChargebackState ->
#{ #{
response => hg_invoice_payment_chargeback:get(ChargebackState), response => hg_invoice_payment_chargeback:get(ChargebackState),
@ -1019,14 +1024,6 @@ start_chargeback(Params, PaymentID, PaymentSession, PaymentOpts, St) ->
} }
end. end.
start_new_chargeback(PaymentID, Params, PaymentSession, PaymentOpts, St) ->
#payproc_InvoicePaymentChargebackParams{occurred_at = OccurredAt} = Params,
CreateResult = hg_invoice_payment:create_chargeback(PaymentSession, PaymentOpts, Params),
wrap_payment_impact(PaymentID, CreateResult, St, OccurredAt).
get_chargeback_id(#payproc_InvoicePaymentChargebackParams{id = ID}) ->
ID.
get_chargeback_state(ID, PaymentState) -> get_chargeback_state(ID, PaymentState) ->
try try
hg_invoice_payment:get_chargeback_state(ID, PaymentState) hg_invoice_payment:get_chargeback_state(ID, PaymentState)
@ -1249,22 +1246,6 @@ create_adjustment_id(#st{adjustments = Adjustments}) ->
%% %%
assert_shop_exists(Shop) ->
hg_invoice_utils:assert_shop_exists(Shop).
assert_party_operable(Party) ->
hg_invoice_utils:assert_party_operable(Party).
assert_shop_operable(Shop) ->
hg_invoice_utils:assert_shop_operable(Shop).
assert_invoice_accessible(St = #st{}) ->
assert_party_accessible(get_party_id(St)),
St.
assert_party_accessible(PartyID) ->
hg_invoice_utils:assert_party_accessible(PartyID).
assume_user_identity(UserInfo) -> assume_user_identity(UserInfo) ->
hg_woody_handler_utils:assume_user_identity(UserInfo). hg_woody_handler_utils:assume_user_identity(UserInfo).
@ -1389,8 +1370,6 @@ make_invoice_context(Context, _) ->
%% %%
-include("domain.hrl").
log_changes(Changes, St) -> log_changes(Changes, St) ->
lists:foreach(fun(C) -> log_change(C, St) end, Changes). lists:foreach(fun(C) -> log_change(C, St) end, Changes).