CAPI-324: Balance handle (#303)

This commit is contained in:
Артем 2018-12-27 18:00:28 +03:00 committed by GitHub
parent a27c4880e2
commit 0d9f6180ec
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 313 additions and 8 deletions

View File

@ -320,7 +320,11 @@ get_operation_access('GetPaymentInstitutionPayoutMethods', _) ->
get_operation_access('GetPaymentInstitutionPayoutSchedules', _) ->
[{[party], read}];
get_operation_access('GetLocationsNames' , _) ->
[].
[];
get_operation_access('CreatePayout' , _) ->
[{[payouts], write}];
get_operation_access('GetPayout' , _) ->
[{[payouts], read}].
-spec get_resource_hierarchy() -> #{atom() => map()}.
@ -329,7 +333,8 @@ get_resource_hierarchy() ->
party => #{invoice_templates => #{invoice_template_invoices => #{}}},
customers => #{bindings => #{}},
invoices => #{payments => #{}},
payment_resources => #{}
payment_resources => #{},
payouts => #{}
}.
-spec get_consumer(claims()) ->

View File

@ -0,0 +1,74 @@
%%%
%%% Msgpack manipulation employed by dmsl interfaces.
%%% Copy from machinery
-module(capi_msgpack).
-include_lib("dmsl/include/dmsl_msgpack_thrift.hrl").
%% API
-export([wrap/1]).
-export([unwrap/1]).
-type t() :: dmsl_msgpack_thrift:'Value'().
-export_type([t/0]).
%%
-spec wrap
(null ) -> t();
(boolean() ) -> t();
(integer() ) -> t();
(float() ) -> t();
(binary() ) -> t(); %% string
({binary, binary()}) -> t(); %% binary
([t()] ) -> t();
(#{t() => t()} ) -> t().
wrap(null) ->
{nl, #msgpack_Nil{}};
wrap(V) when is_boolean(V) ->
{b, V};
wrap(V) when is_integer(V) ->
{i, V};
wrap(V) when is_float(V) ->
V;
wrap(V) when is_binary(V) ->
% Assuming well-formed UTF-8 bytestring.
{str, V};
wrap({binary, V}) when is_binary(V) ->
{bin, V};
wrap(V) when is_list(V) ->
{arr, [wrap(ListItem) || ListItem <- V]};
wrap(V) when is_map(V) ->
{obj, maps:fold(fun(Key, Value, Map) -> Map#{wrap(Key) => wrap(Value)} end, #{}, V)}.
-spec unwrap(t()) ->
null |
boolean() |
integer() |
float() |
binary() | %% string
{binary, binary()} | %% binary
[t()] |
#{t() => t()} .
unwrap({nl, #msgpack_Nil{}}) ->
null;
unwrap({b, V}) when is_boolean(V) ->
V;
unwrap({i, V}) when is_integer(V) ->
V;
unwrap({flt, V}) when is_float(V) ->
V;
unwrap({str, V}) when is_binary(V) ->
% Assuming well-formed UTF-8 bytestring.
V;
unwrap({bin, V}) when is_binary(V) ->
{binary, V};
unwrap({arr, V}) when is_list(V) ->
[unwrap(ListItem) || ListItem <- V];
unwrap({obj, V}) when is_map(V) ->
maps:fold(fun(Key, Value, Map) -> Map#{unwrap(Key) => unwrap(Value)} end, #{}, V).

View File

@ -9,6 +9,7 @@
-include_lib("dmsl/include/dmsl_geo_ip_thrift.hrl").
-include_lib("dmsl/include/dmsl_reporting_thrift.hrl").
-include_lib("dmsl/include/dmsl_payment_tool_provider_thrift.hrl").
-include_lib("dmsl/include/dmsl_payout_processing_thrift.hrl").
-include_lib("binbase_proto/include/binbase_binbase_thrift.hrl").
@ -1312,6 +1313,31 @@ process_request('GetCustomerEvents', Req, Context) ->
#'InvalidRequest'{errors = Errors} ->
{ok, {400, [], logic_error(invalidRequest, format_request_errors(Errors))}}
end
end;
process_request('GetPayout', Req, Context) ->
PayoutID = maps:get(payoutID, Req),
case service_call({payouts, 'Get', [PayoutID]}, Context) of
{ok, Payout} ->
{ok, {200, [], decode_payout_proc_payout(Payout)}};
{exception, #'payout_processing_PayoutNotFound'{}} ->
{ok, {404, [], general_error(<<"Payout not found">>)}}
end;
process_request('CreatePayout', Req, Context) ->
CreateRequest = encode_payout_proc_payout_params(get_party_id(Context), maps:get('PayoutParams', Req)),
case service_call({payouts, 'CreatePayout', [CreateRequest]}, Context) of
{ok, Payout} ->
{ok, {201, [], decode_payout_proc_payout(Payout)}};
{exception, Exception} ->
case Exception of
#'payout_processing_InvalidPayoutTool'{} ->
{ok, {400, [], logic_error(invalidPayoutTool, <<"Invalid payout tool">>)}};
#'payout_processing_InsufficientFunds'{} ->
{ok, {400, [], logic_error(invalidCash, <<"Invalid amount or currency">>)}};
#'InvalidRequest'{errors = Errors} ->
{ok, {400, [], logic_error(invalidRequest, format_request_errors(Errors))}}
end
end.
check_payment_institution(Realm, Residence, PaymentInstitution) ->
@ -2577,6 +2603,9 @@ merchstat_to_domain({bank_account, {russian_payout_account, PayoutAccount}}) ->
merchstat_to_domain({bank_account, {international_payout_account, PayoutAccount}}) ->
#merchstat_InternationalPayoutAccount{bank_account = BankAccount} = PayoutAccount,
{international_bank_account, merchstat_to_domain({international_bank_account, BankAccount})};
merchstat_to_domain({wallet, #merchstat_Wallet{wallet_id = WalletID}}) ->
{wallet_info, #domain_WalletInfo{wallet_id = WalletID}};
merchstat_to_domain({international_bank_account, undefined}) ->
undefined;
merchstat_to_domain({international_bank_account, BankAccount = #merchstat_InternationalBankAccount{}}) ->
@ -4363,3 +4392,82 @@ decode_optional(Arg, DecodeFun) when Arg /= undefined ->
DecodeFun(Arg);
decode_optional(undefined, _) ->
undefined.
encode_msgpack_value(_Key, Value) ->
capi_msgpack:wrap(Value).
encode_payout_proc_metadata(undefined) ->
undefined;
encode_payout_proc_metadata(Data) ->
maps:map(fun encode_msgpack_value/2, Data).
encode_payout_proc_payout_params(PartyID, PayoutParams) ->
#'payout_processing_PayoutParams'{
payout_id = maps:get(<<"id">>, PayoutParams),
shop = #'payout_processing_ShopParams'{
party_id = PartyID,
shop_id = maps:get(<<"shopID">>, PayoutParams)
},
payout_tool_id = maps:get(<<"payoutToolID">>, PayoutParams),
amount = encode_cash(maps:get(<<"amount">>, PayoutParams), maps:get(<<"currency">>, PayoutParams)),
metadata = encode_payout_proc_metadata(maps:get(<<"metadata">>, PayoutParams, undefined))
}.
decode_payout_proc_payout(Payout) ->
merge_and_compact(#{
<<"id" >> => Payout#payout_processing_Payout.id,
<<"shopID" >> => Payout#payout_processing_Payout.shop_id,
<<"createdAt" >> => Payout#payout_processing_Payout.created_at,
<<"amount" >> => Payout#payout_processing_Payout.amount,
<<"fee" >> => Payout#payout_processing_Payout.fee,
<<"currency" >> => decode_currency(Payout#payout_processing_Payout.currency),
<<"payoutToolDetails">> => decode_payout_proc_payout_tool_details(Payout#payout_processing_Payout.type),
<<"payoutSummary" >> => decode_payout_proc_payout_summary(Payout#payout_processing_Payout.summary),
<<"metadata" >> => decode_payout_proc_metadata(Payout#payout_processing_Payout.metadata)
}, decode_payout_proc_payout_status(Payout#payout_processing_Payout.status)).
decode_payout_proc_payout_status({cancelled, #payout_processing_PayoutCancelled{details = Details}}) ->
#{
<<"status" >> => <<"cancelled">>,
<<"cancellationDetails">> => genlib:to_binary(Details)
};
decode_payout_proc_payout_status({Status, _}) ->
#{
<<"status">> => genlib:to_binary(Status)
}.
decode_payout_proc_payout_tool_details(PayoutType) ->
decode_payout_tool_details(payout_proc_to_domain(PayoutType)).
payout_proc_to_domain({bank_account, {russian_payout_account, PayoutAccount}}) ->
#payout_processing_RussianPayoutAccount{bank_account = BankAccount} = PayoutAccount,
{russian_bank_account, BankAccount};
payout_proc_to_domain({bank_account, {international_payout_account, PayoutAccount}}) ->
#payout_processing_InternationalPayoutAccount{bank_account = BankAccount} = PayoutAccount,
{international_bank_account, BankAccount};
payout_proc_to_domain({wallet, #payout_processing_Wallet{wallet_id = WalletID}}) ->
{wallet_info, #domain_WalletInfo{wallet_id = WalletID}}.
decode_payout_proc_payout_summary(PayoutSummary) when is_list(PayoutSummary) ->
[decode_payout_proc_payout_summary_item(PayoutSummaryItem) || PayoutSummaryItem <- PayoutSummary];
decode_payout_proc_payout_summary(undefined) ->
undefined.
decode_payout_proc_payout_summary_item(PayoutSummary) ->
genlib_map:compact(#{
<<"amount" >> => PayoutSummary#payout_processing_PayoutSummaryItem.amount,
<<"fee" >> => PayoutSummary#payout_processing_PayoutSummaryItem.fee,
<<"currency">> => PayoutSummary#payout_processing_PayoutSummaryItem.currency_symbolic_code,
<<"count" >> => PayoutSummary#payout_processing_PayoutSummaryItem.count,
<<"fromTime">> => PayoutSummary#payout_processing_PayoutSummaryItem.from_time,
<<"toTime" >> => PayoutSummary#payout_processing_PayoutSummaryItem.to_time,
<<"type" >> => genlib:to_binary(PayoutSummary#payout_processing_PayoutSummaryItem.operation_type)
}).
decode_payout_proc_metadata(undefined) ->
undefined;
decode_payout_proc_metadata(Data) ->
maps:map(fun decode_msgpack_value/2, Data).
decode_msgpack_value(_Key, Value) ->
capi_msgpack:unwrap(Value).

View File

@ -3,6 +3,7 @@
-define(USD, <<"USD">>).
-define(BANKID_RU, <<"PUTIN">>).
-define(BANKID_US, <<"TRAMP">>).
-define(WALLET_TOOL, <<"TOOL">>).
-define(JSON, <<"{}">>).
-define(INTEGER, 10000).
-define(INTEGER_BINARY, <<"10000">>).
@ -195,7 +196,11 @@
status = {active, #domain_ContractActive{}},
terms = #domain_TermSetHierarchyRef{id = ?INTEGER},
adjustments = [?CONTRACT_ADJUSTMENT],
payout_tools = [?PAYOUT_TOOL(?BANKID_RU, ?RUSSIAN_BANK_ACCOUNT), ?PAYOUT_TOOL(?BANKID_US, ?INTERNATIONAL_BANK_ACCOUNT)]
payout_tools = [
?PAYOUT_TOOL(?BANKID_RU, ?RUSSIAN_BANK_ACCOUNT),
?PAYOUT_TOOL(?BANKID_US, ?INTERNATIONAL_BANK_ACCOUNT),
?PAYOUT_TOOL(?WALLET_TOOL, ?WALLET_INFO)
]
}).
-define(CONTRACTOR, {registered_user, #domain_RegisteredUser{email = ?STRING}}).
@ -462,6 +467,10 @@
aba_rtn = <<"129131673">>
}).
-define(WALLET_INFO, {wallet_info, #domain_WalletInfo{
wallet_id = ?STRING
}}).
-define(WEBHOOK, #webhooker_Webhook{
id = ?INTEGER,
party_id = ?STRING,
@ -567,6 +576,7 @@
-define(STAT_RESPONSE_PAYOUTS, ?STAT_RESPONSE({payouts,
[
?STAT_PAYOUT({wallet, #merchstat_Wallet{wallet_id = ?STRING}}, []),
?STAT_PAYOUT({bank_card, #merchstat_PayoutCard{card = ?STAT_BANK_CARD}}, [?PAYOUT_SUMMARY_ITEM]),
?STAT_PAYOUT({bank_card, #merchstat_PayoutCard{card = ?STAT_BANK_CARD_WITH_TP}}, [?PAYOUT_SUMMARY_ITEM]),
?STAT_PAYOUT({bank_account, ?STAT_PAYOUT_BANK_ACCOUNT_RUS}, undefined),
@ -935,3 +945,41 @@
},
version = ?INTEGER
}).
-define(PAYOUT(Type, PayoutSummary), #'payout_processing_Payout'{
id = ?STRING,
party_id = ?STRING,
shop_id = ?STRING,
contract_id = ?STRING,
created_at = ?TIMESTAMP,
status = {paid, #'payout_processing_PayoutPaid'{}},
amount = ?INTEGER,
fee = ?INTEGER,
currency = #domain_CurrencyRef{
symbolic_code = ?RUB
},
payout_flow = [],
type = Type,
summary = PayoutSummary,
metadata = #{
<<"someKey">> => {str, <<"someBinary">>},
<<"someInt">> => {i, 5},
<<"someList">> => {arr, [{str, <<"list_1">>}, {str, <<"list_2">>}]},
<<"someMap">> => {obj, #{{str, <<"someKey">>} => {i, 123}}},
<<"someNil">> => {nl, #msgpack_Nil{}}
}
}).
-define(WALLET_PAYOUT_TYPE, {wallet, #payout_processing_Wallet{
wallet_id = ?STRING
}}).
-define(PAYOUT_PROC_PAYOUT_SUMMARY_ITEM, #payout_processing_PayoutSummaryItem{
amount = ?INTEGER,
fee = ?INTEGER,
currency_symbolic_code = ?RUB,
from_time = ?TIMESTAMP,
to_time = ?TIMESTAMP,
operation_type = payment,
count = ?INTEGER
}).

View File

@ -11,6 +11,7 @@
-include_lib("dmsl/include/dmsl_merch_stat_thrift.hrl").
-include_lib("dmsl/include/dmsl_reporting_thrift.hrl").
-include_lib("dmsl/include/dmsl_payment_tool_provider_thrift.hrl").
-include_lib("dmsl/include/dmsl_payout_processing_thrift.hrl").
-include_lib("binbase_proto/include/binbase_binbase_thrift.hrl").
-include_lib("capi_dummy_data.hrl").
-include_lib("jose/include/jose_jwk.hrl").
@ -109,6 +110,9 @@
get_payout_tools_ok_test/1,
get_payout_tool_by_id/1,
create_payout/1,
get_payout/1,
create_webhook_ok_test/1,
get_webhooks/1,
get_webhook_by_id/1,
@ -275,6 +279,8 @@ groups() ->
get_contract_adjustment_by_id_ok_test,
get_payout_tools_ok_test,
get_payout_tool_by_id,
create_payout,
get_payout,
create_webhook_ok_test,
get_webhooks,
get_webhook_by_id,
@ -469,7 +475,9 @@ init_per_group(GroupName, Config) when
{[party], read},
{[invoices, payments], write},
{[invoices, payments], read},
{[customers], write}
{[customers], write},
{[payouts], write},
{[payouts], read}
],
{ok, Token} = issue_token(BasePermissions, unlimited),
{ok, Token2} = issue_token(BasePermissions, unlimited),
@ -534,7 +542,7 @@ woody_retry_test(Config) ->
}}
]),
{Time, ?badresp(503)} = timer:tc(capi_client_parties, get_my_party, [?config(context, Config)]),
true = (Time > 4000000) and (Time < 6000000).
true = (Time > 3000000) and (Time < 6000000).
-spec woody_unknown_test(config()) ->
_.
@ -1475,7 +1483,42 @@ get_payout_tools_ok_test(Config) ->
get_payout_tool_by_id(Config) ->
mock_services([{party_management, fun('GetContract', _) -> {ok, ?CONTRACT} end}], Config),
{ok, _} = capi_client_payouts:get_payout_tool_by_id(?config(context, Config), ?STRING, ?BANKID_RU),
{ok, _} = capi_client_payouts:get_payout_tool_by_id(?config(context, Config), ?STRING, ?BANKID_US).
{ok, _} = capi_client_payouts:get_payout_tool_by_id(?config(context, Config), ?STRING, ?BANKID_US),
{ok, _} = capi_client_payouts:get_payout_tool_by_id(?config(context, Config), ?STRING, ?WALLET_TOOL).
-spec create_payout(config()) ->
_.
create_payout(Config) ->
Payout = ?PAYOUT(?WALLET_PAYOUT_TYPE, []),
mock_services([{payouts, fun('CreatePayout', _) -> {ok, Payout} end}], Config),
Req = #{
<<"id">> => ?STRING,
<<"shopID">> => ?STRING,
<<"payoutToolID">> => ?WALLET_TOOL,
<<"amount">> => 2,
<<"currency">> => <<"RUB">>,
<<"metadata">> => #{
<<"payoutBinary">> => <<"sample data">>,
<<"payoutInt">> => 5,
<<"payoutList">> => [
<<"some_1">>,
<<"some_2">>
],
<<"payoutMap">> => #{
<<"someKey">> => 234
},
<<"how_about_null">> => null
}
},
{ok, _} = capi_client_payouts:create_payout(?config(context, Config), Req, ?STRING).
-spec get_payout(config()) ->
_.
get_payout(Config) ->
Payout = ?PAYOUT(?WALLET_PAYOUT_TYPE, [?PAYOUT_PROC_PAYOUT_SUMMARY_ITEM]),
mock_services([{payouts, fun('Get', _) -> {ok, Payout} end}], Config),
{ok, _} = capi_client_payouts:get_payout(?config(context, Config), ?STRING).
-spec create_webhook_ok_test(config()) ->
_.

View File

@ -3,6 +3,8 @@
-export([get_payout_tools/2]).
-export([get_payout_tool_by_id/3]).
-export([get_schedule_by_ref/2]).
-export([create_payout/3]).
-export([get_payout/2]).
-type context() :: capi_client_lib:context().
@ -39,3 +41,26 @@ get_schedule_by_ref(Context, ScheduleRef) ->
{Url, PreparedParams, Opts} = capi_client_lib:make_request(Context, Params),
Response = swag_client_payouts_api:get_schedule_by_ref(Url, PreparedParams, Opts),
capi_client_lib:handle_response(Response).
-spec create_payout(context(), map(), binary()) -> {ok, term()} | {error, term()}.
create_payout(Context, Request, PayoutID) ->
Params = #{
binding => #{
<<"payoutID">> => PayoutID
},
body => Request
},
{Url, PreparedParams, Opts} = capi_client_lib:make_request(Context, Params),
Response = swag_client_payouts_api:create_payout(Url, PreparedParams, Opts),
capi_client_lib:handle_response(Response).
-spec get_payout(context(), binary()) -> {ok, term()} | {error, term()}.
get_payout(Context, PayoutID) ->
Params = #{
binding => #{
<<"payoutID">> => PayoutID
}
},
{Url, PreparedParams, Opts} = capi_client_lib:make_request(Context, Params),
Response = swag_client_payouts_api:get_payout(Url, PreparedParams, Opts),
capi_client_lib:handle_response(Response).

View File

@ -78,6 +78,8 @@ get_service_modname(merchant_stat) ->
{dmsl_merch_stat_thrift, 'MerchantStatistics'};
get_service_modname(reporting) ->
{dmsl_reporting_thrift, 'Reporting'};
get_service_modname(payouts) ->
{dmsl_payout_processing_thrift, 'PayoutManagement'};
get_service_modname(accounter) ->
{dmsl_accounter_thrift, 'Accounter'};
get_service_modname(geo_ip_service) ->

View File

@ -21,7 +21,7 @@
{<<"cowlib">>,{pkg,<<"cowlib">>,<<"1.0.2">>},1},
{<<"dmsl">>,
{git,"git@github.com:rbkmoney/damsel.git",
{ref,"8058f479aa58a1f3c4009cb00cf9944f6f8462eb"}},
{ref,"8c39d8fa87d9ec565a6af7cae1a20ff9860d3d1c"}},
0},
{<<"dmt_client">>,
{git,"git@github.com:rbkmoney/dmt_client.git",

@ -1 +1 @@
Subproject commit 76d5dd7d50145c6be131366a632bab2c52e31ae7
Subproject commit 5f23b3d1cb6f710f47a089d0bd210e056d4f7b40