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),
|
_ = set_invoicing_meta(InvoiceID, PaymentID),
|
||||||
call(InvoiceID, {refund_payment, PaymentID, Params});
|
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) ->
|
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),
|
||||||
@ -599,6 +604,16 @@ handle_call({refund_payment, PaymentID, Params}, St) ->
|
|||||||
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) ->
|
handle_call({create_payment_adjustment, PaymentID, Params}, St) ->
|
||||||
_ = assert_invoice_accessible(St),
|
_ = assert_invoice_accessible(St),
|
||||||
PaymentSession = get_payment_session(PaymentID, St),
|
PaymentSession = get_payment_session(PaymentID, St),
|
||||||
|
@ -45,6 +45,8 @@
|
|||||||
-export([cancel/2]).
|
-export([cancel/2]).
|
||||||
-export([refund/3]).
|
-export([refund/3]).
|
||||||
|
|
||||||
|
-export([manual_refund/3]).
|
||||||
|
|
||||||
-export([create_adjustment/4]).
|
-export([create_adjustment/4]).
|
||||||
-export([capture_adjustment/3]).
|
-export([capture_adjustment/3]).
|
||||||
-export([cancel_adjustment/3]).
|
-export([cancel_adjustment/3]).
|
||||||
@ -921,18 +923,51 @@ refund(Params, St0, Opts) ->
|
|||||||
St = St0#st{opts = Opts},
|
St = St0#st{opts = Opts},
|
||||||
Revision = hg_domain:head(),
|
Revision = hg_domain:head(),
|
||||||
Payment = get_payment(St),
|
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),
|
_ = assert_payment_status(captured, Payment),
|
||||||
Route = get_route(St),
|
|
||||||
Shop = get_shop(Opts),
|
|
||||||
PartyRevision = get_opts_party_revision(Opts),
|
PartyRevision = get_opts_party_revision(Opts),
|
||||||
PaymentInstitution = get_payment_institution(Opts, Revision),
|
|
||||||
Provider = get_route_provider(Route, Revision),
|
|
||||||
_ = assert_previous_refunds_finished(St),
|
_ = assert_previous_refunds_finished(St),
|
||||||
VS0 = collect_validation_varset(St, Opts),
|
|
||||||
Cash = define_refund_cash(Params#payproc_InvoicePaymentRefundParams.cash, Payment),
|
Cash = define_refund_cash(Params#payproc_InvoicePaymentRefundParams.cash, Payment),
|
||||||
_ = assert_refund_cash(Cash, St),
|
_ = assert_refund_cash(Cash, St),
|
||||||
ID = construct_refund_id(St),
|
ID = construct_refund_id(St),
|
||||||
Refund = #domain_InvoicePaymentRefund{
|
#domain_InvoicePaymentRefund {
|
||||||
id = ID,
|
id = ID,
|
||||||
created_at = hg_datetime:format_now(),
|
created_at = hg_datetime:format_now(),
|
||||||
domain_revision = Revision,
|
domain_revision = Revision,
|
||||||
@ -940,18 +975,25 @@ refund(Params, St0, Opts) ->
|
|||||||
status = ?refund_pending(),
|
status = ?refund_pending(),
|
||||||
reason = Params#payproc_InvoicePaymentRefundParams.reason,
|
reason = Params#payproc_InvoicePaymentRefundParams.reason,
|
||||||
cash = Cash
|
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)),
|
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),
|
VS1 = validate_refund(MerchantTerms, Refund, Payment, VS0, Revision),
|
||||||
ProviderPaymentsTerms = get_provider_payments_terms(Route, Revision),
|
ProviderPaymentsTerms = get_provider_payments_terms(Route, Revision),
|
||||||
ProviderTerms = get_provider_refunds_terms(ProviderPaymentsTerms, Refund, Payment, VS1, Revision),
|
ProviderTerms = get_provider_refunds_terms(ProviderPaymentsTerms, Refund, Payment, VS1, Revision),
|
||||||
Cashflow = collect_refund_cashflow(MerchantTerms, ProviderTerms, 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),
|
AccountMap = collect_account_map(Payment, Shop, PaymentInstitution, Provider, VS1, Revision),
|
||||||
FinalCashflow = construct_final_cashflow(Cashflow, collect_cash_flow_context(Refund), AccountMap),
|
FinalCashflow = construct_final_cashflow(Cashflow, collect_cash_flow_context(Refund), AccountMap),
|
||||||
Changes = [
|
{AccountMap, FinalCashflow}.
|
||||||
?refund_created(Refund, FinalCashflow),
|
|
||||||
?session_ev(?refunded(), ?session_started())
|
try_commit_refund(Refund, Changes, AccountMap, St) ->
|
||||||
],
|
ID = Refund#domain_InvoicePaymentRefund.id,
|
||||||
RefundSt = collapse_refund_changes(Changes),
|
RefundSt = collapse_refund_changes(Changes),
|
||||||
AffectedAccounts = prepare_refund_cashflow(RefundSt, St),
|
AffectedAccounts = prepare_refund_cashflow(RefundSt, St),
|
||||||
% NOTE we assume that posting involving merchant settlement account MUST be present in the cashflow
|
% 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_party_status/1]).
|
||||||
-export([invalid_refund_shop_status/1]).
|
-export([invalid_refund_shop_status/1]).
|
||||||
-export([payment_refund_success/1]).
|
-export([payment_refund_success/1]).
|
||||||
|
-export([payment_manual_refund/1]).
|
||||||
-export([payment_partial_refunds_success/1]).
|
-export([payment_partial_refunds_success/1]).
|
||||||
-export([payment_temporary_unavailability_retry_success/1]).
|
-export([payment_temporary_unavailability_retry_success/1]).
|
||||||
-export([payment_temporary_unavailability_too_many_retries/1]).
|
-export([payment_temporary_unavailability_too_many_retries/1]).
|
||||||
@ -204,6 +205,7 @@ groups() ->
|
|||||||
invalid_refund_party_status,
|
invalid_refund_party_status,
|
||||||
invalid_refund_shop_status,
|
invalid_refund_shop_status,
|
||||||
retry_temporary_unavailability_refund,
|
retry_temporary_unavailability_refund,
|
||||||
|
payment_manual_refund,
|
||||||
payment_refund_success,
|
payment_refund_success,
|
||||||
payment_partial_refunds_success,
|
payment_partial_refunds_success,
|
||||||
invalid_amount_payment_partial_refund,
|
invalid_amount_payment_partial_refund,
|
||||||
@ -1447,6 +1449,53 @@ payment_refund_success(C) ->
|
|||||||
?invalid_payment_status(?refunded()) =
|
?invalid_payment_status(?refunded()) =
|
||||||
hg_client_invoicing:refund_payment(InvoiceID, PaymentID, RefundParams, Client).
|
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().
|
-spec payment_partial_refunds_success(config()) -> _ | no_return().
|
||||||
|
|
||||||
payment_partial_refunds_success(C) ->
|
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))
|
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() ->
|
construct_term_set_for_partial_capture_service_permit() ->
|
||||||
TermSet = #domain_TermSet{
|
TermSet = #domain_TermSet{
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
-export([new_capture_payment/4]).
|
-export([new_capture_payment/4]).
|
||||||
|
|
||||||
-export([refund_payment/4]).
|
-export([refund_payment/4]).
|
||||||
|
-export([refund_payment_manual/4]).
|
||||||
-export([get_payment_refund/4]).
|
-export([get_payment_refund/4]).
|
||||||
|
|
||||||
-export([create_adjustment/4]).
|
-export([create_adjustment/4]).
|
||||||
@ -205,6 +206,12 @@ capture_payment(InvoiceID, PaymentID, Reason, Cash, Client) ->
|
|||||||
refund_payment(InvoiceID, PaymentID, Params, Client) ->
|
refund_payment(InvoiceID, PaymentID, Params, Client) ->
|
||||||
map_result_error(gen_server:call(Client, {call, 'RefundPayment', [InvoiceID, PaymentID, Params]})).
|
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()) ->
|
-spec get_payment_refund(invoice_id(), payment_id(), refund_id(), pid()) ->
|
||||||
refund() | woody_error:business_error().
|
refund() | woody_error:business_error().
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@
|
|||||||
{<<"cowlib">>,{pkg,<<"cowlib">>,<<"1.0.2">>},2},
|
{<<"cowlib">>,{pkg,<<"cowlib">>,<<"1.0.2">>},2},
|
||||||
{<<"dmsl">>,
|
{<<"dmsl">>,
|
||||||
{git,"git@github.com:rbkmoney/damsel.git",
|
{git,"git@github.com:rbkmoney/damsel.git",
|
||||||
{ref,"15c8db48ccc6778c4fc7cc998724136e38b6c728"}},
|
{ref,"b966bd5b02dcd2968e46070a75e6c9e35c8b7f82"}},
|
||||||
0},
|
0},
|
||||||
{<<"dmt_client">>,
|
{<<"dmt_client">>,
|
||||||
{git,"git@github.com:rbkmoney/dmt_client.git",
|
{git,"git@github.com:rbkmoney/dmt_client.git",
|
||||||
|
Loading…
Reference in New Issue
Block a user