mirror of
https://github.com/valitydev/capi-v2.git
synced 2024-11-06 01:55:20 +00:00
ED-282/deps: Migrate to feat for idempotency feats (#578)
* ED-282/deps: Migrate to feat for idempotency feats * ED-282/ref: Fix dialyzer warning * ED-282/ref: Fix dialyzer warnings * ED-282/fix: Remove leftover * ED-282/ref: Fix legacy module naming * ED-282/ref: Fix headers module naming * ED-282/fix: Fix v2 legacy fallback * ED-282/fix: Fix v2 compativility code * ED-282/ref: Fix dialyzer and linter warnings * ED-282/fix: Fix bug and dialyzer warnings * ED-282/test: Return create_invoice_legacy_fail * ED-282/ref: Rewrite feat CT reader Make it simpler and more explicit * ED-282/ref: Fix formatting * ED-282/fix: Remove debug leftovers * ED-282/fix: Fix CT feat reader * ED-282/test: Fix unused fields * ED-282/ref: Refactor capi_bender * ED-282/ref: Refactor arg naming in capi_bender * ED-282/fix: Fix capi_bender list_diff for legacy * ED-282/test: Add success test and fix impl * ED-282/fix: Fix types for consistency * ED-282/ref: Tweak elvis to ignore idemp tests * ED-282/ref: Simplify capi_bender API * ED-282/deps: Switch capi_bender to bender_client * ED-282/ref: Remove direct bender call leftovers * ED-282/fix: Return spec for get_context_data * ED-282/ref: Rename bender capi namespace const * ED-282/ref: Remove leftover * ED-282/ref: Remove read_schema redundant clauses * ED-282/ref: Rename bender namespace var * ED-282/ref: Remove leftovers and fix formatting
This commit is contained in:
parent
4e39a79083
commit
8381da653a
@ -1,5 +1,5 @@
|
||||
-ifndef(__capi_feature_schemas__).
|
||||
-define(__capi_feature_schemas__, 42).
|
||||
-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.
|
@ -9,6 +9,7 @@
|
||||
public_key,
|
||||
snowflake,
|
||||
genlib,
|
||||
feat,
|
||||
woody,
|
||||
capi_woody_client,
|
||||
damsel,
|
||||
|
@ -9,35 +9,35 @@
|
||||
-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}.
|
||||
-type identity() :: {identity, identity_hash(), identity_features(), identity_schema()}.
|
||||
-type identity_params() ::
|
||||
{schema, identity_schema(), capi_idemp_features:request()}
|
||||
| {schema, identity_schema(), capi_idemp_features:request(), HashedRequest :: capi_idemp_features:request()}
|
||||
| identity().
|
||||
%% TODO(ED-287): remove identity_request() from below
|
||||
-opaque identity() :: {identity, identity_features(), identity_schema(), identity_request()}.
|
||||
|
||||
-type identity_hash() :: non_neg_integer().
|
||||
-type identity_features() :: capi_idemp_features:features().
|
||||
-type identity_schema() :: capi_idemp_features: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_request() :: feat:request().
|
||||
-type woody_context() :: woody_context:ctx().
|
||||
-type context_data() :: #{binary() => term()}.
|
||||
-type bender_context() :: #{binary() => term()}.
|
||||
|
||||
-type sequence_id() :: binary().
|
||||
-type sequence_params() :: #{minimum => integer()}.
|
||||
-type difference() :: capi_idemp_features:difference().
|
||||
-type difference() :: feat:difference().
|
||||
|
||||
-type constant_id() :: binary().
|
||||
|
||||
-type external_id_conflict_v1() :: {external_id_conflict, id(), undefined, identity_schema()}.
|
||||
-type external_id_conflict_v2() :: {external_id_conflict, id(), difference(), identity_schema()}.
|
||||
-type external_id_conflict() :: external_id_conflict_v1() | external_id_conflict_v2().
|
||||
-type external_id_conflict() :: {external_id_conflict, id(), difference(), identity_schema()}.
|
||||
-type generation_error() :: external_id_conflict().
|
||||
|
||||
-export_type([id/0]).
|
||||
-export_type([external_id/0]).
|
||||
-export_type([issuer_id/0]).
|
||||
-export_type([idempotent_key_params/0]).
|
||||
-export_type([identity_params/0]).
|
||||
-export_type([context_data/0]).
|
||||
-export_type([woody_context/0]).
|
||||
-export_type([difference/0]).
|
||||
@ -46,6 +46,7 @@
|
||||
-export_type([constant_id/0]).
|
||||
-export_type([external_id_conflict/0]).
|
||||
-export_type([bender_context/0]).
|
||||
-export_type([identity/0]).
|
||||
|
||||
-export([gen_snowflake/3]).
|
||||
-export([gen_snowflake/4]).
|
||||
@ -59,37 +60,39 @@
|
||||
-export([gen_constant/5]).
|
||||
-export([try_gen_constant/4]).
|
||||
-export([try_gen_constant/5]).
|
||||
-export([make_identity/1]).
|
||||
-export([make_identity/2]).
|
||||
-export([get_internal_id/2]).
|
||||
|
||||
%% deprecated
|
||||
-define(SCHEMA_VER1, 1).
|
||||
-define(SCHEMA_VER2, 2).
|
||||
-define(BENDER_NAMESPACE, <<"capi">>).
|
||||
|
||||
-spec gen_snowflake(idempotent_key_params() | undefined, identity_params(), woody_context()) ->
|
||||
%% 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()}.
|
||||
gen_snowflake(IdempotentKey, Identity, WoodyContext) ->
|
||||
Context = #{},
|
||||
gen_snowflake(IdempotentKey, Identity, WoodyContext, Context).
|
||||
|
||||
-spec gen_snowflake(idempotent_key_params() | undefined, identity_params(), woody_context(), context_data()) ->
|
||||
-spec gen_snowflake(idempotent_key_params() | undefined, identity(), woody_context(), context_data()) ->
|
||||
{ok, id()} | {ok, id(), context_data()} | {error, 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_params(), woody_context()) -> id().
|
||||
-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_params(), woody_context(), context_data()) ->
|
||||
-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_params(), sequence_id(), woody_context()) ->
|
||||
-spec gen_sequence(idempotent_key_params() | undefined, identity(), sequence_id(), woody_context()) ->
|
||||
{ok, id()} | {ok, id(), context_data()} | {error, generation_error()}.
|
||||
gen_sequence(IdempotentKey, Identity, SequenceID, WoodyContext) ->
|
||||
SequenceParams = #{},
|
||||
@ -97,7 +100,7 @@ gen_sequence(IdempotentKey, Identity, SequenceID, WoodyContext) ->
|
||||
|
||||
-spec gen_sequence(
|
||||
idempotent_key_params() | undefined,
|
||||
identity_params(),
|
||||
identity(),
|
||||
sequence_id(),
|
||||
sequence_params(),
|
||||
woody_context()
|
||||
@ -108,7 +111,7 @@ gen_sequence(IdempotentKey, Identity, SequenceID, SequenceParams, WoodyContext)
|
||||
|
||||
-spec gen_sequence(
|
||||
idempotent_key_params() | undefined,
|
||||
identity_params(),
|
||||
identity(),
|
||||
sequence_id(),
|
||||
sequence_params(),
|
||||
woody_context(),
|
||||
@ -120,7 +123,7 @@ gen_sequence(IdempotentKey, Identity, SequenceID, SequenceParams, WoodyContext,
|
||||
|
||||
-spec try_gen_sequence(
|
||||
idempotent_key_params() | undefined,
|
||||
identity_params(),
|
||||
identity(),
|
||||
sequence_id(),
|
||||
sequence_params(),
|
||||
woody_context(),
|
||||
@ -130,54 +133,54 @@ try_gen_sequence(IdempotentKey, Identity, SequenceID, SequenceParams, WoodyConte
|
||||
IdSchema = build_sequence_schema(SequenceID, SequenceParams),
|
||||
try_generate_id(IdSchema, IdempotentKey, Identity, WoodyContext, ContextData).
|
||||
|
||||
-spec gen_constant(idempotent_key_params(), identity_params(), constant_id(), woody_context()) ->
|
||||
-spec gen_constant(idempotent_key_params(), identity(), constant_id(), woody_context()) ->
|
||||
{ok, id()} | {ok, id(), context_data()} | {error, generation_error()}.
|
||||
gen_constant(IdempotentKey, Identity, ConstantID, WoodyContext) ->
|
||||
Context = #{},
|
||||
gen_constant(IdempotentKey, Identity, ConstantID, WoodyContext, Context).
|
||||
|
||||
-spec gen_constant(idempotent_key_params(), identity_params(), constant_id(), woody_context(), context_data()) ->
|
||||
-spec gen_constant(idempotent_key_params(), identity(), constant_id(), woody_context(), context_data()) ->
|
||||
{ok, id()} | {ok, id(), context_data()} | {error, 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_params(), constant_id(), woody_context()) -> id().
|
||||
-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_params(), constant_id(), woody_context(), context_data()) ->
|
||||
-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_params()) -> identity().
|
||||
make_identity({schema, Schema, Data}) ->
|
||||
Hash = erlang:phash2(Data),
|
||||
Features = capi_idemp_features:read(Schema, Data),
|
||||
{identity, Hash, Features, Schema};
|
||||
make_identity({schema, Schema, Data, HashedData}) ->
|
||||
Hash = erlang:phash2(HashedData),
|
||||
Features = capi_idemp_features:read(Schema, Data),
|
||||
{identity, Hash, Features, Schema};
|
||||
make_identity(Identity = {identity, _Hash, _Features, _Schema}) ->
|
||||
Identity.
|
||||
-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().
|
||||
|
||||
-spec get_internal_id(idempotent_key_params(), woody_context()) ->
|
||||
{ok, binary(), context_data()} | {error, internal_id_not_found}.
|
||||
get_internal_id(IdempotentKeyParams, WoodyContext) ->
|
||||
IdempotentKey = make_idempotent_key(IdempotentKeyParams),
|
||||
case capi_woody_client:call_service(bender, 'GetInternalID', {IdempotentKey}, WoodyContext) of
|
||||
{ok, #bender_GetInternalIDResult{
|
||||
internal_id = InternalID,
|
||||
context = Context
|
||||
}} ->
|
||||
UnmarshaledCtx = capi_msgp_marshalling:unmarshal(Context),
|
||||
{ok, InternalID, get_context_data(UnmarshaledCtx)};
|
||||
{exception, #bender_InternalIDNotFound{}} ->
|
||||
{error, internal_id_not_found}
|
||||
case bender_client:get_internal_id(IdempotentKey, WoodyContext) of
|
||||
{ok, InternalID, Context} ->
|
||||
{ok, InternalID, get_context_data(Context)};
|
||||
{error, _} = Error ->
|
||||
Error
|
||||
end.
|
||||
|
||||
%%%
|
||||
@ -193,7 +196,7 @@ build_sequence_schema(SequenceID, SequenceParams) ->
|
||||
|
||||
build_bender_ctx(Features, Ctx) ->
|
||||
#{
|
||||
<<"version">> => ?SCHEMA_VER2,
|
||||
<<"version">> => ?SCHEMA_VER3,
|
||||
<<"features">> => Features,
|
||||
<<"context_data">> => Ctx
|
||||
}.
|
||||
@ -205,22 +208,27 @@ try_generate_id(BenderIdSchema, IdempotentKey, Identity, WoodyContext, CtxData)
|
||||
case generate_id(BenderIdSchema, IdempotentKey, Identity, WoodyContext, CtxData) of
|
||||
{ok, ID} ->
|
||||
ID;
|
||||
{error, {external_id_conflict, ID, undefined, Schema}} ->
|
||||
logger:warning("This externalID: ~p, used in another request.~n", [ID]),
|
||||
SourceID = get_external_id(IdempotentKey),
|
||||
throw({external_id_conflict, ID, SourceID, Schema});
|
||||
{error, {external_id_conflict, ID, Difference, Schema}} ->
|
||||
ReadableDiff = capi_idemp_features:list_diff_fields(Schema, Difference),
|
||||
{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,
|
||||
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})
|
||||
end.
|
||||
|
||||
generate_id(BenderIdSchema, IdempKeyParams, IdempIdentity, WoodyContext, CtxData) ->
|
||||
generate_id(BenderIdSchema, IdempKeyParams, Identity, WoodyContext, CtxData) ->
|
||||
IdempKey = make_idempotent_key(IdempKeyParams),
|
||||
case IdempKey of
|
||||
undefined -> generator_generate_id(BenderIdSchema, WoodyContext);
|
||||
IdempKey -> bender_generate_id(BenderIdSchema, IdempKey, IdempIdentity, WoodyContext, CtxData)
|
||||
undefined ->
|
||||
ID = generator_generate_id(BenderIdSchema, WoodyContext),
|
||||
{ok, ID};
|
||||
IdempKey ->
|
||||
bender_generate_id(BenderIdSchema, IdempKey, Identity, WoodyContext, CtxData)
|
||||
end.
|
||||
|
||||
-spec make_idempotent_key(idempotent_key_params()) -> idempotent_key() | undefined.
|
||||
@ -230,49 +238,45 @@ make_idempotent_key({_Prefix, _PartyID, undefined}) ->
|
||||
%% If external ID is undefined, no reason to generate it: noone can really use it
|
||||
undefined;
|
||||
make_idempotent_key({Prefix, PartyID, ExternalID}) ->
|
||||
<<"capi/", Prefix/binary, "/", PartyID/binary, "/", ExternalID/binary>>.
|
||||
bender_client:get_idempotent_key(?BENDER_NAMESPACE, Prefix, PartyID, ExternalID).
|
||||
|
||||
bender_generate_id(BenderIdSchema, IdempKey, IdempIdentity, WoodyContext, CtxData) ->
|
||||
{identity, Hash, Features, Schema} = make_identity(IdempIdentity),
|
||||
bender_generate_id(BenderIdSchema, IdempKey, Identity, WoodyContext, CtxData) ->
|
||||
{identity, Features, Schema, _Data} = Identity,
|
||||
BenderCtx = build_bender_ctx(Features, CtxData),
|
||||
Args = {IdempKey, BenderIdSchema, capi_msgp_marshalling:marshal(BenderCtx)},
|
||||
Result =
|
||||
case capi_woody_client:call_service(bender, 'GenerateID', Args, WoodyContext) of
|
||||
{ok, #bender_GenerationResult{internal_id = InternalID, context = undefined}} ->
|
||||
{ok, InternalID};
|
||||
{ok, #bender_GenerationResult{internal_id = InternalID, context = Ctx}} ->
|
||||
{ok, InternalID, capi_msgp_marshalling:unmarshal(Ctx)}
|
||||
end,
|
||||
case Result of
|
||||
case bender_client:gen_id(IdempKey, BenderIdSchema, WoodyContext, BenderCtx) of
|
||||
{ok, ID} ->
|
||||
{ok, ID};
|
||||
{ok, ID, #{<<"version">> := ?SCHEMA_VER1} = DeprecatedCtx} ->
|
||||
check_idempotent_conflict_deprecated(ID, Hash, DeprecatedCtx, Schema);
|
||||
{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.
|
||||
|
||||
generator_generate_id(BenderIDSchema, WoodyContext) ->
|
||||
case BenderIDSchema of
|
||||
{snowflake, #bender_SnowflakeSchema{}} ->
|
||||
{ok, {ID, _}} = bender_generator_client:gen_snowflake(WoodyContext),
|
||||
{ok, ID};
|
||||
bender_generator_client:gen_snowflake(WoodyContext);
|
||||
{sequence, #bender_SequenceSchema{
|
||||
sequence_id = SequenceID,
|
||||
minimum = Minimum
|
||||
}} ->
|
||||
{ok, {ID, _}} = bender_generator_client:gen_sequence(SequenceID, WoodyContext, #{minimum => Minimum}),
|
||||
{ok, ID};
|
||||
bender_generator_client:gen_sequence(SequenceID, WoodyContext, #{minimum => Minimum});
|
||||
{constant, #bender_ConstantSchema{internal_id = InternalID}} ->
|
||||
{ok, InternalID}
|
||||
InternalID
|
||||
end.
|
||||
|
||||
check_idempotent_conflict(ID, Features, SavedBenderCtx, Schema) ->
|
||||
#{
|
||||
<<"version">> := ?SCHEMA_VER2,
|
||||
<<"version">> := ?SCHEMA_VER3,
|
||||
<<"features">> := OtherFeatures
|
||||
} = SavedBenderCtx,
|
||||
case capi_idemp_features:compare(Features, OtherFeatures) of
|
||||
case feat:compare(Features, OtherFeatures) of
|
||||
true ->
|
||||
{ok, ID};
|
||||
{false, Difference} ->
|
||||
@ -281,10 +285,17 @@ check_idempotent_conflict(ID, Features, SavedBenderCtx, Schema) ->
|
||||
|
||||
%% Deprecated idempotent context
|
||||
|
||||
check_idempotent_conflict_deprecated(ID, Hash, #{<<"params_hash">> := Hash}, _schema) ->
|
||||
{ok, ID};
|
||||
check_idempotent_conflict_deprecated(ID, _Hash, #{<<"params_hash">> := _OtherHash}, Schema) ->
|
||||
{error, {external_id_conflict, ID, undefined, Schema}}.
|
||||
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) ->
|
||||
|
@ -1,8 +1,8 @@
|
||||
-module(capi_feature_schemas).
|
||||
|
||||
-type schema() :: capi_idemp_features:schema().
|
||||
-type schema() :: feat:schema().
|
||||
|
||||
-include("capi_feature_schemas.hrl").
|
||||
-include_lib("feat/include/feat.hrl").
|
||||
|
||||
-define(id, 1).
|
||||
-define(invoice_id, 2).
|
||||
@ -56,7 +56,6 @@
|
||||
-define(contact_info, 50).
|
||||
-define(email, 51).
|
||||
-define(phone_number, 52).
|
||||
|
||||
-define(allocation, 53).
|
||||
-define(target, 54).
|
||||
-define(total, 55).
|
||||
@ -64,6 +63,11 @@
|
||||
-define(share, 57).
|
||||
-define(matisse, 58).
|
||||
-define(exponent, 59).
|
||||
-define(instant, 60).
|
||||
-define(hold, 61).
|
||||
-define(vat, 62).
|
||||
-define(unlimited, 63).
|
||||
-define(shop, 64).
|
||||
|
||||
-export([payment/0]).
|
||||
-export([invoice/0]).
|
||||
@ -75,148 +79,183 @@
|
||||
-spec payment() -> schema().
|
||||
payment() ->
|
||||
#{
|
||||
?invoice_id => [<<"invoiceID">>],
|
||||
?make_recurrent => [<<"makeRecurrent">>],
|
||||
?flow => [
|
||||
?invoice_id => <<"invoiceID">>,
|
||||
?make_recurrent => <<"makeRecurrent">>,
|
||||
?flow => {
|
||||
<<"flow">>,
|
||||
#{
|
||||
?discriminator => [<<"type">>],
|
||||
?hold_exp => [<<"onHoldExpiration">>]
|
||||
}
|
||||
],
|
||||
?payer => [
|
||||
{union, <<"type">>, #{
|
||||
<<"PaymentFlowInstant">> => {?instant, #{}},
|
||||
<<"PaymentFlowHold">> =>
|
||||
{?hold, #{
|
||||
?hold_exp => <<"onHoldExpiration">>
|
||||
}}
|
||||
}}
|
||||
},
|
||||
?payer => {
|
||||
<<"payer">>,
|
||||
#{
|
||||
?discriminator => [<<"payerType">>],
|
||||
?payment_tool => [<<"paymentTool">>, payment_tool_schema()],
|
||||
?customer => [<<"customerID">>],
|
||||
?recurrent => [
|
||||
<<"recurrentParentPayment">>,
|
||||
#{
|
||||
?invoice => [<<"invoiceID">>],
|
||||
?payment => [<<"paymentID">>]
|
||||
{union, <<"payerType">>, #{
|
||||
<<"CustomerPayer">> =>
|
||||
{?customer, #{
|
||||
?customer => <<"customerID">>
|
||||
}},
|
||||
<<"RecurrentPayer">> => {
|
||||
?recurrent, #{
|
||||
?recurrent => {
|
||||
<<"recurrentParentPayment">>,
|
||||
#{
|
||||
?invoice => <<"invoiceID">>,
|
||||
?payment => <<"paymentID">>
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
<<"PaymentResourcePayer">> =>
|
||||
{?payment_resource, #{
|
||||
?payment_tool => {<<"paymentTool">>, payment_tool_schema()}
|
||||
}}
|
||||
}}
|
||||
}
|
||||
}.
|
||||
|
||||
-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()}]
|
||||
?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()]
|
||||
?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()}]
|
||||
}
|
||||
}.
|
||||
{union, <<"templateType">>, #{
|
||||
<<"InvoiceTemplateSingleLine">> =>
|
||||
{?single_line, #{
|
||||
?product => <<"product">>,
|
||||
?price => {<<"price">>, invoice_template_line_cost()},
|
||||
?tax => {<<"taxMode">>, tax_mode_schema()}
|
||||
}},
|
||||
<<"InvoiceTemplateMultiLine">> =>
|
||||
{?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()}]
|
||||
?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()]
|
||||
?shop_id => <<"shopID">>,
|
||||
?contact_info => {<<"contactInfo">>, contact_info_schema()}
|
||||
}.
|
||||
|
||||
-spec customer_binding() -> schema().
|
||||
customer_binding() ->
|
||||
#{
|
||||
?payment_resource => [
|
||||
?payment_resource => {
|
||||
<<"paymentResource">>,
|
||||
#{
|
||||
?payment_session => [<<"paymentSession">>],
|
||||
?payment_tool => [<<"paymentTool">>, payment_tool_schema()]
|
||||
?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">>]
|
||||
}
|
||||
}.
|
||||
{union, <<"type">>, #{
|
||||
<<"bank_card">> =>
|
||||
{?bank_card, #{
|
||||
?token => <<"token">>,
|
||||
?exp_date => <<"exp_date">>
|
||||
}},
|
||||
<<"payment_terminal">> => {?terminal, #{?terminal_type => <<"terminal_type">>}},
|
||||
|
||||
<<"digital_wallet">> =>
|
||||
{?wallet, #{
|
||||
?provider => <<"provider">>,
|
||||
?id => <<"id">>,
|
||||
?token => <<"token">>
|
||||
}},
|
||||
<<"crypto_wallet">> =>
|
||||
{?crypto, #{
|
||||
?currency => <<"currency">>
|
||||
}},
|
||||
<<"mobile_commerce">> =>
|
||||
{?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()}]
|
||||
?target => {<<"target">>, allocation_target()},
|
||||
?cart => {<<"cart">>, {set, cart_line_schema()}},
|
||||
?allocation =>
|
||||
{union, <<"allocationBodyType">>, #{
|
||||
<<"AllocationBodyAmount">> =>
|
||||
{?amount, #{
|
||||
?currency => <<"currency">>,
|
||||
?amount => <<"amount">>
|
||||
}},
|
||||
<<"AllocationBodyTotal">> =>
|
||||
{?total, #{
|
||||
?total => <<"total">>,
|
||||
?currency => <<"currency">>,
|
||||
?fee => {<<"fee">>, allocation_fee()}
|
||||
}}
|
||||
}}
|
||||
}.
|
||||
|
||||
-spec allocation_fee() -> schema().
|
||||
allocation_fee() ->
|
||||
#{
|
||||
?target => {<<"target">>, allocation_target()},
|
||||
?fee =>
|
||||
{union, <<"allocationFeeType">>, #{
|
||||
<<"AllocationFeeFixed">> =>
|
||||
{?fixed, #{?amount => <<"amount">>}},
|
||||
<<"AllocationFeeShare">> => {
|
||||
?share, #{
|
||||
?amount => <<"amount">>,
|
||||
?share => {<<"share">>, decimal()}
|
||||
}
|
||||
}
|
||||
}}
|
||||
}.
|
||||
|
||||
-spec allocation_target() -> schema().
|
||||
allocation_target() ->
|
||||
#{
|
||||
?discriminator => [<<"allocationTargetType">>],
|
||||
?shop_id => [<<"shopID">>]
|
||||
}.
|
||||
{union, <<"allocationTargetType">>, #{
|
||||
<<"AllocationTargetShop">> =>
|
||||
{?shop, #{
|
||||
?shop_id => <<"shopID">>
|
||||
}}
|
||||
}}.
|
||||
|
||||
-spec decimal() -> schema().
|
||||
decimal() ->
|
||||
@ -228,61 +267,64 @@ decimal() ->
|
||||
-spec cart_line_schema() -> schema().
|
||||
cart_line_schema() ->
|
||||
#{
|
||||
?product => [<<"product">>],
|
||||
?quantity => [<<"quantity">>],
|
||||
?price => [<<"price">>],
|
||||
?tax => [<<"taxMode">>, tax_mode_schema()]
|
||||
?product => <<"product">>,
|
||||
?quantity => <<"quantity">>,
|
||||
?price => <<"price">>,
|
||||
?tax => {<<"taxMode">>, tax_mode_schema()}
|
||||
}.
|
||||
|
||||
-spec tax_mode_schema() -> schema().
|
||||
tax_mode_schema() ->
|
||||
#{
|
||||
?discriminator => [<<"type">>],
|
||||
?rate => [<<"rate">>]
|
||||
}.
|
||||
{union, <<"type">>, #{
|
||||
<<"InvoiceLineTaxVAT">> => {?vat, #{?rate => <<"rate">>}}
|
||||
}}.
|
||||
|
||||
-spec bank_account_schema() -> schema().
|
||||
bank_account_schema() ->
|
||||
#{
|
||||
?discriminator => [<<"accountType">>],
|
||||
?account => [<<"account">>],
|
||||
?bank_bik => [<<"bankBik">>]
|
||||
}.
|
||||
{union, <<"accountType">>, #{
|
||||
<<"InvoiceRussianBankAccount">> =>
|
||||
{?bank_account, #{
|
||||
?account => <<"account">>,
|
||||
?bank_bik => <<"bankBik">>
|
||||
}}
|
||||
}}.
|
||||
|
||||
invoice_template_line_cost() ->
|
||||
#{
|
||||
?discriminator => [<<"costType">>],
|
||||
?range => #{
|
||||
?currency => [<<"currency">>],
|
||||
?range => [<<"range">>, cost_amount_range()]
|
||||
{union, <<"costType">>, #{
|
||||
<<"InvoiceTemplateLineCostRange">> => {
|
||||
?range, #{
|
||||
?currency => <<"currency">>,
|
||||
?range => {<<"range">>, cost_amount_range()}
|
||||
}
|
||||
},
|
||||
?fixed => #{
|
||||
?currency => [<<"currency">>],
|
||||
?amount => [<<"amount">>]
|
||||
}
|
||||
%% Unlim has no params and is fully contained in discriminator
|
||||
}.
|
||||
<<"InvoiceTemplateLineCostFixed">> =>
|
||||
{?fixed, #{
|
||||
?currency => <<"currency">>,
|
||||
?amount => <<"amount">>
|
||||
}},
|
||||
<<"InvoiceTemplateLineCostUnlim">> => {?unlimited, #{}}
|
||||
}}.
|
||||
|
||||
-spec cost_amount_range() -> schema().
|
||||
cost_amount_range() ->
|
||||
#{
|
||||
?upper_bound => [<<"upperBound">>],
|
||||
?lower_bound => [<<"lowerBound">>]
|
||||
?upper_bound => <<"upperBound">>,
|
||||
?lower_bound => <<"lowerBound">>
|
||||
}.
|
||||
|
||||
-spec lifetime_schema() -> schema().
|
||||
lifetime_schema() ->
|
||||
#{
|
||||
?days => [<<"days">>],
|
||||
?months => [<<"months">>],
|
||||
?years => [<<"years">>]
|
||||
?days => <<"days">>,
|
||||
?months => <<"months">>,
|
||||
?years => <<"years">>
|
||||
}.
|
||||
|
||||
-spec contact_info_schema() -> schema().
|
||||
contact_info_schema() ->
|
||||
#{
|
||||
?email => [<<"email">>],
|
||||
?phone_number => [<<"phoneNumber">>]
|
||||
?email => <<"email">>,
|
||||
?phone_number => <<"phoneNumber">>
|
||||
}.
|
||||
|
||||
-ifdef(TEST).
|
||||
@ -306,16 +348,16 @@ deep_fetch(Map, Keys) ->
|
||||
lists:foldl(fun(K, M) -> maps:get(K, M) end, Map, Keys).
|
||||
|
||||
hash(Term) ->
|
||||
capi_idemp_features:hash(Term).
|
||||
feat:hash(Term).
|
||||
|
||||
read(Schema, Request) ->
|
||||
capi_idemp_features:read(Schema, Request).
|
||||
feat:read(Schema, Request).
|
||||
|
||||
compare(Features1, Features2) ->
|
||||
capi_idemp_features:compare(Features1, Features2).
|
||||
feat:compare(Features1, Features2).
|
||||
|
||||
list_diff_fields(Schema, Diff) ->
|
||||
capi_idemp_features:list_diff_fields(Schema, Diff).
|
||||
feat:list_diff_fields(Schema, Diff).
|
||||
|
||||
-spec test() -> _.
|
||||
|
||||
@ -347,33 +389,20 @@ read_payment_features_test() ->
|
||||
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)
|
||||
}
|
||||
?flow => [?hold, #{?hold_exp => undefined}],
|
||||
?payer => [
|
||||
?payment_resource,
|
||||
#{
|
||||
?payment_tool =>
|
||||
[
|
||||
?bank_card,
|
||||
#{
|
||||
?exp_date => hash(ExpDate),
|
||||
?token => hash(Token)
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
Features = read(payment(), Request),
|
||||
?assertEqual(Payer, Features).
|
||||
@ -397,7 +426,7 @@ compare_payment_bank_card_test() ->
|
||||
|
||||
-spec compare_different_payment_tool_test() -> _.
|
||||
compare_different_payment_tool_test() ->
|
||||
ToolType2 = <<"wallet">>,
|
||||
ToolType2 = <<"digital_wallet">>,
|
||||
Token2 = <<"wallet token">>,
|
||||
PaymentTool1 = bank_card(),
|
||||
PaymentTool2 = #{
|
||||
@ -407,50 +436,7 @@ compare_different_payment_tool_test() ->
|
||||
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">>
|
||||
]).
|
||||
common_compare_tests(payment(), Request1, Request2, [<<"payer">>]).
|
||||
|
||||
-spec read_payment_customer_features_value_test() -> _.
|
||||
read_payment_customer_features_value_test() ->
|
||||
@ -468,12 +454,7 @@ read_payment_customer_features_value_test() ->
|
||||
?invoice_id => undefined,
|
||||
?make_recurrent => undefined,
|
||||
?flow => undefined,
|
||||
?payer => #{
|
||||
?discriminator => hash(PayerType),
|
||||
?customer => hash(CustomerID),
|
||||
?recurrent => undefined,
|
||||
?payment_tool => undefined
|
||||
}
|
||||
?payer => [?customer, #{?customer => hash(CustomerID)}]
|
||||
},
|
||||
Features
|
||||
).
|
||||
@ -498,11 +479,13 @@ read_invoice_features_test() ->
|
||||
?product => hash(Prod2),
|
||||
?price => hash(Price2)
|
||||
},
|
||||
BankAccount = #{
|
||||
?discriminator => hash(<<"InvoiceRussianBankAccount">>),
|
||||
?account => hash(<<"12345678901234567890">>),
|
||||
?bank_bik => hash(<<"123456789">>)
|
||||
},
|
||||
BankAccount = [
|
||||
?bank_account,
|
||||
#{
|
||||
?account => hash(<<"12345678901234567890">>),
|
||||
?bank_bik => hash(<<"123456789">>)
|
||||
}
|
||||
],
|
||||
Invoice = #{
|
||||
?amount => undefined,
|
||||
?currency => hash(Cur),
|
||||
@ -578,6 +561,7 @@ compare_invoices_features_test() ->
|
||||
Product#{
|
||||
<<"price">> => Price2,
|
||||
<<"taxMode">> => #{
|
||||
<<"type">> => <<"InvoiceLineTaxVAT">>,
|
||||
<<"rate">> => <<"18%">>
|
||||
}
|
||||
}
|
||||
@ -587,14 +571,7 @@ compare_invoices_features_test() ->
|
||||
InvoiceWithFullCart = read(Schema, Request3),
|
||||
?assertEqual(
|
||||
{false, #{
|
||||
?cart => #{
|
||||
0 => #{
|
||||
?price => ?difference,
|
||||
?product => ?difference,
|
||||
?quantity => ?difference,
|
||||
?tax => ?difference
|
||||
}
|
||||
}
|
||||
?cart => ?difference
|
||||
}},
|
||||
compare(Invoice2, Invoice1)
|
||||
),
|
||||
@ -611,7 +588,7 @@ compare_invoices_features_test() ->
|
||||
|
||||
{false, Diff} = compare(Invoice1, InvoiceChg1),
|
||||
?assertEqual(
|
||||
[<<"cart.0.price">>, <<"cart.0.taxMode.rate">>],
|
||||
[<<"cart.0.price">>, <<"cart.0.taxMode">>],
|
||||
list_diff_fields(Schema, Diff)
|
||||
),
|
||||
?assert(compare(Invoice1, Invoice1#{?cart => undefined})).
|
||||
@ -651,11 +628,7 @@ compare_customer_features_test() ->
|
||||
Request,
|
||||
RequestSame,
|
||||
RequestDifferent,
|
||||
[
|
||||
<<"shopID">>,
|
||||
<<"contactInfo.email">>,
|
||||
<<"contactInfo.phoneNumber">>
|
||||
]
|
||||
all
|
||||
).
|
||||
|
||||
-spec read_customer_binding_features_test() -> _.
|
||||
@ -666,29 +639,13 @@ read_customer_binding_features_test() ->
|
||||
Features = #{
|
||||
?payment_resource => #{
|
||||
?payment_session => hash(Session),
|
||||
?payment_tool => #{
|
||||
?discriminator => hash(<<"bank_card">>),
|
||||
?bank_card => #{
|
||||
?payment_tool => [
|
||||
?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
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
||||
@ -729,14 +686,9 @@ read_invoice_template_features_test() ->
|
||||
?months => hash(2),
|
||||
?years => hash(3)
|
||||
},
|
||||
?details => #{
|
||||
?discriminator => hash(<<"InvoiceTemplateMultiLine">>),
|
||||
?single_line => #{
|
||||
?product => undefined,
|
||||
?price => undefined,
|
||||
?tax => undefined
|
||||
},
|
||||
?multiline => #{
|
||||
?details => [
|
||||
?multiline,
|
||||
#{
|
||||
?currency => hash(<<"RUB">>),
|
||||
?cart => [
|
||||
[
|
||||
@ -745,7 +697,7 @@ read_invoice_template_features_test() ->
|
||||
?product => hash(?STRING),
|
||||
?quantity => hash(42),
|
||||
?price => hash(?INTEGER),
|
||||
?tax => #{?discriminator => hash(<<"InvoiceLineTaxVAT">>), ?rate => hash(<<"18%">>)}
|
||||
?tax => [?vat, #{?rate => hash(<<"18%">>)}]
|
||||
}
|
||||
],
|
||||
[
|
||||
@ -759,7 +711,7 @@ read_invoice_template_features_test() ->
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
?assertEqual(
|
||||
@ -791,34 +743,43 @@ compare_invoice_template_features_test() ->
|
||||
common_compare_tests(invoice_template(), Request1, Request2, [
|
||||
<<"shopID">>,
|
||||
<<"lifetime.years">>,
|
||||
<<"details.currency">>,
|
||||
<<"details.cart">>
|
||||
<<"details">>
|
||||
]).
|
||||
|
||||
-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,
|
||||
AllocationParams = #{
|
||||
?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)
|
||||
}
|
||||
},
|
||||
?target =>
|
||||
[
|
||||
?shop,
|
||||
#{
|
||||
?shop_id => hash(?STRING)
|
||||
}
|
||||
],
|
||||
?fee => [
|
||||
?share,
|
||||
#{
|
||||
?amount => hash(?INTEGER),
|
||||
?share => #{
|
||||
?matisse => hash(?INTEGER),
|
||||
?exponent => hash(?INTEGER)
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
Features1 = #{
|
||||
?target =>
|
||||
[
|
||||
?shop,
|
||||
#{
|
||||
?shop_id => hash(?STRING)
|
||||
}
|
||||
],
|
||||
?cart => [
|
||||
[
|
||||
0,
|
||||
@ -829,7 +790,8 @@ read_allocation_transaction_test_() ->
|
||||
?tax => undefined
|
||||
}
|
||||
]
|
||||
]
|
||||
],
|
||||
?allocation => [?total, AllocationParams]
|
||||
},
|
||||
Request2 = Request1#{
|
||||
<<"fee">> => #{
|
||||
@ -839,15 +801,22 @@ read_allocation_transaction_test_() ->
|
||||
}
|
||||
},
|
||||
Features2 = Features1#{
|
||||
?fee => #{
|
||||
?target => #{
|
||||
?discriminator => hash(<<"AllocationTargetShop">>),
|
||||
?shop_id => hash(?STRING)
|
||||
},
|
||||
?discriminator => hash(<<"AllocationFeeFixed">>),
|
||||
?amount => hash(1024),
|
||||
?share => undefined
|
||||
}
|
||||
?allocation =>
|
||||
[
|
||||
?total,
|
||||
AllocationParams#{
|
||||
?fee => #{
|
||||
?target =>
|
||||
[
|
||||
?shop,
|
||||
#{
|
||||
?shop_id => hash(?STRING)
|
||||
}
|
||||
],
|
||||
?fee => [?fixed, #{?amount => hash(1024)}]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
[
|
||||
?_assertEqual(Features1, read(allocation_transaction(), Request1)),
|
||||
@ -867,15 +836,15 @@ compare_allocation_transaction_test() ->
|
||||
<<"share">> => undefined
|
||||
}
|
||||
},
|
||||
Request3 = #{
|
||||
<<"target">> => ?ALLOCATION_TARGET#{<<"shopID">> => <<"SomeShop">>},
|
||||
<<"allocationBodyType">> => <<"AllocationBodyAmount">>,
|
||||
<<"amount">> => ?INTEGER,
|
||||
<<"currency">> => ?RUB,
|
||||
<<"cart">> => [
|
||||
#{<<"product">> => ?STRING, <<"quantity">> => 1, <<"price">> => ?INTEGER}
|
||||
]
|
||||
},
|
||||
%% 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,
|
||||
@ -883,14 +852,10 @@ compare_allocation_transaction_test() ->
|
||||
})
|
||||
},
|
||||
common_compare_tests(allocation_transaction(), Request1, Request2, [
|
||||
<<"amount">>, <<"total">>, <<"fee">>
|
||||
<<"fee">>, <<"total">>
|
||||
]),
|
||||
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">>
|
||||
]).
|
||||
%% common_compare_tests(allocation_transaction(), Request1, Request3, all),
|
||||
common_compare_tests(allocation_transaction(), Request1, Request4, [<<"fee">>]).
|
||||
|
||||
-spec demo_compare_allocation_transaction_test() -> _.
|
||||
demo_compare_allocation_transaction_test() ->
|
||||
@ -898,17 +863,16 @@ demo_compare_allocation_transaction_test() ->
|
||||
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">>
|
||||
]).
|
||||
%% Request3 = #{
|
||||
%% <<"fee">> => deep_merge(maps:get(<<"fee">>, Request1), #{
|
||||
%% <<"allocationFeeType">> => <<"AllocationFeeFixed">>
|
||||
%% })
|
||||
%% },
|
||||
common_compare_tests(allocation_transaction(), Request1, Request2, all)
|
||||
%% common_compare_tests(allocation_transaction(), Request1, Request3, [
|
||||
%% <<"fee">>
|
||||
%% ])
|
||||
.
|
||||
|
||||
%%
|
||||
|
||||
@ -920,18 +884,19 @@ payment_resource(Session, Tool) ->
|
||||
}
|
||||
}.
|
||||
|
||||
payment_params(ExternalID, MakeRecurrent) ->
|
||||
payment_params(PaymentTool) ->
|
||||
deep_merge(
|
||||
payment_params(<<"EID">>, <<"Jwe">>, #{}, false),
|
||||
#{<<"payer">> => #{<<"paymentTool">> => PaymentTool}}
|
||||
).
|
||||
|
||||
payment_params(ExternalID, Jwe, ContactInfo, 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#{
|
||||
<<"processingDeadline">> => <<"5m">>,
|
||||
<<"payer">> => #{
|
||||
<<"payerType">> => <<"PaymentResourcePayer">>,
|
||||
<<"paymentSession">> => <<"payment.session">>,
|
||||
@ -940,11 +905,6 @@ payment_params(ExternalID, Jwe, ContactInfo, MakeRecurrent) ->
|
||||
}
|
||||
}).
|
||||
|
||||
payment_params(PaymentTool) ->
|
||||
Params = payment_params(<<"EID">>, <<"Jwe">>, #{}, false),
|
||||
PaymentParams = deep_merge(Params, #{<<"payer">> => #{<<"paymentTool">> => PaymentTool}}),
|
||||
PaymentParams.
|
||||
|
||||
bank_card() ->
|
||||
#{
|
||||
<<"type">> => <<"bank_card">>,
|
||||
@ -964,24 +924,38 @@ lifetime_dummy(Days, Months, Years) ->
|
||||
<<"years">> => Years
|
||||
}.
|
||||
|
||||
%% compare_equal_test(Schema, Request, AnotherRequest) ->
|
||||
%% Features = read(Schema, Request),
|
||||
%% AnotherFeatures = read(Schema, AnotherRequest),
|
||||
%% ?assertEqual(true, compare(Features, AnotherFeatures)).
|
||||
|
||||
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)),
|
||||
case Request =:= RequestWithIgnoredFields of
|
||||
true ->
|
||||
ok;
|
||||
false ->
|
||||
FeaturesIgnored = read(Schema, RequestWithIgnoredFields),
|
||||
?assertEqual(true, compare(Features, FeaturesIgnored))
|
||||
end,
|
||||
|
||||
%% Has correct diff with different request
|
||||
FeaturesDifferent = read(Schema, RequestDifferent),
|
||||
Result = compare(Features, FeaturesDifferent),
|
||||
?assertMatch({false, _}, Result),
|
||||
|
||||
{false, Diff} = Result,
|
||||
?assertEqual(lists:sort(DiffFeatures), lists:sort(list_diff_fields(Schema, Diff))).
|
||||
ActualDiffFeatures = list_diff_fields(Schema, Diff),
|
||||
|
||||
case ActualDiffFeatures =:= all orelse DiffFeatures =:= all of
|
||||
true -> ?assertEqual(DiffFeatures, ActualDiffFeatures);
|
||||
false -> ?assertEqual(lists:sort(DiffFeatures), lists:sort(ActualDiffFeatures))
|
||||
end.
|
||||
-endif.
|
||||
|
984
apps/capi/src/capi_feature_schemas_legacy.erl
Normal file
984
apps/capi/src/capi_feature_schemas_legacy.erl
Normal file
@ -0,0 +1,984 @@
|
||||
-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.
|
@ -301,7 +301,7 @@ mask_customer_notfound(Resolution) ->
|
||||
generate_customer_id(OperationID, PartyID, CustomerParams, #{woody_context := WoodyContext}) ->
|
||||
ExternalID = maps:get(<<"externalID">>, CustomerParams, undefined),
|
||||
IdempKey = {OperationID, PartyID, ExternalID},
|
||||
Identity = {schema, capi_feature_schemas:customer(), CustomerParams},
|
||||
Identity = capi_bender:make_identity(customer, CustomerParams),
|
||||
capi_bender:try_gen_snowflake(IdempKey, Identity, WoodyContext).
|
||||
|
||||
encode_customer_params(CustomerID, PartyID, Params) ->
|
||||
@ -334,9 +334,7 @@ generate_binding_ids(OperationID, CustomerBindingParams, Context = #{woody_conte
|
||||
CustomerBindingParams
|
||||
),
|
||||
|
||||
Identity = capi_bender:make_identity(
|
||||
{schema, capi_feature_schemas:customer_binding(), CustomerBindingParamsEncrypted}
|
||||
),
|
||||
Identity = capi_bender:make_identity(customer_binding, CustomerBindingParamsEncrypted),
|
||||
|
||||
OperationIDBin = erlang:atom_to_binary(OperationID),
|
||||
CustomerBindingID = capi_bender:try_gen_snowflake(
|
||||
|
@ -257,7 +257,7 @@ create_invoice(PartyID, InvoiceTplID, InvoiceParams, Context, BenderPrefix) ->
|
||||
ExternalID = maps:get(<<"externalID">>, InvoiceParams, undefined),
|
||||
IdempotentKey = {BenderPrefix, PartyID, ExternalID},
|
||||
InvoiceParamsWithTemplate = maps:put(<<"invoiceTemplateID">>, InvoiceTplID, InvoiceParams),
|
||||
Identity = {schema, capi_feature_schemas:invoice(), InvoiceParamsWithTemplate},
|
||||
Identity = capi_bender:make_identity(invoice, InvoiceParamsWithTemplate),
|
||||
InvoiceID = capi_bender:try_gen_snowflake(IdempotentKey, Identity, WoodyCtx),
|
||||
CallArgs = {encode_invoice_params_with_tpl(InvoiceID, InvoiceTplID, InvoiceParams)},
|
||||
Call = {invoicing, 'CreateWithTemplate', CallArgs},
|
||||
@ -270,7 +270,7 @@ 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 = {schema, capi_feature_schemas:invoice_template(), TemplateParams},
|
||||
Identity = capi_bender:make_identity(invoice_template, TemplateParams),
|
||||
capi_bender:try_gen_snowflake(IdempKey, Identity, WoodyContext).
|
||||
|
||||
encode_invoice_tpl_create_params(InvoiceTemplateID, PartyID, Params) ->
|
||||
|
@ -291,7 +291,7 @@ create_invoice(PartyID, InvoiceParams, Context, BenderPrefix) ->
|
||||
#{woody_context := WoodyCtx} = Context,
|
||||
ExternalID = maps:get(<<"externalID">>, InvoiceParams, undefined),
|
||||
IdempotentKey = {BenderPrefix, PartyID, ExternalID},
|
||||
Identity = {schema, capi_feature_schemas:invoice(), InvoiceParams},
|
||||
Identity = capi_bender:make_identity(invoice, InvoiceParams),
|
||||
InvoiceID = capi_bender:try_gen_snowflake(IdempotentKey, Identity, WoodyCtx),
|
||||
Call = {invoicing, 'Create', {encode_invoice_params(InvoiceID, PartyID, InvoiceParams)}},
|
||||
capi_handler_utils:service_call_with([user_info], Call, Context).
|
||||
|
@ -19,7 +19,6 @@ prepare(OperationID = 'CreatePayment', Req, Context) ->
|
||||
InvoiceID = maps:get(invoiceID, Req),
|
||||
Invoice = get_invoice_by_id(InvoiceID, Context),
|
||||
PaymentParams = maps:get('PaymentParams', Req),
|
||||
PaymentToken = decode_payment_token(PaymentParams),
|
||||
Authorize = fun() ->
|
||||
Prototypes = [
|
||||
{operation, #{id => OperationID, invoice => InvoiceID}},
|
||||
@ -31,8 +30,7 @@ prepare(OperationID = 'CreatePayment', Req, Context) ->
|
||||
try
|
||||
capi_handler:respond_if_undefined(Invoice, general_error(404, <<"Invoice not found">>)),
|
||||
DomainInvoice = Invoice#payproc_Invoice.invoice,
|
||||
PaymentTool = capi_utils:maybe(PaymentToken, fun(#{payment_tool := V}) -> V end),
|
||||
Result = create_payment(DomainInvoice, PaymentParams, Context, OperationID, PaymentTool),
|
||||
Result = create_payment(DomainInvoice, PaymentParams, Context, OperationID),
|
||||
case Result of
|
||||
{ok, Payment} ->
|
||||
{ok, {201, #{}, decode_invoice_payment(InvoiceID, Payment, Context)}};
|
||||
@ -526,7 +524,10 @@ validate_refund(Params) ->
|
||||
_ -> throw(refund_cart_conflict)
|
||||
end.
|
||||
|
||||
create_payment(Invoice, PaymentParams, Context, OperationID, PaymentTool) ->
|
||||
create_payment(Invoice, PaymentParams, Context, OperationID) ->
|
||||
PaymentToken = decode_payment_token(PaymentParams),
|
||||
PaymentTool = capi_utils:maybe(PaymentToken, fun(#{payment_tool := V}) -> V end),
|
||||
|
||||
InvoiceID = Invoice#domain_Invoice.id,
|
||||
PaymentID = create_payment_id(Invoice, PaymentParams, Context, OperationID, PaymentTool),
|
||||
ExternalID = maps:get(<<"externalID">>, PaymentParams, undefined),
|
||||
@ -534,39 +535,28 @@ create_payment(Invoice, PaymentParams, Context, OperationID, PaymentTool) ->
|
||||
Call = {invoicing, 'StartPayment', {InvoiceID, InvoicePaymentParams}},
|
||||
capi_handler_utils:service_call_with([user_info], Call, Context).
|
||||
|
||||
create_payment_id(Invoice, PaymentParams, Context, OperationID, PaymentTool) ->
|
||||
create_payment_id(Invoice, PaymentParams0, Context, OperationID, PaymentToolThrift) ->
|
||||
InvoiceID = Invoice#domain_Invoice.id,
|
||||
PartyID = Invoice#domain_Invoice.owner_id,
|
||||
Payer = maps:get(<<"payer">>, PaymentParams),
|
||||
Payer = maps:get(<<"payer">>, PaymentParams0),
|
||||
PaymentTool = capi_utils:maybe(PaymentToolThrift, fun capi_handler_decoder_party:decode_payment_tool/1),
|
||||
|
||||
% Temprory decision was made for analytics
|
||||
% Temporary decision for analytics team
|
||||
% TODO: delete this after analytics research will be down
|
||||
_ = log_payer_client_url(Payer, InvoiceID),
|
||||
|
||||
% TODO При наличии paymentToolToken заменяем его раскодированной структурой paymentTool
|
||||
% В противном случае токены будут оказывать влияние на расчет hash2, удаление paymentToolToken
|
||||
% не потребуется при удалении capi_bender:check_idempotent_conflict_deprecated
|
||||
ClearPayer =
|
||||
case PaymentTool of
|
||||
undefined ->
|
||||
Payer;
|
||||
_ ->
|
||||
Payer0 = maps:without([<<"paymentToolToken">>], Payer),
|
||||
Payer0#{<<"paymentTool">> => capi_handler_decoder_party:decode_payment_tool(PaymentTool)}
|
||||
end,
|
||||
|
||||
FullParams = PaymentParams#{
|
||||
PaymentParams = PaymentParams0#{
|
||||
% Требуется для последующей кодировки параметров плательщика
|
||||
<<"invoiceID">> => InvoiceID,
|
||||
% Заменяем на структуру без токена
|
||||
<<"payer">> => ClearPayer
|
||||
<<"payer">> => Payer#{<<"paymentTool">> => PaymentTool}
|
||||
},
|
||||
|
||||
Identity = capi_bender:make_identity({schema, capi_feature_schemas:payment(), FullParams, PaymentParams}),
|
||||
ExternalID = maps:get(<<"externalID">>, PaymentParams, undefined),
|
||||
BenderPrefix = OperationID,
|
||||
IdempotentKey = {BenderPrefix, PartyID, ExternalID},
|
||||
|
||||
SequenceID = InvoiceID,
|
||||
Identity = capi_bender:make_identity(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
|
||||
@ -780,12 +770,13 @@ encode_processing_deadline(Deadline) ->
|
||||
default_processing_deadline() ->
|
||||
genlib_app:env(capi, default_processing_deadline, ?DEFAULT_PROCESSING_DEADLINE).
|
||||
|
||||
create_refund(InvoiceID, PaymentID, RefundParams, Context, BenderPrefix) ->
|
||||
create_refund(InvoiceID, PaymentID, RefundParams0, Context, BenderPrefix) ->
|
||||
PartyID = capi_handler_utils:get_party_id(Context),
|
||||
RefundParamsFull = RefundParams#{<<"invoiceID">> => InvoiceID, <<"paymentID">> => PaymentID},
|
||||
RefundParams = RefundParams0#{<<"invoiceID">> => InvoiceID, <<"paymentID">> => PaymentID},
|
||||
|
||||
ExternalID = maps:get(<<"externalID">>, RefundParams, undefined),
|
||||
IdempotentKey = {BenderPrefix, PartyID, ExternalID},
|
||||
Identity = {schema, capi_feature_schemas:refund(), RefundParamsFull, RefundParams},
|
||||
Identity = capi_bender:make_identity(refund, RefundParams),
|
||||
SequenceID = create_sequence_id([InvoiceID, PaymentID], BenderPrefix),
|
||||
SequenceParams = #{minimum => 100},
|
||||
#{woody_context := WoodyCtx} = Context,
|
||||
|
@ -1,6 +1,6 @@
|
||||
-module(capi_idemp_features).
|
||||
-module(capi_idemp_features_legacy).
|
||||
|
||||
-include("capi_feature_schemas.hrl").
|
||||
-include("capi_feature_schemas_legacy.hrl").
|
||||
|
||||
-type request_key() :: binary().
|
||||
-type request_value() :: integer() | binary() | request() | [request()].
|
@ -7,7 +7,7 @@
|
||||
|
||||
%%
|
||||
|
||||
-spec create_storage() -> capi_idemp_features:event_handler().
|
||||
-spec create_storage() -> feat:event_handler().
|
||||
create_storage() ->
|
||||
%% TODO delete named_table. Make opportunity for concurrent tests.
|
||||
ets:new(?MODULE, [set, public, named_table]).
|
||||
@ -18,105 +18,78 @@ delete_storage() ->
|
||||
|
||||
-spec get_unused_params() -> _.
|
||||
get_unused_params() ->
|
||||
Req = get_request(),
|
||||
maps:keys(capi_ct_helper:map_to_flat(Req)).
|
||||
get_req_paths().
|
||||
|
||||
-spec handle_event(capi_idemp_features:event(), capi_idemp_features:options()) -> ok.
|
||||
handle_event({invalid_schema_fragment, Key, Request}, _Opts) ->
|
||||
throw({extact_idemp_feature, Key, Request});
|
||||
handle_event({request_visited, {request, Req}}, _Opts) ->
|
||||
save_request(Req),
|
||||
-spec handle_event(feat:event(), feat:options()) -> ok.
|
||||
handle_event({request_visited, Req}, _Opts) ->
|
||||
save_req_paths(unroll_request_to_paths(Req));
|
||||
handle_event({request_key_visit, Key, _Value}, _Opts) ->
|
||||
push_path(Key);
|
||||
handle_event({request_key_visited, _Key, _Value}, _Opts) ->
|
||||
delete_subpath(pop_path());
|
||||
handle_event({request_index_visit, N, _Value}, _Opts) ->
|
||||
push_path(N);
|
||||
handle_event({request_index_visited, _N, _Value}, _Opts) ->
|
||||
delete_subpath(pop_path());
|
||||
handle_event({request_variant_visit, _FeatureName, _Variant, _Value}, _Opts) ->
|
||||
ok;
|
||||
handle_event({request_key_index_visit, N}, _Opts) ->
|
||||
push_path({key_index, N}),
|
||||
handle_event({request_variant_visited, _FeatureName, _Variant, _Value}, _Opts) ->
|
||||
ok;
|
||||
handle_event({request_key_index_visited, _N}, _Opts) ->
|
||||
%% delete key_index from stack
|
||||
pop_path(),
|
||||
{Key, List} = pop_path(),
|
||||
%% delete empty map from set
|
||||
List2 = lists:foldl(
|
||||
fun
|
||||
(M, AccIn) when map_size(M) =:= 0 -> AccIn;
|
||||
(M, AccIn) -> [M | AccIn]
|
||||
handle_event(Error, _Opts) ->
|
||||
throw(Error).
|
||||
|
||||
delete_subpath(Path) ->
|
||||
save_req_paths(
|
||||
lists:delete(
|
||||
lists:reverse(Path),
|
||||
get_req_paths()
|
||||
)
|
||||
).
|
||||
|
||||
unroll_request_to_paths(Req) when not is_map(Req), not is_list(Req); map_size(Req) == 0; length(Req) == 0 ->
|
||||
[[]];
|
||||
unroll_request_to_paths(Req) when is_map(Req); is_list(Req) ->
|
||||
lists:flatmap(
|
||||
fun({Key, Nested}) ->
|
||||
lists:map(
|
||||
fun(Rest) -> [Key | Rest] end,
|
||||
unroll_request_to_paths(Nested)
|
||||
)
|
||||
end,
|
||||
[],
|
||||
List
|
||||
),
|
||||
push_path({Key, List2}),
|
||||
ok;
|
||||
handle_event({request_key_visit, {key, Key, SubReq}}, _Opts) ->
|
||||
push_path({Key, SubReq}),
|
||||
ok;
|
||||
handle_event({request_key_visited, {key, Key}}, _Opts) ->
|
||||
Path = get_path(),
|
||||
[{Key, SubReq} | Tail] = Path,
|
||||
delete_subpath(Key, SubReq, Tail),
|
||||
ok.
|
||||
req_to_list(Req)
|
||||
).
|
||||
|
||||
delete_subpath(Key, SubReq, []) when is_map(SubReq), map_size(SubReq) > 0; is_list(SubReq), length(SubReq) > 0 ->
|
||||
Request = get_request(),
|
||||
Request2 = Request#{Key => SubReq},
|
||||
save_request(Request2),
|
||||
ok;
|
||||
delete_subpath(Key, SubReq, [{K, Req} | T]) when
|
||||
is_map(SubReq), map_size(SubReq) > 0; is_list(SubReq), length(SubReq) > 0
|
||||
->
|
||||
Req2 = Req#{Key => SubReq},
|
||||
update_path([{K, Req2} | T]),
|
||||
ok;
|
||||
delete_subpath(Key, _SubReq, []) ->
|
||||
Request = get_request(),
|
||||
Request2 = maps:remove(Key, Request),
|
||||
insert(path, []),
|
||||
save_request(Request2);
|
||||
delete_subpath(Key, _SubReq, [{key_index, Index} = KeyIndex | Tail]) ->
|
||||
[{KeyList, SubReqList} | T] = Tail,
|
||||
{_, SubReqList2} = lists:foldl(
|
||||
fun
|
||||
(SubReq, {I, AccIn}) when I == Index ->
|
||||
SubReq2 = maps:remove(Key, SubReq),
|
||||
{I + 1, [SubReq2 | AccIn]};
|
||||
(_, {N, AccIn}) ->
|
||||
{N + 1, AccIn}
|
||||
end,
|
||||
{0, []},
|
||||
SubReqList
|
||||
),
|
||||
Path = [KeyIndex, {KeyList, lists:reverse(SubReqList2)}] ++ T,
|
||||
update_path(Path),
|
||||
ok;
|
||||
delete_subpath(Key, _SubReq, [{K, Req} | T]) ->
|
||||
Req2 = maps:remove(Key, Req),
|
||||
update_path([{K, Req2} | T]),
|
||||
ok.
|
||||
req_to_list(Map) when is_map(Map) ->
|
||||
maps:to_list(Map);
|
||||
req_to_list(List) when is_list(List) ->
|
||||
lists:zip(lists:seq(0, length(List) - 1), List).
|
||||
|
||||
save_request(Req) ->
|
||||
insert(request, Req).
|
||||
save_req_paths(Paths) ->
|
||||
insert(paths, Paths).
|
||||
|
||||
get_request() ->
|
||||
case ets:lookup(?MODULE, request) of
|
||||
[] -> #{};
|
||||
[{request, Req}] -> Req
|
||||
end.
|
||||
get_req_paths() ->
|
||||
get(paths, []).
|
||||
|
||||
update_path(Path) ->
|
||||
put_path(Path) ->
|
||||
insert(path, Path).
|
||||
|
||||
get_path() ->
|
||||
case ets:lookup(?MODULE, path) of
|
||||
[] -> [];
|
||||
[{path, Path}] -> Path
|
||||
end.
|
||||
get(path, []).
|
||||
|
||||
push_path({Key, Req}) ->
|
||||
Path = get_path(),
|
||||
insert(path, [{Key, Req} | Path]).
|
||||
push_path(Item) ->
|
||||
put_path([Item | get_path()]).
|
||||
|
||||
pop_path() ->
|
||||
[Result | Path] = get_path(),
|
||||
insert(path, Path),
|
||||
Result.
|
||||
Path = get_path(),
|
||||
put_path(tl(Path)),
|
||||
Path.
|
||||
|
||||
insert(Key, Value) ->
|
||||
ets:insert(?MODULE, {Key, Value}).
|
||||
ets:insert(?MODULE, {Key, Value}),
|
||||
ok.
|
||||
|
||||
get(Key, Default) ->
|
||||
case ets:lookup(?MODULE, Key) of
|
||||
[] -> Default;
|
||||
[{Key, Value}] -> Value
|
||||
end.
|
||||
|
@ -18,6 +18,8 @@
|
||||
-export([stop_mocked_service_sup/1]).
|
||||
-export([mock_services/2]).
|
||||
-export([mock_services_/2]).
|
||||
-export([replace_env/1]).
|
||||
-export([restore_env/1]).
|
||||
-export([get_lifetime/0]).
|
||||
-export([map_to_flat/1]).
|
||||
|
||||
@ -33,6 +35,7 @@
|
||||
-type config() :: [{atom(), any()}].
|
||||
-type app_name() :: atom().
|
||||
-type sup_or_config() :: config() | pid().
|
||||
-type replaces() :: #{App :: atom() => #{Key :: atom() => undefined | {value, term()}}}.
|
||||
|
||||
-export_type([config/0]).
|
||||
-export_type([app_name/0]).
|
||||
@ -174,6 +177,7 @@ mock_services(Services, SupOrConfig) ->
|
||||
{BenderClientServices, WoodyServices} = lists:partition(
|
||||
fun
|
||||
({generator, _}) -> true;
|
||||
({bender, _}) -> true;
|
||||
(_) -> false
|
||||
end,
|
||||
Other
|
||||
@ -182,6 +186,45 @@ mock_services(Services, SupOrConfig) ->
|
||||
_ = start_bender_client(mock_services_(BenderClientServices, SupOrConfig)),
|
||||
start_woody_client(mock_services_(WoodyServices, SupOrConfig)).
|
||||
|
||||
-spec replace_env(#{App :: atom() => #{Key :: atom() => Value :: term()}}) -> replaces().
|
||||
replace_env(Env) ->
|
||||
maps:fold(
|
||||
fun(App, AppEnv, Acc) ->
|
||||
AppReplaces =
|
||||
maps:fold(
|
||||
fun(Key, Value, AppAcc) ->
|
||||
AccValue =
|
||||
case application:get_env(App, Key) of
|
||||
undefined -> undefined;
|
||||
{ok, Original} -> {value, Original}
|
||||
end,
|
||||
application:set_env(App, Key, Value),
|
||||
AppAcc#{Key => AccValue}
|
||||
end,
|
||||
#{},
|
||||
AppEnv
|
||||
),
|
||||
Acc#{App => AppReplaces}
|
||||
end,
|
||||
#{},
|
||||
Env
|
||||
).
|
||||
|
||||
-spec restore_env(replaces()) -> ok.
|
||||
restore_env(Replaces) ->
|
||||
maps:foreach(
|
||||
fun(App, AppReplaces) ->
|
||||
maps:foreach(
|
||||
fun
|
||||
(Key, undefined) -> application:unset_env(App, Key);
|
||||
(Key, {value, Original}) -> application:set_env(App, Key, Original)
|
||||
end,
|
||||
AppReplaces
|
||||
)
|
||||
end,
|
||||
Replaces
|
||||
).
|
||||
|
||||
start_party_client(Services) ->
|
||||
start_app(party_client, [{services, Services}]).
|
||||
|
||||
@ -220,6 +263,8 @@ mock_services_(Services, SupPid) when is_pid(SupPid) ->
|
||||
|
||||
get_service_name({generator, _}) ->
|
||||
'Generator';
|
||||
get_service_name({bender, _}) ->
|
||||
'Bender';
|
||||
get_service_name({ServiceName, _Fun}) ->
|
||||
ServiceName;
|
||||
get_service_name({ServiceName, _WoodyService, _Fun}) ->
|
||||
@ -227,6 +272,8 @@ get_service_name({ServiceName, _WoodyService, _Fun}) ->
|
||||
|
||||
mock_service_handler({generator, Fun}) ->
|
||||
mock_service_handler('Generator', {bender_thrift, 'Generator'}, Fun);
|
||||
mock_service_handler({bender, Fun}) ->
|
||||
mock_service_handler('Bender', {bender_thrift, 'Bender'}, Fun);
|
||||
mock_service_handler({party_management, Fun}) ->
|
||||
mock_service_handler(party_management, {dmsl_payment_processing_thrift, 'PartyManagement'}, Fun);
|
||||
mock_service_handler({ServiceName, Fun}) ->
|
||||
|
@ -24,6 +24,7 @@
|
||||
-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]).
|
||||
@ -77,6 +78,7 @@ 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,
|
||||
@ -108,15 +110,19 @@ groups() ->
|
||||
%% starting/stopping
|
||||
%%
|
||||
-spec init_per_suite(config()) -> config().
|
||||
init_per_suite(Config) ->
|
||||
ExtraEnv = [{idempotence_event_handler, {capi_ct_features_reader_event_handler, #{}}}],
|
||||
capi_ct_helper:init_suite(?MODULE, Config, ExtraEnv).
|
||||
init_per_suite(Config0) ->
|
||||
ReplacedEnv =
|
||||
capi_ct_helper:replace_env(
|
||||
#{feat => #{event_handler => {capi_ct_features_reader_event_handler, #{}}}}
|
||||
),
|
||||
Config = capi_ct_helper:init_suite(?MODULE, Config0),
|
||||
[{replaced_env, ReplacedEnv} | Config].
|
||||
|
||||
-spec end_per_suite(config()) -> _.
|
||||
end_per_suite(C) ->
|
||||
_ = capi_ct_helper:stop_mocked_service_sup(?config(suite_test_sup, C)),
|
||||
_ = [application:stop(App) || App <- proplists:get_value(apps, C)],
|
||||
_ = application:unset_env(capi, idempotence_event_handler),
|
||||
ok = capi_ct_helper:restore_env(?config(replaced_env, C)),
|
||||
ok.
|
||||
|
||||
-spec init_per_group(group_name(), config()) -> config().
|
||||
@ -207,12 +213,14 @@ create_payment_ok_test(Config) ->
|
||||
?assertEqual(
|
||||
[
|
||||
[<<"externalID">>],
|
||||
[<<"metadata">>, <<"bla">>],
|
||||
[<<"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">>]
|
||||
],
|
||||
Unused
|
||||
@ -249,8 +257,10 @@ different_payment_tools_test(Config) ->
|
||||
?assertEqual(
|
||||
[
|
||||
[<<"externalID">>],
|
||||
[<<"metadata">>, <<"bla">>],
|
||||
[<<"metadata">>, <<"bla">>, 0],
|
||||
[<<"payer">>, <<"contactInfo">>],
|
||||
[<<"payer">>, <<"paymentSession">>],
|
||||
[<<"payer">>, <<"paymentToolToken">>],
|
||||
[<<"processingDeadline">>]
|
||||
],
|
||||
Unused
|
||||
@ -314,6 +324,33 @@ 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">>,
|
||||
@ -325,7 +362,10 @@ create_invoice_legacy_fail_test(Config) ->
|
||||
[<<"metadata">>, <<"invoice_dummy_metadata">>]
|
||||
],
|
||||
Req2 = Req#{<<"product">> => <<"test_product2">>},
|
||||
Ctx = capi_msgp_marshalling:marshal(#{<<"version">> => 1, <<"params_hash">> => erlang:phash2(Req)}),
|
||||
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}}) ->
|
||||
@ -618,6 +658,7 @@ create_customer_ok_test(Config) ->
|
||||
Req1 = ?CUSTOMER_PARAMS#{<<"externalID">> => genlib:unique()},
|
||||
Req2 = Req1#{<<"externalID">> => genlib:unique()},
|
||||
|
||||
UnusedFeatures = [[<<"externalID">>], [<<"metadata">>, <<"text">>, 0], [<<"metadata">>, <<"text">>, 1]],
|
||||
Result = create_customers(BenderKey, [Req1, Req2], Config),
|
||||
|
||||
[{{ok, #{<<"customer">> := Customer1}}, UnusedFeatures1}, {{ok, #{<<"customer">> := Customer2}}, UnusedFeatures2}] =
|
||||
@ -625,7 +666,7 @@ create_customer_ok_test(Config) ->
|
||||
|
||||
?assertEqual(Customer1, Customer2),
|
||||
?assertEqual(UnusedFeatures1, UnusedFeatures2),
|
||||
?assertEqual(UnusedFeatures1, [[<<"externalID">>], [<<"metadata">>, <<"text">>]]).
|
||||
?assertEqual(UnusedFeatures1, UnusedFeatures).
|
||||
|
||||
-spec create_customer_fail_test(config()) -> _.
|
||||
create_customer_fail_test(Config) ->
|
||||
@ -637,7 +678,9 @@ create_customer_fail_test(Config) ->
|
||||
[CustomerResult1, CustomerResult2] = create_customers(BenderKey, [Req1, Req2], Config),
|
||||
?assertMatch({{ok, _}, _}, CustomerResult1),
|
||||
?assertEqual(
|
||||
{response_error(409, ExternalID, BenderKey), [[<<"externalID">>], [<<"metadata">>, <<"text">>]]},
|
||||
{response_error(409, ExternalID, BenderKey), [
|
||||
[<<"externalID">>], [<<"metadata">>, <<"text">>, 0], [<<"metadata">>, <<"text">>, 1]
|
||||
]},
|
||||
CustomerResult2
|
||||
).
|
||||
|
||||
@ -938,7 +981,7 @@ create_invoices_with_templates(BenderKey, Requests, Config) ->
|
||||
).
|
||||
|
||||
with_feature_storage(Fun) ->
|
||||
capi_ct_features_reader_event_handler:create_storage(),
|
||||
_ = capi_ct_features_reader_event_handler:create_storage(),
|
||||
Result = Fun(),
|
||||
UnusedParams = capi_ct_features_reader_event_handler:get_unused_params(),
|
||||
capi_ct_features_reader_event_handler:delete_storage(),
|
||||
|
@ -92,9 +92,7 @@ get_service_modname(webhook_manager) ->
|
||||
get_service_modname(customer_management) ->
|
||||
{dmsl_payment_processing_thrift, 'CustomerManagement'};
|
||||
get_service_modname(party_management) ->
|
||||
{dmsl_payment_processing_thrift, 'PartyManagement'};
|
||||
get_service_modname(bender) ->
|
||||
{bender_thrift, 'Bender'}.
|
||||
{dmsl_payment_processing_thrift, 'PartyManagement'}.
|
||||
|
||||
get_service_deadline(ServiceName) ->
|
||||
ServiceDeadlines = genlib_app:env(?MODULE, service_deadlines, #{}),
|
||||
|
@ -95,8 +95,7 @@
|
||||
merchant_stat => <<"http://magista:8022/stat">>,
|
||||
reporting => <<"http://reporter:8022/reports">>,
|
||||
payouts => <<"http://payouter:8022/reports">>,
|
||||
geo_ip_service => <<"http://columbus:8022/repo">>,
|
||||
bender => <<"http://bender:8022/v1/bender">>
|
||||
geo_ip_service => <<"http://columbus:8022/repo">>
|
||||
}},
|
||||
{service_deadlines, #{
|
||||
party_management => 5000, % milliseconds
|
||||
|
@ -12,7 +12,12 @@
|
||||
{elvis_style, macro_module_names},
|
||||
{elvis_style, operator_spaces, #{rules => [{right, ","}, {right, "++"}, {left, "++"}]}},
|
||||
{elvis_style, nesting_level, #{level => 4}},
|
||||
{elvis_style, god_modules, #{limit => 30, ignore => [capi_base_api_token_tests_SUITE]}},
|
||||
{elvis_style, god_modules, #{
|
||||
limit => 30,
|
||||
ignore => [
|
||||
capi_base_api_token_tests_SUITE, capi_idempotency_tests_SUITE
|
||||
]
|
||||
}},
|
||||
{elvis_style, no_if_expression},
|
||||
{elvis_style, invalid_dynamic_call, #{ignore => [capi_domain]}},
|
||||
{elvis_style, used_ignored_variable},
|
||||
|
@ -51,7 +51,8 @@
|
||||
{token_keeper_client, {git, "https://github.com/rbkmoney/token-keeper-client.git", {branch, master}}},
|
||||
{party_client, {git, "https://github.com/rbkmoney/party_client_erlang.git", {branch, master}}},
|
||||
{payout_manager_proto, {git, "https://github.com/rbkmoney/payout-manager-proto.git", {branch, master}}},
|
||||
{how_are_you, {git, "https://github.com/rbkmoney/how_are_you.git", {branch, master}}}
|
||||
{how_are_you, {git, "https://github.com/rbkmoney/how_are_you.git", {branch, master}}},
|
||||
{feat, {git, "https://github.com/rbkmoney/feat.git", {branch, master}}}
|
||||
]}.
|
||||
|
||||
%% XRef checks
|
||||
|
@ -3,7 +3,7 @@
|
||||
{<<"bear">>,{pkg,<<"bear">>,<<"0.9.0">>},2},
|
||||
{<<"bender_client">>,
|
||||
{git,"https://github.com/rbkmoney/bender_client_erlang.git",
|
||||
{ref,"69024efc38167c515d1dc7b7c2bb52262ffe7d0d"}},
|
||||
{ref,"29501d6f6425bc310ef6b37b62790126bdff356b"}},
|
||||
0},
|
||||
{<<"bender_proto">>,
|
||||
{git,"https://github.com/rbkmoney/bender-proto.git",
|
||||
@ -54,13 +54,17 @@
|
||||
{git,"https://github.com/rbkmoney/erlang-health.git",
|
||||
{ref,"5958e2f35cd4d09f40685762b82b82f89b4d9333"}},
|
||||
0},
|
||||
{<<"feat">>,
|
||||
{git,"https://github.com/rbkmoney/feat.git",
|
||||
{ref,"bf7dff68c822e58769da962e7f99c3e428a88551"}},
|
||||
0},
|
||||
{<<"folsom">>,
|
||||
{git,"https://github.com/folsom-project/folsom.git",
|
||||
{ref,"62fd0714e6f0b4e7833880afe371a9c882ea0fc2"}},
|
||||
1},
|
||||
{<<"genlib">>,
|
||||
{git,"https://github.com/rbkmoney/genlib.git",
|
||||
{ref,"3e1776536802739d8819351b15d54ec70568aba7"}},
|
||||
{ref,"82c5ff3866e3019eb347c7f1d8f1f847bed28c10"}},
|
||||
0},
|
||||
{<<"gproc">>,{pkg,<<"gproc">>,<<"0.9.0">>},1},
|
||||
{<<"gun">>,
|
||||
|
Loading…
Reference in New Issue
Block a user