mirror of
https://github.com/valitydev/hellgate.git
synced 2024-11-06 10:55:22 +00:00
HG-461: Add manual refund handle (#292)
This commit is contained in:
parent
79fc57713a
commit
f10e9a1e51
@ -191,6 +191,11 @@ handle_function_('RefundPayment', [UserInfo, InvoiceID, PaymentID, Params], _Opt
|
||||
_ = set_invoicing_meta(InvoiceID, PaymentID),
|
||||
call(InvoiceID, {refund_payment, PaymentID, Params});
|
||||
|
||||
handle_function_('CreateManualRefund', [UserInfo, InvoiceID, PaymentID, Params], _Opts) ->
|
||||
ok = assume_user_identity(UserInfo),
|
||||
_ = set_invoicing_meta(InvoiceID, PaymentID),
|
||||
call(InvoiceID, {manual_refund, PaymentID, Params});
|
||||
|
||||
handle_function_('GetPaymentRefund', [UserInfo, InvoiceID, PaymentID, ID], _Opts) ->
|
||||
ok = assume_user_identity(UserInfo),
|
||||
_ = set_invoicing_meta(InvoiceID, PaymentID),
|
||||
@ -599,6 +604,16 @@ handle_call({refund_payment, PaymentID, Params}, St) ->
|
||||
St
|
||||
);
|
||||
|
||||
handle_call({manual_refund, PaymentID, Params}, St) ->
|
||||
_ = assert_invoice_accessible(St),
|
||||
_ = assert_invoice_operable(St),
|
||||
PaymentSession = get_payment_session(PaymentID, St),
|
||||
wrap_payment_impact(
|
||||
PaymentID,
|
||||
hg_invoice_payment:manual_refund(Params, PaymentSession, get_payment_opts(St)),
|
||||
St
|
||||
);
|
||||
|
||||
handle_call({create_payment_adjustment, PaymentID, Params}, St) ->
|
||||
_ = assert_invoice_accessible(St),
|
||||
PaymentSession = get_payment_session(PaymentID, St),
|
||||
|
@ -45,6 +45,8 @@
|
||||
-export([cancel/2]).
|
||||
-export([refund/3]).
|
||||
|
||||
-export([manual_refund/3]).
|
||||
|
||||
-export([create_adjustment/4]).
|
||||
-export([capture_adjustment/3]).
|
||||
-export([cancel_adjustment/3]).
|
||||
@ -921,18 +923,51 @@ refund(Params, St0, Opts) ->
|
||||
St = St0#st{opts = Opts},
|
||||
Revision = hg_domain:head(),
|
||||
Payment = get_payment(St),
|
||||
Refund =
|
||||
prepare_refund(Params, Payment, Revision, St, Opts),
|
||||
{AccountMap, FinalCashflow} =
|
||||
prepare_refund_cashflow(Refund, Payment, Revision, St, Opts),
|
||||
Changes = [
|
||||
?refund_created(Refund, FinalCashflow),
|
||||
?session_ev(?refunded(), ?session_started())
|
||||
],
|
||||
try_commit_refund(Refund, Changes, AccountMap, St).
|
||||
|
||||
-spec manual_refund(refund_params(), st(), opts()) ->
|
||||
{refund(), result()}.
|
||||
|
||||
manual_refund(Params, St0, Opts) ->
|
||||
St = St0#st{opts = Opts},
|
||||
Revision = hg_domain:head(),
|
||||
Payment = get_payment(St),
|
||||
Refund =
|
||||
prepare_refund(Params, Payment, Revision, St, Opts),
|
||||
{AccountMap, FinalCashflow} =
|
||||
prepare_refund_cashflow(Refund, Payment, Revision, St, Opts),
|
||||
TransactionInfo = Params#payproc_InvoicePaymentRefundParams.transaction_info,
|
||||
Changes = [
|
||||
?refund_created(Refund, FinalCashflow),
|
||||
?session_ev(?refunded(), ?session_started())
|
||||
]
|
||||
++ make_transaction_event(TransactionInfo) ++
|
||||
[
|
||||
?session_ev(?refunded(), ?session_finished(?session_succeeded()))
|
||||
],
|
||||
try_commit_refund(Refund, Changes, AccountMap, St).
|
||||
|
||||
make_transaction_event(undefined) ->
|
||||
[];
|
||||
make_transaction_event(TransactionInfo) ->
|
||||
[?session_ev(?refunded(), ?trx_bound(TransactionInfo))].
|
||||
|
||||
prepare_refund(Params, Payment, Revision, St, Opts) ->
|
||||
_ = assert_payment_status(captured, Payment),
|
||||
Route = get_route(St),
|
||||
Shop = get_shop(Opts),
|
||||
PartyRevision = get_opts_party_revision(Opts),
|
||||
PaymentInstitution = get_payment_institution(Opts, Revision),
|
||||
Provider = get_route_provider(Route, Revision),
|
||||
_ = assert_previous_refunds_finished(St),
|
||||
VS0 = collect_validation_varset(St, Opts),
|
||||
Cash = define_refund_cash(Params#payproc_InvoicePaymentRefundParams.cash, Payment),
|
||||
_ = assert_refund_cash(Cash, St),
|
||||
ID = construct_refund_id(St),
|
||||
Refund = #domain_InvoicePaymentRefund{
|
||||
#domain_InvoicePaymentRefund {
|
||||
id = ID,
|
||||
created_at = hg_datetime:format_now(),
|
||||
domain_revision = Revision,
|
||||
@ -940,18 +975,25 @@ refund(Params, St0, Opts) ->
|
||||
status = ?refund_pending(),
|
||||
reason = Params#payproc_InvoicePaymentRefundParams.reason,
|
||||
cash = Cash
|
||||
},
|
||||
}.
|
||||
|
||||
prepare_refund_cashflow(Refund, Payment, Revision, St, Opts) ->
|
||||
Route = get_route(St),
|
||||
Shop = get_shop(Opts),
|
||||
MerchantTerms = get_merchant_refunds_terms(get_merchant_payments_terms(Opts, Revision)),
|
||||
VS0 = collect_validation_varset(St, Opts),
|
||||
VS1 = validate_refund(MerchantTerms, Refund, Payment, VS0, Revision),
|
||||
ProviderPaymentsTerms = get_provider_payments_terms(Route, Revision),
|
||||
ProviderTerms = get_provider_refunds_terms(ProviderPaymentsTerms, Refund, Payment, VS1, Revision),
|
||||
Cashflow = collect_refund_cashflow(MerchantTerms, ProviderTerms, VS1, Revision),
|
||||
PaymentInstitution = get_payment_institution(Opts, Revision),
|
||||
Provider = get_route_provider(Route, Revision),
|
||||
AccountMap = collect_account_map(Payment, Shop, PaymentInstitution, Provider, VS1, Revision),
|
||||
FinalCashflow = construct_final_cashflow(Cashflow, collect_cash_flow_context(Refund), AccountMap),
|
||||
Changes = [
|
||||
?refund_created(Refund, FinalCashflow),
|
||||
?session_ev(?refunded(), ?session_started())
|
||||
],
|
||||
{AccountMap, FinalCashflow}.
|
||||
|
||||
try_commit_refund(Refund, Changes, AccountMap, St) ->
|
||||
ID = Refund#domain_InvoicePaymentRefund.id,
|
||||
RefundSt = collapse_refund_changes(Changes),
|
||||
AffectedAccounts = prepare_refund_cashflow(RefundSt, St),
|
||||
% NOTE we assume that posting involving merchant settlement account MUST be present in the cashflow
|
||||
|
@ -66,6 +66,7 @@
|
||||
-export([invalid_refund_party_status/1]).
|
||||
-export([invalid_refund_shop_status/1]).
|
||||
-export([payment_refund_success/1]).
|
||||
-export([payment_manual_refund/1]).
|
||||
-export([payment_partial_refunds_success/1]).
|
||||
-export([payment_temporary_unavailability_retry_success/1]).
|
||||
-export([payment_temporary_unavailability_too_many_retries/1]).
|
||||
@ -204,6 +205,7 @@ groups() ->
|
||||
invalid_refund_party_status,
|
||||
invalid_refund_shop_status,
|
||||
retry_temporary_unavailability_refund,
|
||||
payment_manual_refund,
|
||||
payment_refund_success,
|
||||
payment_partial_refunds_success,
|
||||
invalid_amount_payment_partial_refund,
|
||||
@ -1447,6 +1449,53 @@ payment_refund_success(C) ->
|
||||
?invalid_payment_status(?refunded()) =
|
||||
hg_client_invoicing:refund_payment(InvoiceID, PaymentID, RefundParams, Client).
|
||||
|
||||
-spec payment_manual_refund(config()) -> _ | no_return().
|
||||
|
||||
payment_manual_refund(C) ->
|
||||
Client = cfg(client, C),
|
||||
PartyClient = cfg(party_client, C),
|
||||
ShopID = hg_ct_helper:create_battle_ready_shop(?cat(2), <<"RUB">>, ?tmpl(2), ?pinst(2), PartyClient),
|
||||
InvoiceID = start_invoice(ShopID, <<"rubberduck">>, make_due_date(10), 42000, C),
|
||||
PaymentID = process_payment(InvoiceID, make_payment_params(), Client),
|
||||
TrxInfo = ?trx_info(<<"test">>, #{}),
|
||||
RefundParams = #payproc_InvoicePaymentRefundParams {
|
||||
reason = <<"manual">>,
|
||||
transaction_info = TrxInfo
|
||||
},
|
||||
PaymentID = await_payment_capture(InvoiceID, PaymentID, Client),
|
||||
% not enough funds on the merchant account
|
||||
?insufficient_account_balance() =
|
||||
hg_client_invoicing:refund_payment_manual(InvoiceID, PaymentID, RefundParams, Client),
|
||||
% top up merchant account
|
||||
InvoiceID2 = start_invoice(ShopID, <<"rubberduck">>, make_due_date(10), 42000, C),
|
||||
PaymentID2 = process_payment(InvoiceID2, make_payment_params(), Client),
|
||||
PaymentID2 = await_payment_capture(InvoiceID2, PaymentID2, Client),
|
||||
% prevent proxy access
|
||||
OriginalRevision = hg_domain:head(),
|
||||
Fixture = payment_manual_refund_fixture(OriginalRevision),
|
||||
ok = hg_domain:upsert(Fixture),
|
||||
% create refund
|
||||
Refund = #domain_InvoicePaymentRefund{id = RefundID} =
|
||||
hg_client_invoicing:refund_payment_manual(InvoiceID, PaymentID, RefundParams, Client),
|
||||
Refund =
|
||||
hg_client_invoicing:get_payment_refund(InvoiceID, PaymentID, RefundID, Client),
|
||||
[
|
||||
?payment_ev(PaymentID, ?refund_ev(RefundID, ?refund_created(Refund, _))),
|
||||
?payment_ev(PaymentID, ?refund_ev(RefundID, ?session_ev(?refunded(), ?session_started()))),
|
||||
?payment_ev(PaymentID, ?refund_ev(RefundID, ?session_ev(?refunded(), ?trx_bound(TrxInfo)))),
|
||||
?payment_ev(PaymentID, ?refund_ev(RefundID, ?session_ev(?refunded(), ?session_finished(?session_succeeded()))))
|
||||
] = next_event(InvoiceID, Client),
|
||||
[
|
||||
?payment_ev(PaymentID, ?refund_ev(RefundID, ?refund_status_changed(?refund_succeeded()))),
|
||||
?payment_ev(PaymentID, ?payment_status_changed(?refunded()))
|
||||
] = next_event(InvoiceID, Client),
|
||||
#domain_InvoicePaymentRefund{status = ?refund_succeeded()} =
|
||||
hg_client_invoicing:get_payment_refund(InvoiceID, PaymentID, RefundID, Client),
|
||||
?invalid_payment_status(?refunded()) =
|
||||
hg_client_invoicing:refund_payment_manual(InvoiceID, PaymentID, RefundParams, Client),
|
||||
% reenable proxy
|
||||
ok = hg_domain:reset(OriginalRevision).
|
||||
|
||||
-spec payment_partial_refunds_success(config()) -> _ | no_return().
|
||||
|
||||
payment_partial_refunds_success(C) ->
|
||||
@ -3702,6 +3751,18 @@ payments_w_bank_conditions_fixture(_Revision) ->
|
||||
hg_ct_fixture:construct_contract_template(?tmpl(4), ?trms(4))
|
||||
].
|
||||
|
||||
payment_manual_refund_fixture(_Revision) ->
|
||||
[
|
||||
{proxy, #domain_ProxyObject{
|
||||
ref = ?prx(1),
|
||||
data = #domain_ProxyDefinition{
|
||||
name = <<"undefined">>,
|
||||
description = <<"undefined">>,
|
||||
url = <<"undefined">>,
|
||||
options = #{}
|
||||
}
|
||||
}}
|
||||
].
|
||||
|
||||
construct_term_set_for_partial_capture_service_permit() ->
|
||||
TermSet = #domain_TermSet{
|
||||
|
@ -23,6 +23,7 @@
|
||||
-export([new_capture_payment/4]).
|
||||
|
||||
-export([refund_payment/4]).
|
||||
-export([refund_payment_manual/4]).
|
||||
-export([get_payment_refund/4]).
|
||||
|
||||
-export([create_adjustment/4]).
|
||||
@ -205,6 +206,12 @@ capture_payment(InvoiceID, PaymentID, Reason, Cash, Client) ->
|
||||
refund_payment(InvoiceID, PaymentID, Params, Client) ->
|
||||
map_result_error(gen_server:call(Client, {call, 'RefundPayment', [InvoiceID, PaymentID, Params]})).
|
||||
|
||||
-spec refund_payment_manual(invoice_id(), payment_id(), refund_params(), pid()) ->
|
||||
refund() | woody_error:business_error().
|
||||
|
||||
refund_payment_manual(InvoiceID, PaymentID, Params, Client) ->
|
||||
map_result_error(gen_server:call(Client, {call, 'CreateManualRefund', [InvoiceID, PaymentID, Params]})).
|
||||
|
||||
-spec get_payment_refund(invoice_id(), payment_id(), refund_id(), pid()) ->
|
||||
refund() | woody_error:business_error().
|
||||
|
||||
|
@ -10,7 +10,7 @@
|
||||
{<<"cowlib">>,{pkg,<<"cowlib">>,<<"1.0.2">>},2},
|
||||
{<<"dmsl">>,
|
||||
{git,"git@github.com:rbkmoney/damsel.git",
|
||||
{ref,"15c8db48ccc6778c4fc7cc998724136e38b6c728"}},
|
||||
{ref,"b966bd5b02dcd2968e46070a75e6c9e35c8b7f82"}},
|
||||
0},
|
||||
{<<"dmt_client">>,
|
||||
{git,"git@github.com:rbkmoney/dmt_client.git",
|
||||
|
Loading…
Reference in New Issue
Block a user