CAPI-231: Put the notion of digital wallets into this shithole (#145)

* CAPI-231: Proudly employ cowboy_access_log

* BA-52: Bump to master rbkmoney/swag@13e66dd

* BA-52: Bump to master rbkmoney/damsel@620cca5
This commit is contained in:
Andrew Mayorov 2018-01-24 17:16:39 +03:00 committed by GitHub
parent 99dc16fd96
commit f653e420a0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 307 additions and 223 deletions

View File

@ -16,6 +16,7 @@
swag_server,
jose,
cowboy_cors,
cowboy_access_log,
rfc3339,
base64url,
snowflake,

View File

@ -159,18 +159,25 @@ process_request('CreatePayment', Req, Context, ReqCtx) ->
process_request('CreatePaymentResource', Req, Context, ReqCtx) ->
Params = maps:get('PaymentResourceParams', Req),
ClientInfo = maps:get(<<"clientInfo">>, Params),
PaymentTool = maps:get(<<"paymentTool">>, Params),
case PaymentTool of
#{<<"paymentToolType">> := <<"CardData">>} ->
process_card_data(ClientInfo, PaymentTool, Context, ReqCtx);
#{<<"paymentToolType">> := <<"PaymentTerminalData">>} ->
process_payment_terminal_data(ClientInfo, PaymentTool, Context);
_ ->
{ok, {400, [], logic_error(
invalidPaymentTool,
<<"Specified payment tool is invalid or unsupported">>
)}}
ClientInfo = enrich_client_info(maps:get(<<"clientInfo">>, Params), Context),
try
V = maps:get(<<"paymentTool">>, Params),
{PaymentTool, PaymentSessionID} = case V of
#{<<"paymentToolType">> := <<"CardData">>} ->
process_card_data(V, ReqCtx);
#{<<"paymentToolType">> := <<"PaymentTerminalData">>} ->
process_payment_terminal_data(V, ReqCtx);
#{<<"paymentToolType">> := <<"DigitalWalletData">>} ->
process_digital_wallet_data(V, ReqCtx)
end,
{ok, {201, [], decode_disposable_payment_resource(#domain_DisposablePaymentResource{
payment_tool = PaymentTool,
payment_session_id = PaymentSessionID,
client_info = encode_client_info(ClientInfo)
})}}
catch
Result ->
Result
end;
process_request('CreateInvoiceAccessToken', Req, Context, ReqCtx) ->
@ -1907,7 +1914,7 @@ encode_payer_params(#{
<<"contactInfo">> := ContactInfo
}) ->
PaymentTool = encode_payment_tool_token(Token),
{ClientInfo, PaymentSession} = unwrap_session(EncodedSession),
{ClientInfo, PaymentSession} = unwrap_payment_session(EncodedSession),
{payment_resource, #payproc_PaymentResourcePayerParams{
resource = #domain_DisposablePaymentResource{
payment_tool = PaymentTool,
@ -1922,7 +1929,9 @@ encode_payment_tool_token(Token) ->
#{<<"type">> := <<"bank_card">>} = Encoded ->
encode_bank_card(Encoded);
#{<<"type">> := <<"payment_terminal">>} = Encoded ->
encode_payment_terminal(Encoded)
encode_payment_terminal(Encoded);
#{<<"type">> := <<"digital_wallet">>} = Encoded ->
encode_digital_wallet(Encoded)
catch
error:badarg ->
erlang:throw(invalid_token)
@ -1950,6 +1959,22 @@ decode_payment_terminal(#domain_PaymentTerminal{
<<"terminal_type">> => Type
}).
decode_digital_wallet(#domain_DigitalWallet{
provider = Provider,
id = ID
}) ->
capi_utils:map_to_base64url(#{
<<"type">> => <<"digital_wallet">>,
<<"provider">> => atom_to_binary(Provider, utf8),
<<"id">> => ID
}).
decode_client_info(ClientInfo) ->
#{
<<"fingerprint">> => ClientInfo#domain_ClientInfo.fingerprint,
<<"ip">> => ClientInfo#domain_ClientInfo.ip_address
}.
encode_client_info(ClientInfo) ->
#domain_ClientInfo{
fingerprint = maps:get(<<"fingerprint">>, ClientInfo),
@ -2334,6 +2359,12 @@ encode_payment_terminal(#{<<"terminal_type">> := Type}) ->
terminal_type = binary_to_existing_atom(Type, utf8)
}}.
encode_digital_wallet(#{<<"provider">> := Provider, <<"id">> := ID}) ->
{digital_wallet, #domain_DigitalWallet{
provider = binary_to_existing_atom(Provider, utf8),
id = ID
}}.
encode_customer_params(PartyID, Params) ->
#payproc_CustomerParams{
party_id = PartyID,
@ -2358,7 +2389,7 @@ encode_customer_binding_params(#{
}
}) ->
PaymentTool = encode_payment_tool_token(Token),
{ClientInfo, PaymentSession} = unwrap_session(EncodedSession),
{ClientInfo, PaymentSession} = unwrap_payment_session(EncodedSession),
#payproc_CustomerBindingParams{
payment_resource = #domain_DisposablePaymentResource{
payment_tool = PaymentTool,
@ -2367,23 +2398,6 @@ encode_customer_binding_params(#{
}
}.
wrap_session(ClientInfo, PaymentSession) ->
capi_utils:map_to_base64url(#{
<<"clientInfo">> => ClientInfo,
<<"paymentSession">> => PaymentSession
}).
unwrap_session(Encoded) ->
#{
<<"clientInfo">> := ClientInfo,
<<"paymentSession">> := PaymentSession
} = try capi_utils:base64url_to_map(Encoded)
catch
error:badarg ->
erlang:throw(invalid_payment_session)
end,
{ClientInfo, PaymentSession}.
decode_invoice_event(#payproc_Event{
id = EventID,
created_at = CreatedAt,
@ -2584,26 +2598,40 @@ decode_payer({payment_resource, #domain_PaymentResourcePayer{
decode_payment_tool_token({bank_card, BankCard}) ->
decode_bank_card(BankCard);
decode_payment_tool_token({payment_terminal, PaymentTerminal}) ->
decode_payment_terminal(PaymentTerminal).
decode_payment_terminal(PaymentTerminal);
decode_payment_tool_token({digital_wallet, DigitalWallet}) ->
decode_digital_wallet(DigitalWallet).
decode_payment_tool_details({bank_card, BankCard}) ->
decode_bank_card_details(<<"PaymentToolDetailsBankCard">>, BankCard);
decode_payment_tool_details({payment_terminal, #domain_PaymentTerminal{
decode_payment_tool_details({bank_card, V}) ->
decode_bank_card_details(V, #{<<"detailsType">> => <<"PaymentToolDetailsBankCard">>});
decode_payment_tool_details({payment_terminal, V}) ->
decode_payment_terminal_details(V, #{<<"detailsType">> => <<"PaymentToolDetailsPaymentTerminal">>});
decode_payment_tool_details({digital_wallet, V}) ->
decode_digital_wallet_details(V, #{<<"detailsType">> => <<"PaymentToolDetailsDigitalWallet">>}).
decode_bank_card_details(#domain_BankCard{
'payment_system' = PaymentSystem,
'masked_pan' = MaskedPan
}, V) ->
V#{
<<"cardNumberMask">> => decode_masked_pan(MaskedPan),
<<"paymentSystem">> => genlib:to_binary(PaymentSystem)
}.
decode_payment_terminal_details(#domain_PaymentTerminal{
terminal_type = Type
}}) ->
#{
<<"detailsType">> => <<"PaymentToolDetailsPaymentTerminal">>,
}, V) ->
V#{
<<"provider">> => genlib:to_binary(Type)
}.
decode_bank_card_details(DetailsType, #domain_BankCard{
'payment_system' = PaymentSystem,
'masked_pan' = MaskedPan
}) ->
#{
<<"detailsType">> => DetailsType,
<<"cardNumberMask">> => decode_masked_pan(MaskedPan),
<<"paymentSystem">> => genlib:to_binary(PaymentSystem)
decode_digital_wallet_details(#domain_DigitalWallet{
provider = qiwi,
id = ID
}, V) ->
V#{
<<"digitalWalletDetailsType">> => <<"DigitalWalletDetailsQIWI">>,
<<"phoneNumberMask">> => mask_phone_number(ID)
}.
-define(MASKED_PAN_MAX_LENGTH, 4).
@ -2613,6 +2641,9 @@ decode_masked_pan(MaskedPan) when byte_size(MaskedPan) > ?MASKED_PAN_MAX_LENGTH
decode_masked_pan(MaskedPan) ->
MaskedPan.
mask_phone_number(PhoneNumber) ->
capi_utils:redact(PhoneNumber, <<"^\\+\\d(\\d{1,10}?)\\d{2,4}$">>).
decode_contact_info(#domain_ContactInfo{
phone_number = PhoneNumber,
email = Email
@ -3047,8 +3078,8 @@ decode_russian_bank_account(#domain_RussianBankAccount{
bank_name = BankName,
bank_post_account = BankPostAccount,
bank_bik = BankBik
}) ->
#{
}, V) ->
V#{
<<"account">> => Account,
<<"bankName">> => BankName,
<<"bankPostAccount">> => BankPostAccount,
@ -3061,8 +3092,8 @@ decode_international_bank_account(#domain_InternationalBankAccount{
bank_address = BankAddress,
iban = Iban,
bic = Bic
}) ->
#{
}, V) ->
V#{
<<"accountHolder">> => AccountHolder,
<<"bankName">> => BankName,
<<"bankAddress">> => BankAddress,
@ -3158,7 +3189,7 @@ decode_legal_entity({
<<"representativePosition">> => RepresentativePosition,
<<"representativeFullName">> => RepresentativeFullName,
<<"representativeDocument">> => RepresentativeDocument,
<<"bankAccount">> => decode_russian_bank_account(BankAccount)
<<"bankAccount">> => decode_russian_bank_account(BankAccount, #{})
};
decode_legal_entity({
international_legal_entity,
@ -3336,18 +3367,12 @@ decode_stat_payout_status({Status, _}) ->
decode_stat_payout_tool_details(PayoutType) ->
decode_payout_tool_details(merchstat_to_domain(PayoutType)).
decode_payout_tool_details({bank_card, BankCard}) ->
decode_bank_card_details(<<"PayoutToolDetailsBankCard">>, BankCard);
decode_payout_tool_details({russian_bank_account, BankAccount}) ->
maps:merge(
#{<<"detailsType">> => <<"PayoutToolDetailsBankAccount">>},
decode_russian_bank_account(BankAccount)
);
decode_payout_tool_details({international_bank_account, BankAccount}) ->
maps:merge(
#{<<"detailsType">> => <<"PayoutToolDetailsInternationalBankAccount">>},
decode_international_bank_account(BankAccount)
).
decode_payout_tool_details({bank_card, V}) ->
decode_bank_card_details(V, #{<<"detailsType">> => <<"PayoutToolDetailsBankCard">>});
decode_payout_tool_details({russian_bank_account, V}) ->
decode_russian_bank_account(V, #{<<"detailsType">> => <<"PayoutToolDetailsBankAccount">>});
decode_payout_tool_details({international_bank_account, V}) ->
decode_international_bank_account(V, #{<<"detailsType">> => <<"PayoutToolDetailsInternationalBankAccount">>}).
encode_payout_type('PayoutCard') ->
<<"bank_card">>;
@ -3840,20 +3865,15 @@ decode_customer_binding(#payproc_CustomerBinding{
decode_disposable_payment_resource(#domain_DisposablePaymentResource{
payment_tool = PaymentTool,
payment_session_id = PaymentSession,
client_info = #domain_ClientInfo{
fingerprint = Fingerprint,
ip_address = IP
}
payment_session_id = PaymentSessionID,
client_info = ClientInfo0
}) ->
ClientInfo = decode_client_info(ClientInfo0),
#{
<<"paymentToolToken">> => decode_payment_tool_token(PaymentTool),
<<"paymentSession">> => PaymentSession,
<<"paymentSession">> => wrap_payment_session(ClientInfo, PaymentSessionID),
<<"paymentToolDetails">> => decode_payment_tool_details(PaymentTool),
<<"clientInfo">> => #{
<<"ip">> => IP,
<<"fingerprint">> => Fingerprint
}
<<"clientInfo">> => ClientInfo
}.
decode_customer_binding_status({Status, StatusInfo}) ->
@ -4122,24 +4142,12 @@ process_search_request_result(QueryType, Result, #{decode_fun := DecodeFun}) ->
end.
get_time(Key, Req) ->
to_universal_time(genlib_map:get(Key, Req)).
to_universal_time(Tz = undefined) ->
Tz;
to_universal_time(Tz) ->
{ok, {Date, Time, Usec, TzOffset}} = rfc3339:parse(Tz),
TzSec = calendar:datetime_to_gregorian_seconds({Date, Time}),
%% The following crappy code is a dialyzer workaround
%% for the wrong rfc3339:parse/1 spec.
{UtcDate, UtcTime} = calendar:gregorian_seconds_to_datetime(
case TzOffset of
_ when is_integer(TzOffset) ->
TzSec - (60*TzOffset);
_ ->
TzSec
end),
{ok, Utc} = rfc3339:format({UtcDate, UtcTime, Usec, 0}),
Utc.
case genlib_map:get(Key, Req) of
Timestamp when is_binary(Timestamp) ->
capi_utils:to_universal_time(Timestamp);
undefined ->
undefined
end.
get_split_interval(SplitSize, minute) ->
SplitSize * 60;
@ -4362,7 +4370,9 @@ decode_payment_methods({value, PaymentMethodRefs}) ->
decode_payment_method(bank_card, PaymentSystems) ->
#{<<"method">> => <<"BankCard">>, <<"paymentSystems">> => lists:map(fun genlib:to_binary/1, PaymentSystems)};
decode_payment_method(payment_terminal, Providers) ->
#{<<"method">> => <<"PaymentTerminal">>, <<"providers">> => lists:map(fun genlib:to_binary/1, Providers)}.
#{<<"method">> => <<"PaymentTerminal">>, <<"providers">> => lists:map(fun genlib:to_binary/1, Providers)};
decode_payment_method(digital_wallet, Providers) ->
#{<<"method">> => <<"DigitalWallet">>, <<"providers">> => lists:map(fun genlib:to_binary/1, Providers)}.
compute_terms(ServiceName, Args, Context) ->
service_call(
@ -4375,81 +4385,87 @@ compute_terms(ServiceName, Args, Context) ->
reply_5xx(Code) when Code >= 500 andalso Code < 600 ->
{Code, [], <<>>}.
process_card_data(ClientInfo0, PaymentTool, Context, ReqCtx) ->
CardData = get_card_data(PaymentTool),
process_card_data(Data, ReqCtx) ->
Result = service_call(
cds_storage,
'PutCardData',
[CardData],
[encode_card_data(Data)],
ReqCtx
),
case Result of
{ok, #'PutCardDataResult'{
session_id = PaymentSession,
session_id = SessionID,
bank_card = BankCard
}} ->
Token = decode_bank_card(BankCard),
PreparedIP = get_prepared_ip(Context),
ClientInfo = ClientInfo0#{<<"ip">> => PreparedIP},
Session = wrap_session(ClientInfo, PaymentSession),
Resp = #{
<<"paymentToolToken">> => Token,
<<"paymentSession">> => Session,
<<"paymentToolDetails">> => decode_payment_tool_details({bank_card, BankCard}),
<<"clientInfo">> => ClientInfo
},
{ok, {201, [], Resp}};
{{bank_card, BankCard}, SessionID};
{exception, Exception} ->
case Exception of
#'InvalidCardData'{} ->
{ok, {400, [], logic_error(invalidRequest, <<"Card data is invalid">>)}};
throw({ok, {400, [], logic_error(invalidRequest, <<"Card data is invalid">>)}});
#'KeyringLocked'{} ->
% TODO
% It's better for the cds to signal woody-level unavailability when the
% keyring is locked, isn't it? It could always mention keyring lock as a
% reason in a woody error definition.
{error, reply_5xx(503)}
throw({error, reply_5xx(503)})
end
end.
get_card_data(PaymentTool) ->
{Month, Year} = parse_exp_date(genlib_map:get(<<"expDate">>, PaymentTool)),
CardNumber = genlib:to_binary(genlib_map:get(<<"cardNumber">>, PaymentTool)),
encode_card_data(CardData) ->
{Month, Year} = parse_exp_date(genlib_map:get(<<"expDate">>, CardData)),
CardNumber = genlib:to_binary(genlib_map:get(<<"cardNumber">>, CardData)),
#'CardData'{
pan = CardNumber,
exp_date = #'ExpDate'{
month = Month,
year = Year
},
cardholder_name = genlib_map:get(<<"cardHolder">>, PaymentTool),
cvv = genlib_map:get(<<"cvv">>, PaymentTool)
cardholder_name = genlib_map:get(<<"cardHolder">>, CardData),
cvv = genlib_map:get(<<"cvv">>, CardData)
}.
get_prepared_ip(Context) ->
#{
ip_address := IP
} = get_peer_info(Context),
genlib:to_binary(inet:ntoa(IP)).
process_payment_terminal_data(ClientInfo0, TerminalData, Context) ->
process_payment_terminal_data(Data, _ReqCtx) ->
PaymentTerminal = #domain_PaymentTerminal{
terminal_type = binary_to_existing_atom(
genlib_map:get(<<"provider">>, TerminalData),
genlib_map:get(<<"provider">>, Data),
utf8
)
},
Token = decode_payment_terminal(PaymentTerminal),
PreparedIP = get_prepared_ip(Context),
ClientInfo = ClientInfo0#{<<"ip">> => PreparedIP},
Session = wrap_session(ClientInfo, <<"">>),
Resp = #{
<<"paymentToolToken">> => Token,
<<"paymentSession">> => Session,
<<"paymentToolDetails">> => decode_payment_tool_details({payment_terminal, PaymentTerminal}),
<<"clientInfo">> => ClientInfo
},
{ok, {201, [], Resp}}.
{{payment_terminal, PaymentTerminal}, <<>>}.
process_digital_wallet_data(Data, _ReqCtx) ->
DigitalWallet = case Data of
#{<<"digitalWalletType">> := <<"DigitalWalletQIWI">>} ->
#domain_DigitalWallet{
provider = qiwi,
id = maps:get(<<"phoneNumber">>, Data)
}
end,
{{digital_wallet, DigitalWallet}, <<>>}.
enrich_client_info(ClientInfo, Context) ->
ClientInfo#{<<"ip">> => prepare_client_ip(Context)}.
prepare_client_ip(Context) ->
#{ip_address := IP} = get_peer_info(Context),
genlib:to_binary(inet:ntoa(IP)).
wrap_payment_session(ClientInfo, PaymentSession) ->
capi_utils:map_to_base64url(#{
<<"clientInfo">> => ClientInfo,
<<"paymentSession">> => PaymentSession
}).
unwrap_payment_session(Encoded) ->
#{
<<"clientInfo">> := ClientInfo,
<<"paymentSession">> := PaymentSession
} = try capi_utils:base64url_to_map(Encoded)
catch
error:badarg ->
erlang:throw(invalid_payment_session)
end,
{ClientInfo, PaymentSession}.
get_default_url_lifetime() ->
Now = erlang:system_time(second),
@ -4460,18 +4476,3 @@ get_default_url_lifetime() ->
Error ->
error(Error)
end.
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-spec test() -> _.
-spec to_universal_time_test() -> _.
to_universal_time_test() ->
?assertEqual(undefined, to_universal_time(undefined)),
?assertEqual(<<"2017-04-19T13:56:07Z">>, to_universal_time(<<"2017-04-19T13:56:07Z">>)),
?assertEqual(<<"2017-04-19T13:56:07.530000Z">>, to_universal_time(<<"2017-04-19T13:56:07.53Z">>)),
?assertEqual(<<"2017-04-19T10:36:07.530000Z">>, to_universal_time(<<"2017-04-19T13:56:07.53+03:20">>)),
?assertEqual(<<"2017-04-19T17:16:07.530000Z">>, to_universal_time(<<"2017-04-19T13:56:07.53-03:20">>)).
-endif. %%TEST

View File

@ -39,7 +39,7 @@ get_cowboy_config(LogicHandler) ->
cowboy_cors,
cowboy_handler
]},
{onrequest, fun ?MODULE:request_hook/1},
{onrequest, cowboy_access_log:get_request_hook()},
{onresponse, fun ?MODULE:response_hook/4}
].
@ -52,10 +52,10 @@ request_hook(Req) ->
-spec response_hook(cowboy:http_status(), cowboy:http_headers(), iodata(), cowboy_req:req()) ->
cowboy_req:req().
response_hook(Code, Headers, _, Req) ->
response_hook(Code, Headers, Body, Req) ->
try
{Code1, Headers1, Req1} = handle_response(Code, Headers, Req),
_ = log_access(Code1, Headers1, Req1),
_ = log_access(Code1, Headers1, Body, Req1),
Req1
catch
Class:Reason ->
@ -110,49 +110,6 @@ get_oops_body_safe(Code) ->
get_oops_body(Code) ->
genlib_map:get(Code, genlib_app:env(?APP, oops_bodies, #{}), undefined).
log_access(Code, Headers, Req) ->
{Method, _} = cowboy_req:method(Req),
{Path, _} = cowboy_req:path(Req),
{ReqLen, _} = cowboy_req:body_length(Req),
{ReqId, _} = cowboy_req:header(<<"x-request-id">>, Req, undefined),
RemAddr = get_remote_addr(Req),
RespLen = get_response_len(Headers),
Duration = get_request_duration(Req),
ReqMeta = [
{remote_addr, RemAddr},
{request_method, Method},
{request_path, Path},
{request_length, ReqLen},
{response_length, RespLen},
{request_time, Duration},
{'http_x-request-id', ReqId},
{status, Code}
],
Meta = orddict:merge(fun(_Key, New, _Old) -> New end, ReqMeta, lager:md()),
%% Call lager:log/5 here directly in order to pass request metadata (fused into
%% lager metadata) without storing it in a process dict via lager:md/1.
lager:log(capi_access_lager_event, info, Meta, "", []).
get_remote_addr(Req) ->
case swag_server_handler_api:determine_peer(Req) of
{{ok, #{ip_address := IP}}, _} ->
genlib:to_binary(inet:ntoa(IP));
{_, _} ->
undefined
end.
get_request_duration(Req) ->
case cowboy_req:meta(?START_TIME_TAG, Req) of
{undefined, _} ->
undefined;
{StartTime, _} ->
(genlib_time:ticks() - StartTime) / 1000000
end.
get_response_len(Headers) ->
case lists:keyfind(<<"content-length">>, 1, Headers) of
{_, Len} ->
genlib:to_int(Len);
false ->
undefined
end.
log_access(Code, Headers, Body, Req) ->
LogFun = cowboy_access_log:get_response_hook(capi_access_lager_event),
LogFun(Code, Headers, Body, Req).

View File

@ -4,6 +4,10 @@
-export([base64url_to_map/1]).
-export([map_to_base64url/1]).
-export([to_universal_time/1]).
-export([redact/2]).
-spec logtag_process(atom(), any()) -> ok.
logtag_process(Key, Value) when is_atom(Key) ->
@ -27,3 +31,57 @@ map_to_base64url(Map) when is_map(Map) ->
_ = lager:debug("encoding map ~p to base64 failed with ~p:~p", [Map, Class, Reason]),
erlang:error(badarg)
end.
-spec redact(Subject :: binary(), Pattern :: binary()) -> Redacted :: binary().
redact(Subject, Pattern) ->
case re:run(Subject, Pattern, [global, {capture, all_but_first, index}]) of
{match, Captures} ->
lists:foldl(fun redact_match/2, Subject, Captures);
nomatch ->
Subject
end.
redact_match({S, Len}, Subject) ->
<<Pre:S/binary, _:Len/binary, Rest/binary>> = Subject,
<<Pre/binary, (binary:copy(<<"*">>, Len))/binary, Rest/binary>>;
redact_match([Capture], Message) ->
redact_match(Capture, Message).
-spec to_universal_time(Timestamp :: binary()) -> TimestampUTC :: binary().
to_universal_time(Timestamp) ->
{ok, {Date, Time, Usec, TZOffset}} = rfc3339:parse(Timestamp),
Seconds = calendar:datetime_to_gregorian_seconds({Date, Time}),
%% The following crappy code is a dialyzer workaround
%% for the wrong rfc3339:parse/1 spec.
{DateUTC, TimeUTC} = calendar:gregorian_seconds_to_datetime(
case TZOffset of
_ when is_integer(TZOffset) ->
Seconds - (60 * TZOffset);
_ ->
Seconds
end
),
{ok, TimestampUTC} = rfc3339:format({DateUTC, TimeUTC, Usec, 0}),
TimestampUTC.
%%
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-spec test() -> _.
-spec to_universal_time_test() -> _.
to_universal_time_test() ->
?assertEqual(<<"2017-04-19T13:56:07Z">>, to_universal_time(<<"2017-04-19T13:56:07Z">>)),
?assertEqual(<<"2017-04-19T13:56:07.530000Z">>, to_universal_time(<<"2017-04-19T13:56:07.53Z">>)),
?assertEqual(<<"2017-04-19T10:36:07.530000Z">>, to_universal_time(<<"2017-04-19T13:56:07.53+03:20">>)),
?assertEqual(<<"2017-04-19T17:16:07.530000Z">>, to_universal_time(<<"2017-04-19T13:56:07.53-03:20">>)).
-spec redact_test() -> _.
redact_test() ->
P1 = <<"^\\+\\d(\\d{1,10}?)\\d{2,4}$">>,
?assertEqual(<<"+7******3210">>, redact(<<"+79876543210">>, P1)),
?assertEqual( <<"+1*11">>, redact(<<"+1111">>, P1)).
-endif.

View File

@ -64,7 +64,10 @@
cancel_payment_ok_test/1,
capture_payment_ok_test/1,
create_payment_tool_token_ok_test/1,
create_visa_payment_resource_ok_test/1,
create_nspkmir_payment_resource_ok_test/1,
create_euroset_payment_resource_ok_test/1,
create_qw_payment_resource_ok_test/1,
get_my_party_ok_test/1,
suspend_my_party_ok_test/1,
@ -167,7 +170,7 @@ invoice_access_token_tests() ->
get_payment_by_id_ok_test,
cancel_payment_ok_test,
capture_payment_ok_test,
create_payment_tool_token_ok_test
{group, payment_resources}
].
customer_access_token_tests() ->
@ -190,6 +193,14 @@ groups() ->
woody_unknown_test
]
},
{payment_resources, [],
[
create_visa_payment_resource_ok_test,
create_nspkmir_payment_resource_ok_test,
create_euroset_payment_resource_ok_test,
create_qw_payment_resource_ok_test
]
},
{operations_by_base_api_token, [],
[
create_invoice_ok_test,
@ -757,9 +768,9 @@ capture_payment_ok_test(Config) ->
mock_services([{invoicing, fun('CapturePayment', _) -> {ok, ok} end}], Config),
ok = capi_client_payments:capture_payment(?config(context, Config), ?STRING, ?STRING, ?STRING).
-spec create_payment_tool_token_ok_test(_) ->
-spec create_visa_payment_resource_ok_test(_) ->
_.
create_payment_tool_token_ok_test(Config) ->
create_visa_payment_resource_ok_test(Config) ->
mock_services([
{cds_storage, fun
('PutCardData', [#'CardData'{pan = <<"411111", _:6/binary, Mask:4/binary>>}]) ->
@ -771,7 +782,30 @@ create_payment_tool_token_ok_test(Config) ->
masked_pan = Mask
},
session_id = ?STRING
}};
}}
end}
], Config),
ClientInfo = #{<<"fingerprint">> => <<"test fingerprint">>},
{ok, #{<<"paymentToolDetails">> := #{
<<"detailsType">> := <<"PaymentToolDetailsBankCard">>,
<<"paymentSystem">> := <<"visa">>,
<<"cardNumberMask">> := <<"1111">>
}}} = capi_client_tokens:create_payment_resource(?config(context, Config), #{
<<"paymentTool">> => #{
<<"paymentToolType">> => <<"CardData">>,
<<"cardNumber">> => <<"4111111111111111">>,
<<"cardHolder">> => <<"Alexander Weinerschnitzel">>,
<<"expDate">> => <<"08/27">>,
<<"cvv">> => <<"232">>
},
<<"clientInfo">> => ClientInfo
}).
-spec create_nspkmir_payment_resource_ok_test(_) ->
_.
create_nspkmir_payment_resource_ok_test(Config) ->
mock_services([
{cds_storage, fun
('PutCardData', [#'CardData'{pan = <<"22001111", _:6/binary, Mask:2/binary>>}]) ->
{ok, #'PutCardDataResult'{
bank_card = #domain_BankCard{
@ -784,27 +818,51 @@ create_payment_tool_token_ok_test(Config) ->
}}
end}
], Config),
PaymentTool = #{
<<"paymentToolType">> => <<"CardData">>,
<<"cardHolder">> => <<"Alexander Weinerschnitzel">>,
<<"expDate">> => <<"08/27">>,
<<"cvv">> => <<"232">>
},
ClientInfo = #{<<"fingerprint">> => <<"test fingerprint">>},
{ok, #{<<"paymentToolDetails">> := #{
<<"detailsType">> := <<"PaymentToolDetailsBankCard">>,
<<"paymentSystem">> := <<"visa">>,
<<"cardNumberMask">> := <<"1111">>
}}} = capi_client_tokens:create_payment_resource(?config(context, Config), #{
<<"paymentTool">> => PaymentTool#{<<"cardNumber">> => <<"4111111111111111">>},
<<"clientInfo">> => ClientInfo
}),
{ok, #{<<"paymentToolDetails">> := #{
<<"detailsType">> := <<"PaymentToolDetailsBankCard">>,
<<"paymentSystem">> := <<"nspkmir">>,
<<"cardNumberMask">> := <<"11">>
}}} = capi_client_tokens:create_payment_resource(?config(context, Config), #{
<<"paymentTool">> => PaymentTool#{<<"cardNumber">> => <<"2200111111111111">>},
<<"paymentTool">> => #{
<<"paymentToolType">> => <<"CardData">>,
<<"cardNumber">> => <<"2200111111111111">>,
<<"cardHolder">> => <<"Alexander Weinerschnitzel">>,
<<"expDate">> => <<"08/27">>,
<<"cvv">> => <<"232">>
},
<<"clientInfo">> => ClientInfo
}).
-spec create_euroset_payment_resource_ok_test(_) ->
_.
create_euroset_payment_resource_ok_test(Config) ->
ClientInfo = #{<<"fingerprint">> => <<"test fingerprint">>},
{ok, #{<<"paymentToolDetails">> := #{
<<"detailsType">> := <<"PaymentToolDetailsPaymentTerminal">>,
<<"provider">> := <<"euroset">>
}}} = capi_client_tokens:create_payment_resource(?config(context, Config), #{
<<"paymentTool">> => #{
<<"paymentToolType">> => <<"PaymentTerminalData">>,
<<"provider">> => <<"euroset">>
},
<<"clientInfo">> => ClientInfo
}).
-spec create_qw_payment_resource_ok_test(_) ->
_.
create_qw_payment_resource_ok_test(Config) ->
ClientInfo = #{<<"fingerprint">> => <<"test fingerprint">>},
{ok, #{<<"paymentToolDetails">> := #{
<<"detailsType">> := <<"PaymentToolDetailsDigitalWallet">>,
<<"digitalWalletDetailsType">> := <<"DigitalWalletDetailsQIWI">>,
<<"phoneNumberMask">> := <<"+7******3210">>
}}} = capi_client_tokens:create_payment_resource(?config(context, Config), #{
<<"paymentTool">> => #{
<<"paymentToolType">> => <<"DigitalWalletData">>,
<<"digitalWalletType">> => <<"DigitalWalletQIWI">>,
<<"phoneNumber">> => <<"+79876543210">>
},
<<"clientInfo">> => ClientInfo
}).

View File

@ -62,6 +62,11 @@
{git, "https://github.com/danielwhite/cowboy_cors.git",
{branch, "master"}
}
},
{cowboy_access_log,
{git, "git@github.com:rbkmoney/cowboy_access_log.git",
{branch, "master"}
}
}
]}.

View File

@ -2,6 +2,10 @@
[{<<"base64url">>,{pkg,<<"base64url">>,<<"0.0.1">>},0},
{<<"certifi">>,{pkg,<<"certifi">>,<<"0.4.0">>},1},
{<<"cowboy">>,{pkg,<<"cowboy">>,<<"1.0.4">>},0},
{<<"cowboy_access_log">>,
{git,"git@github.com:rbkmoney/cowboy_access_log.git",
{ref,"a06c299a9b05f6868c48f40b1aeb7c00b775ce12"}},
0},
{<<"cowboy_cors">>,
{git,"https://github.com/danielwhite/cowboy_cors.git",
{ref,"392f5804b63fff2bd0fda67671d5b2fbe0badd37"}},
@ -9,7 +13,7 @@
{<<"cowlib">>,{pkg,<<"cowlib">>,<<"1.0.2">>},1},
{<<"dmsl">>,
{git,"git@github.com:rbkmoney/damsel.git",
{ref,"07fbb3057e78e37611e642160a7201fe31d6ff69"}},
{ref,"4ca2329c564b3730dbd742ae370be9919905b9de"}},
0},
{<<"genlib">>,
{git,"https://github.com/rbkmoney/genlib.git",
@ -32,7 +36,7 @@
{<<"metrics">>,{pkg,<<"metrics">>,<<"1.0.1">>},1},
{<<"mimerl">>,{pkg,<<"mimerl">>,<<"1.0.2">>},1},
{<<"parse_trans">>,
{git,"git@github.com:rbkmoney/parse_trans.git",
{git,"https://github.com/rbkmoney/parse_trans.git",
{ref,"5ee45f5bfa6c04329bea3281977b774f04c89f11"}},
0},
{<<"pooler">>,{pkg,<<"pooler">>,<<"1.5.0">>},0},
@ -49,7 +53,7 @@
1},
{<<"woody">>,
{git,"git@github.com:rbkmoney/woody_erlang.git",
{ref,"2d00bda10454534e230d452b7338debafaf0a869"}},
{ref,"ad1e91050c36d8de15f1c7d8dd8a2c682d2d158c"}},
0},
{<<"woody_user_identity">>,
{git,"git@github.com:rbkmoney/woody_erlang_user_identity.git",

@ -1 +1 @@
Subproject commit 963e8f2933bc71710e81c4a18416ff18fede9549
Subproject commit 8d4b00cd075196f364f25beb6d0093cb741b97a3