diff --git a/Dockerfile.sh b/Dockerfile.sh index 17e0fc8..be2788c 100755 --- a/Dockerfile.sh +++ b/Dockerfile.sh @@ -3,23 +3,21 @@ cat < COPY ./_build/prod/rel/hellgate /opt/hellgate +WORKDIR /opt/hellgate CMD /opt/hellgate/bin/hellgate foreground EXPOSE 8022 -LABEL base_image_tag=$BASE_IMAGE_TAG -LABEL build_image_tag=$BUILD_IMAGE_TAG -# A bit of magic to get a proper branch name -# even when the HEAD is detached (Hey Jenkins! -# BRANCH_NAME is available in Jenkins env). -LABEL branch=$( \ - if [ "HEAD" != $(git rev-parse --abbrev-ref HEAD) ]; then \ - echo $(git rev-parse --abbrev-ref HEAD); \ - elif [ -n "$BRANCH_NAME" ]; then \ - echo $BRANCH_NAME; \ - else \ - echo $(git name-rev --name-only HEAD); \ - fi) -LABEL commit=$(git rev-parse HEAD) -LABEL commit_number=$(git rev-list --count HEAD) -WORKDIR /opt/hellgate +LABEL com.rbkmoney.$SERVICE_NAME.parent=$BASE_IMAGE_NAME \ + com.rbkmoney.$SERVICE_NAME.parent_tag=$BASE_IMAGE_TAG \ + com.rbkmoney.$SERVICE_NAME.build_img=build \ + com.rbkmoney.$SERVICE_NAME.build_img_tag=$BUILD_IMAGE_TAG \ + com.rbkmoney.$SERVICE_NAME.commit_id=$(git rev-parse HEAD) \ + com.rbkmoney.$SERVICE_NAME.commit_number=$(git rev-list --count HEAD) \ + com.rbkmoney.$SERVICE_NAME.branch=$( \ + if [ "HEAD" != $(git rev-parse --abbrev-ref HEAD) ]; then \ + echo $(git rev-parse --abbrev-ref HEAD); \ + elif [ -n "$BRANCH_NAME" ]; then \ + echo $BRANCH_NAME; \ + else \ + echo $(git name-rev --name-only HEAD); \ + fi) EOF - diff --git a/Makefile b/Makefile index 9539868..ae9d264 100644 --- a/Makefile +++ b/Makefile @@ -49,7 +49,7 @@ xref: submodules lint: elvis rock -dialyze: +dialyze: submodules $(REBAR) dialyzer start: submodules @@ -58,7 +58,7 @@ start: submodules devrel: submodules $(REBAR) release -release: distclean +release: submodules $(REBAR) as prod release clean: diff --git a/apps/hellgate/src/hg_cashflow.erl b/apps/hellgate/src/hg_cashflow.erl index 396cb7c..13869bd 100644 --- a/apps/hellgate/src/hg_cashflow.erl +++ b/apps/hellgate/src/hg_cashflow.erl @@ -94,8 +94,8 @@ revert_details(Details) -> -define(fixed(Cash), {fixed, #domain_CashVolumeFixed{cash = Cash}}). --define(share(P, Q, Of), - {share, #domain_CashVolumeShare{'parts' = ?rational(P, Q), 'of' = Of}}). +-define(share(P, Q, Of, RoundingMethod), + {share, #domain_CashVolumeShare{'parts' = ?rational(P, Q), 'of' = Of, 'rounding_method' = RoundingMethod}}). -define(product(Fun, CVs), {product, {Fun, CVs}}). -define(rational(P, Q), @@ -103,8 +103,8 @@ revert_details(Details) -> compute_volume(?fixed(Cash), _Context) -> Cash; -compute_volume(?share(P, Q, Of), Context) -> - compute_parts_of(P, Q, resolve_constant(Of, Context)); +compute_volume(?share(P, Q, Of, RoundingMethod), Context) -> + compute_parts_of(P, Q, resolve_constant(Of, Context), RoundingMethod); compute_volume(?product(Fun, CVs) = CV0, Context) -> case ordsets:size(CVs) of N when N > 0 -> @@ -113,12 +113,13 @@ compute_volume(?product(Fun, CVs) = CV0, Context) -> error({misconfiguration, {'Cash volume product over empty set', CV0}}) end. -compute_parts_of(P, Q, Cash = #domain_Cash{amount = Amount}) -> +compute_parts_of(P, Q, Cash = #domain_Cash{amount = Amount}, RoundingMethod) -> Cash#domain_Cash{amount = genlib_rational:round( genlib_rational:mul( genlib_rational:new(Amount), genlib_rational:new(P, Q) - ) + ), + get_rounding_method(RoundingMethod) )}. compute_product(Fun, [CV | CVRest], CV0, Context) -> @@ -149,6 +150,13 @@ resolve_constant(Constant, Context) -> error({misconfiguration, {'Cash flow constant not found', {Constant, Context}}}) end. +get_rounding_method(undefined) -> + round_half_away_from_zero; +get_rounding_method(round_half_towards_zero) -> + round_half_towards_zero; +get_rounding_method(round_half_away_from_zero) -> + round_half_away_from_zero. + %% -include("domain.hrl"). diff --git a/apps/hellgate/src/hg_payment_tool.erl b/apps/hellgate/src/hg_payment_tool.erl index 6ecc5dd..57ca716 100644 --- a/apps/hellgate/src/hg_payment_tool.erl +++ b/apps/hellgate/src/hg_payment_tool.erl @@ -22,7 +22,9 @@ get_method({bank_card, #domain_BankCard{payment_system = PaymentSystem}}) -> #domain_PaymentMethodRef{id = {bank_card, PaymentSystem}}; get_method({payment_terminal, #domain_PaymentTerminal{terminal_type = TerminalType}}) -> - #domain_PaymentMethodRef{id = {payment_terminal, TerminalType}}. + #domain_PaymentMethodRef{id = {payment_terminal, TerminalType}}; +get_method({digital_wallet, #domain_DigitalWallet{provider = Provider}}) -> + #domain_PaymentMethodRef{id = {digital_wallet, Provider}}. %% @@ -30,62 +32,109 @@ get_method({payment_terminal, #domain_PaymentTerminal{terminal_type = TerminalTy test_condition({bank_card, C}, {bank_card, V = #domain_BankCard{}}, Rev) -> test_bank_card_condition(C, V, Rev); +test_condition({payment_terminal, C}, {payment_terminal, V = #domain_PaymentTerminal{}}, Rev) -> + test_payment_terminal_condition(C, V, Rev); +test_condition({digital_wallet, C}, {digital_wallet, V = #domain_DigitalWallet{}}, Rev) -> + test_digital_wallet_condition(C, V, Rev); test_condition(_PaymentTool, _Condition, _Rev) -> false. -test_bank_card_condition({payment_system_is, Ps}, #domain_BankCard{payment_system = Ps0}, _Rev) -> +test_bank_card_condition(#domain_BankCardCondition{definition = Def}, V, Rev) when Def /= undefined -> + test_bank_card_condition_def(Def, V, Rev); + +% legacy +test_bank_card_condition(#domain_BankCardCondition{payment_system_is = Ps}, V, Rev) when Ps /= undefined -> + test_bank_card_condition_def({payment_system_is, Ps}, V, Rev); +test_bank_card_condition(#domain_BankCardCondition{bin_in = RangeRef}, V, Rev) when RangeRef /= undefined -> + test_bank_card_condition_def({bin_in, RangeRef}, V, Rev); + +test_bank_card_condition(#domain_BankCardCondition{}, _, _Rev) -> + true. + +test_bank_card_condition_def({payment_system_is, Ps}, #domain_BankCard{payment_system = Ps0}, _Rev) -> Ps =:= Ps0; -test_bank_card_condition({bin_in, RangeRef}, #domain_BankCard{bin = BIN}, Rev) -> +test_bank_card_condition_def({bin_in, RangeRef}, #domain_BankCard{bin = BIN}, Rev) -> #domain_BankCardBINRange{bins = BINs} = hg_domain:get(Rev, {bank_card_bin_range, RangeRef}), ordsets:is_element(BIN, BINs). --include("legacy_structures.hrl"). +test_payment_terminal_condition(#domain_PaymentTerminalCondition{definition = Def}, V, Rev) -> + Def =:= undefined orelse test_payment_terminal_condition_def(Def, V, Rev). + +test_payment_terminal_condition_def({provider_is, V1}, #domain_PaymentTerminal{terminal_type = V2}, _Rev) -> + V1 =:= V2. + +test_digital_wallet_condition(#domain_DigitalWalletCondition{definition = Def}, V, Rev) -> + Def =:= undefined orelse test_digital_wallet_condition_def(Def, V, Rev). + +test_digital_wallet_condition_def({provider_is, V1}, #domain_DigitalWallet{provider = V2}, _Rev) -> + V1 =:= V2. + %% Marshalling +-include("legacy_structures.hrl"). + -spec marshal(t()) -> hg_msgpack_marshalling:value(). marshal(PaymentTool) -> marshal(payment_tool, PaymentTool). -marshal(payment_tool, {bank_card, #domain_BankCard{} = BankCard}) -> - [2, #{ +marshal(payment_tool, {PaymentMethod, V}) -> + [3, marshal(payment_method, PaymentMethod), marshal(PaymentMethod, V)]; + +marshal(bank_card = T, #domain_BankCard{} = BankCard) -> + #{ <<"token">> => marshal(str, BankCard#domain_BankCard.token), - <<"payment_system">> => marshal(payment_system, BankCard#domain_BankCard.payment_system), + <<"payment_system">> => marshal({T, payment_system}, BankCard#domain_BankCard.payment_system), <<"bin">> => marshal(str, BankCard#domain_BankCard.bin), <<"masked_pan">> => marshal(str, BankCard#domain_BankCard.masked_pan) - }]; -marshal(payment_tool, {payment_terminal, #domain_PaymentTerminal{terminal_type = TerminalType}}) -> - [2, marshal(terminal_type, TerminalType)]; + }; +marshal(payment_terminal = T, #domain_PaymentTerminal{terminal_type = TerminalType}) -> + marshal({T, type}, TerminalType); +marshal(digital_wallet = T, #domain_DigitalWallet{} = DigitalWallet) -> + #{ + <<"provider">> => marshal({T, provider}, DigitalWallet#domain_DigitalWallet.provider), + <<"id">> => marshal(str, DigitalWallet#domain_DigitalWallet.id) + }; -marshal(payment_system, visa) -> +marshal(payment_method, bank_card) -> + <<"card">>; +marshal(payment_method, payment_terminal) -> + <<"payterm">>; +marshal(payment_method, digital_wallet) -> + <<"wallet">>; + +marshal({bank_card, payment_system}, visa) -> <<"visa">>; -marshal(payment_system, mastercard) -> +marshal({bank_card, payment_system}, mastercard) -> <<"mastercard">>; -marshal(payment_system, visaelectron) -> +marshal({bank_card, payment_system}, visaelectron) -> <<"visaelectron">>; -marshal(payment_system, maestro) -> +marshal({bank_card, payment_system}, maestro) -> <<"maestro">>; -marshal(payment_system, forbrugsforeningen) -> +marshal({bank_card, payment_system}, forbrugsforeningen) -> <<"forbrugsforeningen">>; -marshal(payment_system, dankort) -> +marshal({bank_card, payment_system}, dankort) -> <<"dankort">>; -marshal(payment_system, amex) -> +marshal({bank_card, payment_system}, amex) -> <<"amex">>; -marshal(payment_system, dinersclub) -> +marshal({bank_card, payment_system}, dinersclub) -> <<"dinersclub">>; -marshal(payment_system, discover) -> +marshal({bank_card, payment_system}, discover) -> <<"discover">>; -marshal(payment_system, unionpay) -> +marshal({bank_card, payment_system}, unionpay) -> <<"unionpay">>; -marshal(payment_system, jcb) -> +marshal({bank_card, payment_system}, jcb) -> <<"jcb">>; -marshal(payment_system, nspkmir) -> +marshal({bank_card, payment_system}, nspkmir) -> <<"nspkmir">>; -marshal(terminal_type, euroset) -> +marshal({payment_terminal, type}, euroset) -> <<"euroset">>; +marshal({digital_wallet, provider}, qiwi) -> + <<"qiwi">>; + marshal(_, Other) -> Other. @@ -97,61 +146,123 @@ marshal(_, Other) -> unmarshal(PaymentTool) -> unmarshal(payment_tool, PaymentTool). -unmarshal(payment_tool, [2, #{ - <<"token">> := Token, - <<"payment_system">> := PaymentSystem, - <<"bin">> := Bin, - <<"masked_pan">> := MaskedPan -}]) -> - {bank_card, #domain_BankCard{ - token = unmarshal(str, Token), - payment_system = unmarshal(payment_system, PaymentSystem), - bin = unmarshal(str, Bin), - masked_pan = unmarshal(str, MaskedPan) - }}; +unmarshal(payment_tool, [3, PMV, V]) -> + PaymentMethod = unmarshal(payment_method, PMV), + {PaymentMethod, unmarshal(PaymentMethod, V)}; + +unmarshal(bank_card = T, #{ + <<"token">> := Token, + <<"payment_system">> := PaymentSystem, + <<"bin">> := Bin, + <<"masked_pan">> := MaskedPan +}) -> + #domain_BankCard{ + token = unmarshal(str, Token), + payment_system = unmarshal({T, payment_system}, PaymentSystem), + bin = unmarshal(str, Bin), + masked_pan = unmarshal(str, MaskedPan) + }; +unmarshal(payment_terminal = T, TerminalType) -> + #domain_PaymentTerminal{ + terminal_type = unmarshal({T, type}, TerminalType) + }; +unmarshal(digital_wallet = T, #{ + <<"provider">> := Provider, + <<"id">> := ID +}) -> + #domain_DigitalWallet{ + provider = unmarshal({T, provider}, Provider), + id = unmarshal(str, ID) + }; + +unmarshal(payment_tool, [2, #{<<"token">>:= _} = BankCard]) -> + {bank_card, unmarshal(bank_card, BankCard)}; unmarshal(payment_tool, [2, TerminalType]) -> {payment_terminal, #domain_PaymentTerminal{ - terminal_type = unmarshal(terminal_type, TerminalType) + terminal_type = unmarshal({payment_terminal, type}, TerminalType) }}; unmarshal(payment_tool, [1, ?legacy_bank_card(Token, PaymentSystem, Bin, MaskedPan)]) -> {bank_card, #domain_BankCard{ token = unmarshal(str, Token), - payment_system = unmarshal(payment_system, PaymentSystem), + payment_system = unmarshal({bank_card, payment_system}, PaymentSystem), bin = unmarshal(str, Bin), masked_pan = unmarshal(str, MaskedPan) }}; -unmarshal(payment_system, <<"visa">>) -> +unmarshal(payment_method, <<"card">>) -> + bank_card; +unmarshal(payment_method, <<"payterm">>) -> + payment_terminal; +unmarshal(payment_method, <<"wallet">>) -> + digital_wallet; + +unmarshal({bank_card, payment_system}, <<"visa">>) -> visa; -unmarshal(payment_system, <<"mastercard">>) -> +unmarshal({bank_card, payment_system}, <<"mastercard">>) -> mastercard; -unmarshal(payment_system, <<"visaelectron">>) -> +unmarshal({bank_card, payment_system}, <<"visaelectron">>) -> visaelectron; -unmarshal(payment_system, <<"maestro">>) -> +unmarshal({bank_card, payment_system}, <<"maestro">>) -> maestro; -unmarshal(payment_system, <<"forbrugsforeningen">>) -> +unmarshal({bank_card, payment_system}, <<"forbrugsforeningen">>) -> forbrugsforeningen; -unmarshal(payment_system, <<"dankort">>) -> +unmarshal({bank_card, payment_system}, <<"dankort">>) -> dankort; -unmarshal(payment_system, <<"amex">>) -> +unmarshal({bank_card, payment_system}, <<"amex">>) -> amex; -unmarshal(payment_system, <<"dinersclub">>) -> +unmarshal({bank_card, payment_system}, <<"dinersclub">>) -> dinersclub; -unmarshal(payment_system, <<"discover">>) -> +unmarshal({bank_card, payment_system}, <<"discover">>) -> discover; -unmarshal(payment_system, <<"unionpay">>) -> +unmarshal({bank_card, payment_system}, <<"unionpay">>) -> unionpay; -unmarshal(payment_system, <<"jcb">>) -> +unmarshal({bank_card, payment_system}, <<"jcb">>) -> jcb; -unmarshal(payment_system, <<"nspkmir">>) -> +unmarshal({bank_card, payment_system}, <<"nspkmir">>) -> nspkmir; -unmarshal(terminal_type, <<"euroset">>) -> +unmarshal({payment_terminal, type}, <<"euroset">>) -> euroset; -unmarshal(payment_system, PaymentSystem) when is_atom(PaymentSystem) -> - PaymentSystem; +unmarshal({digital_wallet, provider}, <<"qiwi">>) -> + qiwi; unmarshal(_, Other) -> - Other. \ No newline at end of file + Other. + +-ifdef(TEST). + +-include_lib("eunit/include/eunit.hrl"). + +-spec test() -> _. + +-type testcase() :: {_, fun()}. + +-spec legacy_unmarshalling_test_() -> [testcase()]. +legacy_unmarshalling_test_() -> + PT1 = {bank_card, #domain_BankCard{ + token = <<"abcdefabcdefabcdefabcdef">>, + payment_system = nspkmir, + bin = <<"22002201">>, + masked_pan = <<"11">> + }}, + PT2 = {payment_terminal, #domain_PaymentTerminal{ + terminal_type = euroset + }}, + [ + ?_assertEqual(PT1, unmarshal(legacy_marshal(2, PT1))), + ?_assertEqual(PT2, unmarshal(legacy_marshal(2, PT2))) + ]. + +legacy_marshal(_Vsn = 2, {bank_card, #domain_BankCard{} = BankCard}) -> + [2, #{ + <<"token">> => marshal(str, BankCard#domain_BankCard.token), + <<"payment_system">> => marshal({bank_card, payment_system}, BankCard#domain_BankCard.payment_system), + <<"bin">> => marshal(str, BankCard#domain_BankCard.bin), + <<"masked_pan">> => marshal(str, BankCard#domain_BankCard.masked_pan) + }]; +legacy_marshal(_Vsn = 2, {payment_terminal, #domain_PaymentTerminal{terminal_type = TerminalType}}) -> + [2, marshal({payment_terminal, type}, TerminalType)]. + +-endif. diff --git a/apps/hellgate/test/hg_ct_domain.hrl b/apps/hellgate/test/hg_ct_domain.hrl index ed22b8d..c5c4bb3 100644 --- a/apps/hellgate/test/hg_ct_domain.hrl +++ b/apps/hellgate/test/hg_ct_domain.hrl @@ -32,6 +32,8 @@ }}}). -define(share(P, Q, C), {share, #domain_CashVolumeShare{parts = #'Rational'{p = P, q = Q}, 'of' = C}}). +-define(share_with_rounding_method(P, Q, C, RM), {share, #domain_CashVolumeShare{parts = #'Rational'{p = P, q = Q}, 'of' = C, 'rounding_method' = RM}}). + -define(cfpost(A1, A2, V), #domain_CashFlowPosting{ source = A1, diff --git a/apps/hellgate/test/hg_ct_helper.erl b/apps/hellgate/test/hg_ct_helper.erl index dd0b290..1e1bedb 100644 --- a/apps/hellgate/test/hg_ct_helper.erl +++ b/apps/hellgate/test/hg_ct_helper.erl @@ -47,13 +47,10 @@ -export([make_invoice_details/1]). -export([make_invoice_details/2]). +-export([make_disposable_payment_resource/1]). -export([make_customer_params/3]). --export([make_customer_binding_params/0]). +-export([make_customer_binding_params/1]). --export([bank_card_simple_token/0]). --export([make_bad_payment_tool/0]). --export([is_bad_payment_tool/1]). --export([make_disposable_payment_resource/0]). -export([make_meta_ns/0]). -export([make_meta_data/0]). -export([make_meta_data/1]). @@ -583,46 +580,13 @@ make_shop_details(Name, Description) -> description = Description }. --spec bank_card_simple_token() -> string(). +-spec make_disposable_payment_resource({dmsl_domain_thrift:'PaymentTool'(), dmsl_domain_thrift:'SessionID'()}) -> + hg_domain_thrift:'DisposablePaymentResource'(). -bank_card_simple_token() -> - <<"TOKEN42">>. - --spec make_bad_payment_tool() -> {hg_domain_thrift:'PaymentTool'(), hg_domain_thrift:'PaymentSessionID'()}. - -make_bad_payment_tool() -> - { - {bank_card, #domain_BankCard{ - token = bank_card_simple_token(), - payment_system = visa, - bin = <<"000000">>, - masked_pan = <<"0000">> - }}, - <<"SESSION00">> - }. - --spec is_bad_payment_tool(hg_domain_thrift:'PaymentTool'()) -> boolean(). - -is_bad_payment_tool(PaymentTool) -> - {BadPaymentTool, _} = make_bad_payment_tool(), - BadPaymentTool =:= PaymentTool. - --spec make_disposable_payment_resource() -> - { - hg_domain_thrift:'PaymentTool'(), - hg_domain_thrift:'PaymentSessionID'(), - hg_domain_thrift:'ClientInfo'() - }. - -make_disposable_payment_resource() -> +make_disposable_payment_resource({PaymentTool, SessionID}) -> #domain_DisposablePaymentResource{ - payment_tool = {bank_card, #domain_BankCard{ - token = bank_card_simple_token(), - payment_system = visa, - bin = <<"424242">>, - masked_pan = <<"4242">> - }}, - payment_session_id = <<"SESSION42">>, + payment_tool = PaymentTool, + payment_session_id = SessionID, client_info = #domain_ClientInfo{} }. @@ -660,11 +624,12 @@ make_customer_params(PartyID, ShopID, EMail) -> metadata = ?null() }. --spec make_customer_binding_params() -> dmsl_payment_processing_thrift:'CustomerBindingParams'(). +-spec make_customer_binding_params({dmsl_domain_thrift:'PaymentTool'(), dmsl_domain_thrift:'SessionID'()}) -> + dmsl_payment_processing_thrift:'CustomerBindingParams'(). -make_customer_binding_params() -> +make_customer_binding_params(PaymentToolSession) -> #payproc_CustomerBindingParams{ - payment_resource = make_disposable_payment_resource() + payment_resource = make_disposable_payment_resource(PaymentToolSession) }. %% diff --git a/apps/hellgate/test/hg_customer_tests_SUITE.erl b/apps/hellgate/test/hg_customer_tests_SUITE.erl index ef5faae..acc1460 100644 --- a/apps/hellgate/test/hg_customer_tests_SUITE.erl +++ b/apps/hellgate/test/hg_customer_tests_SUITE.erl @@ -230,10 +230,8 @@ start_binding_w_failure(C) -> CustomerParams = hg_ct_helper:make_customer_params(PartyID, ShopID, cfg(test_case_name, C)), Customer = hg_client_customer:create(CustomerParams, Client), #payproc_Customer{id = CustomerID} = Customer, - {PaymentTool, Session} = hg_ct_helper:make_bad_payment_tool(), - CustomerBindingParams = #payproc_CustomerBindingParams{ - payment_resource = make_disposable_payment_resource(PaymentTool, Session) - }, + CustomerBindingParams = + hg_ct_helper:make_customer_binding_params(hg_dummy_provider:make_payment_tool(forbidden)), CustomerBinding = hg_client_customer:start_binding(CustomerID, CustomerBindingParams, Client), Customer1 = hg_client_customer:get(CustomerID, Client), #payproc_Customer{id = CustomerID, bindings = Bindings} = Customer1, @@ -255,7 +253,8 @@ start_binding(C) -> CustomerParams = hg_ct_helper:make_customer_params(PartyID, ShopID, cfg(test_case_name, C)), Customer = hg_client_customer:create(CustomerParams, Client), #payproc_Customer{id = CustomerID} = Customer, - CustomerBindingParams = hg_ct_helper:make_customer_binding_params(), + CustomerBindingParams = + hg_ct_helper:make_customer_binding_params(hg_dummy_provider:make_payment_tool(no_preauth)), CustomerBinding = hg_client_customer:start_binding(CustomerID, CustomerBindingParams, Client), Customer1 = hg_client_customer:get(CustomerID, Client), #payproc_Customer{id = CustomerID, bindings = Bindings} = Customer1, @@ -278,10 +277,8 @@ start_binding_w_tds(C) -> CustomerParams = hg_ct_helper:make_customer_params(PartyID, ShopID, cfg(test_case_name, C)), Customer = hg_client_customer:create(CustomerParams, Client), #payproc_Customer{id = CustomerID} = Customer, - {PaymentTool, Session} = hg_dummy_provider:make_payment_tool(preauth_3ds), - CustomerBindingParams = #payproc_CustomerBindingParams{ - payment_resource = make_disposable_payment_resource(PaymentTool, Session) - }, + CustomerBindingParams = + hg_ct_helper:make_customer_binding_params(hg_dummy_provider:make_payment_tool(preauth_3ds)), CustomerBinding = hg_client_customer:start_binding(CustomerID, CustomerBindingParams, Client), Customer1 = hg_client_customer:get(CustomerID, Client), #payproc_Customer{id = CustomerID, bindings = Bindings} = Customer1, @@ -308,7 +305,8 @@ start_two_bindings(C) -> ShopID = cfg(shop_id, C), CustomerParams = hg_ct_helper:make_customer_params(PartyID, ShopID, cfg(test_case_name, C)), #payproc_Customer{id = CustomerID} = hg_client_customer:create(CustomerParams, Client), - CustomerBindingParams = hg_ct_helper:make_customer_binding_params(), + CustomerBindingParams = + hg_ct_helper:make_customer_binding_params(hg_dummy_provider:make_payment_tool(no_preauth)), CustomerBinding1 = #payproc_CustomerBinding{id = CustomerBindingID1} = hg_client_customer:start_binding(CustomerID, CustomerBindingParams, Client), CustomerBinding2 = #payproc_CustomerBinding{id = CustomerBindingID2} = @@ -392,7 +390,8 @@ start_binding_not_permitted(C) -> CustomerParams = hg_ct_helper:make_customer_params(PartyID, ShopID, cfg(test_case_name, C)), #payproc_Customer{id = CustomerID} = hg_client_customer:create(CustomerParams, Client), ok = hg_domain:upsert(construct_domain_fixture(construct_simple_term_set())), - CustomerBindingParams = hg_ct_helper:make_customer_binding_params(), + CustomerBindingParams = + hg_ct_helper:make_customer_binding_params(hg_dummy_provider:make_payment_tool(no_preauth)), {exception, #payproc_OperationNotPermitted{}} = hg_client_customer:start_binding(CustomerID, CustomerBindingParams, Client). diff --git a/apps/hellgate/test/hg_dummy_provider.erl b/apps/hellgate/test/hg_dummy_provider.erl index 0fa5e23..2ae5924 100644 --- a/apps/hellgate/test/hg_dummy_provider.erl +++ b/apps/hellgate/test/hg_dummy_provider.erl @@ -133,27 +133,17 @@ handle_function( % Recurrent tokens % -generate_token( - undefined, - #prxprv_RecurrentTokenInfo{ - payment_tool = #prxprv_RecurrentPaymentTool{ - payment_resource = #domain_DisposablePaymentResource{ - payment_tool = PaymentTool - } - } - }, - _Opts -) -> - case hg_ct_helper:is_bad_payment_tool(PaymentTool) of - true -> +generate_token(undefined, #prxprv_RecurrentTokenInfo{payment_tool = RecurrentPaytool}, _Opts) -> + case get_recurrent_paytool_scenario(RecurrentPaytool) of + forbidden -> #prxprv_RecurrentTokenProxyResult{ - intent = ?recurrent_token_finish_w_failure(#'Failure'{code = <<"badparams">>}) + intent = ?recurrent_token_finish_w_failure(#'Failure'{code = <<"forbidden">>}) }; - false -> + _ -> token_sleep(1, <<"sleeping">>) end; -generate_token(<<"sleeping">>, #prxprv_RecurrentTokenInfo{payment_tool = PaymentTool}, _Opts) -> - case get_recurrent_paytool_scenario(PaymentTool) of +generate_token(<<"sleeping">>, #prxprv_RecurrentTokenInfo{payment_tool = RecurrentPaytool}, _Opts) -> + case get_recurrent_paytool_scenario(RecurrentPaytool) of preauth_3ds -> Tag = generate_recurent_tag(), Uri = get_callback_url(), @@ -245,6 +235,9 @@ process_payment(?processed(), undefined, PaymentInfo, _) -> due = get_invoice_due_date(PaymentInfo) }}, suspend(SPID, 2, <<"suspended">>, UserInteraction); + digital_wallet -> + %% simple workflow + sleep(1, <<"sleeping">>); recurrent -> %% simple workflow without 3DS sleep(1, <<"sleeping">>) @@ -343,30 +336,43 @@ get_recurrent_paytool_scenario(#prxprv_RecurrentPaymentTool{payment_resource = P PaymentTool = get_payment_tool(PaymentResource), get_payment_tool_scenario(PaymentTool). +get_payment_tool_scenario({'bank_card', #domain_BankCard{token = <<"no_preauth">>}}) -> + no_preauth; get_payment_tool_scenario({'bank_card', #domain_BankCard{token = <<"preauth_3ds">>}}) -> preauth_3ds; get_payment_tool_scenario({'bank_card', #domain_BankCard{token = <<"preauth_3ds_offsite">>}}) -> preauth_3ds_offsite; -get_payment_tool_scenario({'bank_card', _}) -> - no_preauth; +get_payment_tool_scenario({'bank_card', #domain_BankCard{token = <<"forbidden">>}}) -> + forbidden; get_payment_tool_scenario({'payment_terminal', #domain_PaymentTerminal{terminal_type = euroset}}) -> - terminal. + terminal; +get_payment_tool_scenario({'digital_wallet', #domain_DigitalWallet{provider = qiwi}}) -> + digital_wallet. -spec make_payment_tool(atom()) -> {hg_domain_thrift:'PaymentTool'(), hg_domain_thrift:'PaymentSessionID'()}. +make_payment_tool(no_preauth) -> + make_simple_payment_tool(<<"no_preauth">>, visa); make_payment_tool(preauth_3ds) -> - construct_payment_tool_and_session(<<"preauth_3ds">>, visa, <<"666666">>, <<"666">>, <<"SESSION666">>); + make_simple_payment_tool(<<"preauth_3ds">>, visa); make_payment_tool(preauth_3ds_offsite) -> make_simple_payment_tool(<<"preauth_3ds_offsite">>, jcb); -make_payment_tool(no_preauth) -> - Token = <<"no_preauth">>, - make_simple_payment_tool(Token, visa); +make_payment_tool(forbidden) -> + make_simple_payment_tool(<<"forbidden">>, visa); make_payment_tool(terminal) -> { {payment_terminal, #domain_PaymentTerminal{ terminal_type = euroset }}, - <<"">> + <<>> + }; +make_payment_tool(digital_wallet) -> + { + {digital_wallet, #domain_DigitalWallet{ + provider = qiwi, + id = <<"+79876543210">> + }}, + <<>> }. make_simple_payment_tool(Token, PaymentSystem) -> diff --git a/apps/hellgate/test/hg_invoice_template_tests_SUITE.erl b/apps/hellgate/test/hg_invoice_template_tests_SUITE.erl index 3c6cf16..5da5bd8 100644 --- a/apps/hellgate/test/hg_invoice_template_tests_SUITE.erl +++ b/apps/hellgate/test/hg_invoice_template_tests_SUITE.erl @@ -537,6 +537,7 @@ construct_domain_fixture() -> hg_ct_fixture:construct_payment_method(?pmt(bank_card, visa)), hg_ct_fixture:construct_payment_method(?pmt(bank_card, mastercard)), hg_ct_fixture:construct_payment_method(?pmt(payment_terminal, euroset)), + hg_ct_fixture:construct_payment_method(?pmt(digital_wallet, qiwi)), {payment_institution, #domain_PaymentInstitutionObject{ ref = ?pinst(1), diff --git a/apps/hellgate/test/hg_invoice_tests_SUITE.erl b/apps/hellgate/test/hg_invoice_tests_SUITE.erl index 7086084..131e87d 100644 --- a/apps/hellgate/test/hg_invoice_tests_SUITE.erl +++ b/apps/hellgate/test/hg_invoice_tests_SUITE.erl @@ -32,6 +32,7 @@ -export([payment_success/1]). -export([payment_w_terminal_success/1]). +-export([payment_w_wallet_success/1]). -export([payment_w_customer_success/1]). -export([payment_w_incorrect_customer/1]). -export([payment_w_deleted_customer/1]). @@ -48,6 +49,7 @@ -export([payment_hold_capturing/1]). -export([payment_hold_auto_capturing/1]). -export([payment_refund_success/1]). +-export([rounding_cashflow_volume/1]). -export([payment_with_offsite_preauth_success/1]). -export([payment_with_offsite_preauth_failed/1]). -export([terms_retrieval/1]). @@ -93,6 +95,7 @@ all() -> invalid_payment_amount, payment_success, payment_w_terminal_success, + payment_w_wallet_success, payment_w_customer_success, payment_w_incorrect_customer, payment_w_deleted_customer, @@ -112,6 +115,7 @@ all() -> payment_refund_success, + rounding_cashflow_volume, {group, offsite_preauth_payment}, terms_retrieval, @@ -225,9 +229,15 @@ end_per_group(_Group, _C) -> -spec init_per_testcase(test_case_name(), config()) -> config(). -init_per_testcase(payment_adjustment_success, C) -> +init_per_testcase(Name, C) when Name == payment_adjustment_success; Name == rounding_cashflow_volume -> Revision = hg_domain:head(), - ok = hg_domain:upsert(get_adjustment_fixture(Revision)), + Fixture = case Name of + payment_adjustment_success -> + get_adjustment_fixture(Revision); + rounding_cashflow_volume -> + get_cashflow_rounding_fixture(Revision) + end, + ok = hg_domain:upsert(Fixture), [{original_domain_revision, Revision} | init_per_testcase(C)]; init_per_testcase(_Name, C) -> init_per_testcase(C). @@ -520,6 +530,19 @@ payment_w_terminal_success(C) -> [?payment_state(?payment_w_status(PaymentID, ?captured()))] ) = hg_client_invoicing:get(InvoiceID, Client). +-spec payment_w_wallet_success(config()) -> _ | no_return(). + +payment_w_wallet_success(C) -> + Client = cfg(client, C), + InvoiceID = start_invoice(<<"bubbleblob">>, make_due_date(10), 42000, C), + PaymentParams = make_wallet_payment_params(), + PaymentID = process_payment(InvoiceID, PaymentParams, Client), + PaymentID = await_payment_capture(InvoiceID, PaymentID, Client), + ?invoice_state( + ?invoice_w_status(?invoice_paid()), + [?payment_state(?payment_w_status(PaymentID, ?captured()))] + ) = hg_client_invoicing:get(InvoiceID, Client). + -spec payment_w_customer_success(config()) -> test_return(). payment_w_customer_success(C) -> @@ -979,6 +1002,93 @@ payment_hold_auto_capturing(C) -> _ = assert_invalid_post_request(get_post_request(UserInteraction)), PaymentID = await_payment_capture(InvoiceID, PaymentID, undefined, Client). +-spec rounding_cashflow_volume(config()) -> _ | no_return(). + +rounding_cashflow_volume(C) -> + Client = cfg(client, C), + InvoiceID = start_invoice(<<"rubberduck">>, make_due_date(10), 100000, C), + PaymentParams = make_payment_params(), + ?payment_state(?payment(PaymentID)) = hg_client_invoicing:start_payment(InvoiceID, PaymentParams, Client), + [ + ?payment_ev(PaymentID, ?payment_started(?payment_w_status(?pending()), _, _, CF)), + ?payment_ev(PaymentID, ?session_ev(?processed(), ?session_started())) + ] = next_event(InvoiceID, Client), + ?cash(0, <<"RUB">>) = get_cashflow_volume({provider, settlement}, {merchant, settlement}, CF), + ?cash(1, <<"RUB">>) = get_cashflow_volume({system, settlement}, {provider, settlement}, CF), + ?cash(1, <<"RUB">>) = get_cashflow_volume({system, settlement}, {external, outcome}, CF). + +get_cashflow_rounding_fixture(Revision) -> + PaymentInstituition = hg_domain:get(Revision, {payment_institution, ?pinst(1)}), + [ + {payment_institution, #domain_PaymentInstitutionObject{ + ref = ?pinst(1), + data = PaymentInstituition#domain_PaymentInstitution{ + providers = {value, ?ordset([ + ?prv(100) + ])} + }} + }, + {provider, #domain_ProviderObject{ + ref = ?prv(100), + data = #domain_Provider{ + name = <<"Rounding">>, + description = <<>>, + abs_account = <<>>, + terminal = {value, [?trm(100)]}, + proxy = #domain_Proxy{ref = ?prx(1), additional = #{}}, + accounts = hg_ct_fixture:construct_provider_account_set([?cur(<<"RUB">>)]), + payment_terms = #domain_PaymentsProvisionTerms{ + currencies = {value, ?ordset([ + ?cur(<<"RUB">>) + ])}, + categories = {value, ?ordset([ + ?cat(1) + ])}, + cash_limit = {value, ?cashrng( + {inclusive, ?cash( 1000, <<"RUB">>)}, + {exclusive, ?cash(100000000, <<"RUB">>)} + )}, + payment_methods = {value, ?ordset([ + ?pmt(bank_card, visa) + ])}, + cash_flow = {value, [ + ?cfpost( + {provider, settlement}, + {merchant, settlement}, + ?share_with_rounding_method(1, 200000, payment_amount, round_half_towards_zero) + ), + ?cfpost( + {system, settlement}, + {provider, settlement}, + ?share_with_rounding_method(1, 200000, payment_amount, round_half_away_from_zero) + ), + ?cfpost( + {system, settlement}, + {external, outcome}, + ?share(1, 200000, payment_amount) + ) + ]} + } + } + }}, + {terminal, #domain_TerminalObject{ + ref = ?trm(100), + data = #domain_Terminal{ + name = <<"Rounding Terminal">>, + description = <<>>, + risk_coverage = low + } + }} + ]. + +get_cashflow_volume(Source, Destination, CF) -> + [Volume] = [V || #domain_FinalCashFlowPosting{ + source = #domain_FinalCashFlowAccount{account_type = S}, + destination = #domain_FinalCashFlowAccount{account_type = D}, + volume = V + } <- CF, S == Source, D == Destination], + Volume. + %% -spec terms_retrieval(config()) -> _ | no_return(). @@ -992,6 +1102,7 @@ terms_retrieval(C) -> ?pmt(bank_card, jcb), ?pmt(bank_card, mastercard), ?pmt(bank_card, visa), + ?pmt(digital_wallet, qiwi), ?pmt(payment_terminal, euroset) ]} }} = TermSet1, @@ -1188,6 +1299,10 @@ make_terminal_payment_params() -> {PaymentTool, Session} = hg_dummy_provider:make_payment_tool(terminal), make_payment_params(PaymentTool, Session, instant). +make_wallet_payment_params() -> + {PaymentTool, Session} = hg_dummy_provider:make_payment_tool(digital_wallet), + make_payment_params(PaymentTool, Session, instant). + make_tds_payment_params() -> make_tds_payment_params(instant). @@ -1350,7 +1465,11 @@ make_customer_w_rec_tool(PartyID, ShopID, Client) -> #payproc_Customer{id = CustomerID} = hg_client_customer:create(CustomerParams, Client), #payproc_CustomerBinding{id = BindingID} = - hg_client_customer:start_binding(CustomerID, hg_ct_helper:make_customer_binding_params(), Client), + hg_client_customer:start_binding( + CustomerID, + hg_ct_helper:make_customer_binding_params(hg_dummy_provider:make_payment_tool(no_preauth)), + Client + ), ok = wait_for_binding_success(CustomerID, BindingID, Client), CustomerID. @@ -1401,7 +1520,8 @@ construct_domain_fixture() -> ?pmt(bank_card, visa), ?pmt(bank_card, mastercard), ?pmt(bank_card, jcb), - ?pmt(payment_terminal, euroset) + ?pmt(payment_terminal, euroset), + ?pmt(digital_wallet, qiwi) ])} } ]}, @@ -1545,6 +1665,7 @@ construct_domain_fixture() -> hg_ct_fixture:construct_payment_method(?pmt(bank_card, mastercard)), hg_ct_fixture:construct_payment_method(?pmt(bank_card, jcb)), hg_ct_fixture:construct_payment_method(?pmt(payment_terminal, euroset)), + hg_ct_fixture:construct_payment_method(?pmt(digital_wallet, qiwi)), hg_ct_fixture:construct_proxy(?prx(1), <<"Dummy proxy">>), hg_ct_fixture:construct_proxy(?prx(2), <<"Inspector proxy">>), @@ -1695,6 +1816,7 @@ construct_domain_fixture() -> }] } }}, + {provider, #domain_ProviderObject{ ref = ?prv(1), data = #domain_Provider{ @@ -1729,7 +1851,9 @@ construct_domain_fixture() -> )}, cash_flow = {decisions, [ #domain_CashFlowDecision{ - if_ = {condition, {payment_tool, {bank_card, {payment_system_is, visa}}}}, + if_ = {condition, {payment_tool, {bank_card, #domain_BankCardCondition{ + definition = {payment_system_is, visa} + }}}}, then_ = {value, [ ?cfpost( {provider, settlement}, @@ -1744,7 +1868,9 @@ construct_domain_fixture() -> ]} }, #domain_CashFlowDecision{ - if_ = {condition, {payment_tool, {bank_card, {payment_system_is, mastercard}}}}, + if_ = {condition, {payment_tool, {bank_card, #domain_BankCardCondition{ + definition = {payment_system_is, mastercard} + }}}}, then_ = {value, [ ?cfpost( {provider, settlement}, @@ -1759,7 +1885,9 @@ construct_domain_fixture() -> ]} }, #domain_CashFlowDecision{ - if_ = {condition, {payment_tool, {bank_card, {payment_system_is, jcb}}}}, + if_ = {condition, {payment_tool, {bank_card, #domain_BankCardCondition{ + definition = {payment_system_is, jcb} + }}}}, then_ = {value, [ ?cfpost( {provider, settlement}, @@ -1777,7 +1905,9 @@ construct_domain_fixture() -> holds = #domain_PaymentHoldsProvisionTerms{ lifetime = {decisions, [ #domain_HoldLifetimeDecision{ - if_ = {condition, {payment_tool, {bank_card, {payment_system_is, visa}}}}, + if_ = {condition, {payment_tool, {bank_card, #domain_BankCardCondition{ + payment_system_is = visa + }}}}, then_ = {value, ?hold_lifetime(5)} } ]} @@ -1810,6 +1940,7 @@ construct_domain_fixture() -> risk_coverage = high } }}, + {provider, #domain_ProviderObject{ ref = ?prv(2), data = #domain_Provider{ @@ -1912,6 +2043,7 @@ construct_domain_fixture() -> risk_coverage = high } }}, + {provider, #domain_ProviderObject{ ref = ?prv(3), data = #domain_Provider{ @@ -1934,7 +2066,8 @@ construct_domain_fixture() -> ?cat(1) ])}, payment_methods = {value, ?ordset([ - ?pmt(payment_terminal, euroset) + ?pmt(payment_terminal, euroset), + ?pmt(digital_wallet, qiwi) ])}, cash_limit = {value, ?cashrng( {inclusive, ?cash( 1000, <<"RUB">>)}, @@ -1963,6 +2096,7 @@ construct_domain_fixture() -> risk_coverage = low } }} + ]. construct_term_set_for_cost(LowerBound, UpperBound) -> diff --git a/apps/hellgate/test/hg_recurrent_paytools_tests_SUITE.erl b/apps/hellgate/test/hg_recurrent_paytools_tests_SUITE.erl index d9a750c..46f1d7a 100644 --- a/apps/hellgate/test/hg_recurrent_paytools_tests_SUITE.erl +++ b/apps/hellgate/test/hg_recurrent_paytools_tests_SUITE.erl @@ -312,7 +312,7 @@ recurrent_paytool_creation_not_permitted(C) -> %% make_bad_recurrent_paytool_params(PartyID, ShopID) -> - {PaymentTool, Session} = hg_ct_helper:make_bad_payment_tool(), + {PaymentTool, Session} = hg_dummy_provider:make_payment_tool(forbidden), PaymentResource = make_disposable_payment_resource(PaymentTool, Session), #payproc_RecurrentPaymentToolParams{ party_id = PartyID, diff --git a/docker-compose.sh b/docker-compose.sh index f455872..a0cc4cd 100755 --- a/docker-compose.sh +++ b/docker-compose.sh @@ -17,7 +17,7 @@ services: condition: service_healthy dominant: - image: dr.rbkmoney.com/rbkmoney/dominant:3a58d9d20c6229002d8744bbd1745869fe5695f8 + image: dr.rbkmoney.com/rbkmoney/dominant:68d75c0d8523a150a68852de4572ad1c1ee140ef command: /opt/dominant/bin/dominant foreground depends_on: machinegun: @@ -32,7 +32,7 @@ services: test: "curl http://localhost:8022/" interval: 5s timeout: 1s - retries: 12 + retries: 20 shumway: image: dr.rbkmoney.com/rbkmoney/shumway:7a5f95ee1e8baa42fdee9c08cc0ae96cd7187d55 @@ -51,7 +51,7 @@ services: test: "curl http://localhost:8022/" interval: 5s timeout: 1s - retries: 12 + retries: 20 shumway-db: image: dr.rbkmoney.com/rbkmoney/postgres:9.6 diff --git a/rebar.lock b/rebar.lock index 0fd8cbc..9bf0243 100644 --- a/rebar.lock +++ b/rebar.lock @@ -4,7 +4,7 @@ {<<"cowlib">>,{pkg,<<"cowlib">>,<<"1.0.2">>},2}, {<<"dmsl">>, {git,"git@github.com:rbkmoney/damsel.git", - {ref,"07fbb3057e78e37611e642160a7201fe31d6ff69"}}, + {ref,"4ca2329c564b3730dbd742ae370be9919905b9de"}}, 0}, {<<"dmt_client">>, {git,"git@github.com:rbkmoney/dmt_client.git", @@ -16,7 +16,7 @@ 1}, {<<"genlib">>, {git,"https://github.com/rbkmoney/genlib.git", - {ref,"7fc1ca1a57dbe2b8b837951095e314c32afd6c9a"}}, + {ref,"3e5690def4297e9c5d00ace6ae9995ea9fac525e"}}, 0}, {<<"goldrush">>,{pkg,<<"goldrush">>,<<"0.1.8">>},1}, {<<"gproc">>,{pkg,<<"gproc">>,<<"0.6.1">>},0},