diff --git a/apps/hellgate/include/payment_events.hrl b/apps/hellgate/include/payment_events.hrl index 8adc8cb1b..44569024e 100644 --- a/apps/hellgate/include/payment_events.hrl +++ b/apps/hellgate/include/payment_events.hrl @@ -174,6 +174,10 @@ {invoice_payment_adjustment_status_changed, #payproc_InvoicePaymentAdjustmentStatusChanged{status = Status}} ). +-define(adjustment_clock_update(Clock), + {invoice_payment_adjustment_clock_update, #payproc_InvoicePaymentClockUpdate{clock = Clock}} +). + -define(adjustment_pending(), {pending, #domain_InvoicePaymentAdjustmentPending{}} ). diff --git a/apps/hellgate/src/hg_invoice_payment.erl b/apps/hellgate/src/hg_invoice_payment.erl index eb8ec21ac..6dc7764cf 100644 --- a/apps/hellgate/src/hg_invoice_payment.erl +++ b/apps/hellgate/src/hg_invoice_payment.erl @@ -1660,30 +1660,35 @@ cancel_adjustment(ID, St, Options) -> finalize_adjustment(ID, Intent, St, Options = #{timestamp := Timestamp}) -> Adjustment = get_adjustment(ID, St), ok = assert_adjustment_status(processed, Adjustment), - ok = finalize_adjustment_cashflow(Intent, Adjustment, St, Options), - Status = - case Intent of - capture -> - ?adjustment_captured(Timestamp); - cancel -> - ?adjustment_cancelled(Timestamp) - end, - Event = ?adjustment_ev(ID, ?adjustment_status_changed(Status)), - {ok, {[Event], hg_machine_action:new()}}. + case finalize_adjustment_cashflow(Intent, Adjustment, St, Options) of + {ok, Clock} -> + Status = + case Intent of + capture -> + ?adjustment_captured(Timestamp); + cancel -> + ?adjustment_cancelled(Timestamp) + end, + ClockEvent = ?adjustment_ev(ID, ?adjustment_clock_update(Clock)), + Event = ?adjustment_ev(ID, ?adjustment_status_changed(Status)), + {ok, {[ClockEvent, Event], hg_machine_action:new()}}; + {error, not_ready} -> + woody_error:raise(system, {external, resource_unavailable, <<"Accounter was not ready">>}) + end. -prepare_adjustment_cashflow(Adjustment, St, Options) -> +prepare_adjustment_cashflow(Adjustment, St, Options = #{timestamp := Timestamp}) -> PlanID = construct_adjustment_plan_id(Adjustment, St, Options), Plan = get_adjustment_cashflow_plan(Adjustment), - plan(PlanID, Plan). + plan_adjustment_cashflow(PlanID, Plan, Timestamp, St#st.clock). -finalize_adjustment_cashflow(Intent, Adjustment, St, Options) -> +finalize_adjustment_cashflow(Intent, Adjustment, St, Options = #{timestamp := Timestamp}) -> PlanID = construct_adjustment_plan_id(Adjustment, St, Options), Plan = get_adjustment_cashflow_plan(Adjustment), case Intent of capture -> - commit(PlanID, Plan); + commit_adjustment_cashflow(PlanID, Plan, Timestamp, St#st.clock); cancel -> - rollback(PlanID, Plan) + rollback_adjustment_cashflow(PlanID, Plan, Timestamp, St#st.clock) end. get_adjustment_cashflow_plan(#domain_InvoicePaymentAdjustment{ @@ -1699,23 +1704,20 @@ number_plan([[] | Tail], Number, Acc) -> number_plan([NonEmpty | Tail], Number, Acc) -> number_plan(Tail, Number + 1, [{Number, NonEmpty} | Acc]). -plan(_PlanID, []) -> - ok; -plan(PlanID, Plan) -> - _ = hg_accounting:plan(PlanID, Plan), - ok. +plan_adjustment_cashflow(_PlanID, [], _Timestamp, Clock) -> + {ok, Clock}; +plan_adjustment_cashflow(PlanID, Plan, Timestamp, Clock) -> + hg_accounting_new:plan(PlanID, Plan, Timestamp, Clock). -commit(_PlanID, []) -> - ok; -commit(PlanID, Plan) -> - _ = hg_accounting:commit(PlanID, Plan), - ok. +commit_adjustment_cashflow(_PlanID, [], _Timestamp, Clock) -> + {ok, Clock}; +commit_adjustment_cashflow(PlanID, Plan, Timestamp, Clock) -> + hg_accounting_new:commit(PlanID, Plan, Timestamp, Clock). -rollback(_PlanID, []) -> - ok; -rollback(PlanID, Plan) -> - _ = hg_accounting:rollback(PlanID, Plan), - ok. +rollback_adjustment_cashflow(_PlanID, [], _Timestamp, Clock) -> + {ok, Clock}; +rollback_adjustment_cashflow(PlanID, Plan, Timestamp, Clock) -> + hg_accounting_new:rollback(PlanID, Plan, Timestamp, Clock). assert_adjustment_status(Status, #domain_InvoicePaymentAdjustment{status = {Status, _}}) -> ok; @@ -1966,12 +1968,20 @@ get_manual_refund_events(#refund_st{transaction_info = TransactionInfo}) -> %% -spec process_adjustment_cashflow(adjustment_id(), action(), st()) -> machine_result(). -process_adjustment_cashflow(ID, _Action, St) -> +process_adjustment_cashflow(ID, Action, St) -> Opts = get_opts(St), Adjustment = get_adjustment(ID, St), - ok = prepare_adjustment_cashflow(Adjustment, St, Opts), - Events = [?adjustment_ev(ID, ?adjustment_status_changed(?adjustment_processed()))], - {done, {Events, hg_machine_action:new()}}. + case prepare_adjustment_cashflow(Adjustment, St, Opts) of + {ok, Clock} -> + Events = [ + ?adjustment_ev(ID, ?adjustment_clock_update(Clock)), + ?adjustment_ev(ID, ?adjustment_status_changed(?adjustment_processed())) + ], + {done, {Events, hg_machine_action:new()}}; + {error, not_ready} -> + _ = logger:warning("Accounter was not ready, retrying"), + {next, {[], hg_machine_action:set_timeout(0, Action)}} + end. process_accounter_update(Action, St = #st{partial_cash_flow = FinalCashflow, capture_params = CaptureParams}) -> #{timestamp := Timestamp} = Opts = get_opts(St), @@ -3091,12 +3101,15 @@ merge_change(Change = ?adjustment_ev(ID, Event), St, Opts) -> ?adjustment_status_changed(?adjustment_processed()) -> _ = validate_transition({adjustment_new, ID}, Change, St, Opts), St#st{activity = {adjustment_pending, ID}}; + ?adjustment_clock_update(_) -> + _ = validate_transition([{adjustment_new, ID}, {adjustment_pending, ID}], Change, St, Opts), + St; ?adjustment_status_changed(_) -> _ = validate_transition({adjustment_pending, ID}, Change, St, Opts), St#st{activity = idle} end, Adjustment = merge_adjustment_change(Event, try_get_adjustment(ID, St1)), - St2 = set_adjustment(ID, Adjustment, St1), + St2 = set_adjustment_clock(Event, set_adjustment(ID, Adjustment, St1)), % TODO new cashflow imposed implicitly on the payment state? rough case get_adjustment_status(Adjustment) of ?adjustment_captured(_) -> @@ -3188,6 +3201,8 @@ merge_refund_change(?session_ev(?refunded(), Change), St) -> merge_adjustment_change(?adjustment_created(Adjustment), undefined) -> Adjustment; +merge_adjustment_change(?adjustment_clock_update(_), Adjustment) -> + Adjustment; merge_adjustment_change(?adjustment_status_changed(Status), Adjustment) -> Adjustment#domain_InvoicePaymentAdjustment{status = Status}. @@ -3364,6 +3379,14 @@ try_get_adjustment(ID, #st{adjustments = As}) -> set_adjustment(ID, Adjustment, St = #st{adjustments = As}) -> St#st{adjustments = lists:keystore(ID, #domain_InvoicePaymentAdjustment.id, As, Adjustment)}. +set_adjustment_clock(?adjustment_clock_update(Clock), St = #st{activity = {AdjustmentActivity, _}}) when + AdjustmentActivity =:= adjustment_new; + AdjustmentActivity =:= adjustment_pending +-> + St#st{clock = Clock}; +set_adjustment_clock(_, St = #st{}) -> + St. + merge_session_change(?session_finished(Result), Session, Opts) -> Session2 = Session#{status := finished, result => Result}, accrue_session_timing(finished, started, Opts, Session2); diff --git a/apps/hellgate/test/hg_invoice_tests_SUITE.erl b/apps/hellgate/test/hg_invoice_tests_SUITE.erl index 8ff528896..7eff0f5fe 100644 --- a/apps/hellgate/test/hg_invoice_tests_SUITE.erl +++ b/apps/hellgate/test/hg_invoice_tests_SUITE.erl @@ -1907,6 +1907,7 @@ payment_adjustment_success(C) -> ?invalid_adjustment_pending(AdjustmentID) = hg_client_invoicing:create_payment_adjustment(InvoiceID, PaymentID, make_adjustment_params(), Client), [ + ?payment_ev(PaymentID, ?adjustment_ev(AdjustmentID, ?adjustment_clock_update(_))), ?payment_ev(PaymentID, ?adjustment_ev(AdjustmentID, ?adjustment_status_changed(?adjustment_processed()))) ] = next_event(InvoiceID, Client), ok = @@ -1916,6 +1917,7 @@ payment_adjustment_success(C) -> ?invalid_adjustment_status(?adjustment_captured(_)) = hg_client_invoicing:cancel_payment_adjustment(InvoiceID, PaymentID, AdjustmentID, Client), [ + ?payment_ev(PaymentID, ?adjustment_ev(AdjustmentID, ?adjustment_clock_update(_))), ?payment_ev(PaymentID, ?adjustment_ev(AdjustmentID, ?adjustment_status_changed(?adjustment_captured(_)))) ] = next_event(InvoiceID, Client), %% verify that cash deposited correctly everywhere @@ -2043,6 +2045,7 @@ payment_adjustment_captured_partial(C) -> #domain_InvoicePaymentAdjustment{new_cash_flow = CF2} = ?adjustment_reason(AdjReason) = hg_client_invoicing:get_payment_adjustment(InvoiceID, PaymentID, AdjustmentID, Client), + % verify that cash deposited correctly everywhere PrvAccount2 = get_cashflow_account({provider, settlement}, CF2), SysAccount2 = get_cashflow_account({system, settlement}, CF2), MrcAccount2 = get_cashflow_account({merchant, settlement}, CF2), @@ -2252,7 +2255,8 @@ get_cashflow_account(Type, CF) -> } <- CF, T == Type ], - hg_ct_helper:get_balance(ID). + {ok, Balance} = hg_accounting_new:get_balance(ID), + Balance. -spec invalid_payment_w_deprived_party(config()) -> test_return(). invalid_payment_w_deprived_party(C) -> @@ -5425,10 +5429,12 @@ execute_payment_adjustment(InvoiceID, PaymentID, Params, Client) -> ?payment_ev(PaymentID, ?adjustment_ev(AdjustmentID, ?adjustment_created(Adjustment))) ] = next_event(InvoiceID, Client), [ + ?payment_ev(PaymentID, ?adjustment_ev(AdjustmentID, ?adjustment_clock_update(_))), ?payment_ev(PaymentID, ?adjustment_ev(AdjustmentID, ?adjustment_status_changed(?adjustment_processed()))) ] = next_event(InvoiceID, Client), ok = hg_client_invoicing:capture_payment_adjustment(InvoiceID, PaymentID, AdjustmentID, Client), [ + ?payment_ev(PaymentID, ?adjustment_ev(AdjustmentID, ?adjustment_clock_update(_))), ?payment_ev(PaymentID, ?adjustment_ev(AdjustmentID, ?adjustment_status_changed(?adjustment_captured(_)))) ] = next_event(InvoiceID, Client), AdjustmentID.