mirror of
https://github.com/valitydev/capi-v2.git
synced 2024-11-06 10:05:21 +00:00
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
This commit is contained in:
parent
9bd2cf3098
commit
5b7bd81e35
@ -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{
|
||||
|
@ -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
|
||||
<<Prefix:ByteSize/bytes, "0">> -> strip_trailing_zeroes(Prefix);
|
||||
Fractional -> Fractional
|
||||
end.
|
||||
|
||||
build_decoded_crypto_amount(Integral, <<>>) ->
|
||||
Integral;
|
||||
build_decoded_crypto_amount(Integral, Fractional) ->
|
||||
<<Integral/binary, ".", Fractional/binary>>.
|
||||
|
||||
-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.
|
||||
|
@ -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}};
|
||||
|
@ -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.
|
||||
end.
|
||||
|
@ -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{} ->
|
||||
|
@ -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().
|
||||
|
@ -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])).
|
||||
|
@ -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()) ->
|
||||
|
@ -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).
|
||||
|
@ -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",
|
||||
|
@ -1 +1 @@
|
||||
Subproject commit 8e7cd05c06b531f7548428b748db443d498c4b51
|
||||
Subproject commit bb3cee35cfc9723fa120461a37b630fe9aaf1f9d
|
Loading…
Reference in New Issue
Block a user