mirror of
https://github.com/valitydev/hellgate.git
synced 2024-11-06 02:45:20 +00:00
OPS-356: Remove invoice adj (#88)
* removed invoice adj * bumped to valitydev/damsel@c32f50f and valitydev/limiter-proto@e045813
This commit is contained in:
parent
db39bf920d
commit
50a7f09c24
@ -14,33 +14,6 @@
|
||||
{invoice_status_changed, #payproc_InvoiceStatusChanged{status = Status}}
|
||||
).
|
||||
|
||||
-define(invoice_adjustment_ev(ID, Payload),
|
||||
{invoice_adjustment_change, #payproc_InvoiceAdjustmentChange{
|
||||
id = ID,
|
||||
payload = Payload
|
||||
}}
|
||||
).
|
||||
|
||||
-define(invoice_adjustment_ev(ID, Payload, OccurredAt),
|
||||
{invoice_adjustment_change, #payproc_InvoiceAdjustmentChange{
|
||||
id = ID,
|
||||
payload = Payload,
|
||||
occurred_at = OccurredAt
|
||||
}}
|
||||
).
|
||||
|
||||
-define(invoice_adjustment_created(Adjustment),
|
||||
{invoice_adjustment_created, #payproc_InvoiceAdjustmentCreated{
|
||||
adjustment = Adjustment
|
||||
}}
|
||||
).
|
||||
|
||||
-define(invoice_adjustment_status_changed(Status),
|
||||
{invoice_adjustment_status_changed, #payproc_InvoiceAdjustmentStatusChanged{
|
||||
status = Status
|
||||
}}
|
||||
).
|
||||
|
||||
-define(payment_ev(PaymentID, Payload),
|
||||
{invoice_payment_change, #payproc_InvoicePaymentChange{
|
||||
id = PaymentID,
|
||||
|
@ -67,7 +67,6 @@
|
||||
activity :: undefined | activity(),
|
||||
invoice :: undefined | invoice(),
|
||||
payments = [] :: [{payment_id(), payment_st()}],
|
||||
adjustments = [] :: [adjustment()],
|
||||
party :: undefined | party()
|
||||
}).
|
||||
|
||||
@ -77,11 +76,7 @@
|
||||
|
||||
-type activity() ::
|
||||
invoice
|
||||
| {payment, payment_id()}
|
||||
| {adjustment_new, adjustment_id()}
|
||||
| {adjustment_pending, adjustment_id()}.
|
||||
|
||||
-type adjustment_id() :: dmsl_domain_thrift:'InvoiceAdjustmentID'().
|
||||
| {payment, payment_id()}.
|
||||
|
||||
%% API
|
||||
|
||||
@ -189,10 +184,6 @@ handle_function_('Get', {InvoiceID, undefined}, _Opts) ->
|
||||
handle_function_('GetEvents', {InvoiceID, Range}, _Opts) ->
|
||||
_ = set_invoicing_meta(InvoiceID),
|
||||
get_public_history(InvoiceID, Range);
|
||||
handle_function_('GetInvoiceAdjustment', {InvoiceID, ID}, _Opts) ->
|
||||
St = get_state(InvoiceID),
|
||||
ok = set_invoicing_meta(InvoiceID),
|
||||
get_adjustment(ID, St);
|
||||
handle_function_('GetPayment', {InvoiceID, PaymentID}, _Opts) ->
|
||||
_ = set_invoicing_meta(InvoiceID, PaymentID),
|
||||
St = get_state(InvoiceID),
|
||||
@ -231,7 +222,6 @@ handle_function_(Fun, Args, _Opts) when
|
||||
Fun =:= 'CancelPayment' orelse
|
||||
Fun =:= 'RefundPayment' orelse
|
||||
Fun =:= 'CreateManualRefund' orelse
|
||||
Fun =:= 'CreateInvoiceAdjustment' orelse
|
||||
Fun =:= 'CreateChargeback' orelse
|
||||
Fun =:= 'CancelChargeback' orelse
|
||||
Fun =:= 'AcceptChargeback' orelse
|
||||
@ -463,18 +453,10 @@ map_history_error({error, notfound}) ->
|
||||
-type invoice() :: dmsl_domain_thrift:'Invoice'().
|
||||
-type party() :: dmsl_domain_thrift:'Party'().
|
||||
|
||||
-type adjustment() :: dmsl_payproc_thrift:'InvoiceAdjustment'().
|
||||
|
||||
-type payment_id() :: dmsl_domain_thrift:'InvoicePaymentID'().
|
||||
-type payment_st() :: hg_invoice_payment:st().
|
||||
|
||||
-define(payment_pending(PaymentID), #payproc_InvoicePaymentPending{id = PaymentID}).
|
||||
-define(adjustment_target_status(Status), #domain_InvoiceAdjustment{
|
||||
state =
|
||||
{status_change, #domain_InvoiceAdjustmentStatusChangeState{
|
||||
scenario = #domain_InvoiceAdjustmentStatusChange{target_status = Status}
|
||||
}}
|
||||
}).
|
||||
|
||||
%%
|
||||
|
||||
@ -534,24 +516,6 @@ handle_signal(timeout, St = #st{activity = {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{activity = {adjustment_new, ID}}) ->
|
||||
OccurredAt = hg_datetime:format_now(),
|
||||
{ok, {Changes, Action}} = hg_invoice_adjustment:process(OccurredAt),
|
||||
#{
|
||||
changes => wrap_adjustment_changes(ID, Changes, OccurredAt),
|
||||
action => Action,
|
||||
state => St
|
||||
};
|
||||
handle_signal(timeout, St = #st{activity = {adjustment_pending, ID}}) ->
|
||||
_ = assert_adjustment_processed(ID, St),
|
||||
OccurredAt = hg_datetime:format_now(),
|
||||
?adjustment_target_status(Status) = get_adjustment(ID, St),
|
||||
{ok, {Changes, Action}} = hg_invoice_adjustment:capture(OccurredAt),
|
||||
#{
|
||||
changes => wrap_adjustment_changes(ID, Changes, OccurredAt),
|
||||
action => set_invoice_timer(Status, Action, St),
|
||||
state => St
|
||||
};
|
||||
handle_signal(timeout, St = #st{activity = invoice}) ->
|
||||
% invoice is expired
|
||||
handle_expiration(St).
|
||||
@ -611,12 +575,10 @@ process_call(Call, #{history := History}) ->
|
||||
handle_call({{'Invoicing', 'StartPayment'}, {_InvoiceID, PaymentParams}}, St0) ->
|
||||
St = St0#st{party = hg_party:get_party(get_party_id(St0))},
|
||||
_ = assert_invoice(operable, St),
|
||||
_ = assert_all_adjustments_finalised(St),
|
||||
start_payment(PaymentParams, St);
|
||||
handle_call({{'Invoicing', 'RegisterPayment'}, {_InvoiceID, PaymentParams}}, St0) ->
|
||||
St = St0#st{party = hg_party:get_party(get_party_id(St0))},
|
||||
_ = assert_invoice(unblocked, St),
|
||||
_ = assert_all_adjustments_finalised(St),
|
||||
register_payment(PaymentParams, St);
|
||||
handle_call({{'Invoicing', 'CapturePayment'}, {_InvoiceID, PaymentID, Params}}, St0) ->
|
||||
St = St0#st{party = hg_party:get_party(get_party_id(St0))},
|
||||
@ -665,15 +627,6 @@ handle_call({{'Invoicing', 'Rescind'}, {_InvoiceID, Reason}}, St0) ->
|
||||
action => hg_machine_action:unset_timer(),
|
||||
state => St
|
||||
};
|
||||
handle_call({{'Invoicing', 'CreateInvoiceAdjustment'}, {_InvoiceID, Params}}, St) ->
|
||||
ID = create_adjustment_id(St),
|
||||
TargetStatus = get_adjustment_params_target_status(Params),
|
||||
InvoiceStatus = get_invoice_status(St),
|
||||
ok = assert_no_pending_payment(St),
|
||||
ok = assert_adjustment_target_status(TargetStatus, InvoiceStatus),
|
||||
ok = assert_all_adjustments_finalised(St),
|
||||
OccurredAt = hg_datetime:format_now(),
|
||||
wrap_adjustment_impact(ID, hg_invoice_adjustment:create(ID, Params, OccurredAt), St, OccurredAt);
|
||||
handle_call({{'Invoicing', 'RefundPayment'}, {_InvoiceID, PaymentID, Params}}, St0) ->
|
||||
St = St0#st{party = hg_party:get_party(get_party_id(St0))},
|
||||
_ = assert_invoice(operable, St),
|
||||
@ -898,17 +851,6 @@ wrap_payment_impact(PaymentID, {Response, {Changes, Action}}, St, OccurredAt) ->
|
||||
state => St
|
||||
}.
|
||||
|
||||
wrap_adjustment_changes(AdjustmentID, Changes, OccurredAt) ->
|
||||
[?invoice_adjustment_ev(AdjustmentID, C, OccurredAt) || C <- Changes].
|
||||
|
||||
wrap_adjustment_impact(AdjustmentID, {Response, {Changes, Action}}, St, OccurredAt) ->
|
||||
#{
|
||||
response => Response,
|
||||
changes => wrap_adjustment_changes(AdjustmentID, Changes, OccurredAt),
|
||||
action => Action,
|
||||
state => St
|
||||
}.
|
||||
|
||||
handle_result(#{} = Result) ->
|
||||
St = validate_changes(Result),
|
||||
_ = log_changes(maps:get(changes, Result, []), St),
|
||||
@ -1117,24 +1059,6 @@ merge_change(?invoice_created(Invoice), St, _Opts) ->
|
||||
St#st{activity = invoice, invoice = Invoice};
|
||||
merge_change(?invoice_status_changed(Status), St = #st{invoice = I}, _Opts) ->
|
||||
St#st{invoice = I#domain_Invoice{status = Status}};
|
||||
merge_change(?invoice_adjustment_ev(ID, Event), St, _Opts) ->
|
||||
St1 =
|
||||
case Event of
|
||||
?invoice_adjustment_created(_Adjustment) ->
|
||||
St#st{activity = {adjustment_new, ID}};
|
||||
?invoice_adjustment_status_changed({processed, _}) ->
|
||||
St#st{activity = {adjustment_pending, ID}};
|
||||
?invoice_adjustment_status_changed(_Status) ->
|
||||
St#st{activity = invoice}
|
||||
end,
|
||||
Adjustment = merge_adjustment_change(Event, try_get_adjustment(ID, St1)),
|
||||
St2 = set_adjustment(ID, Adjustment, St1),
|
||||
case get_adjustment_status(Adjustment) of
|
||||
{captured, _} ->
|
||||
apply_adjustment_status(Adjustment, St2);
|
||||
_ ->
|
||||
St2
|
||||
end;
|
||||
merge_change(?payment_ev(PaymentID, Change), St = #st{invoice = #domain_Invoice{id = InvoiceID}}, Opts) ->
|
||||
PaymentSession = try_get_payment_session(PaymentID, St),
|
||||
PaymentSession1 = merge_payment_change(Change, PaymentSession, Opts#{invoice_id => InvoiceID}),
|
||||
@ -1204,76 +1128,6 @@ set_payment_session(PaymentID, PaymentSession, St = #st{payments = Payments}) ->
|
||||
|
||||
%%
|
||||
|
||||
get_adjustment_params_target_status(#payproc_InvoiceAdjustmentParams{
|
||||
scenario = {status_change, #domain_InvoiceAdjustmentStatusChange{target_status = Status}}
|
||||
}) ->
|
||||
Status.
|
||||
|
||||
get_invoice_status(#st{invoice = #domain_Invoice{status = Status}}) ->
|
||||
Status.
|
||||
|
||||
assert_adjustment_target_status(TargetStatus, Status) when TargetStatus =:= Status ->
|
||||
throw(#payproc_InvoiceAlreadyHasStatus{status = Status});
|
||||
assert_adjustment_target_status({TargetStatus, _}, {Status, _}) when
|
||||
TargetStatus =:= unpaid, Status =/= paid; TargetStatus =:= paid, Status =/= unpaid
|
||||
->
|
||||
throw(#payproc_InvoiceAdjustmentStatusUnacceptable{});
|
||||
assert_adjustment_target_status(_TargetStatus, _Status) ->
|
||||
ok.
|
||||
|
||||
assert_all_adjustments_finalised(#st{adjustments = Adjustments}) ->
|
||||
lists:foreach(fun assert_adjustment_finalised/1, Adjustments).
|
||||
|
||||
assert_adjustment_finalised(#domain_InvoiceAdjustment{id = ID, status = {Status, _}}) when
|
||||
Status =:= pending; Status =:= processed
|
||||
->
|
||||
throw(#payproc_InvoiceAdjustmentPending{id = ID});
|
||||
assert_adjustment_finalised(_) ->
|
||||
ok.
|
||||
|
||||
assert_adjustment_processed(ID, #st{adjustments = Adjustments}) ->
|
||||
case lists:keyfind(ID, #domain_InvoiceAdjustment.id, Adjustments) of
|
||||
#domain_InvoiceAdjustment{status = {processed, _}} ->
|
||||
ok;
|
||||
#domain_InvoiceAdjustment{status = Status} ->
|
||||
throw(#payproc_InvalidInvoiceAdjustmentStatus{status = Status})
|
||||
end.
|
||||
|
||||
merge_adjustment_change(?invoice_adjustment_created(Adjustment), undefined) ->
|
||||
Adjustment;
|
||||
merge_adjustment_change(?invoice_adjustment_status_changed(Status), Adjustment) ->
|
||||
Adjustment#domain_InvoiceAdjustment{status = Status}.
|
||||
|
||||
get_adjustment(ID, St) ->
|
||||
case try_get_adjustment(ID, St) of
|
||||
Adjustment = #domain_InvoiceAdjustment{} ->
|
||||
Adjustment;
|
||||
undefined ->
|
||||
throw(#payproc_InvoiceAdjustmentNotFound{})
|
||||
end.
|
||||
|
||||
try_get_adjustment(ID, #st{adjustments = As}) ->
|
||||
case lists:keyfind(ID, #domain_InvoiceAdjustment.id, As) of
|
||||
V = #domain_InvoiceAdjustment{} ->
|
||||
V;
|
||||
false ->
|
||||
undefined
|
||||
end.
|
||||
|
||||
set_adjustment(ID, Adjustment, St = #st{adjustments = As}) ->
|
||||
St#st{adjustments = lists:keystore(ID, #domain_InvoiceAdjustment.id, As, Adjustment)}.
|
||||
|
||||
get_adjustment_status(#domain_InvoiceAdjustment{status = Status}) ->
|
||||
Status.
|
||||
|
||||
apply_adjustment_status(?adjustment_target_status(Status), St = #st{invoice = Invoice}) ->
|
||||
St#st{invoice = Invoice#domain_Invoice{status = Status}}.
|
||||
|
||||
create_adjustment_id(#st{adjustments = Adjustments}) ->
|
||||
integer_to_binary(length(Adjustments) + 1).
|
||||
|
||||
%%
|
||||
|
||||
make_invoice_params(Params) ->
|
||||
#payproc_InvoiceWithTemplateParams{
|
||||
id = InvoiceID,
|
||||
@ -1407,20 +1261,6 @@ get_log_params(?invoice_created(Invoice), _St) ->
|
||||
get_invoice_event_log(invoice_created, unpaid, Invoice);
|
||||
get_log_params(?invoice_status_changed({StatusName, _}), #st{invoice = Invoice}) ->
|
||||
get_invoice_event_log(invoice_status_changed, StatusName, Invoice);
|
||||
get_log_params(?invoice_adjustment_ev(_ID, Change), #st{invoice = Invoice}) ->
|
||||
case hg_invoice_adjustment:get_log_params(Change) of
|
||||
{ok, Params} ->
|
||||
{ok,
|
||||
maps:update_with(
|
||||
params,
|
||||
fun(V) ->
|
||||
[{invoice, get_invoice_params(Invoice)} | V]
|
||||
end,
|
||||
Params
|
||||
)};
|
||||
undefined ->
|
||||
undefined
|
||||
end;
|
||||
get_log_params(?payment_ev(PaymentID, Change), St = #st{invoice = Invoice}) ->
|
||||
PaymentSession = try_get_payment_session(PaymentID, St),
|
||||
case hg_invoice_payment:get_log_params(Change, PaymentSession) of
|
||||
|
@ -1,173 +0,0 @@
|
||||
-module(hg_invoice_adjustment).
|
||||
|
||||
-include("invoice_events.hrl").
|
||||
|
||||
-include_lib("damsel/include/dmsl_domain_thrift.hrl").
|
||||
|
||||
-export(
|
||||
[
|
||||
create/2,
|
||||
create/3,
|
||||
process/1,
|
||||
capture/0,
|
||||
capture/1
|
||||
]
|
||||
).
|
||||
|
||||
-export([get_log_params/1]).
|
||||
|
||||
-type adjustment() ::
|
||||
dmsl_domain_thrift:'InvoiceAdjustment'().
|
||||
|
||||
-type id() ::
|
||||
dmsl_domain_thrift:'InvoiceAdjustmentID'().
|
||||
|
||||
-type params() ::
|
||||
dmsl_payproc_thrift:'InvoiceAdjustmentParams'().
|
||||
|
||||
-type adjustment_state() ::
|
||||
dmsl_domain_thrift:'InvoiceAdjustmentState'().
|
||||
|
||||
-type captured() ::
|
||||
{captured, dmsl_domain_thrift:'InvoiceAdjustmentCaptured'()}.
|
||||
|
||||
-type processed() ::
|
||||
{processed, dmsl_domain_thrift:'InvoiceAdjustmentProcessed'()}.
|
||||
|
||||
-type result() ::
|
||||
{[change()], action()}.
|
||||
|
||||
-type change() ::
|
||||
dmsl_payproc_thrift:'InvoiceAdjustmentChangePayload'().
|
||||
|
||||
-type action() ::
|
||||
hg_machine_action:t().
|
||||
|
||||
-type log_params() :: #{
|
||||
type := invoice_adjustment_event,
|
||||
params := list(),
|
||||
message := string()
|
||||
}.
|
||||
|
||||
% API
|
||||
|
||||
-spec create(id(), params()) -> {adjustment(), result()}.
|
||||
create(ID, Params) ->
|
||||
Timestamp = hg_datetime:format_now(),
|
||||
create(ID, Params, Timestamp).
|
||||
|
||||
-spec create(id(), params(), hg_datetime:timestamp()) -> {adjustment(), result()}.
|
||||
create(ID, Params, Timestamp) ->
|
||||
Adjustment = build_adjustment(ID, Params, Timestamp),
|
||||
Changes = [?invoice_adjustment_created(Adjustment)],
|
||||
Action = hg_machine_action:instant(),
|
||||
{Adjustment, {Changes, Action}}.
|
||||
|
||||
-spec process(hg_datetime:timestamp()) -> {ok, result()}.
|
||||
process(Timestamp) ->
|
||||
AdjustmentTarget = build_adjustment_target(processed, Timestamp),
|
||||
Changes = [?invoice_adjustment_status_changed(AdjustmentTarget)],
|
||||
Action = hg_machine_action:instant(),
|
||||
{ok, {Changes, Action}}.
|
||||
|
||||
-spec capture() -> {ok, result()}.
|
||||
capture() ->
|
||||
capture(hg_datetime:format_now()).
|
||||
|
||||
-spec capture(hg_datetime:timestamp()) -> {ok, result()}.
|
||||
capture(Timestamp) ->
|
||||
AdjustmentTarget = build_adjustment_target(captured, Timestamp),
|
||||
Changes = [?invoice_adjustment_status_changed(AdjustmentTarget)],
|
||||
Action = hg_machine_action:new(),
|
||||
{ok, {Changes, Action}}.
|
||||
|
||||
-spec get_log_params(change()) -> {ok, log_params()} | undefined.
|
||||
get_log_params(Change) ->
|
||||
do_get_log_params(Change).
|
||||
|
||||
% Internal
|
||||
|
||||
-spec build_adjustment(id(), params(), hg_datetime:timestamp()) -> adjustment().
|
||||
build_adjustment(ID, Params, Timestamp) ->
|
||||
#domain_InvoiceAdjustment{
|
||||
id = ID,
|
||||
reason = Params#payproc_InvoiceAdjustmentParams.reason,
|
||||
status = {pending, #domain_InvoiceAdjustmentPending{}},
|
||||
created_at = Timestamp,
|
||||
domain_revision = hg_domain:head(),
|
||||
state = build_adjustment_state(Params)
|
||||
}.
|
||||
|
||||
-spec build_adjustment_state(params()) -> adjustment_state().
|
||||
build_adjustment_state(Params) ->
|
||||
case Params#payproc_InvoiceAdjustmentParams.scenario of
|
||||
{status_change, StatusChange} ->
|
||||
{status_change, #domain_InvoiceAdjustmentStatusChangeState{scenario = StatusChange}}
|
||||
end.
|
||||
|
||||
-spec build_adjustment_target
|
||||
(processed, hg_datetime:timestamp()) -> processed();
|
||||
(captured, hg_datetime:timestamp()) -> captured().
|
||||
build_adjustment_target(processed, _Timestamp) ->
|
||||
{processed, #domain_InvoiceAdjustmentProcessed{}};
|
||||
build_adjustment_target(captured, Timestamp) ->
|
||||
{captured, #domain_InvoiceAdjustmentCaptured{at = Timestamp}}.
|
||||
|
||||
-spec do_get_log_params(change()) -> {ok, log_params()} | undefined.
|
||||
do_get_log_params(?invoice_adjustment_created(Adjustment)) ->
|
||||
Params = #{
|
||||
adjustment => Adjustment,
|
||||
event_type => invoice_adjustment_created
|
||||
},
|
||||
make_log_params(Params);
|
||||
do_get_log_params(?invoice_adjustment_status_changed(Status)) ->
|
||||
Params = #{
|
||||
adjustment_status => Status,
|
||||
event_type => invoice_adjustment_status_changed
|
||||
},
|
||||
make_log_params(Params);
|
||||
do_get_log_params(_) ->
|
||||
undefined.
|
||||
|
||||
-spec make_log_params(Params :: map()) -> {ok, log_params()}.
|
||||
make_log_params(#{event_type := EventType} = Params) ->
|
||||
LogParams = maps:fold(
|
||||
fun(K, V, Acc) ->
|
||||
build_log_param(K, V) ++ Acc
|
||||
end,
|
||||
[],
|
||||
Params
|
||||
),
|
||||
Message = get_log_message(EventType),
|
||||
{ok, #{
|
||||
type => invoice_adjustment_event,
|
||||
message => Message,
|
||||
params => LogParams
|
||||
}}.
|
||||
|
||||
-spec build_log_param(atom(), term()) -> [{atom(), term()}].
|
||||
build_log_param(adjustment, #domain_InvoiceAdjustment{} = Adjustment) ->
|
||||
[
|
||||
{id, Adjustment#domain_InvoiceAdjustment.id},
|
||||
{reason, Adjustment#domain_InvoiceAdjustment.reason}
|
||||
| build_log_param(state, Adjustment#domain_InvoiceAdjustment.state)
|
||||
];
|
||||
build_log_param(state, {status_change, State}) ->
|
||||
Scenario = State#domain_InvoiceAdjustmentStatusChangeState.scenario,
|
||||
TargetStatus = Scenario#domain_InvoiceAdjustmentStatusChange.target_status,
|
||||
build_log_param(target_status, TargetStatus);
|
||||
build_log_param(adjustment_status, {Status, _Details}) ->
|
||||
[{status, Status}];
|
||||
build_log_param(target_status, {Status, _Details}) ->
|
||||
[{status, Status}];
|
||||
build_log_param(_Key, _Value) ->
|
||||
[].
|
||||
|
||||
-spec get_log_message(EventType) -> string() when
|
||||
EventType ::
|
||||
invoice_adjustment_created
|
||||
| invoice_adjustment_status_changed.
|
||||
get_log_message(invoice_adjustment_created) ->
|
||||
"Invoice adjustment created";
|
||||
get_log_message(invoice_adjustment_status_changed) ->
|
||||
"Invoice adjustment status changed".
|
@ -1,879 +0,0 @@
|
||||
-module(hg_invoice_adjustment_tests_SUITE).
|
||||
|
||||
-include("hg_ct_domain.hrl").
|
||||
|
||||
-include_lib("stdlib/include/assert.hrl").
|
||||
|
||||
-export([all/0]).
|
||||
-export([groups/0]).
|
||||
-export([init_per_suite/1]).
|
||||
-export([end_per_suite/1]).
|
||||
-export([init_per_testcase/2]).
|
||||
-export([end_per_testcase/2]).
|
||||
|
||||
-export([invoice_adjustment_capture/1]).
|
||||
-export([invoice_adjustment_existing_invoice_status/1]).
|
||||
-export([invoice_adjustment_invalid_invoice_status/1]).
|
||||
-export([invoice_adjustment_payment_pending/1]).
|
||||
-export([invoice_adjustment_invoice_expiration_after_capture/1]).
|
||||
|
||||
-behaviour(supervisor).
|
||||
|
||||
-export([init/1]).
|
||||
|
||||
-spec init([]) -> {ok, {supervisor:sup_flags(), [supervisor:child_spec()]}}.
|
||||
init([]) ->
|
||||
{ok, {#{strategy => one_for_all, intensity => 1, period => 1}, []}}.
|
||||
|
||||
%% tests descriptions
|
||||
|
||||
-type config() :: hg_ct_helper:config().
|
||||
-type test_case_name() :: hg_ct_helper:test_case_name().
|
||||
-type group_name() :: hg_ct_helper:group_name().
|
||||
-type test_return() :: _ | no_return().
|
||||
|
||||
cfg(Key, C) ->
|
||||
hg_ct_helper:cfg(Key, C).
|
||||
|
||||
-spec all() -> [test_case_name() | {group, group_name()}].
|
||||
all() ->
|
||||
[
|
||||
{group, all_tests}
|
||||
].
|
||||
|
||||
-spec groups() -> [{group_name(), list(), [test_case_name()]}].
|
||||
groups() ->
|
||||
[
|
||||
{all_tests, [parallel], [
|
||||
invoice_adjustment_capture,
|
||||
invoice_adjustment_existing_invoice_status,
|
||||
invoice_adjustment_invalid_invoice_status,
|
||||
invoice_adjustment_payment_pending,
|
||||
invoice_adjustment_invoice_expiration_after_capture
|
||||
]}
|
||||
].
|
||||
|
||||
%% starting/stopping
|
||||
|
||||
-spec init_per_suite(config()) -> config().
|
||||
init_per_suite(C) ->
|
||||
% _ = dbg:tracer(),
|
||||
% _ = dbg:p(all, c),
|
||||
% _ = dbg:tpl({'hg_invoice_payment', 'p', '_'}, x),
|
||||
CowboySpec = hg_dummy_provider:get_http_cowboy_spec(),
|
||||
|
||||
{Apps, Ret} = hg_ct_helper:start_apps([
|
||||
woody,
|
||||
scoper,
|
||||
dmt_client,
|
||||
bender_client,
|
||||
party_client,
|
||||
hg_proto,
|
||||
hellgate,
|
||||
{cowboy, CowboySpec}
|
||||
]),
|
||||
_ = hg_domain:insert(construct_domain_fixture()),
|
||||
RootUrl = maps:get(hellgate_root_url, Ret),
|
||||
|
||||
PartyID = hg_utils:unique_id(),
|
||||
PartyClient = {party_client:create_client(), party_client:create_context()},
|
||||
ShopID = hg_ct_helper:create_party_and_shop(PartyID, ?cat(1), <<"RUB">>, ?tmpl(1), ?pinst(1), PartyClient),
|
||||
|
||||
{ok, SupPid} = supervisor:start_link(?MODULE, []),
|
||||
_ = unlink(SupPid),
|
||||
NewC = [
|
||||
{party_id, PartyID},
|
||||
{shop_id, ShopID},
|
||||
{root_url, RootUrl},
|
||||
{apps, Apps},
|
||||
{test_sup, SupPid}
|
||||
| C
|
||||
],
|
||||
|
||||
ok = start_proxies([{hg_dummy_provider, 1, NewC}, {hg_dummy_inspector, 2, NewC}]),
|
||||
NewC.
|
||||
|
||||
-spec end_per_suite(config()) -> _.
|
||||
end_per_suite(C) ->
|
||||
_ = hg_domain:cleanup(),
|
||||
_ = [application:stop(App) || App <- cfg(apps, C)],
|
||||
exit(cfg(test_sup, C), shutdown).
|
||||
|
||||
-spec init_per_testcase(test_case_name(), config()) -> config().
|
||||
init_per_testcase(_Name, C) ->
|
||||
init_per_testcase(C).
|
||||
|
||||
init_per_testcase(C) ->
|
||||
ApiClient = hg_ct_helper:create_client(cfg(root_url, C)),
|
||||
Client = hg_client_invoicing:start_link(ApiClient),
|
||||
ClientTpl = hg_client_invoice_templating:start_link(ApiClient),
|
||||
ok = hg_context:save(hg_context:create()),
|
||||
[{client, Client}, {client_tpl, ClientTpl} | C].
|
||||
|
||||
-spec end_per_testcase(test_case_name(), config()) -> _.
|
||||
end_per_testcase(_Name, _C) ->
|
||||
ok = hg_context:cleanup().
|
||||
|
||||
-include("invoice_events.hrl").
|
||||
-include("payment_events.hrl").
|
||||
-include("customer_events.hrl").
|
||||
|
||||
-define(invoice(ID), #domain_Invoice{id = ID}).
|
||||
-define(payment(ID), #domain_InvoicePayment{id = ID}).
|
||||
-define(invoice_state(Invoice), #payproc_Invoice{invoice = Invoice}).
|
||||
-define(payment_state(Payment), #payproc_InvoicePayment{payment = Payment}).
|
||||
-define(payment_w_status(Status), #domain_InvoicePayment{status = Status}).
|
||||
-define(trx_info(ID), #domain_TransactionInfo{id = ID}).
|
||||
|
||||
-define(merchant_to_system_share_1, ?share(45, 1000, operation_amount)).
|
||||
-define(merchant_to_system_share_2, ?share(100, 1000, operation_amount)).
|
||||
|
||||
%% ADJ
|
||||
|
||||
-spec invoice_adjustment_capture(config()) -> test_return().
|
||||
invoice_adjustment_capture(C) ->
|
||||
Client = cfg(client, C),
|
||||
ShopID = cfg(shop_id, C),
|
||||
PartyID = cfg(party_id, C),
|
||||
InvoiceParams = make_invoice_params(PartyID, ShopID, <<"rubberduck">>, make_due_date(10), make_cash(10000)),
|
||||
InvoiceID = create_invoice(InvoiceParams, Client),
|
||||
[?invoice_created(_Invoice)] = next_event(InvoiceID, Client),
|
||||
PaymentID = process_payment(InvoiceID, make_payment_params(?pmt_sys(<<"visa-ref">>)), Client),
|
||||
PaymentID = await_payment_capture(InvoiceID, PaymentID, Client),
|
||||
Unpaid = {unpaid, #domain_InvoiceUnpaid{}},
|
||||
AdjustmentParams = #payproc_InvoiceAdjustmentParams{
|
||||
reason = <<"kek">>,
|
||||
scenario =
|
||||
{status_change, #domain_InvoiceAdjustmentStatusChange{
|
||||
target_status = Unpaid
|
||||
}}
|
||||
},
|
||||
Adjustment = hg_client_invoicing:create_invoice_adjustment(InvoiceID, AdjustmentParams, Client),
|
||||
?assertMatch({pending, #domain_InvoiceAdjustmentPending{}}, Adjustment#domain_InvoiceAdjustment.status),
|
||||
[?invoice_adjustment_ev(ID, ?invoice_adjustment_created(Adjustment))] = next_event(InvoiceID, Client),
|
||||
[?invoice_adjustment_ev(ID, ?invoice_adjustment_status_changed(Processed))] = next_event(InvoiceID, Client),
|
||||
?assertMatch({processed, #domain_InvoiceAdjustmentProcessed{}}, Processed),
|
||||
[?invoice_adjustment_ev(ID, ?invoice_adjustment_status_changed(Captured))] = next_event(InvoiceID, Client),
|
||||
?assertMatch({captured, #domain_InvoiceAdjustmentCaptured{}}, Captured),
|
||||
#payproc_Invoice{invoice = #domain_Invoice{status = FinalStatus}} = hg_client_invoicing:get(InvoiceID, Client),
|
||||
?assertMatch(Unpaid, FinalStatus).
|
||||
|
||||
-spec invoice_adjustment_invalid_invoice_status(config()) -> test_return().
|
||||
invoice_adjustment_invalid_invoice_status(C) ->
|
||||
Client = cfg(client, C),
|
||||
ShopID = cfg(shop_id, C),
|
||||
PartyID = cfg(party_id, C),
|
||||
InvoiceParams = make_invoice_params(PartyID, ShopID, <<"rubberduck">>, make_cash(10000)),
|
||||
InvoiceID = create_invoice(InvoiceParams, Client),
|
||||
[?invoice_created(_)] = next_event(InvoiceID, Client),
|
||||
PaymentID = process_payment(InvoiceID, make_payment_params(?pmt_sys(<<"visa-ref">>)), Client),
|
||||
PaymentID = await_payment_capture(InvoiceID, PaymentID, Client),
|
||||
AdjustmentParams = #payproc_InvoiceAdjustmentParams{
|
||||
reason = <<"kek">>,
|
||||
scenario =
|
||||
{status_change, #domain_InvoiceAdjustmentStatusChange{
|
||||
target_status = {unpaid, #domain_InvoiceUnpaid{}}
|
||||
}}
|
||||
},
|
||||
ok = hg_client_invoicing:fulfill(InvoiceID, <<"ok">>, Client),
|
||||
{exception, E} = hg_client_invoicing:create_invoice_adjustment(InvoiceID, AdjustmentParams, Client),
|
||||
?assertMatch(#payproc_InvoiceAdjustmentStatusUnacceptable{}, E).
|
||||
|
||||
-spec invoice_adjustment_existing_invoice_status(config()) -> test_return().
|
||||
invoice_adjustment_existing_invoice_status(C) ->
|
||||
Client = cfg(client, C),
|
||||
ShopID = cfg(shop_id, C),
|
||||
PartyID = cfg(party_id, C),
|
||||
InvoiceParams = make_invoice_params(PartyID, ShopID, <<"rubberduck">>, make_cash(10000)),
|
||||
InvoiceID = create_invoice(InvoiceParams, Client),
|
||||
[?invoice_created(_)] = next_event(InvoiceID, Client),
|
||||
PaymentID = process_payment(InvoiceID, make_payment_params(?pmt_sys(<<"visa-ref">>)), Client),
|
||||
PaymentID = await_payment_capture(InvoiceID, PaymentID, Client),
|
||||
Paid = {paid, #domain_InvoicePaid{}},
|
||||
AdjustmentParams = #payproc_InvoiceAdjustmentParams{
|
||||
reason = <<"kek">>,
|
||||
scenario =
|
||||
{status_change, #domain_InvoiceAdjustmentStatusChange{
|
||||
target_status = Paid
|
||||
}}
|
||||
},
|
||||
{exception, E} = hg_client_invoicing:create_invoice_adjustment(InvoiceID, AdjustmentParams, Client),
|
||||
?assertMatch(#payproc_InvoiceAlreadyHasStatus{status = Paid}, E).
|
||||
|
||||
-spec invoice_adjustment_payment_pending(config()) -> test_return().
|
||||
invoice_adjustment_payment_pending(C) ->
|
||||
Client = cfg(client, C),
|
||||
ShopID = cfg(shop_id, C),
|
||||
PartyID = cfg(party_id, C),
|
||||
InvoiceParams = make_invoice_params(PartyID, ShopID, <<"rubberduck">>, make_due_date(10), make_cash(10000)),
|
||||
InvoiceID = create_invoice(InvoiceParams, Client),
|
||||
[?invoice_created(_)] = next_event(InvoiceID, Client),
|
||||
PaymentID = start_payment(InvoiceID, make_tds_payment_params(instant, ?pmt_sys(<<"visa-ref">>)), Client),
|
||||
Paid = {paid, #domain_InvoicePaid{}},
|
||||
AdjustmentParams = #payproc_InvoiceAdjustmentParams{
|
||||
reason = <<"kek">>,
|
||||
scenario =
|
||||
{status_change, #domain_InvoiceAdjustmentStatusChange{
|
||||
target_status = Paid
|
||||
}}
|
||||
},
|
||||
{exception, E} = hg_client_invoicing:create_invoice_adjustment(InvoiceID, AdjustmentParams, Client),
|
||||
?assertMatch(#payproc_InvoicePaymentPending{id = PaymentID}, E),
|
||||
UserInteraction = await_payment_process_interaction(InvoiceID, PaymentID, Client),
|
||||
_ = assert_success_interaction(UserInteraction),
|
||||
ok = await_payment_process_interaction_completion(InvoiceID, PaymentID, UserInteraction, Client),
|
||||
PaymentID = await_payment_process_finish(InvoiceID, PaymentID, Client),
|
||||
PaymentID = await_payment_capture(InvoiceID, PaymentID, Client).
|
||||
|
||||
-spec invoice_adjustment_invoice_expiration_after_capture(config()) -> test_return().
|
||||
invoice_adjustment_invoice_expiration_after_capture(C) ->
|
||||
Client = cfg(client, C),
|
||||
ShopID = cfg(shop_id, C),
|
||||
PartyID = cfg(party_id, C),
|
||||
InvoiceParams = make_invoice_params(PartyID, ShopID, <<"rubberduck">>, make_due_date(10), make_cash(10000)),
|
||||
InvoiceID = create_invoice(InvoiceParams, Client),
|
||||
[?invoice_created(_)] = next_event(InvoiceID, Client),
|
||||
Context = #base_Content{
|
||||
type = <<"application/x-erlang-binary">>,
|
||||
data = erlang:term_to_binary({you, 643, "not", [<<"welcome">>, here]})
|
||||
},
|
||||
PaymentParams = set_payment_context(Context, make_payment_params(?pmt_sys(<<"visa-ref">>))),
|
||||
PaymentID = process_payment(InvoiceID, PaymentParams, Client),
|
||||
PaymentID = await_payment_capture(InvoiceID, PaymentID, Client),
|
||||
Unpaid = {unpaid, #domain_InvoiceUnpaid{}},
|
||||
AdjustmentParams = #payproc_InvoiceAdjustmentParams{
|
||||
reason = <<"kek">>,
|
||||
scenario =
|
||||
{status_change, #domain_InvoiceAdjustmentStatusChange{
|
||||
target_status = Unpaid
|
||||
}}
|
||||
},
|
||||
Adjustment = hg_client_invoicing:create_invoice_adjustment(InvoiceID, AdjustmentParams, Client),
|
||||
?assertMatch({pending, #domain_InvoiceAdjustmentPending{}}, Adjustment#domain_InvoiceAdjustment.status),
|
||||
[?invoice_adjustment_ev(ID, ?invoice_adjustment_created(Adjustment))] = next_event(InvoiceID, Client),
|
||||
[?invoice_adjustment_ev(ID, ?invoice_adjustment_status_changed(Processed))] = next_event(InvoiceID, Client),
|
||||
?assertMatch({processed, #domain_InvoiceAdjustmentProcessed{}}, Processed),
|
||||
[?invoice_adjustment_ev(ID, ?invoice_adjustment_status_changed(Captured))] = next_event(InvoiceID, Client),
|
||||
?assertMatch({captured, #domain_InvoiceAdjustmentCaptured{}}, Captured),
|
||||
#payproc_Invoice{invoice = #domain_Invoice{status = Unpaid}} = hg_client_invoicing:get(InvoiceID, Client),
|
||||
[?invoice_status_changed(?invoice_cancelled(_))] = next_event(InvoiceID, Client).
|
||||
|
||||
%% ADJ
|
||||
|
||||
-spec construct_domain_fixture() -> [hg_domain:object()].
|
||||
construct_domain_fixture() ->
|
||||
TestTermSet = #domain_TermSet{
|
||||
payments = #domain_PaymentsServiceTerms{
|
||||
currencies =
|
||||
{value,
|
||||
?ordset([
|
||||
?cur(<<"RUB">>)
|
||||
])},
|
||||
categories =
|
||||
{value,
|
||||
?ordset([
|
||||
?cat(1)
|
||||
])},
|
||||
payment_methods =
|
||||
{decisions, [
|
||||
#domain_PaymentMethodDecision{
|
||||
if_ = {constant, true},
|
||||
then_ =
|
||||
{value,
|
||||
?ordset([
|
||||
?pmt(bank_card, ?bank_card(<<"visa-ref">>))
|
||||
])}
|
||||
}
|
||||
]},
|
||||
cash_limit =
|
||||
{decisions, [
|
||||
#domain_CashLimitDecision{
|
||||
if_ = {condition, {currency_is, ?cur(<<"RUB">>)}},
|
||||
then_ =
|
||||
{value,
|
||||
?cashrng(
|
||||
{inclusive, ?cash(10, <<"RUB">>)},
|
||||
{exclusive, ?cash(420000000, <<"RUB">>)}
|
||||
)}
|
||||
}
|
||||
]},
|
||||
fees =
|
||||
{decisions, [
|
||||
#domain_CashFlowDecision{
|
||||
if_ =
|
||||
{condition,
|
||||
{payment_tool,
|
||||
{bank_card, #domain_BankCardCondition{
|
||||
definition = {category_is, ?bc_cat(1)}
|
||||
}}}},
|
||||
then_ =
|
||||
{value, [
|
||||
?cfpost(
|
||||
{merchant, settlement},
|
||||
{system, settlement},
|
||||
?merchant_to_system_share_2
|
||||
)
|
||||
]}
|
||||
},
|
||||
#domain_CashFlowDecision{
|
||||
if_ = {condition, {currency_is, ?cur(<<"RUB">>)}},
|
||||
then_ =
|
||||
{value, [
|
||||
?cfpost(
|
||||
{merchant, settlement},
|
||||
{system, settlement},
|
||||
?merchant_to_system_share_1
|
||||
)
|
||||
]}
|
||||
}
|
||||
]},
|
||||
holds = #domain_PaymentHoldsServiceTerms{
|
||||
payment_methods =
|
||||
{value,
|
||||
?ordset([
|
||||
?pmt(bank_card, ?bank_card(<<"visa-ref">>))
|
||||
])},
|
||||
lifetime =
|
||||
{decisions, [
|
||||
#domain_HoldLifetimeDecision{
|
||||
if_ = {condition, {currency_is, ?cur(<<"RUB">>)}},
|
||||
then_ = {value, #domain_HoldLifetime{seconds = 10}}
|
||||
}
|
||||
]}
|
||||
},
|
||||
refunds = #domain_PaymentRefundsServiceTerms{
|
||||
payment_methods =
|
||||
{value,
|
||||
?ordset([
|
||||
?pmt(bank_card, ?bank_card(<<"visa-ref">>))
|
||||
])},
|
||||
fees =
|
||||
{value, [
|
||||
?cfpost(
|
||||
{merchant, settlement},
|
||||
{system, settlement},
|
||||
?fixed(100, <<"RUB">>)
|
||||
)
|
||||
]},
|
||||
eligibility_time = {value, #base_TimeSpan{minutes = 1}},
|
||||
partial_refunds = #domain_PartialRefundsServiceTerms{
|
||||
cash_limit =
|
||||
{decisions, [
|
||||
#domain_CashLimitDecision{
|
||||
if_ = {condition, {currency_is, ?cur(<<"RUB">>)}},
|
||||
then_ =
|
||||
{value,
|
||||
?cashrng(
|
||||
{inclusive, ?cash(1000, <<"RUB">>)},
|
||||
{exclusive, ?cash(1000000000, <<"RUB">>)}
|
||||
)}
|
||||
}
|
||||
]}
|
||||
}
|
||||
}
|
||||
},
|
||||
recurrent_paytools = #domain_RecurrentPaytoolsServiceTerms{
|
||||
payment_methods =
|
||||
{value,
|
||||
ordsets:from_list([
|
||||
?pmt(bank_card, ?bank_card(<<"visa-ref">>))
|
||||
])}
|
||||
}
|
||||
},
|
||||
[
|
||||
hg_ct_fixture:construct_bank_card_category(
|
||||
?bc_cat(1),
|
||||
<<"Bank card category">>,
|
||||
<<"Corporative">>,
|
||||
[<<"*CORPORAT*">>]
|
||||
),
|
||||
|
||||
hg_ct_fixture:construct_currency(?cur(<<"RUB">>)),
|
||||
|
||||
hg_ct_fixture:construct_category(?cat(1), <<"Test category">>, test),
|
||||
|
||||
hg_ct_fixture:construct_payment_method(?pmt(bank_card, ?bank_card(<<"visa-ref">>))),
|
||||
|
||||
hg_ct_fixture:construct_proxy(?prx(1), <<"Dummy proxy">>),
|
||||
hg_ct_fixture:construct_proxy(?prx(2), <<"Inspector proxy">>),
|
||||
|
||||
hg_ct_fixture:construct_inspector(?insp(1), <<"Rejector">>, ?prx(2), #{<<"risk_score">> => <<"low">>}),
|
||||
|
||||
hg_ct_fixture:construct_contract_template(?tmpl(1), ?trms(1)),
|
||||
|
||||
hg_ct_fixture:construct_system_account_set(?sas(1)),
|
||||
hg_ct_fixture:construct_external_account_set(?eas(1)),
|
||||
|
||||
{payment_institution, #domain_PaymentInstitutionObject{
|
||||
ref = ?pinst(1),
|
||||
data = #domain_PaymentInstitution{
|
||||
name = <<"Test Inc.">>,
|
||||
system_account_set = {value, ?sas(1)},
|
||||
default_contract_template = {value, ?tmpl(1)},
|
||||
payment_routing_rules = #domain_RoutingRules{
|
||||
policies = ?ruleset(2),
|
||||
prohibitions = ?ruleset(1)
|
||||
},
|
||||
inspector =
|
||||
{decisions, [
|
||||
#domain_InspectorDecision{
|
||||
if_ = {condition, {currency_is, ?cur(<<"RUB">>)}},
|
||||
then_ =
|
||||
{decisions, [
|
||||
#domain_InspectorDecision{
|
||||
if_ =
|
||||
{condition,
|
||||
{cost_in,
|
||||
?cashrng(
|
||||
{inclusive, ?cash(0, <<"RUB">>)},
|
||||
{exclusive, ?cash(500000, <<"RUB">>)}
|
||||
)}},
|
||||
then_ = {value, ?insp(1)}
|
||||
}
|
||||
]}
|
||||
}
|
||||
]},
|
||||
residences = [],
|
||||
realm = test
|
||||
}
|
||||
}},
|
||||
{routing_rules, #domain_RoutingRulesObject{
|
||||
ref = ?ruleset(1),
|
||||
data = #domain_RoutingRuleset{
|
||||
name = <<"Prohibitions: all is allow">>,
|
||||
decisions = {candidates, []}
|
||||
}
|
||||
}},
|
||||
{routing_rules, #domain_RoutingRulesObject{
|
||||
ref = ?ruleset(2),
|
||||
data = #domain_RoutingRuleset{
|
||||
name = <<"Prohibitions: all is allow">>,
|
||||
decisions =
|
||||
{candidates, [
|
||||
?candidate({constant, true}, ?trm(1))
|
||||
]}
|
||||
}
|
||||
}},
|
||||
{globals, #domain_GlobalsObject{
|
||||
ref = #domain_GlobalsRef{},
|
||||
data = #domain_Globals{
|
||||
external_account_set =
|
||||
{decisions, [
|
||||
#domain_ExternalAccountSetDecision{
|
||||
if_ = {constant, true},
|
||||
then_ = {value, ?eas(1)}
|
||||
}
|
||||
]},
|
||||
payment_institutions = ?ordset([?pinst(1)])
|
||||
}
|
||||
}},
|
||||
|
||||
{term_set_hierarchy, #domain_TermSetHierarchyObject{
|
||||
ref = ?trms(1),
|
||||
data = #domain_TermSetHierarchy{
|
||||
term_sets = [
|
||||
#domain_TimedTermSet{
|
||||
action_time = #base_TimestampInterval{},
|
||||
terms = TestTermSet
|
||||
}
|
||||
]
|
||||
}
|
||||
}},
|
||||
|
||||
{provider, #domain_ProviderObject{
|
||||
ref = ?prv(1),
|
||||
data = #domain_Provider{
|
||||
name = <<"Brovider">>,
|
||||
description = <<"A provider but bro">>,
|
||||
proxy = #domain_Proxy{
|
||||
ref = ?prx(1),
|
||||
additional = #{
|
||||
<<"override">> => <<"brovider">>
|
||||
}
|
||||
},
|
||||
abs_account = <<"1234567890">>,
|
||||
accounts = hg_ct_fixture:construct_provider_account_set([?cur(<<"RUB">>)]),
|
||||
terms = #domain_ProvisionTermSet{
|
||||
payments = #domain_PaymentsProvisionTerms{
|
||||
currencies =
|
||||
{value,
|
||||
?ordset([
|
||||
?cur(<<"RUB">>)
|
||||
])},
|
||||
categories =
|
||||
{value,
|
||||
?ordset([
|
||||
?cat(1)
|
||||
])},
|
||||
payment_methods =
|
||||
{value,
|
||||
?ordset([
|
||||
?pmt(bank_card, ?bank_card(<<"visa-ref">>))
|
||||
])},
|
||||
cash_limit =
|
||||
{value,
|
||||
?cashrng(
|
||||
{inclusive, ?cash(1000, <<"RUB">>)},
|
||||
{exclusive, ?cash(1000000000, <<"RUB">>)}
|
||||
)},
|
||||
cash_flow =
|
||||
{decisions, [
|
||||
#domain_CashFlowDecision{
|
||||
if_ =
|
||||
{condition,
|
||||
{payment_tool,
|
||||
{bank_card, #domain_BankCardCondition{
|
||||
definition =
|
||||
{payment_system, #domain_PaymentSystemCondition{
|
||||
payment_system_is = ?pmt_sys(<<"visa-ref">>)
|
||||
}}
|
||||
}}}},
|
||||
then_ =
|
||||
{value, [
|
||||
?cfpost(
|
||||
{provider, settlement},
|
||||
{merchant, settlement},
|
||||
?share(1, 1, operation_amount)
|
||||
),
|
||||
?cfpost(
|
||||
{system, settlement},
|
||||
{provider, settlement},
|
||||
?share(18, 1000, operation_amount)
|
||||
)
|
||||
]}
|
||||
}
|
||||
]},
|
||||
holds = #domain_PaymentHoldsProvisionTerms{
|
||||
lifetime =
|
||||
{decisions, [
|
||||
#domain_HoldLifetimeDecision{
|
||||
if_ =
|
||||
{condition,
|
||||
{payment_tool,
|
||||
{bank_card, #domain_BankCardCondition{
|
||||
definition =
|
||||
{payment_system, #domain_PaymentSystemCondition{
|
||||
payment_system_is = ?pmt_sys(<<"visa-ref">>)
|
||||
}}
|
||||
}}}},
|
||||
then_ = {value, ?hold_lifetime(12)}
|
||||
}
|
||||
]}
|
||||
},
|
||||
refunds = #domain_PaymentRefundsProvisionTerms{
|
||||
cash_flow =
|
||||
{value, [
|
||||
?cfpost(
|
||||
{merchant, settlement},
|
||||
{provider, settlement},
|
||||
?share(1, 1, operation_amount)
|
||||
)
|
||||
]},
|
||||
partial_refunds = #domain_PartialRefundsProvisionTerms{
|
||||
cash_limit =
|
||||
{value,
|
||||
?cashrng(
|
||||
{inclusive, ?cash(10, <<"RUB">>)},
|
||||
{exclusive, ?cash(1000000000, <<"RUB">>)}
|
||||
)}
|
||||
}
|
||||
},
|
||||
chargebacks = #domain_PaymentChargebackProvisionTerms{
|
||||
cash_flow =
|
||||
{value, [
|
||||
?cfpost(
|
||||
{merchant, settlement},
|
||||
{provider, settlement},
|
||||
?share(1, 1, operation_amount)
|
||||
)
|
||||
]}
|
||||
}
|
||||
},
|
||||
recurrent_paytools = #domain_RecurrentPaytoolsProvisionTerms{
|
||||
categories = {value, ?ordset([?cat(1)])},
|
||||
payment_methods =
|
||||
{value,
|
||||
?ordset([
|
||||
?pmt(bank_card, ?bank_card(<<"visa-ref">>))
|
||||
])},
|
||||
cash_value = {value, ?cash(1000, <<"RUB">>)}
|
||||
}
|
||||
}
|
||||
}
|
||||
}},
|
||||
{terminal, #domain_TerminalObject{
|
||||
ref = ?trm(1),
|
||||
data = #domain_Terminal{
|
||||
name = <<"Brominal 1">>,
|
||||
description = <<"Brominal 1">>,
|
||||
risk_coverage = high,
|
||||
provider_ref = ?prv(1)
|
||||
}
|
||||
}},
|
||||
hg_ct_fixture:construct_payment_system(?pmt_sys(<<"visa-ref">>), <<"visa payment system">>)
|
||||
].
|
||||
|
||||
start_service_handler(Module, C, HandlerOpts) ->
|
||||
start_service_handler(Module, Module, C, HandlerOpts).
|
||||
|
||||
start_service_handler(Name, Module, C, HandlerOpts) ->
|
||||
IP = "127.0.0.1",
|
||||
Port = get_random_port(),
|
||||
Opts = maps:merge(HandlerOpts, #{hellgate_root_url => cfg(root_url, C)}),
|
||||
ChildSpec = hg_test_proxy:get_child_spec(Name, Module, IP, Port, Opts),
|
||||
{ok, _} = supervisor:start_child(cfg(test_sup, C), ChildSpec),
|
||||
hg_test_proxy:get_url(Module, IP, Port).
|
||||
|
||||
start_proxies(Proxies) ->
|
||||
setup_proxies(
|
||||
lists:map(
|
||||
fun
|
||||
Mapper({Module, ProxyID, Context}) ->
|
||||
Mapper({Module, ProxyID, #{}, Context});
|
||||
Mapper({Module, ProxyID, ProxyOpts, Context}) ->
|
||||
construct_proxy(ProxyID, start_service_handler(Module, Context, #{}), ProxyOpts)
|
||||
end,
|
||||
Proxies
|
||||
)
|
||||
).
|
||||
|
||||
setup_proxies(Proxies) ->
|
||||
_ = hg_domain:upsert(Proxies),
|
||||
ok.
|
||||
|
||||
get_random_port() ->
|
||||
rand:uniform(32768) + 32767.
|
||||
|
||||
construct_proxy(ID, Url, Options) ->
|
||||
{proxy, #domain_ProxyObject{
|
||||
ref = ?prx(ID),
|
||||
data = #domain_ProxyDefinition{
|
||||
name = Url,
|
||||
description = Url,
|
||||
url = Url,
|
||||
options = Options
|
||||
}
|
||||
}}.
|
||||
|
||||
make_cash(Amount) ->
|
||||
hg_ct_helper:make_cash(Amount, <<"RUB">>).
|
||||
|
||||
make_invoice_params(PartyID, ShopID, Product, Cost) ->
|
||||
hg_ct_helper:make_invoice_params(PartyID, ShopID, Product, Cost).
|
||||
|
||||
make_invoice_params(PartyID, ShopID, Product, Due, Cost) ->
|
||||
hg_ct_helper:make_invoice_params(PartyID, ShopID, Product, Due, Cost).
|
||||
|
||||
make_due_date(LifetimeSeconds) ->
|
||||
genlib_time:unow() + LifetimeSeconds.
|
||||
|
||||
create_invoice(InvoiceParams, Client) ->
|
||||
?invoice_state(?invoice(InvoiceID)) = hg_client_invoicing:create(InvoiceParams, Client),
|
||||
InvoiceID.
|
||||
|
||||
next_event(InvoiceID, Client) ->
|
||||
%% timeout should be at least as large as hold expiration in construct_domain_fixture/0
|
||||
next_event(InvoiceID, 12000, Client).
|
||||
|
||||
next_event(InvoiceID, Timeout, Client) ->
|
||||
case hg_client_invoicing:pull_event(InvoiceID, Timeout, Client) of
|
||||
{ok, ?invoice_ev(Changes)} ->
|
||||
case filter_changes(Changes) of
|
||||
L when length(L) > 0 ->
|
||||
L;
|
||||
[] ->
|
||||
next_event(InvoiceID, Timeout, Client)
|
||||
end;
|
||||
Result ->
|
||||
Result
|
||||
end.
|
||||
|
||||
filter_changes(Changes) ->
|
||||
lists:filtermap(fun filter_change/1, Changes).
|
||||
|
||||
filter_change(?payment_ev(_, C)) ->
|
||||
filter_change(C);
|
||||
filter_change(?chargeback_ev(_, C)) ->
|
||||
filter_change(C);
|
||||
filter_change(?refund_ev(_, C)) ->
|
||||
filter_change(C);
|
||||
filter_change(?session_ev(_, ?proxy_st_changed(_))) ->
|
||||
false;
|
||||
filter_change(?session_ev(_, ?session_suspended(_, _))) ->
|
||||
false;
|
||||
filter_change(?session_ev(_, ?session_activated())) ->
|
||||
false;
|
||||
filter_change(_) ->
|
||||
true.
|
||||
|
||||
set_payment_context(Context, Params = #payproc_InvoicePaymentParams{}) ->
|
||||
Params#payproc_InvoicePaymentParams{context = Context}.
|
||||
|
||||
make_payment_params(PmtSys) ->
|
||||
make_payment_params(instant, PmtSys).
|
||||
|
||||
make_payment_params(FlowType, PmtSys) ->
|
||||
{PaymentTool, Session} = hg_dummy_provider:make_payment_tool(no_preauth, PmtSys),
|
||||
make_payment_params(PaymentTool, Session, FlowType).
|
||||
|
||||
make_tds_payment_params(FlowType, PmtSys) ->
|
||||
{PaymentTool, Session} = hg_dummy_provider:make_payment_tool(preauth_3ds, PmtSys),
|
||||
make_payment_params(PaymentTool, Session, FlowType).
|
||||
|
||||
make_payment_params(PaymentTool, Session, FlowType) ->
|
||||
Flow =
|
||||
case FlowType of
|
||||
instant ->
|
||||
{instant, #payproc_InvoicePaymentParamsFlowInstant{}}
|
||||
end,
|
||||
#payproc_InvoicePaymentParams{
|
||||
payer =
|
||||
{payment_resource, #payproc_PaymentResourcePayerParams{
|
||||
resource = #domain_DisposablePaymentResource{
|
||||
payment_tool = PaymentTool,
|
||||
payment_session_id = Session,
|
||||
client_info = #domain_ClientInfo{}
|
||||
},
|
||||
contact_info = #domain_ContactInfo{}
|
||||
}},
|
||||
flow = Flow
|
||||
}.
|
||||
|
||||
start_payment(InvoiceID, PaymentParams, Client) ->
|
||||
?payment_state(?payment(PaymentID)) = hg_client_invoicing:start_payment(InvoiceID, PaymentParams, Client),
|
||||
[
|
||||
?payment_ev(PaymentID, ?payment_started(?payment_w_status(?pending())))
|
||||
] = next_event(InvoiceID, Client),
|
||||
[
|
||||
?payment_ev(PaymentID, ?risk_score_changed(_))
|
||||
] = next_event(InvoiceID, Client),
|
||||
[
|
||||
?payment_ev(PaymentID, ?route_changed(_))
|
||||
] = next_event(InvoiceID, Client),
|
||||
[
|
||||
?payment_ev(PaymentID, ?cash_flow_changed(_))
|
||||
] = next_event(InvoiceID, Client),
|
||||
PaymentID.
|
||||
|
||||
process_payment(InvoiceID, PaymentParams, Client) ->
|
||||
process_payment(InvoiceID, PaymentParams, Client, 0).
|
||||
|
||||
process_payment(InvoiceID, PaymentParams, Client, Restarts) ->
|
||||
PaymentID = start_payment(InvoiceID, PaymentParams, Client),
|
||||
PaymentID = await_payment_session_started(InvoiceID, PaymentID, Client, ?processed()),
|
||||
PaymentID = await_payment_process_finish(InvoiceID, PaymentID, Client, Restarts).
|
||||
|
||||
await_payment_session_started(InvoiceID, PaymentID, Client, Target) ->
|
||||
[
|
||||
?payment_ev(PaymentID, ?session_ev(Target, ?session_started()))
|
||||
] = next_event(InvoiceID, Client),
|
||||
PaymentID.
|
||||
|
||||
await_payment_process_finish(InvoiceID, PaymentID, Client) ->
|
||||
await_payment_process_finish(InvoiceID, PaymentID, Client, 0).
|
||||
|
||||
await_payment_process_finish(InvoiceID, PaymentID, Client, Restarts) ->
|
||||
PaymentID = await_sessions_restarts(PaymentID, ?processed(), InvoiceID, Client, Restarts),
|
||||
[
|
||||
?payment_ev(PaymentID, ?session_ev(?processed(), ?trx_bound(?trx_info(_)))),
|
||||
?payment_ev(PaymentID, ?session_ev(?processed(), ?session_finished(?session_succeeded())))
|
||||
] = next_event(InvoiceID, Client),
|
||||
[
|
||||
?payment_ev(PaymentID, ?payment_status_changed(?processed()))
|
||||
] = next_event(InvoiceID, Client),
|
||||
PaymentID.
|
||||
|
||||
get_payment_cost(InvoiceID, PaymentID, Client) ->
|
||||
#payproc_InvoicePayment{
|
||||
payment = #domain_InvoicePayment{cost = Cost}
|
||||
} = hg_client_invoicing:get_payment(InvoiceID, PaymentID, Client),
|
||||
Cost.
|
||||
|
||||
await_payment_capture(InvoiceID, PaymentID, Client) ->
|
||||
await_payment_capture(InvoiceID, PaymentID, ?timeout_reason(), Client).
|
||||
|
||||
await_payment_capture(InvoiceID, PaymentID, Reason, Client) ->
|
||||
await_payment_capture(InvoiceID, PaymentID, Reason, Client, 0).
|
||||
|
||||
await_payment_capture(InvoiceID, PaymentID, Reason, Client, Restarts) ->
|
||||
Cost = get_payment_cost(InvoiceID, PaymentID, Client),
|
||||
[
|
||||
?payment_ev(PaymentID, ?payment_capture_started(Reason, Cost, _, _Allocation)),
|
||||
?payment_ev(PaymentID, ?session_ev(?captured(Reason, Cost), ?session_started()))
|
||||
] = next_event(InvoiceID, Client),
|
||||
await_payment_capture_finish(InvoiceID, PaymentID, Reason, Client, Restarts).
|
||||
|
||||
await_payment_capture_finish(InvoiceID, PaymentID, Reason, Client, Restarts) ->
|
||||
Cost = get_payment_cost(InvoiceID, PaymentID, Client),
|
||||
await_payment_capture_finish(InvoiceID, PaymentID, Reason, Client, Restarts, Cost).
|
||||
|
||||
await_payment_capture_finish(InvoiceID, PaymentID, Reason, Client, Restarts, Cost) ->
|
||||
await_payment_capture_finish(InvoiceID, PaymentID, Reason, Client, Restarts, Cost, undefined).
|
||||
|
||||
await_payment_capture_finish(InvoiceID, PaymentID, Reason, Client, Restarts, Cost, Cart) ->
|
||||
Target = ?captured(Reason, Cost, Cart),
|
||||
PaymentID = await_sessions_restarts(PaymentID, Target, InvoiceID, Client, Restarts),
|
||||
[
|
||||
?payment_ev(PaymentID, ?session_ev(Target, ?session_finished(?session_succeeded())))
|
||||
] = next_event(InvoiceID, Client),
|
||||
[
|
||||
?payment_ev(PaymentID, ?payment_status_changed(Target)),
|
||||
?invoice_status_changed(?invoice_paid())
|
||||
] = next_event(InvoiceID, Client),
|
||||
PaymentID.
|
||||
|
||||
await_payment_process_interaction(InvoiceID, PaymentID, Client) ->
|
||||
Events0 = next_event(InvoiceID, Client),
|
||||
[
|
||||
?payment_ev(PaymentID, ?session_ev(?processed(), ?session_started()))
|
||||
] = Events0,
|
||||
Events1 = next_event(InvoiceID, Client),
|
||||
[
|
||||
?payment_ev(
|
||||
PaymentID,
|
||||
?session_ev(?processed(), ?interaction_changed(UserInteraction, ?interaction_requested))
|
||||
)
|
||||
] = Events1,
|
||||
UserInteraction.
|
||||
|
||||
await_payment_process_interaction_completion(InvoiceID, PaymentID, UserInteraction, Client) ->
|
||||
[
|
||||
?payment_ev(
|
||||
PaymentID,
|
||||
?session_ev(?processed(), ?interaction_changed(UserInteraction, ?interaction_completed))
|
||||
)
|
||||
] = next_event(InvoiceID, Client),
|
||||
ok.
|
||||
|
||||
-dialyzer({no_match, await_sessions_restarts/5}).
|
||||
|
||||
await_sessions_restarts(PaymentID, _Target, _InvoiceID, _Client, 0) ->
|
||||
PaymentID;
|
||||
await_sessions_restarts(PaymentID, ?refunded() = Target, InvoiceID, Client, Restarts) when Restarts > 0 ->
|
||||
[
|
||||
?payment_ev(PaymentID, ?refund_ev(_, ?session_ev(Target, ?session_finished(?session_failed(_))))),
|
||||
?payment_ev(PaymentID, ?refund_ev(_, ?session_ev(Target, ?session_started())))
|
||||
] = next_event(InvoiceID, Client),
|
||||
await_sessions_restarts(PaymentID, Target, InvoiceID, Client, Restarts - 1);
|
||||
await_sessions_restarts(PaymentID, Target, InvoiceID, Client, Restarts) when Restarts > 0 ->
|
||||
[
|
||||
?payment_ev(PaymentID, ?session_ev(Target, ?session_finished(?session_failed(_)))),
|
||||
?payment_ev(PaymentID, ?session_ev(Target, ?session_started()))
|
||||
] = next_event(InvoiceID, Client),
|
||||
await_sessions_restarts(PaymentID, Target, InvoiceID, Client, Restarts - 1).
|
||||
|
||||
assert_success_interaction(UserInteraction) ->
|
||||
{URL, Form} = get_post_request(UserInteraction),
|
||||
{ok, 200, _RespHeaders, _ClientRef} = post_request(URL, Form).
|
||||
|
||||
get_post_request(?redirect(URL, Form)) ->
|
||||
{URL, Form};
|
||||
get_post_request(?payterm_receipt(SPID)) ->
|
||||
URL = hg_dummy_provider:get_callback_url(),
|
||||
{URL, #{<<"tag">> => SPID}}.
|
||||
|
||||
post_request(URL, Form) ->
|
||||
Method = post,
|
||||
Headers = [],
|
||||
Body = {form, maps:to_list(Form)},
|
||||
hackney:request(Method, URL, Headers, Body).
|
@ -17,9 +17,6 @@
|
||||
-export([repair_scenario/3]).
|
||||
-export([get_limit_values/3]).
|
||||
|
||||
-export([create_invoice_adjustment/3]).
|
||||
-export([get_invoice_adjustment/3]).
|
||||
|
||||
-export([start_payment/3]).
|
||||
-export([register_payment/3]).
|
||||
-export([get_payment/3]).
|
||||
@ -70,9 +67,6 @@
|
||||
-type invoice_params() :: dmsl_payproc_thrift:'InvoiceParams'().
|
||||
-type invoice_params_tpl() :: dmsl_payproc_thrift:'InvoiceWithTemplateParams'().
|
||||
|
||||
-type invoice_adjustment() :: dmsl_domain_thrift:'InvoiceAdjustment'().
|
||||
-type invoice_adjustment_params() :: dmsl_payproc_thrift:'InvoiceAdjustmentParams'().
|
||||
|
||||
-type payment_params() :: dmsl_payproc_thrift:'InvoicePaymentParams'().
|
||||
-type register_payment_params() :: dmsl_payproc_thrift:'RegisterInvoicePaymentParams'().
|
||||
|
||||
@ -157,18 +151,6 @@ repair_scenario(InvoiceID, Scenario, Client) ->
|
||||
get_limit_values(InvoiceID, PaymentID, Client) ->
|
||||
map_result_error(gen_server:call(Client, {call, 'GetPaymentRoutesLimitValues', [InvoiceID, PaymentID]})).
|
||||
|
||||
-spec create_invoice_adjustment(invoice_id(), invoice_adjustment_params(), pid()) ->
|
||||
invoice_adjustment() | woody_error:business_error().
|
||||
create_invoice_adjustment(InvoiceID, Params, Client) ->
|
||||
Args = [InvoiceID, Params],
|
||||
map_result_error(gen_server:call(Client, {call, 'CreateInvoiceAdjustment', Args})).
|
||||
|
||||
-spec get_invoice_adjustment(invoice_id(), invoice_adjustment_params(), pid()) ->
|
||||
invoice_adjustment() | woody_error:business_error().
|
||||
get_invoice_adjustment(InvoiceID, ID, Client) ->
|
||||
Args = [InvoiceID, ID],
|
||||
map_result_error(gen_server:call(Client, {call, 'GetInvoiceAdjustment', Args})).
|
||||
|
||||
-spec start_payment(invoice_id(), payment_params(), pid()) -> payment_st() | woody_error:business_error().
|
||||
start_payment(InvoiceID, PaymentParams, Client) ->
|
||||
map_result_error(gen_server:call(Client, {call, 'StartPayment', [InvoiceID, PaymentParams]})).
|
||||
|
@ -17,7 +17,7 @@
|
||||
{<<"cowlib">>,{pkg,<<"cowlib">>,<<"2.11.0">>},2},
|
||||
{<<"damsel">>,
|
||||
{git,"https://github.com/valitydev/damsel.git",
|
||||
{ref,"bfedcb9dbb0bfdbd7a06a86417b49be6e807b98d"}},
|
||||
{ref,"c32f50f875bd1c58957e507420a23f7d2fac3ecb"}},
|
||||
0},
|
||||
{<<"dmt_client">>,
|
||||
{git,"https://github.com/valitydev/dmt-client.git",
|
||||
@ -45,7 +45,7 @@
|
||||
{<<"jsx">>,{pkg,<<"jsx">>,<<"3.1.0">>},1},
|
||||
{<<"limiter_proto">>,
|
||||
{git,"https://github.com/valitydev/limiter-proto.git",
|
||||
{ref,"bbd2c0dce044dd5b4e424fc8e38a0023a1685a22"}},
|
||||
{ref,"e045813d32e67432e5592d582e59e45df05da647"}},
|
||||
0},
|
||||
{<<"metrics">>,{pkg,<<"metrics">>,<<"1.0.1">>},2},
|
||||
{<<"mg_proto">>,
|
||||
|
Loading…
Reference in New Issue
Block a user