diff --git a/apps/capi/include/capi_feature_schemas_legacy.hrl b/apps/capi/include/capi_feature_schemas_legacy.hrl deleted file mode 100644 index 12cd7d6..0000000 --- a/apps/capi/include/capi_feature_schemas_legacy.hrl +++ /dev/null @@ -1,9 +0,0 @@ --ifndef(__capi_feature_schemas_legacy__). --define(__capi_feature_schemas_legacy__, 42). - -% Marking some feature as `discriminator` will make featureset comparator consider two sets with different -% `discriminator` values as _different everywhere_ which usually helps with diff readability. --define(discriminator, -1). --define(difference, -1). - --endif. diff --git a/apps/capi/src/capi_bender.erl b/apps/capi/src/capi_bender.erl index 35899d0..0f0db37 100644 --- a/apps/capi/src/capi_bender.erl +++ b/apps/capi/src/capi_bender.erl @@ -1,7 +1,6 @@ -module(capi_bender). -include_lib("bender_proto/include/bender_thrift.hrl"). --include_lib("bender_proto/include/msgpack_thrift.hrl"). -type id() :: binary(). -type idempotent_key_prefix() :: binary() | atom(). @@ -9,17 +8,11 @@ -type issuer_id() :: dmsl_domain_thrift:'PartyID'() | dmsl_payment_processing_thrift:'UserID'(). -type idempotent_key() :: binary(). -type idempotent_key_params() :: {idempotent_key_prefix(), issuer_id(), external_id() | undefined}. -%% TODO(ED-287): remove identity_request() from below --opaque identity() :: {identity, identity_features(), identity_schema(), identity_request()}. + +-opaque identity() :: {identity, identity_features(), identity_schema()}. -type identity_features() :: feat:features(). - -%% TODO(ED-287): switch back to passing schema by value (`schemas:schema()`) -%% and not by name (`schema`) after V2 is removed -%% -type identity_schema() :: feat:schema(). --type identity_schema_name() :: atom(). --type identity_schema() :: identity_schema_name(). - +-type identity_schema() :: feat:schema(). -type identity_request() :: feat:request(). -type woody_context() :: woody_context:ctx(). -type context_data() :: #{binary() => term()}. @@ -34,6 +27,8 @@ -type external_id_conflict() :: {external_id_conflict, id(), difference(), identity_schema()}. -type generation_error() :: external_id_conflict(). +-type throws(_T) :: no_return(). + -export_type([id/0]). -export_type([external_id/0]). -export_type([issuer_id/0]). @@ -50,50 +45,32 @@ -export([gen_snowflake/3]). -export([gen_snowflake/4]). --export([try_gen_snowflake/3]). --export([try_gen_snowflake/4]). -export([gen_sequence/4]). -export([gen_sequence/5]). -export([gen_sequence/6]). --export([try_gen_sequence/6]). -export([gen_constant/4]). -export([gen_constant/5]). --export([try_gen_constant/4]). --export([try_gen_constant/5]). -export([make_identity/2]). -export([get_internal_id/2]). -define(BENDER_NAMESPACE, <<"capi">>). -%% deprecated --define(SCHEMA_VER2, 2). -define(SCHEMA_VER3, 3). -spec gen_snowflake(idempotent_key_params() | undefined, identity(), woody_context()) -> - {ok, id()} | {ok, id(), context_data()} | {error, generation_error()}. + id() | throws(generation_error()). gen_snowflake(IdempotentKey, Identity, WoodyContext) -> Context = #{}, gen_snowflake(IdempotentKey, Identity, WoodyContext, Context). -spec gen_snowflake(idempotent_key_params() | undefined, identity(), woody_context(), context_data()) -> - {ok, id()} | {ok, id(), context_data()} | {error, generation_error()}. + id() | throws(generation_error()). gen_snowflake(IdempotentKey, Identity, WoodyContext, Context) -> - IdSchema = {snowflake, #bender_SnowflakeSchema{}}, - generate_id(IdSchema, IdempotentKey, Identity, WoodyContext, Context). - --spec try_gen_snowflake(idempotent_key_params() | undefined, identity(), woody_context()) -> id(). -try_gen_snowflake(IdempotentKey, Identity, WoodyContext) -> - Context = #{}, - try_gen_snowflake(IdempotentKey, Identity, WoodyContext, Context). - --spec try_gen_snowflake(idempotent_key_params() | undefined, identity(), woody_context(), context_data()) -> - id(). -try_gen_snowflake(IdempotentKey, Identity, WoodyContext, Context) -> IdSchema = {snowflake, #bender_SnowflakeSchema{}}, try_generate_id(IdSchema, IdempotentKey, Identity, WoodyContext, Context). -spec gen_sequence(idempotent_key_params() | undefined, identity(), sequence_id(), woody_context()) -> - {ok, id()} | {ok, id(), context_data()} | {error, generation_error()}. + id() | throws(generation_error()). gen_sequence(IdempotentKey, Identity, SequenceID, WoodyContext) -> SequenceParams = #{}, gen_sequence(IdempotentKey, Identity, SequenceID, SequenceParams, WoodyContext). @@ -104,7 +81,7 @@ gen_sequence(IdempotentKey, Identity, SequenceID, WoodyContext) -> sequence_id(), sequence_params(), woody_context() -) -> {ok, id()} | {ok, id(), context_data()} | {error, generation_error()}. +) -> id() | throws(generation_error()). gen_sequence(IdempotentKey, Identity, SequenceID, SequenceParams, WoodyContext) -> Context = #{}, gen_sequence(IdempotentKey, Identity, SequenceID, SequenceParams, WoodyContext, Context). @@ -116,61 +93,27 @@ gen_sequence(IdempotentKey, Identity, SequenceID, SequenceParams, WoodyContext) sequence_params(), woody_context(), context_data() -) -> {ok, id()} | {ok, id(), context_data()} | {error, generation_error()}. +) -> id() | throws(generation_error()). gen_sequence(IdempotentKey, Identity, SequenceID, SequenceParams, WoodyContext, Context) -> IdSchema = build_sequence_schema(SequenceID, SequenceParams), - generate_id(IdSchema, IdempotentKey, Identity, WoodyContext, Context). - --spec try_gen_sequence( - idempotent_key_params() | undefined, - identity(), - sequence_id(), - sequence_params(), - woody_context(), - context_data() -) -> id() | no_return(). -try_gen_sequence(IdempotentKey, Identity, SequenceID, SequenceParams, WoodyContext, ContextData) -> - IdSchema = build_sequence_schema(SequenceID, SequenceParams), - try_generate_id(IdSchema, IdempotentKey, Identity, WoodyContext, ContextData). + try_generate_id(IdSchema, IdempotentKey, Identity, WoodyContext, Context). -spec gen_constant(idempotent_key_params(), identity(), constant_id(), woody_context()) -> - {ok, id()} | {ok, id(), context_data()} | {error, generation_error()}. + id() | throws(generation_error()). gen_constant(IdempotentKey, Identity, ConstantID, WoodyContext) -> Context = #{}, gen_constant(IdempotentKey, Identity, ConstantID, WoodyContext, Context). -spec gen_constant(idempotent_key_params(), identity(), constant_id(), woody_context(), context_data()) -> - {ok, id()} | {ok, id(), context_data()} | {error, generation_error()}. + id() | throws(generation_error()). gen_constant(IdempotentKey, Identity, ConstantID, WoodyContext, Context) -> - IdSchema = {constant, #bender_ConstantSchema{internal_id = ConstantID}}, - generate_id(IdSchema, IdempotentKey, Identity, WoodyContext, Context). - --spec try_gen_constant(idempotent_key_params(), identity(), constant_id(), woody_context()) -> id(). -try_gen_constant(IdempotentKey, Identity, ConstantID, WoodyContext) -> - Context = #{}, - try_gen_constant(IdempotentKey, Identity, ConstantID, WoodyContext, Context). - --spec try_gen_constant(idempotent_key_params(), identity(), constant_id(), woody_context(), context_data()) -> - id(). -try_gen_constant(IdempotentKey, Identity, ConstantID, WoodyContext, Context) -> IdSchema = {constant, #bender_ConstantSchema{internal_id = ConstantID}}, try_generate_id(IdSchema, IdempotentKey, Identity, WoodyContext, Context). -spec make_identity(identity_schema(), identity_request()) -> identity(). make_identity(Schema, Data) -> - Features = feat:read(read_schema(Schema), Data), - {identity, Features, Schema, Data}. - -transform_identity_to_deprecated_v2({identity, _NewFeatures, Schema, Data}) -> - LegacyFeatures = capi_idemp_features_legacy:read(read_schema_deprecated_v2(Schema), Data), - {identity, LegacyFeatures, Schema, Data}. - -%% TODO(ED-287): (see above) -read_schema(SchemaName) when is_atom(SchemaName) -> - capi_feature_schemas:SchemaName(). - -read_schema_deprecated_v2(SchemaName) when is_atom(SchemaName) -> - capi_feature_schemas_legacy:SchemaName(). + Features = feat:read(Schema, Data), + {identity, Features, Schema}. -spec get_internal_id(idempotent_key_params(), woody_context()) -> {ok, binary(), context_data()} | {error, internal_id_not_found}. @@ -208,14 +151,8 @@ try_generate_id(BenderIdSchema, IdempotentKey, Identity, WoodyContext, CtxData) case generate_id(BenderIdSchema, IdempotentKey, Identity, WoodyContext, CtxData) of {ok, ID} -> ID; - {error, {Err, ID, Difference, Schema}} when Err == external_id_conflict; Err == external_id_conflict_legacy -> - ReadableDiff = - case Err of - external_id_conflict -> - feat:list_diff_fields(read_schema(Schema), Difference); - external_id_conflict_legacy -> - capi_idemp_features_legacy:list_diff_fields(read_schema_deprecated_v2(Schema), Difference) - end, + {error, {external_id_conflict, ID, Difference, Schema}} -> + ReadableDiff = feat:list_diff_fields(Schema, Difference), logger:warning("This externalID: ~p, used in another request.~nDifference: ~p", [ID, ReadableDiff]), SourceID = get_external_id(IdempotentKey), throw({external_id_conflict, ID, SourceID, Schema}) @@ -241,19 +178,11 @@ make_idempotent_key({Prefix, PartyID, ExternalID}) -> bender_client:get_idempotent_key(?BENDER_NAMESPACE, Prefix, PartyID, ExternalID). bender_generate_id(BenderIdSchema, IdempKey, Identity, WoodyContext, CtxData) -> - {identity, Features, Schema, _Data} = Identity, + {identity, Features, Schema} = Identity, BenderCtx = build_bender_ctx(Features, CtxData), case bender_client:gen_id(IdempKey, BenderIdSchema, WoodyContext, BenderCtx) of {ok, ID} -> {ok, ID}; - {ok, ID, #{<<"version">> := ?SCHEMA_VER2} = SavedBenderCtx} -> - {identity, FeaturesDeprecated, Schema, _} = transform_identity_to_deprecated_v2(Identity), - check_idempotent_conflict_deprecated_v2( - ID, - FeaturesDeprecated, - SavedBenderCtx, - Schema - ); {ok, ID, #{<<"version">> := ?SCHEMA_VER3} = SavedBenderCtx} -> check_idempotent_conflict(ID, Features, SavedBenderCtx, Schema) end. @@ -283,20 +212,6 @@ check_idempotent_conflict(ID, Features, SavedBenderCtx, Schema) -> {error, {external_id_conflict, ID, Difference, Schema}} end. -%% Deprecated idempotent context - -check_idempotent_conflict_deprecated_v2(ID, Features, SavedBenderCtx, Schema) -> - #{ - <<"version">> := ?SCHEMA_VER2, - <<"features">> := OtherFeatures - } = SavedBenderCtx, - case capi_idemp_features_legacy:compare(Features, OtherFeatures) of - true -> - {ok, ID}; - {false, Difference} -> - {error, {external_id_conflict_legacy, ID, Difference, Schema}} - end. - -spec get_context_data(bender_context()) -> undefined | context_data(). get_context_data(Context) -> maps:get(<<"context_data">>, Context, #{}). diff --git a/apps/capi/src/capi_feature_schemas_legacy.erl b/apps/capi/src/capi_feature_schemas_legacy.erl deleted file mode 100644 index 310f946..0000000 --- a/apps/capi/src/capi_feature_schemas_legacy.erl +++ /dev/null @@ -1,984 +0,0 @@ --module(capi_feature_schemas_legacy). - --type schema() :: capi_idemp_features_legacy:schema(). - --include("capi_feature_schemas_legacy.hrl"). - --define(id, 1). --define(invoice_id, 2). --define(make_recurrent, 3). --define(flow, 4). --define(hold_exp, 5). --define(payer, 6). --define(payment_tool, 7). --define(token, 8). --define(bank_card, 9). --define(exp_date, 10). --define(terminal, 11). --define(terminal_type, 12). --define(wallet, 13). --define(provider, 14). --define(crypto, 15). --define(currency, 16). --define(mobile_commerce, 17). --define(operator, 18). --define(phone, 19). --define(customer, 20). --define(recurrent, 21). --define(invoice, 22). --define(payment, 23). --define(shop_id, 24). --define(amount, 25). --define(product, 26). --define(due_date, 27). --define(cart, 28). --define(quantity, 29). --define(price, 30). --define(tax, 31). --define(rate, 32). --define(bank_account, 33). --define(account, 34). --define(bank_bik, 35). --define(payment_resource, 36). --define(payment_session, 37). --define(lifetime, 38). --define(details, 39). --define(days, 40). --define(months, 41). --define(years, 42). --define(single_line, 43). --define(multiline, 44). --define(range, 45). --define(fixed, 46). --define(lower_bound, 47). --define(upper_bound, 48). --define(invoice_template_id, 49). --define(contact_info, 50). --define(email, 51). --define(phone_number, 52). --define(allocation, 53). --define(target, 54). --define(total, 55). --define(fee, 56). --define(share, 57). --define(matisse, 58). --define(exponent, 59). - --export([payment/0]). --export([invoice/0]). --export([invoice_template/0]). --export([refund/0]). --export([customer_binding/0]). --export([customer/0]). - --spec payment() -> schema(). -payment() -> - #{ - ?invoice_id => [<<"invoiceID">>], - ?make_recurrent => [<<"makeRecurrent">>], - ?flow => [ - <<"flow">>, - #{ - ?discriminator => [<<"type">>], - ?hold_exp => [<<"onHoldExpiration">>] - } - ], - ?payer => [ - <<"payer">>, - #{ - ?discriminator => [<<"payerType">>], - ?payment_tool => [<<"paymentTool">>, payment_tool_schema()], - ?customer => [<<"customerID">>], - ?recurrent => [ - <<"recurrentParentPayment">>, - #{ - ?invoice => [<<"invoiceID">>], - ?payment => [<<"paymentID">>] - } - ] - } - ] - }. - --spec invoice() -> schema(). -invoice() -> - #{ - ?shop_id => [<<"shopID">>], - ?amount => [<<"amount">>], - ?currency => [<<"currency">>], - ?product => [<<"product">>], - ?due_date => [<<"dueDate">>], - ?cart => [<<"cart">>, {set, cart_line_schema()}], - ?bank_account => [<<"bankAccount">>, bank_account_schema()], - ?invoice_template_id => [<<"invoiceTemplateID">>], - ?allocation => [<<"allocation">>, {set, allocation_transaction()}] - }. - --spec invoice_template() -> schema(). -invoice_template() -> - #{ - ?shop_id => [<<"shopID">>], - ?lifetime => [<<"lifetime">>, lifetime_schema()], - ?details => [<<"details">>, invoice_template_details_schema()] - }. - --spec invoice_template_details_schema() -> schema(). -invoice_template_details_schema() -> - #{ - ?discriminator => [<<"templateType">>], - ?single_line => #{ - ?product => [<<"product">>], - ?price => [<<"price">>, invoice_template_line_cost()], - ?tax => [<<"taxMode">>, tax_mode_schema()] - }, - ?multiline => #{ - ?currency => [<<"currency">>], - ?cart => [<<"cart">>, {set, cart_line_schema()}] - } - }. - --spec refund() -> schema(). -refund() -> - #{ - ?amount => [<<"amount">>], - ?currency => [<<"currency">>], - ?cart => [<<"cart">>, {set, cart_line_schema()}], - ?allocation => [<<"allocation">>, {set, allocation_transaction()}] - }. - --spec customer() -> schema(). -customer() -> - #{ - ?shop_id => [<<"shopID">>], - ?contact_info => [<<"contactInfo">>, contact_info_schema()] - }. - --spec customer_binding() -> schema(). -customer_binding() -> - #{ - ?payment_resource => [ - <<"paymentResource">>, - #{ - ?payment_session => [<<"paymentSession">>], - ?payment_tool => [<<"paymentTool">>, payment_tool_schema()] - } - ] - }. - --spec payment_tool_schema() -> schema(). -payment_tool_schema() -> - #{ - ?discriminator => [<<"type">>], - ?bank_card => #{ - ?token => [<<"token">>], - ?exp_date => [<<"exp_date">>] - }, - ?terminal => #{ - ?discriminator => [<<"terminal_type">>] - }, - ?wallet => #{ - ?provider => [<<"provider">>], - ?id => [<<"id">>], - ?token => [<<"token">>] - }, - ?crypto => #{ - ?currency => [<<"currency">>] - }, - ?mobile_commerce => #{ - ?operator => [<<"operator">>], - ?phone => [<<"phone">>] - } - }. - --spec allocation_transaction() -> schema(). -allocation_transaction() -> - #{ - ?target => [<<"target">>, allocation_target()], - ?discriminator => [<<"allocationBodyType">>], - ?amount => [<<"amount">>], - ?total => [<<"total">>], - ?currency => [<<"currency">>], - ?fee => [ - <<"fee">>, - #{ - ?target => [<<"target">>, allocation_target()], - ?discriminator => [<<"allocationFeeType">>], - ?amount => [<<"amount">>], - ?share => [<<"share">>, decimal()] - } - ], - ?cart => [<<"cart">>, {set, cart_line_schema()}] - }. - --spec allocation_target() -> schema(). -allocation_target() -> - #{ - ?discriminator => [<<"allocationTargetType">>], - ?shop_id => [<<"shopID">>] - }. - --spec decimal() -> schema(). -decimal() -> - #{ - ?matisse => [<<"m">>], - ?exponent => [<<"exp">>] - }. - --spec cart_line_schema() -> schema(). -cart_line_schema() -> - #{ - ?product => [<<"product">>], - ?quantity => [<<"quantity">>], - ?price => [<<"price">>], - ?tax => [<<"taxMode">>, tax_mode_schema()] - }. - --spec tax_mode_schema() -> schema(). -tax_mode_schema() -> - #{ - ?discriminator => [<<"type">>], - ?rate => [<<"rate">>] - }. - --spec bank_account_schema() -> schema(). -bank_account_schema() -> - #{ - ?discriminator => [<<"accountType">>], - ?account => [<<"account">>], - ?bank_bik => [<<"bankBik">>] - }. - -invoice_template_line_cost() -> - #{ - ?discriminator => [<<"costType">>], - ?range => #{ - ?currency => [<<"currency">>], - ?range => [<<"range">>, cost_amount_range()] - }, - ?fixed => #{ - ?currency => [<<"currency">>], - ?amount => [<<"amount">>] - } - %% Unlim has no params and is fully contained in discriminator - }. - --spec cost_amount_range() -> schema(). -cost_amount_range() -> - #{ - ?upper_bound => [<<"upperBound">>], - ?lower_bound => [<<"lowerBound">>] - }. - --spec lifetime_schema() -> schema(). -lifetime_schema() -> - #{ - ?days => [<<"days">>], - ?months => [<<"months">>], - ?years => [<<"years">>] - }. - --spec contact_info_schema() -> schema(). -contact_info_schema() -> - #{ - ?email => [<<"email">>], - ?phone_number => [<<"phoneNumber">>] - }. - --ifdef(TEST). --include_lib("eunit/include/eunit.hrl"). --include_lib("capi_dummy_data.hrl"). - -deep_merge(M1, M2) -> - maps:fold( - fun - (K, V, MAcc) when is_map(V) -> - Value = deep_merge(maps:get(K, MAcc, #{}), V), - MAcc#{K => Value}; - (K, V, MAcc) -> - MAcc#{K => V} - end, - M1, - M2 - ). - -deep_fetch(Map, Keys) -> - lists:foldl(fun(K, M) -> maps:get(K, M) end, Map, Keys). - -hash(Term) -> - capi_idemp_features_legacy:hash(Term). - -read(Schema, Request) -> - capi_idemp_features_legacy:read(Schema, Request). - -compare(Features1, Features2) -> - capi_idemp_features_legacy:compare(Features1, Features2). - -list_diff_fields(Schema, Diff) -> - capi_idemp_features_legacy:list_diff_fields(Schema, Diff). - --spec test() -> _. - --spec read_payment_features_test() -> _. - -read_payment_features_test() -> - PayerType = <<"PaymentResourcePayer">>, - ToolType = <<"bank_card">>, - Token = <<"cds token">>, - CardHolder = <<"0x42">>, - Category = <<"BUSINESS">>, - ExpDate = {exp_date, 02, 2022}, - Flow = <<"PaymentFlowHold">>, - Request = #{ - <<"flow">> => #{ - <<"type">> => Flow - }, - <<"payer">> => #{ - <<"payerType">> => PayerType, - <<"paymentTool">> => #{ - <<"type">> => ToolType, - <<"token">> => Token, - <<"exp_date">> => ExpDate, - <<"cardholder_name">> => CardHolder, - <<"category">> => Category - } - } - }, - Payer = #{ - ?invoice_id => undefined, - ?make_recurrent => undefined, - ?flow => #{ - ?discriminator => hash(Flow), - ?hold_exp => undefined - }, - ?payer => #{ - ?discriminator => hash(PayerType), - ?customer => undefined, - ?recurrent => undefined, - ?payment_tool => #{ - ?discriminator => hash(ToolType), - ?bank_card => #{ - ?exp_date => hash(ExpDate), - ?token => hash(Token) - }, - ?crypto => #{?currency => undefined}, - ?mobile_commerce => #{ - ?operator => undefined, - ?phone => undefined - }, - ?terminal => #{?discriminator => undefined}, - ?wallet => #{ - ?id => undefined, - ?provider => undefined, - ?token => hash(Token) - } - } - } - }, - Features = read(payment(), Request), - ?assertEqual(Payer, Features). - --spec compare_payment_bank_card_test() -> _. -compare_payment_bank_card_test() -> - Token2 = <<"cds token 2">>, - CardHolder2 = <<"Cake">>, - - PaymentTool1 = bank_card(), - PaymentTool2 = PaymentTool1#{ - <<"token">> => Token2, - <<"cardholder_name">> => CardHolder2 - }, - Request1 = payment_params(PaymentTool1), - Request2 = payment_params(PaymentTool2), - - common_compare_tests(payment(), Request1, Request2, [ - <<"payer.paymentTool.token">> - ]). - --spec compare_different_payment_tool_test() -> _. -compare_different_payment_tool_test() -> - ToolType2 = <<"wallet">>, - Token2 = <<"wallet token">>, - PaymentTool1 = bank_card(), - PaymentTool2 = #{ - <<"type">> => ToolType2, - <<"token">> => Token2 - }, - Request1 = payment_params(PaymentTool1), - Request2 = payment_params(PaymentTool2), - - common_compare_tests(payment(), Request1, Request2, [<<"payer.paymentTool">>]). - --spec feature_multi_accessor_test() -> _. -feature_multi_accessor_test() -> - Request1 = #{ - <<"payer">> => #{ - <<"payerType">> => <<"PaymentResourcePayer">>, - <<"paymentTool">> => #{ - <<"wrapper">> => bank_card() - } - } - }, - Request2 = deep_merge(Request1, #{ - <<"payer">> => #{ - <<"paymentTool">> => #{ - <<"wrapper">> => #{ - <<"token">> => <<"cds token 2">>, - <<"cardholder_name">> => <<"Cake">> - } - } - } - }), - Schema = #{ - <<"payer">> => [ - <<"payer">>, - #{ - <<"type">> => [<<"payerType">>], - <<"tool">> => [ - <<"paymentTool">>, - <<"wrapper">>, - #{ - <<"$type">> => [<<"type">>], - <<"bank_card">> => #{ - <<"token">> => [<<"token">>], - <<"exp_date">> => [<<"exp_date">>] - } - } - ] - } - ] - }, - common_compare_tests(Schema, Request1, Request2, [ - <<"payer.paymentTool.wrapper.token">> - ]). - --spec read_payment_customer_features_value_test() -> _. -read_payment_customer_features_value_test() -> - PayerType = <<"CustomerPayer">>, - CustomerID = <<"some customer id">>, - Request = #{ - <<"payer">> => #{ - <<"payerType">> => PayerType, - <<"customerID">> => CustomerID - } - }, - Features = read(payment(), Request), - ?assertEqual( - #{ - ?invoice_id => undefined, - ?make_recurrent => undefined, - ?flow => undefined, - ?payer => #{ - ?discriminator => hash(PayerType), - ?customer => hash(CustomerID), - ?recurrent => undefined, - ?payment_tool => undefined - } - }, - Features - ). - --spec read_invoice_features_test() -> _. -read_invoice_features_test() -> - ShopID = <<"shopus">>, - Cur = <<"XXX">>, - Prod1 = <<"yellow duck">>, - Prod2 = <<"blue duck">>, - DueDate = <<"2019-08-24T14:15:22Z">>, - Price1 = 10000, - Price2 = 20000, - Quantity = 1, - Product = #{ - ?product => hash(Prod1), - ?quantity => hash(Quantity), - ?price => hash(Price1), - ?tax => undefined - }, - Product2 = Product#{ - ?product => hash(Prod2), - ?price => hash(Price2) - }, - BankAccount = #{ - ?discriminator => hash(<<"InvoiceRussianBankAccount">>), - ?account => hash(<<"12345678901234567890">>), - ?bank_bik => hash(<<"123456789">>) - }, - Invoice = #{ - ?amount => undefined, - ?currency => hash(Cur), - ?shop_id => hash(ShopID), - ?product => undefined, - ?due_date => hash(DueDate), - ?bank_account => BankAccount, - ?cart => [ - [1, Product], - [0, Product2] - ], - ?invoice_template_id => undefined, - ?allocation => undefined - }, - Request = #{ - <<"externalID">> => <<"externalID">>, - <<"dueDate">> => DueDate, - <<"shopID">> => ShopID, - <<"currency">> => Cur, - <<"description">> => <<"Wild birds.">>, - <<"bankAccount">> => #{ - <<"accountType">> => <<"InvoiceRussianBankAccount">>, - <<"account">> => <<"12345678901234567890">>, - <<"bankBik">> => <<"123456789">> - }, - <<"cart">> => [ - #{<<"product">> => Prod2, <<"quantity">> => 1, <<"price">> => Price2}, - #{<<"product">> => Prod1, <<"quantity">> => 1, <<"price">> => Price1, <<"not feature">> => <<"hmm">>} - ], - <<"metadata">> => #{} - }, - - Features = read(invoice(), Request), - ?assertEqual(Invoice, Features), - - TemplateID = <<"42">>, - RequestWithTemplate = Request#{<<"invoiceTemplateID">> => TemplateID}, - FeaturesWithTemplate = read(invoice(), RequestWithTemplate), - ?assertEqual(hash(TemplateID), maps:get(?invoice_template_id, FeaturesWithTemplate)). - --spec compare_invoices_features_test() -> _. -compare_invoices_features_test() -> - ShopID = <<"shopus">>, - Cur = <<"RUB">>, - Prod1 = <<"yellow duck">>, - Prod2 = <<"blue duck">>, - Price1 = 10000, - Price2 = 20000, - Product = #{ - <<"product">> => Prod1, - <<"quantity">> => 1, - <<"price">> => Price1, - <<"taxMode">> => #{ - <<"type">> => <<"InvoiceLineTaxVAT">>, - <<"rate">> => <<"10%">> - } - }, - Request1 = #{ - <<"shopID">> => ShopID, - <<"currency">> => Cur, - <<"cart">> => [Product] - }, - Request2 = deep_merge(Request1, #{ - <<"cart">> => [#{<<"product">> => Prod2, <<"price">> => Price2}] - }), - Request3 = deep_merge(Request1, #{ - <<"cart">> => [#{<<"product">> => Prod2, <<"price">> => Price2, <<"quantity">> => undefined}] - }), - Schema = invoice(), - Invoice1 = read(Schema, Request1), - InvoiceChg1 = read(Schema, Request1#{ - <<"cart">> => [ - Product#{ - <<"price">> => Price2, - <<"taxMode">> => #{ - <<"rate">> => <<"18%">> - } - } - ] - }), - Invoice2 = read(Schema, Request2), - InvoiceWithFullCart = read(Schema, Request3), - ?assertEqual( - {false, #{ - ?cart => #{ - 0 => #{ - ?price => ?difference, - ?product => ?difference, - ?quantity => ?difference, - ?tax => ?difference - } - } - }}, - compare(Invoice2, Invoice1) - ), - ?assert(compare(Invoice1, Invoice1)), - %% Feature was deleted - ?assert(compare(InvoiceWithFullCart, Invoice2)), - %% Feature was add - ?assert(compare(Invoice2, InvoiceWithFullCart)), - %% When second request didn't contain feature, this situation detected as conflict. - ?assertEqual( - {false, #{?cart => ?difference}}, - compare(Invoice1#{?cart => undefined}, Invoice1) - ), - - {false, Diff} = compare(Invoice1, InvoiceChg1), - ?assertEqual( - [<<"cart.0.price">>, <<"cart.0.taxMode.rate">>], - list_diff_fields(Schema, Diff) - ), - ?assert(compare(Invoice1, Invoice1#{?cart => undefined})). - --spec read_customer_features_test() -> _. -read_customer_features_test() -> - Request = ?CUSTOMER_PARAMS, - Features = #{ - ?shop_id => hash(?STRING), - ?contact_info => #{ - ?email => hash(<<"bla@bla.ru">>), - ?phone_number => undefined - } - }, - ?assertEqual( - Features, - read(customer(), Request) - ). - --spec compare_customer_features_test() -> _. -compare_customer_features_test() -> - Request = ?CUSTOMER_PARAMS, - RequestSame = Request#{ - <<"partyID">> => <<"ANOTHER PARTY">>, - - <<"metadata">> => #{<<"text">> => <<"sample text">>} - }, - RequestDifferent = Request#{ - <<"shopID">> => hash(<<"Another shop">>), - <<"contactInfo">> => #{ - <<"email">> => hash(<<"bla@example.com">>), - <<"phoneNumber">> => <<"8-800-555-35-35">> - } - }, - common_compare_tests( - customer(), - Request, - RequestSame, - RequestDifferent, - [ - <<"shopID">>, - <<"contactInfo.email">>, - <<"contactInfo.phoneNumber">> - ] - ). - --spec read_customer_binding_features_test() -> _. -read_customer_binding_features_test() -> - Session = ?TEST_PAYMENT_SESSION(<<"Session">>), - Tool = ?TEST_PAYMENT_TOOL(<<"visa">>, <<"TOKEN">>), - Request = payment_resource(Session, Tool), - Features = #{ - ?payment_resource => #{ - ?payment_session => hash(Session), - ?payment_tool => #{ - ?discriminator => hash(<<"bank_card">>), - ?bank_card => #{ - ?token => hash(<<"TOKEN">>), - ?exp_date => hash(<<"12/2012">>) - }, - - ?terminal => #{ - ?discriminator => undefined - }, - ?wallet => #{ - ?provider => undefined, - ?id => undefined, - ?token => hash(<<"TOKEN">>) - }, - ?crypto => #{ - ?currency => undefined - }, - ?mobile_commerce => #{ - ?operator => undefined, - ?phone => undefined - } - } - } - }, - - ?assertEqual( - Features, - read(customer_binding(), Request) - ). - --spec compare_customer_binding_features_test() -> _. -compare_customer_binding_features_test() -> - Session1 = ?TEST_PAYMENT_SESSION(<<"Session1">>), - Tool1 = ?TEST_PAYMENT_TOOL(<<"visa">>), - Request1 = payment_resource(Session1, Tool1), - - Session2 = ?TEST_PAYMENT_SESSION(<<"Session2">>), - Tool2 = ?TEST_PAYMENT_TOOL(<<"mastercard">>)#{<<"exp_date">> => <<"01/2020">>}, - Request2 = payment_resource(Session2, Tool2), - - common_compare_tests(customer_binding(), Request1, Request2, [ - <<"paymentResource.paymentTool.exp_date">>, - <<"paymentResource.paymentSession">> - ]). - -%% Add invoice_template tests - --spec read_invoice_template_features_test() -> _. -read_invoice_template_features_test() -> - ShopID = <<"1">>, - Request = #{ - <<"shopID">> => ShopID, - <<"lifetime">> => lifetime_dummy(1, 2, 3), - <<"details">> => ?INVOICE_TMPL_DETAILS_PARAMS(42) - }, - Features = #{ - ?shop_id => hash(ShopID), - ?lifetime => #{ - ?days => hash(1), - ?months => hash(2), - ?years => hash(3) - }, - ?details => #{ - ?discriminator => hash(<<"InvoiceTemplateMultiLine">>), - ?single_line => #{ - ?product => undefined, - ?price => undefined, - ?tax => undefined - }, - ?multiline => #{ - ?currency => hash(<<"RUB">>), - ?cart => [ - [ - 1, - #{ - ?product => hash(?STRING), - ?quantity => hash(42), - ?price => hash(?INTEGER), - ?tax => #{?discriminator => hash(<<"InvoiceLineTaxVAT">>), ?rate => hash(<<"18%">>)} - } - ], - [ - 0, - #{ - ?product => hash(?STRING), - ?quantity => hash(42), - ?price => hash(?INTEGER), - ?tax => undefined - } - ] - ] - } - } - }, - - ?assertEqual( - Features, - read(invoice_template(), Request) - ). - --spec compare_invoice_template_features_test() -> _. -compare_invoice_template_features_test() -> - ShopID1 = <<"1">>, - ShopID2 = <<"2">>, - Request1 = #{ - <<"shopID">> => ShopID1, - <<"lifetime">> => lifetime_dummy(1, 2, 3), - <<"details">> => ?INVOICE_TMPL_DETAILS_PARAMS(42) - }, - Request2 = deep_merge( - Request1, - #{ - <<"shopID">> => ShopID2, - <<"lifetime">> => lifetime_dummy(1, 2, 42), - <<"details">> => #{ - <<"currency">> => ?USD, - <<"cart">> => [hd(deep_fetch(Request1, [<<"details">>, <<"cart">>]))] - } - } - ), - - common_compare_tests(invoice_template(), Request1, Request2, [ - <<"shopID">>, - <<"lifetime.years">>, - <<"details.currency">>, - <<"details.cart">> - ]). - --spec read_allocation_transaction_test_() -> _. -read_allocation_transaction_test_() -> - Request1 = ?ALLOCATION_TRANSACTION_PARAMS, - Features1 = #{ - ?target => #{ - ?discriminator => hash(<<"AllocationTargetShop">>), - ?shop_id => hash(?STRING) - }, - ?discriminator => hash(<<"AllocationBodyTotal">>), - ?amount => undefined, - ?total => hash(?INTEGER), - ?currency => hash(?USD), - ?fee => #{ - ?target => #{ - ?discriminator => hash(<<"AllocationTargetShop">>), - ?shop_id => hash(?STRING) - }, - ?discriminator => hash(<<"AllocationFeeShare">>), - ?amount => hash(?INTEGER), - ?share => #{ - ?matisse => hash(?INTEGER), - ?exponent => hash(?INTEGER) - } - }, - ?cart => [ - [ - 0, - #{ - ?product => hash(?STRING), - ?quantity => hash(?INTEGER), - ?price => hash(?INTEGER), - ?tax => undefined - } - ] - ] - }, - Request2 = Request1#{ - <<"fee">> => #{ - <<"target">> => ?ALLOCATION_TARGET, - <<"allocationFeeType">> => <<"AllocationFeeFixed">>, - <<"amount">> => 1024 - } - }, - Features2 = Features1#{ - ?fee => #{ - ?target => #{ - ?discriminator => hash(<<"AllocationTargetShop">>), - ?shop_id => hash(?STRING) - }, - ?discriminator => hash(<<"AllocationFeeFixed">>), - ?amount => hash(1024), - ?share => undefined - } - }, - [ - ?_assertEqual(Features1, read(allocation_transaction(), Request1)), - ?_assertEqual(Features2, read(allocation_transaction(), Request2)) - ]. - --spec compare_allocation_transaction_test() -> _. -compare_allocation_transaction_test() -> - Request1 = ?ALLOCATION_TRANSACTION_PARAMS, - Request2 = ?ALLOCATION_TRANSACTION_PARAMS#{ - <<"total">> => 1024, - <<"amount">> => 512, - <<"fee">> => #{ - <<"target">> => ?ALLOCATION_TARGET, - <<"allocationFeeType">> => <<"AllocationFeeFixed">>, - <<"amount">> => ?INTEGER, - <<"share">> => undefined - } - }, - Request3 = #{ - <<"target">> => ?ALLOCATION_TARGET#{<<"shopID">> => <<"SomeShop">>}, - <<"allocationBodyType">> => <<"AllocationBodyAmount">>, - <<"amount">> => ?INTEGER, - <<"currency">> => ?RUB, - <<"cart">> => [ - #{<<"product">> => ?STRING, <<"quantity">> => 1, <<"price">> => ?INTEGER} - ] - }, - Request4 = Request1#{ - <<"fee">> => deep_merge(maps:get(<<"fee">>, Request1), #{ - <<"amount">> => 1024, - <<"share">> => #{<<"m">> => 1024, <<"exp">> => 1024} - }) - }, - common_compare_tests(allocation_transaction(), Request1, Request2, [ - <<"amount">>, <<"total">>, <<"fee">> - ]), - common_compare_tests(allocation_transaction(), Request1, Request3, [ - <<"target.shopID">>, <<"allocationBodyType">>, <<"currency">>, <<"amount">>, <<"cart.0.quantity">> - ]), - common_compare_tests(allocation_transaction(), Request1, Request4, [ - <<"fee.amount">>, <<"fee.share.m">>, <<"fee.share.exp">> - ]). - --spec demo_compare_allocation_transaction_test() -> _. -demo_compare_allocation_transaction_test() -> - Request1 = ?ALLOCATION_TRANSACTION_PARAMS, - Request2 = #{ - <<"allocationBodyType">> => <<"AllocationBodyAmount">> - }, - Request3 = #{ - <<"fee">> => deep_merge(maps:get(<<"fee">>, Request1), #{ - <<"allocationFeeType">> => <<"AllocationFeeFixed">> - }) - }, - common_compare_tests(allocation_transaction(), Request1, Request2, [ - <<"allocationBodyType">> - ]), - common_compare_tests(allocation_transaction(), Request1, Request3, [ - <<"fee">> - ]). - -payment_resource(Session, Tool) -> - #{ - <<"paymentResource">> => #{ - <<"paymentSession">> => Session, - <<"paymentTool">> => Tool - } - }. - -payment_params(ExternalID, MakeRecurrent) -> - genlib_map:compact(#{ - <<"externalID">> => ExternalID, - <<"flow">> => #{<<"type">> => <<"PaymentFlowInstant">>}, - <<"makeRecurrent">> => MakeRecurrent, - <<"metadata">> => #{<<"bla">> => <<"*">>}, - <<"processingDeadline">> => <<"5m">> - }). - -payment_params(ExternalID, Jwe, ContactInfo, MakeRecurrent) -> - Params = payment_params(ExternalID, MakeRecurrent), - genlib_map:compact(Params#{ - <<"payer">> => #{ - <<"payerType">> => <<"PaymentResourcePayer">>, - <<"paymentSession">> => <<"payment.session">>, - <<"paymentToolToken">> => Jwe, - <<"contactInfo">> => ContactInfo - } - }). - -payment_params(PaymentTool) -> - Params = payment_params(<<"EID">>, <<"Jwe">>, #{}, false), - PaymentParams = deep_merge(Params, #{<<"payer">> => #{<<"paymentTool">> => PaymentTool}}), - PaymentParams. - -bank_card() -> - #{ - <<"type">> => <<"bank_card">>, - <<"token">> => <<"cds token">>, - <<"payment_system">> => <<"visa">>, - <<"bin">> => <<"411111">>, - <<"last_digits">> => <<"1111">>, - <<"exp_date">> => <<"2019-08-24T14:15:22Z">>, - <<"cardholder_name">> => <<"Degus Degusovich">>, - <<"is_cvv_empty">> => false - }. - -lifetime_dummy(Days, Months, Years) -> - #{ - <<"days">> => Days, - <<"months">> => Months, - <<"years">> => Years - }. - -common_compare_tests(Schema, Request, RequestDifferent, DiffFeatures) -> - common_compare_tests(Schema, Request, Request, RequestDifferent, DiffFeatures). - -common_compare_tests(Schema, Request, RequestWithIgnoredFields, RequestDifferent, DiffFeatures) -> - Features = read(Schema, Request), - FeaturesIgnored = read(Schema, RequestWithIgnoredFields), - FeaturesDifferent = read(Schema, RequestDifferent), - - %% Equal to self - ?assertEqual(true, compare(Features, Features)), - %% Equal to feature-wise same request - ?assertEqual(true, compare(Features, FeaturesIgnored)), - - %% Has correct diff with different request - Result = compare(Features, FeaturesDifferent), - ?assertMatch({false, _}, Result), - - {false, Diff} = Result, - ?assertEqual(lists:sort(DiffFeatures), lists:sort(list_diff_fields(Schema, Diff))). - --endif. diff --git a/apps/capi/src/capi_handler_customers.erl b/apps/capi/src/capi_handler_customers.erl index a2f5c7e..dd82f14 100644 --- a/apps/capi/src/capi_handler_customers.erl +++ b/apps/capi/src/capi_handler_customers.erl @@ -6,7 +6,13 @@ -export([prepare/3]). --import(capi_handler_utils, [general_error/2, logic_error/1, logic_error/2, map_service_result/1]). +-import(capi_handler_utils, [ + general_error/2, + logic_error/1, + logic_error/2, + conflict_error/1, + map_service_result/1 +]). -spec prepare( OperationID :: capi_handler:operation_id(), @@ -45,7 +51,7 @@ prepare('CreateCustomer' = OperationID, Req, Context) -> end catch throw:{external_id_conflict, ID, UsedExternalID, _Schema} -> - {ok, logic_error('externalIDConflict', {ID, UsedExternalID})} + {ok, conflict_error({ID, UsedExternalID})} end end, {ok, #{authorize => Authorize, process => Process}}; @@ -165,7 +171,7 @@ prepare('CreateBinding' = OperationID, Req, Context) -> {error, invalid_payment_session} -> {ok, logic_error('invalidPaymentSession', <<"Specified payment session is invalid">>)}; {error, {external_id_conflict, ID, UsedExternalID, _Schema}} -> - {ok, logic_error('externalIDConflict', {ID, UsedExternalID})} + {ok, conflict_error({ID, UsedExternalID})} end end, {ok, #{authorize => Authorize, process => Process}}; @@ -290,8 +296,8 @@ mask_customer_notfound(Resolution) -> generate_customer_id(OperationID, PartyID, CustomerParams, #{woody_context := WoodyContext}) -> ExternalID = maps:get(<<"externalID">>, CustomerParams, undefined), IdempKey = {OperationID, PartyID, ExternalID}, - Identity = capi_bender:make_identity(customer, CustomerParams), - capi_bender:try_gen_snowflake(IdempKey, Identity, WoodyContext). + Identity = capi_bender:make_identity(capi_feature_schemas:customer(), CustomerParams), + capi_bender:gen_snowflake(IdempKey, Identity, WoodyContext). encode_customer_params(CustomerID, PartyID, Params) -> #payproc_CustomerParams{ @@ -323,15 +329,18 @@ generate_binding_ids(OperationID, CustomerBindingParams, Context = #{woody_conte CustomerBindingParams ), - Identity = capi_bender:make_identity(customer_binding, CustomerBindingParamsEncrypted), + Identity = capi_bender:make_identity( + capi_feature_schemas:customer_binding(), + CustomerBindingParamsEncrypted + ), OperationIDBin = erlang:atom_to_binary(OperationID), - CustomerBindingID = capi_bender:try_gen_snowflake( + CustomerBindingID = capi_bender:gen_snowflake( {<>, UserID, ExternalID}, Identity, WoodyContext ), - RecPaymentToolID = capi_bender:try_gen_snowflake( + RecPaymentToolID = capi_bender:gen_snowflake( {<>, UserID, ExternalID}, Identity, WoodyContext diff --git a/apps/capi/src/capi_handler_decoder_invoicing.erl b/apps/capi/src/capi_handler_decoder_invoicing.erl index 7e0b520..51f8114 100644 --- a/apps/capi/src/capi_handler_decoder_invoicing.erl +++ b/apps/capi/src/capi_handler_decoder_invoicing.erl @@ -183,11 +183,9 @@ decode_payer( customer_id = ID }} ) -> - PaymentToolSwag = capi_handler_decoder_party:decode_payment_tool(PaymentTool), #{ <<"payerType">> => <<"CustomerPayer">>, <<"customerID">> => ID, - <<"paymentToolToken">> => capi_handler_decoder_party:wrap_payment_tool_token(PaymentToolSwag), <<"paymentToolDetails">> => capi_handler_decoder_party:decode_payment_tool_details(PaymentTool) }; decode_payer( @@ -197,10 +195,8 @@ decode_payer( contact_info = ContactInfo }} ) -> - PaymentToolSwag = capi_handler_decoder_party:decode_payment_tool(PaymentTool), #{ <<"payerType">> => <<"RecurrentPayer">>, - <<"paymentToolToken">> => capi_handler_decoder_party:wrap_payment_tool_token(PaymentToolSwag), <<"paymentToolDetails">> => capi_handler_decoder_party:decode_payment_tool_details(PaymentTool), <<"contactInfo">> => capi_handler_decoder_party:decode_contact_info(ContactInfo), <<"recurrentParentPayment">> => decode_recurrent_parent(RecurrentParent) diff --git a/apps/capi/src/capi_handler_decoder_party.erl b/apps/capi/src/capi_handler_decoder_party.erl index 5eb5261..33e777c 100644 --- a/apps/capi/src/capi_handler_decoder_party.erl +++ b/apps/capi/src/capi_handler_decoder_party.erl @@ -19,8 +19,6 @@ -export([decode_payment_tool/1]). -export([decode_payment_tool_details/1]). --export([wrap_payment_tool_token/1]). - %% -spec decode_shop_location(capi_handler_encoder:encode_data()) -> capi_handler_decoder_utils:decode_data(). @@ -177,74 +175,18 @@ decode_payment_tool({mobile_commerce, MobileCommerce}) -> decode_payment_tool({crypto_currency, CryptoCurrency}) -> decode_crypto_wallet(CryptoCurrency). --spec wrap_payment_tool_token(capi_handler_decoder_utils:decode_data()) -> binary(). -wrap_payment_tool_token(#{<<"type">> := <<"bank_card">>} = BankCard) -> - Fields = [ - <<"token">>, - <<"payment_system">>, - <<"bin">>, - <<"masked_pan">>, - <<"token_provider">>, - <<"issuer_country">>, - <<"bank_name">>, - <<"metadata">>, - <<"is_cvv_empty">> - ], - BankCard1 = maps:with(Fields, BankCard), - capi_utils:map_to_base64url(BankCard1); -wrap_payment_tool_token(#{<<"type">> := <<"payment_terminal">>} = PaymentTerminal) -> - capi_utils:map_to_base64url(PaymentTerminal); -wrap_payment_tool_token(#{<<"type">> := <<"digital_wallet">>} = DigitalWallet) -> - capi_utils:map_to_base64url(DigitalWallet); -wrap_payment_tool_token(#{<<"type">> := <<"crypto_currency">>} = CryptoCurrency) -> - capi_utils:map_to_base64url(CryptoCurrency); -wrap_payment_tool_token(#{<<"type">> := <<"mobile_commerce">>} = MobileCommerce) -> - capi_utils:map_to_base64url(MobileCommerce). - decode_bank_card(#domain_BankCard{ 'token' = Token, 'payment_system' = PaymentSystem, - 'bin' = Bin, - 'last_digits' = LastDigits, - 'payment_token' = BankCardTokenServiceRef, - 'issuer_country' = IssuerCountry, - 'bank_name' = BankName, - 'metadata' = Metadata, - 'is_cvv_empty' = IsCVVEmpty, - 'exp_date' = ExpDate, - 'cardholder_name' = CardHolder - % 'tokenization_method' = TokenizationMethod + 'exp_date' = ExpDate }) -> genlib_map:compact(#{ <<"type">> => <<"bank_card">>, <<"token">> => Token, <<"payment_system">> => capi_handler_decoder_utils:decode_payment_system_ref(PaymentSystem), - <<"bin">> => Bin, - <<"masked_pan">> => LastDigits, - <<"token_provider">> => capi_utils:maybe( - BankCardTokenServiceRef, - fun capi_handler_decoder_utils:decode_bank_card_token_service_ref/1 - ), - <<"issuer_country">> => IssuerCountry, - <<"bank_name">> => BankName, - <<"metadata">> => decode_bank_card_metadata(Metadata), - <<"is_cvv_empty">> => decode_bank_card_cvv_flag(IsCVVEmpty), - <<"exp_date">> => ExpDate, - <<"cardholder_name">> => CardHolder - % TODO: Uncomment or delete this when we negotiate deploying non-breaking changes - % <<"tokenization_method">> => TokenizationMethod + <<"exp_date">> => ExpDate }). -decode_bank_card_cvv_flag(undefined) -> - undefined; -decode_bank_card_cvv_flag(CVVFlag) when is_atom(CVVFlag) -> - erlang:atom_to_binary(CVVFlag, utf8). - -decode_bank_card_metadata(undefined) -> - undefined; -decode_bank_card_metadata(Meta) -> - maps:map(fun(_, Data) -> capi_msgp_marshalling:unmarshal(Data) end, Meta). - decode_payment_terminal(#domain_PaymentTerminal{payment_service = PaymentService}) -> #{ <<"type">> => <<"payment_terminal">>, @@ -345,15 +287,13 @@ decode_digital_wallet_details(#domain_DigitalWallet{payment_service = Provider}, -spec decode_disposable_payment_resource(capi_handler_encoder:encode_data()) -> capi_handler_decoder_utils:decode_data(). -decode_disposable_payment_resource(Resource) -> - #domain_DisposablePaymentResource{payment_tool = PaymentTool, payment_session_id = SessionID} = Resource, - ClientInfo = decode_client_info(Resource#domain_DisposablePaymentResource.client_info), - PaymentToolSwag = decode_payment_tool(PaymentTool), +decode_disposable_payment_resource(#domain_DisposablePaymentResource{ + payment_tool = PaymentTool, + client_info = ClientInfo +}) -> #{ - <<"paymentToolToken">> => wrap_payment_tool_token(PaymentToolSwag), - <<"paymentSession">> => capi_handler_utils:wrap_payment_session(ClientInfo, SessionID), <<"paymentToolDetails">> => decode_payment_tool_details(PaymentTool), - <<"clientInfo">> => ClientInfo + <<"clientInfo">> => decode_client_info(ClientInfo) }. decode_client_info(undefined) -> diff --git a/apps/capi/src/capi_handler_invoice_templates.erl b/apps/capi/src/capi_handler_invoice_templates.erl index a4ce23b..d032f11 100644 --- a/apps/capi/src/capi_handler_invoice_templates.erl +++ b/apps/capi/src/capi_handler_invoice_templates.erl @@ -7,7 +7,7 @@ -export([prepare/3]). --import(capi_handler_utils, [general_error/2, logic_error/2, map_service_result/1]). +-import(capi_handler_utils, [general_error/2, logic_error/2, conflict_error/1, map_service_result/1]). -spec prepare( OperationID :: capi_handler:operation_id(), @@ -53,7 +53,7 @@ prepare('CreateInvoiceTemplate' = OperationID, Req, Context) -> throw:zero_invoice_lifetime -> {ok, logic_error('invalidRequest', <<"Lifetime cannot be zero">>)}; throw:{external_id_conflict, ID, UsedExternalID, _Schema} -> - {ok, logic_error('externalIDConflict', {ID, UsedExternalID})} + {ok, conflict_error({ID, UsedExternalID})} end end, {ok, #{authorize => Authorize, process => Process}}; @@ -185,7 +185,7 @@ prepare('CreateInvoiceWithTemplate' = OperationID, Req, Context) -> throw:{bad_invoice_params, amount_no_currency} -> {ok, logic_error('invalidRequest', <<"Currency is required for the amount">>)}; throw:{external_id_conflict, InvoiceID, ExternalID, _Schema} -> - {ok, logic_error('externalIDConflict', {InvoiceID, ExternalID})} + {ok, conflict_error({InvoiceID, ExternalID})} end end, {ok, #{authorize => Authorize, process => Process}}; @@ -245,8 +245,8 @@ create_invoice(PartyID, InvoiceTplID, InvoiceParams, Context, BenderPrefix) -> ExternalID = maps:get(<<"externalID">>, InvoiceParams, undefined), IdempotentKey = {BenderPrefix, PartyID, ExternalID}, InvoiceParamsWithTemplate = maps:put(<<"invoiceTemplateID">>, InvoiceTplID, InvoiceParams), - Identity = capi_bender:make_identity(invoice, InvoiceParamsWithTemplate), - InvoiceID = capi_bender:try_gen_snowflake(IdempotentKey, Identity, WoodyCtx), + Identity = capi_bender:make_identity(capi_feature_schemas:invoice(), InvoiceParamsWithTemplate), + InvoiceID = capi_bender:gen_snowflake(IdempotentKey, Identity, WoodyCtx), CallArgs = {encode_invoice_params_with_tpl(InvoiceID, InvoiceTplID, InvoiceParams)}, Call = {invoicing, 'CreateWithTemplate', CallArgs}, capi_handler_utils:service_call_with([user_info], Call, Context). @@ -258,8 +258,8 @@ get_invoice_template(ID, Context) -> generate_invoice_template_id(OperationID, TemplateParams, PartyID, #{woody_context := WoodyContext}) -> ExternalID = maps:get(<<"externalID">>, TemplateParams, undefined), IdempKey = {OperationID, PartyID, ExternalID}, - Identity = capi_bender:make_identity(invoice_template, TemplateParams), - capi_bender:try_gen_snowflake(IdempKey, Identity, WoodyContext). + Identity = capi_bender:make_identity(capi_feature_schemas:invoice_template(), TemplateParams), + capi_bender:gen_snowflake(IdempKey, Identity, WoodyContext). encode_invoice_tpl_create_params(InvoiceTemplateID, PartyID, Params) -> Details = encode_invoice_tpl_details(genlib_map:get(<<"details">>, Params)), diff --git a/apps/capi/src/capi_handler_invoices.erl b/apps/capi/src/capi_handler_invoices.erl index 2ee7d9a..f69d784 100644 --- a/apps/capi/src/capi_handler_invoices.erl +++ b/apps/capi/src/capi_handler_invoices.erl @@ -6,7 +6,7 @@ -export([prepare/3]). --import(capi_handler_utils, [general_error/2, logic_error/2, map_service_result/1]). +-import(capi_handler_utils, [general_error/2, logic_error/2, conflict_error/1, map_service_result/1]). -spec prepare( OperationID :: capi_handler:operation_id(), @@ -61,7 +61,7 @@ prepare('CreateInvoice' = OperationID, Req, Context) -> throw:invalid_invoice_cost -> {ok, logic_error('invalidInvoiceCost', <<"Invalid invoice amount">>)}; throw:{external_id_conflict, InvoiceID, ExternalID, _Schema} -> - {ok, logic_error('externalIDConflict', {InvoiceID, ExternalID})}; + {ok, conflict_error({InvoiceID, ExternalID})}; throw:allocation_wrong_cart -> {ok, logic_error('invalidAllocation', <<"Wrong cart">>)}; throw:allocation_duplicate -> @@ -274,8 +274,8 @@ create_invoice(PartyID, InvoiceParams, Context, BenderPrefix) -> #{woody_context := WoodyCtx} = Context, ExternalID = maps:get(<<"externalID">>, InvoiceParams, undefined), IdempotentKey = {BenderPrefix, PartyID, ExternalID}, - Identity = capi_bender:make_identity(invoice, InvoiceParams), - InvoiceID = capi_bender:try_gen_snowflake(IdempotentKey, Identity, WoodyCtx), + Identity = capi_bender:make_identity(capi_feature_schemas:invoice(), InvoiceParams), + InvoiceID = capi_bender:gen_snowflake(IdempotentKey, Identity, WoodyCtx), Call = {invoicing, 'Create', {encode_invoice_params(InvoiceID, PartyID, InvoiceParams)}}, capi_handler_utils:service_call_with([user_info], Call, Context). diff --git a/apps/capi/src/capi_handler_payments.erl b/apps/capi/src/capi_handler_payments.erl index 3349644..9bc4c6e 100644 --- a/apps/capi/src/capi_handler_payments.erl +++ b/apps/capi/src/capi_handler_payments.erl @@ -6,7 +6,7 @@ -export([prepare/3]). --import(capi_handler_utils, [general_error/2, logic_error/1, logic_error/2]). +-import(capi_handler_utils, [general_error/2, logic_error/1, logic_error/2, conflict_error/1]). -define(DEFAULT_PROCESSING_DEADLINE, <<"30m">>). @@ -60,7 +60,7 @@ prepare(OperationID = 'CreatePayment', Req, Context) -> throw:invalid_processing_deadline -> {ok, logic_error('invalidProcessingDeadline', <<"Specified processing deadline is invalid">>)}; throw:{external_id_conflict, PaymentID, ExternalID, _Schema} -> - {ok, logic_error('externalIDConflict', {PaymentID, ExternalID})} + {ok, conflict_error({PaymentID, ExternalID})} end end, {ok, #{authorize => Authorize, process => Process}}; @@ -307,7 +307,7 @@ prepare(OperationID = 'CreateRefund', Req, Context) -> throw:invoice_cart_empty -> {ok, logic_error('invalidInvoiceCart', <<"Wrong size. Path to item: cart">>)}; throw:{external_id_conflict, RefundID, ExternalID, _Schema} -> - {ok, logic_error('externalIDConflict', {RefundID, ExternalID})}; + {ok, conflict_error({RefundID, ExternalID})}; throw:allocation_duplicate -> {ok, logic_error('invalidAllocation', <<"Duplicate shop">>)}; throw:allocation_wrong_cart -> @@ -500,12 +500,12 @@ create_payment_id(Invoice, PaymentParams0, Context, OperationID, PaymentToolThri IdempotentKey = {BenderPrefix, PartyID, ExternalID}, SequenceID = InvoiceID, - Identity = capi_bender:make_identity(payment, PaymentParams), + Identity = capi_bender:make_identity(capi_feature_schemas:payment(), PaymentParams), SequenceParams = #{}, #{woody_context := WoodyCtx} = Context, %% We put `invoice_id` in a context here because `get_payment_by_external_id()` needs it to work CtxData = #{<<"invoice_id">> => InvoiceID}, - capi_bender:try_gen_sequence(IdempotentKey, Identity, SequenceID, SequenceParams, WoodyCtx, CtxData). + capi_bender:gen_sequence(IdempotentKey, Identity, SequenceID, SequenceParams, WoodyCtx, CtxData). find_payment_by_id(PaymentID, #payproc_Invoice{payments = Payments}) -> Fun = fun(#payproc_InvoicePayment{payment = #domain_InvoicePayment{id = ID}}) -> @@ -706,13 +706,13 @@ create_refund(InvoiceID, PaymentID, RefundParams0, Context, BenderPrefix) -> ExternalID = maps:get(<<"externalID">>, RefundParams, undefined), IdempotentKey = {BenderPrefix, PartyID, ExternalID}, - Identity = capi_bender:make_identity(refund, RefundParams), + Identity = capi_bender:make_identity(capi_feature_schemas:refund(), RefundParams), SequenceID = create_sequence_id([InvoiceID, PaymentID], BenderPrefix), SequenceParams = #{minimum => 100}, #{woody_context := WoodyCtx} = Context, %% We put `invoice_id` and `payment_id` in a context here because `get_refund_by_external_id/2` needs it to work CtxData = #{<<"invoice_id">> => InvoiceID, <<"payment_id">> => PaymentID}, - RefundID = capi_bender:try_gen_sequence(IdempotentKey, Identity, SequenceID, SequenceParams, WoodyCtx, CtxData), + RefundID = capi_bender:gen_sequence(IdempotentKey, Identity, SequenceID, SequenceParams, WoodyCtx, CtxData), refund_payment(RefundID, InvoiceID, PaymentID, RefundParams, Context). refund_payment(RefundID, InvoiceID, PaymentID, RefundParams, Context) -> diff --git a/apps/capi/src/capi_handler_search.erl b/apps/capi/src/capi_handler_search.erl index 3a333eb..d994073 100644 --- a/apps/capi/src/capi_handler_search.erl +++ b/apps/capi/src/capi_handler_search.erl @@ -222,7 +222,6 @@ decode_stat_payer( ) -> #{ <<"payerType">> => <<"CustomerPayer">>, - <<"paymentToolToken">> => decode_stat_payment_tool_token(PaymentTool), <<"paymentToolDetails">> => decode_stat_payment_tool_details(PaymentTool), <<"customerID">> => ID }; @@ -236,7 +235,6 @@ decode_stat_payer( ) -> #{ <<"payerType">> => <<"RecurrentPayer">>, - <<"paymentToolToken">> => decode_stat_payment_tool_token(PaymentTool), <<"paymentToolDetails">> => decode_stat_payment_tool_details(PaymentTool), <<"contactInfo">> => genlib_map:compact(#{ <<"phoneNumber">> => PhoneNumber, @@ -256,7 +254,6 @@ decode_stat_payer( ) -> genlib_map:compact(#{ <<"payerType">> => <<"PaymentResourcePayer">>, - <<"paymentToolToken">> => decode_stat_payment_tool_token(PaymentTool), <<"paymentToolDetails">> => decode_stat_payment_tool_details(PaymentTool), <<"paymentSession">> => PaymentSession, <<"clientInfo">> => genlib_map:compact(#{ @@ -296,80 +293,6 @@ decode_stat_payment_status({Status, StatusInfo}, Context) -> <<"error">> => Error }. -decode_stat_payment_tool_token({bank_card, BankCard}) -> - decode_bank_card(BankCard); -decode_stat_payment_tool_token({payment_terminal, PaymentTerminal}) -> - decode_payment_terminal(PaymentTerminal); -decode_stat_payment_tool_token({digital_wallet, DigitalWallet}) -> - decode_digital_wallet(DigitalWallet); -decode_stat_payment_tool_token({crypto_currency, CryptoCurrency}) -> - decode_crypto_wallet(CryptoCurrency); -decode_stat_payment_tool_token({mobile_commerce, MobileCommerce}) -> - decode_mobile_commerce(MobileCommerce). - -decode_bank_card(#merchstat_BankCard{ - 'token' = Token, - 'payment_system' = PaymentSystem, - 'bin' = Bin, - 'masked_pan' = MaskedPan, - 'payment_token' = BankCardTokenServiceRef -}) -> - capi_utils:map_to_base64url( - genlib_map:compact(#{ - <<"type">> => <<"bank_card">>, - <<"token">> => Token, - <<"payment_system">> => capi_handler_decoder_utils:decode_payment_system_ref(PaymentSystem), - <<"bin">> => Bin, - <<"masked_pan">> => MaskedPan, - <<"token_provider">> => capi_utils:maybe( - BankCardTokenServiceRef, - fun capi_handler_decoder_utils:decode_bank_card_token_service_ref/1 - ), - <<"issuer_country">> => undefined, - <<"bank_name">> => undefined, - <<"metadata">> => undefined - }) - ). - -decode_payment_terminal(#merchstat_PaymentTerminal{ - terminal_type = Type -}) -> - capi_utils:map_to_base64url(#{ - <<"type">> => <<"payment_terminal">>, - <<"terminal_type">> => Type - }). - -decode_digital_wallet(#merchstat_DigitalWallet{ - provider = Provider, - id = ID -}) -> - capi_utils:map_to_base64url(#{ - <<"type">> => <<"digital_wallet">>, - <<"provider">> => atom_to_binary(Provider, utf8), - <<"id">> => ID - }). - -decode_crypto_wallet(CryptoCurrency) -> - capi_utils:map_to_base64url(#{ - <<"type">> => <<"crypto_wallet">>, - <<"crypto_currency">> => capi_handler_decoder_utils:convert_crypto_currency_to_swag(CryptoCurrency) - }). - -decode_mobile_commerce(MobileCommerce) -> - #merchstat_MobileCommerce{ - operator = Operator, - phone = #merchstat_MobilePhone{ - cc = Cc, - ctn = Ctn - } - } = MobileCommerce, - Phone = #{<<"cc">> => Cc, <<"ctn">> => Ctn}, - capi_utils:map_to_base64url(#{ - <<"type">> => <<"mobile_commerce">>, - <<"phone">> => Phone, - <<"operator">> => atom_to_binary(Operator, utf8) - }). - decode_stat_payment_tool_details({bank_card, V}) -> decode_bank_card_details(V, #{<<"detailsType">> => <<"PaymentToolDetailsBankCard">>}); decode_stat_payment_tool_details({payment_terminal, V}) -> diff --git a/apps/capi/src/capi_handler_utils.erl b/apps/capi/src/capi_handler_utils.erl index 673ef0f..b49d036 100644 --- a/apps/capi/src/capi_handler_utils.erl +++ b/apps/capi/src/capi_handler_utils.erl @@ -3,6 +3,7 @@ -include_lib("damsel/include/dmsl_payment_processing_thrift.hrl"). -include_lib("damsel/include/dmsl_domain_thrift.hrl"). +-export([conflict_error/1]). -export([general_error/2]). -export([logic_error/1]). -export([logic_error/2]). @@ -49,6 +50,21 @@ | dmsl_domain_thrift:'InvoiceTemplate'(). -type token_source() :: capi_auth:token_spec() | entity(). +-spec conflict_error(binary() | {binary(), binary()}) -> response(). +conflict_error({ID, ExternalID}) -> + Data = #{ + <<"externalID">> => ExternalID, + <<"id">> => ID, + <<"message">> => <<"This 'externalID' has been used by another request">> + }, + create_error_resp(409, Data); +conflict_error(ExternalID) -> + Data = #{ + <<"externalID">> => ExternalID, + <<"message">> => <<"This 'externalID' has been used by another request">> + }, + create_error_resp(409, Data). + -spec general_error(cowboy:http_status(), binary()) -> response(). general_error(Code, Message) -> create_error_resp(Code, #{<<"message">> => genlib:to_binary(Message)}). @@ -57,24 +73,7 @@ general_error(Code, Message) -> logic_error('invalidPaymentToolToken') -> logic_error('invalidPaymentToolToken', <<"Specified payment tool token is invalid">>). --spec logic_error - (term(), io_lib:chars() | binary()) -> response(); - (term(), {binary(), binary() | undefined}) -> response(). -logic_error('externalIDConflict', {ID, undefined}) -> - logic_error('externalIDConflict', {ID, <<"undefined">>}); -logic_error('externalIDConflict', {ID, ExternalID}) -> - Data = #{ - <<"externalID">> => ExternalID, - <<"id">> => ID, - <<"message">> => <<"This 'externalID' has been used by another request">> - }, - create_error_resp(409, Data); -logic_error('externalIDConflict', ExternalID) -> - Data = #{ - <<"externalID">> => ExternalID, - <<"message">> => <<"This 'externalID' has been used by another request">> - }, - create_error_resp(409, Data); +-spec logic_error(term(), iodata()) -> response(). logic_error(Code, Message) -> Data = #{<<"code">> => genlib:to_binary(Code), <<"message">> => genlib:to_binary(Message)}, create_error_resp(400, Data). diff --git a/apps/capi/src/capi_idemp_features_legacy.erl b/apps/capi/src/capi_idemp_features_legacy.erl deleted file mode 100644 index eaa0011..0000000 --- a/apps/capi/src/capi_idemp_features_legacy.erl +++ /dev/null @@ -1,247 +0,0 @@ --module(capi_idemp_features_legacy). - --include("capi_feature_schemas_legacy.hrl"). - --type request_key() :: binary(). --type request_value() :: integer() | binary() | request() | [request()]. --type request() :: #{request_key() := request_value()}. - --type feature_name() :: integer(). --type feature_value() :: integer() | features() | [feature_value()] | undefined. --type features() :: #{feature_name() := feature_value()}. --type schema() :: - #{ - feature_name() := [request_key() | schema() | {set, schema()}] - } - | #{ - ?discriminator := [request_key()], - feature_name() := schema() - }. - --type difference() :: features(). - --type event() :: - {invalid_schema_fragment, feature_name(), request()} - | {request_visited, {request, request()}} - | {request_key_index_visit, integer()} - | {request_key_index_visited, integer()} - | {request_key_visit, {key, integer(), request()}} - | {request_key_visited, {key, integer()}}. - --type event_handler() :: {module(), options()} | undefined. --type options() :: term(). - --export_type([event_handler/0]). --export_type([event/0]). --export_type([schema/0]). --export_type([request/0]). --export_type([difference/0]). --export_type([features/0]). --export_type([feature_name/0]). --export_type([feature_value/0]). --export_type([options/0]). - --export([read/2, read/3]). --export([compare/2]). --export([hash/1]). --export([list_diff_fields/2]). - --callback handle_event(event(), options()) -> ok. - --spec read(schema(), request()) -> features(). -read(Schema, Request) -> - read(get_event_handler(), Schema, Request). - --spec read(event_handler(), schema(), request()) -> features(). -read(Handler, Schema, Request) -> - handle_event(get_event_handler(Handler), {request_visited, {request, Request}}), - read_(Schema, Request, Handler). - -read_(Schema, Request, Handler) -> - Result = maps:fold( - fun - (Name, Fs, Acc) when is_map(Fs) -> - Value = read_(Fs, Request, Handler), - Acc#{Name => Value}; - (Name, Accessor, Acc) when is_list(Accessor) -> - FeatureValue = read_request_value(Accessor, Request, Handler), - Acc#{Name => FeatureValue}; - (_Name, 'reserved', Acc) -> - Acc - end, - #{}, - Schema - ), - Result. - -read_request_value([], undefined, _) -> - undefined; -read_request_value([], Value, _) -> - hash(Value); -read_request_value([Schema = #{}], Request = #{}, Handler) -> - read_(Schema, Request, Handler); -read_request_value([{set, Schema = #{}}], List, Handler) when is_list(List) -> - {_, ListIndex} = lists:foldl(fun(Item, {N, Acc}) -> {N + 1, [{N, Item} | Acc]} end, {0, []}, List), - ListSorted = lists:keysort(2, ListIndex), - lists:foldl( - fun({Index, Req}, Acc) -> - handle_event(get_event_handler(Handler), {request_key_index_visit, Index}), - Value = read_(Schema, Req, Handler), - handle_event(get_event_handler(Handler), {request_key_index_visited, Index}), - [[Index, Value] | Acc] - end, - [], - ListSorted - ); -read_request_value([Key | Rest], Request = #{}, Handler) when is_binary(Key) -> - SubRequest = maps:get(Key, Request, undefined), - handle_event(get_event_handler(Handler), {request_key_visit, {key, Key, SubRequest}}), - Value = read_request_value(Rest, SubRequest, Handler), - handle_event(get_event_handler(Handler), {request_key_visited, {key, Key}}), - Value; -read_request_value(_, undefined, _) -> - undefined; -read_request_value(Key, Request, Handler) -> - handle_event(get_event_handler(Handler), {invalid_schema_fragment, Key, Request}). - -handle_event(undefined, {invalid_schema_fragment, Key, Request}) -> - logger:warning("Unable to extract idemp feature with schema: ~p from client request subset: ~p", [Key, Request]), - undefined; -handle_event(undefined, _Event) -> - ok; -handle_event({Mod, Opts}, Event) -> - Mod:handle_event(Event, Opts). - -get_event_handler() -> - genlib_app:env(capi, idempotence_event_handler). - -get_event_handler({Mod, Options}) -> - {Mod, Options}; -get_event_handler(undefined) -> - undefined. - --spec hash(term()) -> integer(). -hash(V) -> - erlang:phash2(V). - --spec list_diff_fields(schema(), difference()) -> [binary()]. -list_diff_fields(Schema, Diff) -> - {ConvertedDiff, _} = list_diff_fields_(Diff, Schema, {[], []}), - lists:foldl( - fun(Keys, AccIn) -> - KeysBin = lists:map(fun genlib:to_binary/1, Keys), - Item = list_to_binary(lists:join(<<".">>, KeysBin)), - case lists:member(Item, AccIn) of - false -> - [Item | AccIn]; - _ -> - AccIn - end - end, - [], - ConvertedDiff - ). - -list_diff_fields_(Diffs, {set, Schema}, Acc) when is_map(Schema) -> - maps:fold( - fun(I, Diff, {PathsAcc, PathRev}) -> - list_diff_fields_(Diff, Schema, {PathsAcc, [I | PathRev]}) - end, - Acc, - Diffs - ); -list_diff_fields_(Diff, Schema, Acc) when is_map(Schema) -> - zipfold( - fun - (_Feature, ?difference, [Key | _SchemaPart], {PathsAcc, PathRev}) -> - Path = lists:reverse([Key | PathRev]), - {[Path | PathsAcc], PathRev}; - (_Feature, DiffPart, SchemaPart, {_PathsAcc, PathRev} = AccIn) -> - {NewPathsAcc, _NewPathRev} = list_diff_fields_(DiffPart, SchemaPart, AccIn), - {NewPathsAcc, PathRev} - end, - Acc, - Diff, - Schema - ); -list_diff_fields_(Diff, [Schema], Acc) -> - list_diff_fields_(Diff, Schema, Acc); -list_diff_fields_(Diff, [Key | Schema], {PathsAcc, PathRev}) -> - list_diff_fields_(Diff, Schema, {PathsAcc, [Key | PathRev]}). - --spec compare(features(), features()) -> true | {false, difference()}. -compare(Features, FeaturesWith) -> - case compare_features(Features, FeaturesWith) of - Diff when map_size(Diff) > 0 -> - {false, Diff}; - _ -> - true - end. - -compare_features(Fs, FsWith) -> - zipfold( - fun - (Key, Values, ValuesWith, Diff) when is_list(ValuesWith), is_list(Values) -> - compare_list_features(Key, Values, ValuesWith, Diff); - (Key, Value, ValueWith, Diff) when is_map(ValueWith) and is_map(Value) -> - compare_features_(Key, Value, ValueWith, Diff); - %% We expect that clients may _at any time_ change their implementation and start - %% sending information they were not sending beforehand, so this is not considered a - %% conflict. Yet, we DO NOT expect them to do the opposite, to stop sending - %% information they were sending, this is still a conflict. - (_Key, _Value, undefined, Diff) -> - Diff; - (_Key, Value, Value, Diff) -> - Diff; - (Key, Value, ValueWith, Diff) when Value =/= ValueWith -> - Diff#{Key => ?difference} - end, - #{}, - Fs, - FsWith - ). - -compare_list_features(Key, L1, L2, Diff) when length(L1) =/= length(L2) -> - Diff#{Key => ?difference}; -compare_list_features(Key, L1, L2, Acc) -> - case compare_list_features_(L1, L2, #{}) of - Diff when map_size(Diff) > 0 -> - Acc#{Key => Diff}; - #{} -> - Acc - end. - -compare_list_features_([], [], Diff) -> - Diff; -compare_list_features_([[Index, V1] | Values], [[_, V2] | ValuesWith], Acc) -> - Diff = compare_features_(Index, V1, V2, Acc), - compare_list_features_(Values, ValuesWith, Diff). - -compare_features_(Key, Value, ValueWith, Diff) when is_map(Value) and is_map(ValueWith) -> - case compare_features(Value, ValueWith) of - ValueWith -> - % different everywhere - Diff#{Key => ?difference}; - #{?discriminator := _} -> - % Different with regard to discriminator, semantically same as different everywhere. - Diff#{Key => ?difference}; - Diff1 when map_size(Diff1) > 0 -> - Diff#{Key => Diff1}; - #{} -> - % no notable differences - Diff - end. - -zipfold(Fun, Acc, M1, M2) -> - maps:fold( - fun(Key, V1, AccIn) -> - case maps:find(Key, M2) of - {ok, V2} -> - Fun(Key, V1, V2, AccIn); - error -> - AccIn - end - end, - Acc, - M1 - ). diff --git a/apps/capi/src/capi_msgp_marshalling.erl b/apps/capi/src/capi_msgp_marshalling.erl index 1d5f4b2..ed9ab7f 100644 --- a/apps/capi/src/capi_msgp_marshalling.erl +++ b/apps/capi/src/capi_msgp_marshalling.erl @@ -52,3 +52,53 @@ unmarshal({obj, Object}) -> maps:fold(fun(K, V, Acc) -> maps:put(unmarshal(K), unmarshal(V), Acc) end, #{}, Object); unmarshal({arr, Array}) -> lists:map(fun unmarshal/1, Array). + +-ifdef(TEST). +-include_lib("eunit/include/eunit.hrl"). + +-spec test() -> _. + +-define(INSTANCES, [ + {undefined, {nl, #msgpack_Nil{}}}, + {42, {i, 42}}, + {false, {b, false}}, + { + #{ + 3.1415 => 1.337, + <<"there">> => [<<"be">>, {bin, <<"🐲"/utf8>>}, <<"dragons">>], + false => #{<<"is">> => true} + }, + {obj, #{ + {flt, 3.1415} => {flt, 1.337}, + {str, <<"there">>} => {arr, [{str, <<"be">>}, {bin, <<"🐲"/utf8>>}, {str, <<"dragons">>}]}, + {b, false} => {obj, #{{str, <<"is">>} => {b, true}}} + }} + } +]). + +-spec marshalling_test_() -> _. +marshalling_test_() -> + [?_assertEqual(marshal(Instance), Marshalled) || {Instance, Marshalled} <- ?INSTANCES]. + +-spec unmarshalling_test_() -> _. +unmarshalling_test_() -> + [?_assertEqual(Instance, unmarshal(Marshalled)) || {Instance, Marshalled} <- ?INSTANCES]. + +-spec symmetric_marshalling_test_() -> _. +symmetric_marshalling_test_() -> + [?_assertEqual(Instance, unmarshal(marshal(Instance))) || {Instance, _} <- ?INSTANCES]. + +-spec symmetric_unmarshalling_test_() -> _. +symmetric_unmarshalling_test_() -> + [?_assertEqual(Marshalled, marshal(unmarshal(Marshalled))) || {_, Marshalled} <- ?INSTANCES]. + +-spec thrift_serialize_test_() -> _. +thrift_serialize_test_() -> + [?_test(serialize_thrift(marshal(Instance))) || {Instance, _} <- ?INSTANCES]. + +serialize_thrift(Term) -> + C1 = thrift_strict_binary_codec:new(), + {ok, C2} = thrift_strict_binary_codec:write(C1, {struct, union, {dmsl_msgpack_thrift, 'Value'}}, Term), + thrift_strict_binary_codec:close(C2). + +-endif. diff --git a/apps/capi/test/capi_base_api_token_tests_SUITE.erl b/apps/capi/test/capi_base_api_token_tests_SUITE.erl index 8175231..7fd56b5 100644 --- a/apps/capi/test/capi_base_api_token_tests_SUITE.erl +++ b/apps/capi/test/capi_base_api_token_tests_SUITE.erl @@ -2075,7 +2075,7 @@ get_payment_revenue_stats_ok_test(Config) -> {offset, 2}, {from_time, {{2015, 08, 11}, {19, 42, 36}}}, {to_time, {{2020, 08, 11}, {19, 42, 36}}}, - {split_unit, minute}, + {split_unit, hour}, {split_size, 1} ], {ok, _} = capi_client_analytics:get_payment_revenue_stats(?config(context, Config), ?STRING, Query). @@ -2094,7 +2094,7 @@ get_payment_geo_stats_ok_test(Config) -> {offset, 0}, {from_time, {{2015, 08, 11}, {19, 42, 37}}}, {to_time, {{2020, 08, 11}, {19, 42, 37}}}, - {split_unit, minute}, + {split_unit, day}, {split_size, 1} ], {ok, _} = capi_client_analytics:get_payment_geo_stats(?config(context, Config), ?STRING, Query). @@ -2113,7 +2113,7 @@ get_payment_rate_stats_ok_test(Config) -> {offset, 0}, {from_time, {{2015, 08, 11}, {19, 42, 38}}}, {to_time, {{2020, 08, 11}, {19, 42, 38}}}, - {split_unit, minute}, + {split_unit, week}, {split_size, 1} ], {ok, _} = capi_client_analytics:get_payment_rate_stats(?config(context, Config), ?STRING, Query). @@ -2132,7 +2132,7 @@ get_payment_method_stats_ok_test(Config) -> {offset, 0}, {from_time, {{2015, 08, 11}, {19, 42, 35}}}, {to_time, {{2020, 08, 11}, {19, 42, 35}}}, - {split_unit, minute}, + {split_unit, month}, {split_size, 1}, {'paymentMethod', <<"bankCard">>} ], diff --git a/apps/capi/test/capi_dummy_data.hrl b/apps/capi/test/capi_dummy_data.hrl index b10a266..57cedca 100644 --- a/apps/capi/test/capi_dummy_data.hrl +++ b/apps/capi/test/capi_dummy_data.hrl @@ -16,6 +16,7 @@ -define(TEST_USER_REALM, <<"external">>). -define(TEST_RULESET_ID, <<"test/api">>). -define(API_TOKEN, <<"letmein">>). +-define(EMAIL, <<"test@test.ru">>). -define(RATIONAL, #'Rational'{p = ?INTEGER, q = ?INTEGER}). @@ -198,6 +199,7 @@ last_digits = <<"411111******1111">> }). +-define(BANK_CARD(PS, ExpDate), ?BANK_CARD(PS, ExpDate, <<"CARD HODLER">>)). -define(BANK_CARD(PS, ExpDate, CardHolder), ?BANK_CARD(PS, ExpDate, CardHolder, undefined)). -define(BANK_CARD(PS, ExpDate, CardHolder, Category), #domain_BankCard{ token = PS, @@ -215,9 +217,19 @@ token = Token }). +-define(MOBILE_COMMERCE(Operator, CC, CTN), #domain_MobileCommerce{ + operator = #domain_MobileOperatorRef{id = Operator}, + phone = #domain_MobilePhone{ + cc = CC, + ctn = CTN + } +}). + +-define(CRYPTO_CURRENCY_BTC, #domain_CryptoCurrencyRef{id = <<"bitcoin">>}). + -define(CONTACT_INFO, #domain_ContactInfo{ phone_number = ?STRING, - email = <<"test@test.ru">> + email = ?EMAIL }). -define(EXP_DATE(Month, Year), #domain_BankCardExpDate{ @@ -1220,7 +1232,7 @@ }} }, #domain_PaymentMethodRef{ - id = {crypto_currency, #domain_CryptoCurrencyRef{id = <<"bitcoin">>}} + id = {crypto_currency, ?CRYPTO_CURRENCY_BTC} }, #domain_PaymentMethodRef{ id = {crypto_currency, #domain_CryptoCurrencyRef{id = <<"bitcoin_cash">>}} diff --git a/apps/capi/test/capi_idempotency_tests_SUITE.erl b/apps/capi/test/capi_idempotency_tests_SUITE.erl index 9a654ea..ccd7819 100644 --- a/apps/capi/test/capi_idempotency_tests_SUITE.erl +++ b/apps/capi/test/capi_idempotency_tests_SUITE.erl @@ -23,8 +23,6 @@ -export([second_request_with_idempotent_feature_test/1]). -export([second_request_without_idempotent_feature_test/1]). -export([create_invoice_ok_test/1]). --export([create_invoice_legacy_fail_test/1]). --export([create_invoice_legacy_ok_test/1]). -export([create_invoice_fail_test/1]). -export([create_invoice_idemp_cart_ok_test/1]). -export([create_invoice_idemp_cart_fail_test/1]). @@ -44,8 +42,6 @@ -type config() :: [{atom(), any()}]. -type group_name() :: atom(). --define(DIFFERENCE, -1). - -behaviour(supervisor). -spec init([]) -> {ok, {supervisor:sup_flags(), [supervisor:child_spec()]}}. @@ -77,8 +73,6 @@ groups() -> ]}, {invoice_creation, [], [ create_invoice_ok_test, - create_invoice_legacy_fail_test, - create_invoice_legacy_ok_test, create_invoice_fail_test, create_invoice_idemp_cart_fail_test, create_invoice_idemp_cart_ok_test, @@ -216,9 +210,6 @@ create_payment_ok_test(Config) -> [<<"metadata">>, <<"bla">>, 0], [<<"payer">>, <<"contactInfo">>], [<<"payer">>, <<"paymentSession">>], - [<<"payer">>, <<"paymentTool">>, <<"bin">>], - [<<"payer">>, <<"paymentTool">>, <<"cardholder_name">>], - [<<"payer">>, <<"paymentTool">>, <<"masked_pan">>], [<<"payer">>, <<"paymentTool">>, <<"payment_system">>], [<<"payer">>, <<"paymentToolToken">>], [<<"processingDeadline">>] @@ -324,63 +315,6 @@ create_invoice_ok_test(Config) -> ?assertEqual(ExternalID, maps:get(<<"externalID">>, Invoice1)), ?assertEqual(Invoice1, Invoice2). --spec create_invoice_legacy_ok_test(config()) -> _. -create_invoice_legacy_ok_test(Config) -> - BenderKey = <<"bender_key">>, - ExternalID = <<"ok_merch_id">>, - Req = invoice_params(ExternalID), - Unused = [ - [<<"description">>], - [<<"externalID">>], - [<<"metadata">>, <<"invoice_dummy_metadata">>] - ], - Ctx = capi_msgp_marshalling:marshal(#{ - <<"version">> => 2, - <<"features">> => capi_idemp_features_legacy:read(capi_feature_schemas_legacy:invoice(), Req) - }), - _ = capi_ct_helper:mock_services( - [ - {invoicing, fun('Create', {_UserInfo, #payproc_InvoiceParams{id = ID, external_id = EID}}) -> - {ok, ?PAYPROC_INVOICE_WITH_ID(ID, EID)} - end}, - {bender, fun('GenerateID', _) -> {ok, capi_ct_helper_bender:get_result(BenderKey, Ctx)} end} - ], - Config - ), - {{ok, ActualInvoice}, ActualUnused} = create_invoice_(Req, Config), - ?assertEqual(Unused, ActualUnused), - {{ok, ActualInvoice}, ActualUnused} = create_invoice_(Req, Config). - --spec create_invoice_legacy_fail_test(config()) -> _. -create_invoice_legacy_fail_test(Config) -> - BenderKey = <<"bender_key">>, - ExternalID = <<"merch_id">>, - Req = invoice_params(ExternalID), - Unused = [ - [<<"description">>], - [<<"externalID">>], - [<<"metadata">>, <<"invoice_dummy_metadata">>] - ], - Req2 = Req#{<<"product">> => <<"test_product2">>}, - Ctx = capi_msgp_marshalling:marshal(#{ - <<"version">> => 2, - <<"features">> => capi_idemp_features_legacy:read(capi_feature_schemas_legacy:invoice(), Req) - }), - _ = capi_ct_helper:mock_services( - [ - {invoicing, fun('Create', {_UserInfo, #payproc_InvoiceParams{id = ID, external_id = EID}}) -> - {ok, ?PAYPROC_INVOICE_WITH_ID(ID, EID)} - end}, - {bender, fun('GenerateID', _) -> {ok, capi_ct_helper_bender:get_result(BenderKey, Ctx)} end} - ], - Config - ), - {{ok, Invoice1}, _} = create_invoice_(Req, Config), - #{<<"invoice">> := #{<<"id">> := InvoiceID}} = Invoice1, - {Response, Unused2} = create_invoice_(Req2, Config), - ?assertEqual(Unused, Unused2), - ?assertEqual(response_error(409, ExternalID, InvoiceID), Response). - -spec create_invoice_fail_test(config()) -> _. create_invoice_fail_test(Config) -> BenderKey = <<"bender_key">>, @@ -703,8 +637,6 @@ create_customer_binding_ok_test(Config) -> ?assertMatch( {{ok, _}, [ [<<"externalID">>], - [<<"paymentResource">>, <<"paymentTool">>, <<"bin">>], - [<<"paymentResource">>, <<"paymentTool">>, <<"masked_pan">>], [<<"paymentResource">>, <<"paymentTool">>, <<"payment_system">>] ]}, BindingResult1 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 1e15469..9a99e38 100644 --- a/apps/capi/test/capi_invoice_access_token_tests_SUITE.erl +++ b/apps/capi/test/capi_invoice_access_token_tests_SUITE.erl @@ -25,8 +25,9 @@ create_payment_ok_test/1, create_payment_expired_test/1, create_payment_qiwi_access_token_ok_test/1, + create_payment_crypto_ok_test/1, + create_payment_mobile_commerce_ok_test/1, create_payment_with_empty_cvv_ok_test/1, - create_payment_with_googlepay_encrypt_ok_test/1, get_payments_ok_test/1, get_payment_by_id_ok_test/1, get_payment_by_id_trx_ok_test/1, @@ -73,9 +74,10 @@ invoice_access_token_tests() -> create_payment_ok_test, create_payment_expired_test, create_payment_with_empty_cvv_ok_test, - create_payment_with_googlepay_encrypt_ok_test, get_payments_ok_test, create_payment_qiwi_access_token_ok_test, + create_payment_crypto_ok_test, + create_payment_mobile_commerce_ok_test, create_first_recurrent_payment_ok_test, create_second_recurrent_payment_ok_test ]. @@ -297,7 +299,7 @@ create_payment_ok_test(Config) -> ?STRING, Config ), - PaymentToolToken = get_encrypted_token(<<"visa">>, ?EXP_DATE(2, 2020)), + PaymentToolToken = encrypt_payment_tool({bank_card, ?BANK_CARD(<<"visa">>, ?EXP_DATE(2, 2020))}), Req = ?PAYMENT_PARAMS(ExternalID, PaymentToolToken), {ok, #{ <<"id">> := BenderKey, @@ -347,61 +349,30 @@ create_payment_expired_test(Config) -> -spec create_payment_with_empty_cvv_ok_test(config()) -> _. create_payment_with_empty_cvv_ok_test(Config) -> - _ = capi_ct_helper:mock_services( - [ - {invoicing, fun - ('Get', _) -> - {ok, ?PAYPROC_INVOICE}; - ( - 'StartPayment', - { - _UserInfo, - _InvoiceID, - #payproc_InvoicePaymentParams{ - payer = - {payment_resource, #payproc_PaymentResourcePayerParams{ - resource = #domain_DisposablePaymentResource{ - payment_tool = { - bank_card, - #domain_BankCard{is_cvv_empty = true} - } - } - }} - } - } - ) -> - {ok, ?PAYPROC_PAYMENT} - end}, - {generator, fun('GenerateID', _) -> capi_ct_helper_bender:generate_id(<<"bender_key">>) end} - ], - Config - ), - _ = capi_ct_helper_bouncer:mock_assert_invoice_op_ctx( - <<"CreatePayment">>, - ?STRING, - ?STRING, - ?STRING, - Config - ), - PaymentToolToken = get_encrypted_token(<<"visa">>, ?EXP_DATE(1, 2020), true), - Req2 = #{ - <<"flow">> => #{<<"type">> => <<"PaymentFlowInstant">>}, - <<"payer">> => #{ - <<"payerType">> => <<"PaymentResourcePayer">>, - <<"paymentSession">> => ?TEST_PAYMENT_SESSION, - <<"paymentToolToken">> => PaymentToolToken, - <<"contactInfo">> => #{ - <<"email">> => <<"bla@bla.ru">> - } - } - }, - {ok, _} = capi_client_payments:create_payment(?config(context, Config), Req2, ?STRING). + BankCard = ?BANK_CARD(<<"visa">>, ?EXP_DATE(1, 2020), <<"CARD HODLER">>), + BankCardNoCVV = BankCard#domain_BankCard{is_cvv_empty = true}, + {ok, _} = create_payment_w_payment_tool({bank_card, BankCardNoCVV}, Config). -spec create_payment_qiwi_access_token_ok_test(_) -> _. create_payment_qiwi_access_token_ok_test(Config) -> Provider = <<"qiwi">>, WalletID = <<"+79876543210">>, Token = <<"blarg">>, + DigitalWallet = ?DIGITAL_WALLET(Provider, WalletID, Token), + {ok, _} = create_payment_w_payment_tool({digital_wallet, DigitalWallet}, Config). + +-spec create_payment_crypto_ok_test(_) -> _. +create_payment_crypto_ok_test(Config) -> + {ok, _} = create_payment_w_payment_tool({crypto_currency, ?CRYPTO_CURRENCY_BTC}, Config). + +-spec create_payment_mobile_commerce_ok_test(_) -> _. +create_payment_mobile_commerce_ok_test(Config) -> + MobileCommerce = ?MOBILE_COMMERCE(<<"mts">>, <<"123">>, <<"4567890">>), + {ok, _} = create_payment_w_payment_tool({mobile_commerce, MobileCommerce}, Config). + +-spec create_payment_w_payment_tool(PaymentTool, config()) -> _ when + PaymentTool :: dmsl_domain_thrift:'PaymentTool'(). +create_payment_w_payment_tool(PaymentTool, Config) -> _ = capi_ct_helper:mock_services( [ {invoicing, fun @@ -411,93 +382,30 @@ create_payment_qiwi_access_token_ok_test(Config) -> ?assertMatch( {payment_resource, #payproc_PaymentResourcePayerParams{ resource = #domain_DisposablePaymentResource{ - payment_tool = { - digital_wallet, - ?DIGITAL_WALLET(Provider, WalletID, Token) - } + payment_tool = PaymentTool } }}, Params#payproc_InvoicePaymentParams.payer ), {ok, ?PAYPROC_PAYMENT} end}, - {generator, fun('GenerateID', _) -> capi_ct_helper_bender:generate_id(<<"bender_key">>) end} + {generator, fun('GenerateID', _) -> + capi_ct_helper_bender:generate_id(?STRING) + end} ], Config ), - _ = capi_ct_helper_bouncer:mock_assert_invoice_op_ctx( - <<"CreatePayment">>, - ?STRING, - ?STRING, - ?STRING, - Config - ), - PaymentToolToken = get_encrypted_token({Provider, WalletID, Token}), + _ = capi_ct_helper_bouncer:mock_assert_invoice_op_ctx(<<"CreatePayment">>, ?STRING, ?STRING, ?STRING, Config), Req = #{ <<"flow">> => #{<<"type">> => <<"PaymentFlowInstant">>}, <<"payer">> => #{ <<"payerType">> => <<"PaymentResourcePayer">>, <<"paymentSession">> => ?TEST_PAYMENT_SESSION, - <<"paymentToolToken">> => PaymentToolToken, - <<"contactInfo">> => #{<<"email">> => <<"bla@bla.ru">>} + <<"paymentToolToken">> => encrypt_payment_tool(PaymentTool), + <<"contactInfo">> => #{<<"email">> => ?EMAIL} } }, - {ok, _} = capi_client_payments:create_payment(?config(context, Config), Req, ?STRING). - --spec create_payment_with_googlepay_encrypt_ok_test(_) -> _. -create_payment_with_googlepay_encrypt_ok_test(Config) -> - _ = capi_ct_helper:mock_services( - [ - {invoicing, fun - ('Get', _) -> - {ok, ?PAYPROC_INVOICE}; - ( - 'StartPayment', - { - _UserInfo, - _InvoiceID, - #payproc_InvoicePaymentParams{ - payer = - {payment_resource, #payproc_PaymentResourcePayerParams{ - resource = #domain_DisposablePaymentResource{ - payment_tool = { - bank_card, - #domain_BankCard{ - is_cvv_empty = undefined, - payment_system = #domain_PaymentSystemRef{id = <<"mastercard">>} - } - } - } - }} - } - } - ) -> - {ok, ?PAYPROC_PAYMENT} - end}, - {generator, fun('GenerateID', _) -> capi_ct_helper_bender:generate_id(<<"bender_key">>) end} - ], - Config - ), - _ = capi_ct_helper_bouncer:mock_assert_invoice_op_ctx( - <<"CreatePayment">>, - ?STRING, - ?STRING, - ?STRING, - Config - ), - PaymentToolToken = get_encrypted_token(<<"mastercard">>, ?EXP_DATE(1, 2020)), - Req2 = #{ - <<"flow">> => #{<<"type">> => <<"PaymentFlowInstant">>}, - <<"payer">> => #{ - <<"payerType">> => <<"PaymentResourcePayer">>, - <<"paymentSession">> => ?TEST_PAYMENT_SESSION, - <<"paymentToolToken">> => PaymentToolToken, - <<"contactInfo">> => #{ - <<"email">> => <<"bla@bla.ru">> - } - } - }, - {ok, _} = capi_client_payments:create_payment(?config(context, Config), Req2, ?STRING). + capi_client_payments:create_payment(?config(context, Config), Req, ?STRING). -spec get_payments_ok_test(config()) -> _. get_payments_ok_test(Config) -> @@ -665,7 +573,7 @@ create_first_recurrent_payment_ok_test(Config) -> ], Config ), - PaymentToolToken = get_encrypted_token(<<"visa">>, ?EXP_DATE(1, 2020)), + PaymentToolToken = encrypt_payment_tool({bank_card, ?BANK_CARD(<<"visa">>, ?EXP_DATE(1, 2020))}), Req2 = #{ <<"flow">> => #{<<"type">> => <<"PaymentFlowInstant">>}, <<"makeRecurrent">> => true, @@ -768,31 +676,6 @@ get_failed_payment_with_invalid_cvv(Config) -> % mock_services([{invoicing, fun('GetPayment', _) -> {ok, ?PAYPROC_PAYMENT} end}], Config), capi_client_payments:get_payment_by_id(?config(context, Config), ?STRING, ?STRING). -get_encrypted_token({Provider, ID, TokenID}) -> - PaymentTool = - {digital_wallet, #domain_DigitalWallet{ - payment_service = #domain_PaymentServiceRef{id = Provider}, - id = ID, - token = TokenID - }}, - encrypt_payment_tool(PaymentTool). - -get_encrypted_token(PS, ExpDate) -> - get_encrypted_token(PS, ExpDate, undefined). - -get_encrypted_token(PS, ExpDate, IsCvvEmpty) -> - PaymentTool = - {bank_card, #domain_BankCard{ - token = ?TEST_PAYMENT_TOKEN, - payment_system = #domain_PaymentSystemRef{id = PS}, - bin = <<"411111">>, - last_digits = <<"1111">>, - exp_date = ExpDate, - cardholder_name = <<"Degus Degusovich">>, - is_cvv_empty = IsCvvEmpty - }}, - encrypt_payment_tool(PaymentTool). - encrypt_payment_tool(PaymentTool) -> encrypt_payment_tool(PaymentTool, undefined). diff --git a/elvis.config b/elvis.config index dc246b7..b2144c5 100644 --- a/elvis.config +++ b/elvis.config @@ -37,8 +37,7 @@ {elvis_style, macro_names, #{ ignore => [ % Abuses lowercase macros too much - capi_feature_schemas, - capi_feature_schemas_legacy + capi_feature_schemas ] }} ] diff --git a/rebar.lock b/rebar.lock index 5975ea1..d7049f8 100644 --- a/rebar.lock +++ b/rebar.lock @@ -119,11 +119,11 @@ {<<"ssl_verify_fun">>,{pkg,<<"ssl_verify_fun">>,<<"1.1.6">>},2}, {<<"swag_client">>, {git,"https://github.com/valitydev/swag-payments", - {ref,"03520bda80db540cfe2f431bc035e9c9c165fb99"}}, + {ref,"187da507554108df60a317582ec8a8d40433c9e5"}}, 0}, {<<"swag_server">>, {git,"https://github.com/valitydev/swag-payments", - {ref,"347f081b2823c17c390fcedefb58bad9de35034f"}}, + {ref,"04149a7aad1b9fad8d1992bc29bffa0de5fbd498"}}, 0}, {<<"thrift">>, {git,"https://github.com/valitydev/thrift_erlang.git",