From 5b7bd81e35867a049ab330c9522de3ffe6dd9765 Mon Sep 17 00:00:00 2001 From: Roman Pushkov Date: Fri, 29 Nov 2019 12:27:26 +0300 Subject: [PATCH] CAPI-397: QR code & crypto currency user interaction (#443) * decode user interaction * fix failing tests: invoice sessions and refunds * update lockfile * fix failing tests: event range * CAPI-398: Fix refunds encoder (#444) * CAPI-398: Fix refunds encoder * Typo * update tests * add revision to getinvoicepaymentmethods * add revision to getinvoicepaymentmethodsbytemplateid * update swag * explicit decimal points for crypto * change unwrap to match on ok tuple * rework crypto interaction decoder * add unit test for crypto decoder, fix * formatting fix * extra test cases * review updates * shorten line * typo fix --- apps/capi/src/capi_handler_customers.erl | 3 +- .../src/capi_handler_decoder_invoicing.erl | 86 +++++++++++++++++++ .../src/capi_handler_invoice_templates.erl | 14 +-- apps/capi/src/capi_handler_invoices.erl | 8 +- apps/capi/src/capi_handler_payments.erl | 5 +- apps/capi/src/capi_handler_utils.erl | 4 +- apps/capi/test/capi_dummy_data.hrl | 8 +- .../capi_invoice_access_token_tests_SUITE.erl | 3 +- ...oice_template_access_token_tests_SUITE.erl | 3 +- rebar.lock | 2 +- schemes/swag | 2 +- 11 files changed, 119 insertions(+), 19 deletions(-) diff --git a/apps/capi/src/capi_handler_customers.erl b/apps/capi/src/capi_handler_customers.erl index 0fcedcd..5589756 100644 --- a/apps/capi/src/capi_handler_customers.erl +++ b/apps/capi/src/capi_handler_customers.erl @@ -204,7 +204,8 @@ process_request(_OperationID, _Req, _Context) -> {error, noimpl}. get_customer_by_id(CustomerID, Context) -> - capi_handler_utils:service_call({customer_management, 'Get', [CustomerID]}, Context). + EventRange = #payproc_EventRange{}, + capi_handler_utils:service_call({customer_management, 'Get', [CustomerID, EventRange]}, Context). encode_customer_params(PartyID, Params) -> #payproc_CustomerParams{ diff --git a/apps/capi/src/capi_handler_decoder_invoicing.erl b/apps/capi/src/capi_handler_decoder_invoicing.erl index 3ec9bb7..14a3fbb 100644 --- a/apps/capi/src/capi_handler_decoder_invoicing.erl +++ b/apps/capi/src/capi_handler_decoder_invoicing.erl @@ -37,6 +37,18 @@ decode_user_interaction({redirect, BrowserRequest}) -> #{ <<"interactionType">> => <<"Redirect">>, <<"request">> => decode_browser_request(BrowserRequest) + }; +decode_user_interaction({qr_code_display_request, QrCodeDisplayRequest}) -> + #{ + <<"interactionType">> => <<"QrCodeDisplayRequest">>, + <<"qrCode">> => decode_qr_code(QrCodeDisplayRequest) + }; +decode_user_interaction({crypto_currency_transfer_request, CryptoCurrencyTransferRequest}) -> + #{ + <<"interactionType">> => <<"CryptoCurrencyTransferRequest">>, + <<"cryptoAddress">> => CryptoCurrencyTransferRequest#'CryptoCurrencyTransferRequest'.crypto_address, + <<"symbolicCode">> => decode_crypto_symcode(CryptoCurrencyTransferRequest), + <<"cryptoAmount">> => decode_crypto_amount(CryptoCurrencyTransferRequest) }. decode_browser_request({get_request, #'BrowserGetRequest'{uri = UriTemplate}}) -> @@ -51,6 +63,55 @@ decode_browser_request({post_request, #'BrowserPostRequest'{uri = UriTemplate, f <<"form">> => decode_user_interaction_form(UserInteractionForm) }. +decode_qr_code(#'QrCodeDisplayRequest'{qr_code = QrCode}) -> + QrCode#'QrCode'.payload. + +decode_crypto_symcode(#'CryptoCurrencyTransferRequest'{crypto_cash = Cash}) -> + Cash#'CryptoCash'.crypto_symbolic_code. + +decode_crypto_amount(#'CryptoCurrencyTransferRequest'{crypto_cash = Cash}) -> + % apparently Q is always a power of ten + Amount = Cash#'CryptoCash'.crypto_amount, + ok = ensure_correct_exponent(Amount), + Integral = decode_integral_part(Amount), + Fractional = decode_fractional_part(Amount), + build_decoded_crypto_amount(Integral, Fractional). + +ensure_correct_exponent(#'Rational'{q = Q}) -> + Log = math:log10(Q), + case Log - trunc(Log) of + 0.0 -> ok; + _ -> error('expected a power of 10 denominator') + end. + +decode_integral_part(#'Rational'{p = P, q = Q}) -> + erlang:integer_to_binary(P div Q). + +decode_fractional_part(#'Rational'{p = P, q = Q}) -> + Exponent = get_exponent(Q), + build_fractional(P rem Q, Exponent). + +get_exponent(Q) -> + erlang:trunc(math:log10(Q)). + +build_fractional(_Fractional, _Exponent = 0) -> + <<>>; +build_fractional(Fractional, Exponent) -> + BinaryFractional = erlang:integer_to_binary(Fractional), + strip_trailing_zeroes(genlib_string:pad_numeric(BinaryFractional, Exponent)). + +strip_trailing_zeroes(Fractional) -> + ByteSize = byte_size(Fractional) - 1, + case Fractional of + <> -> strip_trailing_zeroes(Prefix); + Fractional -> Fractional + end. + +build_decoded_crypto_amount(Integral, <<>>) -> + Integral; +build_decoded_crypto_amount(Integral, Fractional) -> + <>. + -spec decode_user_interaction_form(map()) -> capi_handler_decoder_utils:decode_data(). @@ -408,3 +469,28 @@ make_invoice_and_token(Invoice, PartyID, ExtraProperties) -> ExtraProperties ) }. + +%% + +-ifdef(EUNIT). + +-include_lib("eunit/include/eunit.hrl"). + +-spec test() -> _. + +-spec crypto_amount_decoder_test() -> _. +crypto_amount_decoder_test() -> + ?assertError('expected a power of 10 denominator', decode_crypto_amount(build_request(1, 2))), + ?assertEqual(<<"1100000007" >>, decode_crypto_amount(build_request(1100000007, 1 ))), + ?assertEqual(<< "1" >>, decode_crypto_amount(build_request(100000000 , 100000000))), + ?assertEqual(<< "1.1" >>, decode_crypto_amount(build_request(110000000 , 100000000))), + ?assertEqual(<<"11.00000007">>, decode_crypto_amount(build_request(1100000007, 100000000))), + ?assertEqual(<< "0.11000007">>, decode_crypto_amount(build_request(11000007 , 100000000))), + ?assertEqual(<< "0.110007" >>, decode_crypto_amount(build_request(11000700 , 100000000))). + +build_request(P, Q) -> + Amount = #'Rational'{p = P, q = Q}, + Cash = #'CryptoCash'{crypto_amount = Amount, crypto_symbolic_code = <<>>}, + #'CryptoCurrencyTransferRequest'{crypto_address = <<>>, crypto_cash = Cash}. + +-endif. diff --git a/apps/capi/src/capi_handler_invoice_templates.erl b/apps/capi/src/capi_handler_invoice_templates.erl index 6b8f60a..d7ff79c 100644 --- a/apps/capi/src/capi_handler_invoice_templates.erl +++ b/apps/capi/src/capi_handler_invoice_templates.erl @@ -1,5 +1,6 @@ -module(capi_handler_invoice_templates). +-include_lib("damsel/include/dmsl_domain_thrift.hrl"). -include_lib("damsel/include/dmsl_payment_processing_thrift.hrl"). -behaviour(capi_handler). @@ -151,13 +152,12 @@ process_request('CreateInvoiceWithTemplate' = OperationID, Req, Context) -> end; process_request('GetInvoicePaymentMethodsByTemplateID', Req, Context) -> - Result = - capi_handler_decoder_invoicing:construct_payment_methods( - invoice_templating, - [maps:get('invoiceTemplateID', Req), capi_utils:unwrap(rfc3339:format(erlang:system_time()))], - Context - ), - case Result of + InvoiceTemplateID = maps:get('invoiceTemplateID', Req), + Timestamp = capi_utils:unwrap(rfc3339:format(erlang:system_time())), + {ok, Party} = capi_handler_utils:get_my_party(Context), + Revision = Party#domain_Party.revision, + Args = [InvoiceTemplateID, Timestamp, {revision, Revision}], + case capi_handler_decoder_invoicing:construct_payment_methods(invoice_templating, Args, Context) of {ok, PaymentMethods0} when is_list(PaymentMethods0) -> PaymentMethods = capi_utils:deduplicate_payment_methods(PaymentMethods0), {ok, {200, #{}, PaymentMethods}}; diff --git a/apps/capi/src/capi_handler_invoices.erl b/apps/capi/src/capi_handler_invoices.erl index 089d249..6c7bdfd 100644 --- a/apps/capi/src/capi_handler_invoices.erl +++ b/apps/capi/src/capi_handler_invoices.erl @@ -163,7 +163,11 @@ process_request('GetInvoiceEvents', Req, Context) -> end; process_request('GetInvoicePaymentMethods', Req, Context) -> - case capi_handler_decoder_invoicing:construct_payment_methods(invoicing, [maps:get(invoiceID, Req)], Context) of + InvoiceID = maps:get(invoiceID, Req), + Party = capi_utils:unwrap(capi_handler_utils:get_my_party(Context)), + Revision = Party#domain_Party.revision, + Args = [InvoiceID, {revision, Revision}], + case capi_handler_decoder_invoicing:construct_payment_methods(invoicing, Args, Context) of {ok, PaymentMethods0} when is_list(PaymentMethods0) -> PaymentMethods = capi_utils:deduplicate_payment_methods(PaymentMethods0), {ok, {200, #{}, PaymentMethods}}; @@ -360,4 +364,4 @@ get_invoice_by_external_id(ExternalID, #{woody_context := WoodyContext} = Contex capi_handler_utils:get_invoice_by_id(InvoiceID, Context); Error -> Error - end. \ No newline at end of file + end. diff --git a/apps/capi/src/capi_handler_payments.erl b/apps/capi/src/capi_handler_payments.erl index b8a6923..14ed384 100644 --- a/apps/capi/src/capi_handler_payments.erl +++ b/apps/capi/src/capi_handler_payments.erl @@ -318,7 +318,10 @@ process_request('CreateRefund' = OperationID, Req, Context) -> process_request('GetRefunds', Req, Context) -> case capi_handler_utils:get_payment_by_id(maps:get(invoiceID, Req), maps:get(paymentID, Req), Context) of {ok, #payproc_InvoicePayment{refunds = Refunds}} -> - {ok, {200, #{}, [capi_handler_decoder_invoicing:decode_refund(R, Context) || R <- Refunds]}}; + {ok, {200, #{}, [ + capi_handler_decoder_invoicing:decode_refund(R, Context) + || #payproc_InvoicePaymentRefund{refund = R} <- Refunds + ]}}; {exception, Exception} -> case Exception of #payproc_InvalidUser{} -> diff --git a/apps/capi/src/capi_handler_utils.erl b/apps/capi/src/capi_handler_utils.erl index 1f6c969..eb5e09d 100644 --- a/apps/capi/src/capi_handler_utils.erl +++ b/apps/capi/src/capi_handler_utils.erl @@ -303,7 +303,9 @@ wrap_payment_session(ClientInfo, PaymentSession) -> woody:result(). get_invoice_by_id(InvoiceID, Context) -> - service_call_with([user_info], {invoicing, 'Get', [InvoiceID]}, Context). + EventRange = #payproc_EventRange{}, + Args = [InvoiceID, EventRange], + service_call_with([user_info], {invoicing, 'Get', Args}, Context). -spec get_payment_by_id(binary(), binary(), processing_context()) -> woody:result(). diff --git a/apps/capi/test/capi_dummy_data.hrl b/apps/capi/test/capi_dummy_data.hrl index 5e4069d..8759e85 100644 --- a/apps/capi/test/capi_dummy_data.hrl +++ b/apps/capi/test/capi_dummy_data.hrl @@ -229,9 +229,11 @@ -define(RECURRENT_PAYMENT, ?RECURRENT_PAYMENT({pending, #domain_InvoicePaymentPending{}})). -define(PAYPROC_PAYMENT(Payment, Refunds, Adjustments), #payproc_InvoicePayment{ - payment = Payment, - refunds = Refunds, - adjustments = Adjustments + payment = Payment, + refunds = [#payproc_InvoicePaymentRefund{refund = R, sessions = []} || R <- Refunds], + sessions = [], + legacy_refunds = Refunds, + adjustments = Adjustments }). -define(PAYPROC_PAYMENT, ?PAYPROC_PAYMENT(?PAYMENT, [?REFUND], [?ADJUSTMENT])). diff --git a/apps/capi/test/capi_invoice_access_token_tests_SUITE.erl b/apps/capi/test/capi_invoice_access_token_tests_SUITE.erl index b61458a..a98ab10 100644 --- a/apps/capi/test/capi_invoice_access_token_tests_SUITE.erl +++ b/apps/capi/test/capi_invoice_access_token_tests_SUITE.erl @@ -206,7 +206,8 @@ get_invoice_events_ok_test(Config) -> -spec get_invoice_payment_methods_ok_test(config()) -> _. get_invoice_payment_methods_ok_test(Config) -> - capi_ct_helper:mock_services([{invoicing, fun('ComputeTerms', _) -> {ok, ?TERM_SET} end}], Config), + capi_ct_helper:mock_services([{invoicing, fun('ComputeTerms', _) -> {ok, ?TERM_SET} end}, + {party_management, fun('Get', _) -> {ok, ?PARTY} end}], Config), {ok, _} = capi_client_invoices:get_invoice_payment_methods(?config(context, Config), ?STRING). -spec create_payment_ok_test(config()) -> diff --git a/apps/capi/test/capi_invoice_template_access_token_tests_SUITE.erl b/apps/capi/test/capi_invoice_template_access_token_tests_SUITE.erl index e2c99f8..fe08e51 100644 --- a/apps/capi/test/capi_invoice_template_access_token_tests_SUITE.erl +++ b/apps/capi/test/capi_invoice_template_access_token_tests_SUITE.erl @@ -148,5 +148,6 @@ get_invoice_template_ok_test(Config) -> -spec get_invoice_payment_methods_by_tpl_id_ok_test(config()) -> _. get_invoice_payment_methods_by_tpl_id_ok_test(Config) -> - capi_ct_helper:mock_services([{'invoice_templating', fun('ComputeTerms', _) -> {ok, ?TERM_SET} end}], Config), + capi_ct_helper:mock_services([{invoice_templating, fun('ComputeTerms', _) -> {ok, ?TERM_SET} end}, + {party_management, fun('Get', _) -> {ok, ?PARTY} end}], Config), {ok, _} = capi_client_invoice_templates:get_invoice_payment_methods(?config(context, Config), ?STRING). diff --git a/rebar.lock b/rebar.lock index 4342a43..56acbdb 100644 --- a/rebar.lock +++ b/rebar.lock @@ -23,7 +23,7 @@ {<<"cowlib">>,{pkg,<<"cowlib">>,<<"2.6.0">>},1}, {<<"damsel">>, {git,"git@github.com:rbkmoney/damsel.git", - {ref,"4339b6c286741fc9a4bc851a92eb58ebbcce81ab"}}, + {ref,"99a0e83ac031635ae9fc9947e5c0191fcb8cce45"}}, 0}, {<<"dmt_client">>, {git,"git@github.com:rbkmoney/dmt_client.git", diff --git a/schemes/swag b/schemes/swag index 8e7cd05..bb3cee3 160000 --- a/schemes/swag +++ b/schemes/swag @@ -1 +1 @@ -Subproject commit 8e7cd05c06b531f7548428b748db443d498c4b51 +Subproject commit bb3cee35cfc9723fa120461a37b630fe9aaf1f9d