%%%------------------------------------------------------------------- %%% @author cheese %%% @copyright (C) 2014, %%% @doc %%% %%% @end %%% Created : 20. Jan 2014 4:04 PM %%%------------------------------------------------------------------- -module(protocol_tic). -author("cheese"). -include("../../include/types_finance_protocol.hrl"). %% API -export([parse/1, get_field_description/1]). -export([internal_parse_fields/4, internal_parse_fields_from_byte_in_bitmap/5, internal_parse_field_from_bytes/2, internal_get_valid_message_size/2, internal_get_view_data/2, internal_check_size/2, internal_get_valid_data_bytes/2]). parse(<<_L2L1:2/binary, _Header:8/binary, MTI_Raw:4/binary, Bitmap_1:16/binary, Data/binary>>) -> MTI = list_to_integer(binary_to_list(MTI_Raw)), IsExtBitmap = is_ext_bitmap(binary_to_list(Bitmap_1)), {Bitmap_2, _Data2} = internal_get_valid_data_bytes(Data, IsExtBitmap), NewBitmap = <>, Fields = internal_parse_fields(NewBitmap, 1, Data, []), #parsed_iso_message{ mti = MTI, fields = Fields }; parse(Other) -> logger:info(#{ message => "Invalid request: ~w~n", invalidInfo => [Other]}), {error, invalid_data}. internal_get_valid_data_bytes(Data, IsExtBitmap) -> case IsExtBitmap of false -> {<<>>, Data}; true -> <> = Data, {Bitmap_2, Data2} end. is_ext_bitmap([B1 | _OtherBytes]) -> B_HEX = bytes_extension:int_as_hex(B1), %%logger:simple_message("B1: ~w , B_HEX: ~w", [B1, B_HEX]), B_HEX >= 8. %%---------------------------------------------------------------------------------------------------------------------- %% Разбор полей по битовой маске %%---------------------------------------------------------------------------------------------------------------------- -spec(internal_parse_fields(binary(), number(), binary(), [#iso_field{}]) -> [#iso_field{}]). internal_parse_fields(<<>>, _Pos, _Data, AllFields) -> lists:reverse(AllFields); internal_parse_fields(<>, Pos, Data, AllFields) -> ByteNum = bytes_extension:one_string_hex_to_int(Bt), case internal_parse_fields_from_byte_in_bitmap(ByteNum, Pos, 8, Data, AllFields) of {error, Reason} -> {error, Reason}; {NewData, NewAllFields} -> internal_parse_fields(Tail, Pos + 4, NewData, NewAllFields) end. %%---------------------------------------------------------------------------------------------------------------------- %% Разбор одного байта из битовой маски %%---------------------------------------------------------------------------------------------------------------------- internal_parse_fields_from_byte_in_bitmap(_Bt, _Pos, 0, Data, AllFields) -> {Data, AllFields}; internal_parse_fields_from_byte_in_bitmap(Bt, Pos, Pow, Data, AllFields) -> IsPresent = Bt band Pow, %%logger:simple_message("Bt: ~w, Pos: ~w, Pow: ~w, IsPresent: ~w", [Bt, Pos, Pow, IsPresent]), case IsPresent > 0 of true -> %logger:simple_message("=============================================", []), %logger:simple_message("Filed ~w present", [Pos]), Res = get_field_description(Pos), %logger:simple_message("Res: ~p", [Res]), case Res of undefined -> internal_parse_fields_from_byte_in_bitmap(Bt, Pos + 1, Pow div 2, Data, AllFields); FieldInfo -> case internal_parse_field_from_bytes(FieldInfo, Data) of {error, Reason} -> %logger:simple_message("Field not parsed: ~w", [Reason]), {error, Reason}; {Field, NewBytes} -> %logger:simple_message("Field parsed: ~p", [Field]), internal_parse_fields_from_byte_in_bitmap(Bt, Pos + 1, Pow div 2, NewBytes, [Field | AllFields]) end end; false -> internal_parse_fields_from_byte_in_bitmap(Bt, Pos + 1, Pow div 2, Data, AllFields) end. %%---------------------------------------------------------------------------------------------------------------------- %% Разбор одного поля %%---------------------------------------------------------------------------------------------------------------------- -spec(internal_parse_field_from_bytes(#iso_field{}, binary()) -> {error, any()} | {#iso_field{}, binary()}). internal_parse_field_from_bytes(#iso_field{header = Header} = FieldInfo, Bytes) -> %logger:simple_message("Process pos: ~p", [FieldInfo#iso_field.pos]), case internal_get_valid_message_size(FieldInfo, Bytes) of {error, Reason} -> {error, {Reason, {field_idx, FieldInfo#iso_field.pos}}}; RealDataSizeFixed -> %logger:simple_message("RealDataSizeFixed: ~p", [RealDataSizeFixed]), FieldDataBytes = binary:part(Bytes, Header, RealDataSizeFixed), BytesOther = binary:part(Bytes, Header + RealDataSizeFixed, byte_size(Bytes) - Header - RealDataSizeFixed), %%logger:simple_message("BytesOther: ~w", [BytesOther]), NewFieldInfoRaw = FieldInfo#iso_field{ raw_data = FieldDataBytes }, ViewData = internal_get_view_data(NewFieldInfoRaw, FieldDataBytes), NewFieldInfo = NewFieldInfoRaw#iso_field{ view_data = ViewData }, {NewFieldInfo, BytesOther} end; %%---------------------------------------------------------------------------------------------------------------------- internal_parse_field_from_bytes(FieldInfo, _Bytes) -> {error, {'Not parsed field', FieldInfo}}. %%---------------------------------------------------------------------------------------------------------------------- %% Получить размер реальных данных в поле %%---------------------------------------------------------------------------------------------------------------------- internal_get_valid_message_size(#iso_field{header = 0, size = Size}, Bytes) -> internal_check_size(Size, Bytes); internal_get_valid_message_size(#iso_field{header = Header}, Bytes) -> RealDataSize = list_to_integer(binary_to_list(binary:part(Bytes, 0, Header))), %%logger:simple_message("RealDataSize: ~p", [RealDataSize]), internal_check_size(RealDataSize, Bytes). %%---------------------------------------------------------------------------------------------------------------------- %% Получить данные %%---------------------------------------------------------------------------------------------------------------------- internal_check_size(Size, Bytes) -> ByteSize = byte_size(Bytes), case Size > ByteSize of true -> {error, {not_enough_data, {field_size, Size}, {bite_size, ByteSize}}}; false -> Size end. %%---------------------------------------------------------------------------------------------------------------------- %% Получить данные %%---------------------------------------------------------------------------------------------------------------------- internal_get_view_data(#iso_field{type = Type}, Bytes) when Type == d -> bytes_extension:bcd_string(Bytes); internal_get_view_data(_, Bytes) -> Bytes. %%---------------------------------------------------------------------------------------------------------------------- %% Описание одного поля протокола %%---------------------------------------------------------------------------------------------------------------------- get_field_description(0 = Pos) -> #iso_field{ pos = Pos, size = 16, header = 0, type = s, info = "Primary bit map" }; get_field_description(1 = Pos) -> #iso_field{ pos = Pos, size = 16, header = 0, type = s, info = "Secondary bit map" }; get_field_description(2 = Pos) -> #iso_field{ pos = Pos, size = 19, header = 2, type = s, info = "Primary Account Number" }; get_field_description(3 = Pos) -> #iso_field{ pos = Pos, size = 7, header = 0, type = s, info = "Processing Code" }; get_field_description(4 = Pos) -> #iso_field{ pos = Pos, size = 12, header = 0, type = s, info = "Transaction amount" }; get_field_description(5 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(6 = Pos) -> #iso_field{ pos = Pos, size = 12, header = 0, type = s, info = "Cardholder billing amount" }; get_field_description(7 = Pos) -> #iso_field{ pos = Pos, size = 10, header = 0, type = s, info = "Transmission date and time" }; get_field_description(8 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(9 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(10 = Pos) -> #iso_field{ pos = Pos, size = 8, header = 0, type = s, info = "Conversion Rate, Cardholder Billing" }; get_field_description(11 = Pos) -> #iso_field{ pos = Pos, size = 6, header = 0, type = s, info = "STAN" }; get_field_description(12 = Pos) -> #iso_field{ pos = Pos, size = 6, header = 0, type = s, info = "Local transaction time (HHMMSS)" }; get_field_description(13 = Pos) -> #iso_field{ pos = Pos, size = 4, header = 0, type = s, info = "Local transaction date (MMDD)" }; get_field_description(14 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(15 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(16 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(17 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(18 = Pos) -> #iso_field{ pos = Pos, size = 4, header = 0, type = s, info = "Merchant category code" }; get_field_description(19 = Pos) -> #iso_field{ pos = Pos, size = 3, header = 0, type = s, info = "Acquiring institution country code" }; get_field_description(20 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(21 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(22 = Pos) -> #iso_field{ pos = Pos, size = 3, header = 0, type = s, info = "Point of service entry mode" }; get_field_description(23 = Pos) -> #iso_field{ pos = Pos, size = 3, header = 0, type = s, info = "Card sequence number" }; get_field_description(24 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(25 = Pos) -> #iso_field{ pos = Pos, size = 3, header = 0, type = s, info = "Point of service condition code" }; get_field_description(26 = Pos) -> #iso_field{ pos = Pos, size = 4, header = 0, type = s, info = "Message reason code" }; get_field_description(27 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(28 = Pos) -> #iso_field{ pos = Pos, size = 9, header = 0, type = s, info = "Aquier/Issuer fee amount" }; get_field_description(29 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(30 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(31 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(32 = Pos) -> #iso_field{ pos = Pos, size = 11, header = 2, type = s, info = "Acquiring Institution Identification Code" }; get_field_description(33 = Pos) -> #iso_field{ pos = Pos, size = 11, header = 2, type = s, info = "Forwarding Institution Identification Code" }; get_field_description(34 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(35 = Pos) -> #iso_field{ pos = Pos, size = 37, header = 2, type = s, info = "Track 2 Data" }; get_field_description(36 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(37 = Pos) -> #iso_field{ pos = Pos, size = 12, header = 0, type = s, info = "Retrieval Reference Number" }; get_field_description(38 = Pos) -> #iso_field{ pos = Pos, size = 6, header = 0, type = s, info = "Authorization Identification Response" }; get_field_description(39 = Pos) -> #iso_field{ pos = Pos, size = 5, header = 0, type = s, info = "Response" }; get_field_description(40 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(41 = Pos) -> #iso_field{ pos = Pos, size = 49, header = 2, type = s, info = "Card Acceptor Terminal Identification" }; get_field_description(42 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(43 = Pos) -> #iso_field{ pos = Pos, size = 222, header = 0, type = s, info = "Card Acceptor Name/Location" }; get_field_description(44 = Pos) -> #iso_field{ pos = Pos, size = 2, header = 0, type = s, info = "Result PIN/CVV" }; get_field_description(45 = Pos) -> #iso_field{ pos = Pos, size = 76, header = 2, type = s, info = "Track 1 Data" }; get_field_description(46 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(47 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(48 = Pos) -> #iso_field{ pos = Pos, size = 31, header = 0, type = s, info = "Reference to other transaction" }; get_field_description(49 = Pos) -> #iso_field{ pos = Pos, size = 3, header = 0, type = s, info = "Transaction Currency Code" }; get_field_description(50 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(51 = Pos) -> #iso_field{ pos = Pos, size = 3, header = 0, type = s, info = "Cardholder Billing Currency Code" }; get_field_description(52 = Pos) -> #iso_field{ pos = Pos, size = 16, header = 0, type = s, info = "Personal Identification Number" }; get_field_description(53 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(54 = Pos) -> #iso_field{ pos = Pos, size = 12, header = 0, type = s, info = "Adjustment amount" }; get_field_description(55 = Pos) -> #iso_field{ pos = Pos, size = 255, header = 3, type = b, info = "ICC System Related Data" }; get_field_description(56 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(57 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(58 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(59 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(60 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(61 = Pos) -> #iso_field{ pos = Pos, size = 14, header = 0, type = s, info = "Card Issuer Data" }; get_field_description(62 = Pos) -> #iso_field{ pos = Pos, size = 100, header = 3, type = s, info = "External Transaction Attributes" }; get_field_description(63 = Pos) -> #iso_field{ pos = Pos, size = 16, header = 0, type = s, info = "New Pin" }; get_field_description(64 = Pos) -> #iso_field{ pos = Pos, size = 16, header = 0, type = s, info = "MAC" }; get_field_description(65 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(66 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(67 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(68 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(69 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(70 = Pos) -> #iso_field{ pos = Pos, size = 3, header = 0, type = s, info = "Network Management Information Code " }; get_field_description(71 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(72 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(73 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(74 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(75 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(76 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(77 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(78 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(79 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(80 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(81 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(82 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(83 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(84 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(85 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(86 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(87 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(88 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(89 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(90 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(91 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(92 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(93 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(94 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(95 = Pos) -> #iso_field{ pos = Pos, size = 24, header = 0, type = s, info = "Replacement Amount Data" }; get_field_description(96 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(97 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(98 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(99 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(100 = Pos) -> #iso_field{ pos = Pos, size = 11, header = 2, type = s, info = "Receiving Institution Identification Code" }; get_field_description(101 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(102 = Pos) -> #iso_field{ pos = Pos, size = 30, header = 2, type = s, info = "Account Identification 1" }; get_field_description(103 = Pos) -> #iso_field{ pos = Pos, size = 30, header = 2, type = s, info = "Account Identification 2" }; get_field_description(104 = Pos) -> #iso_field{ pos = Pos, size = 4, header = 0, type = s, info = "Host Net Identification " }; get_field_description(105 = Pos) -> #iso_field{ pos = Pos, size = 25, header = 0, type = s, info = "Balance data" }; get_field_description(106 = Pos) -> #iso_field{ pos = Pos, size = 46, header = 0, type = s, info = "Multi Currency Data" }; get_field_description(107 = Pos) -> #iso_field{ pos = Pos, size = 12, header = 0, type = s, info = "RRN (F37) sent to final interchange" }; get_field_description(108 = Pos) -> #iso_field{ pos = Pos, size = 999, header = 3, type = s, info = "Regional Listing Data/String Message" }; get_field_description(109 = Pos) -> #iso_field{ pos = Pos, size = 593, header = 3, type = s, info = "Multi Account Data" }; get_field_description(110 = Pos) -> #iso_field{ pos = Pos, size = 9, header = 1, type = s, info = "Numeric Message" }; get_field_description(111 = Pos) -> #iso_field{ pos = Pos, size = 99, header = 2, type = s, info = "Лицевой номер, например номер телефона или лицевого счета" }; get_field_description(112 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(113 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(114 = Pos) -> #iso_field{ pos = Pos, size = 250, header = 3, type = s, info = "Statement" }; get_field_description(115 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(116 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(117 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(118 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(119 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(120 = Pos) -> #iso_field{ pos = Pos, size = 0, header = 0, type = s, info = "???" }; get_field_description(121 = Pos) -> #iso_field{ pos = Pos, size = 47, header = 0, type = s, info = "Additional POS Data" }; get_field_description(122 = Pos) -> #iso_field{ pos = Pos, size = 48, header = 0, type = s, info = "3D-Secure Data" }; get_field_description(123 = Pos) -> #iso_field{ pos = Pos, size = 999, header = 3, type = s, info = "Misc. Transaction Attributes" }; get_field_description(124 = Pos) -> #iso_field{ pos = Pos, size = 99999, header = 5, type = s, info = "Misc. Transaction Attributes 2" }; get_field_description(125 = Pos) -> #iso_field{ pos = Pos, size = 99999, header = 5, type = s, info = "Administrative Transaction Data" }; get_field_description(126 = Pos) -> #iso_field{ pos = Pos, size = 34, header = 0, type = s, info = "Параметры предавторизации" }; get_field_description(127 = Pos) -> #iso_field{ pos = Pos, size = 99999, header = 5, type = s, info = "Доп. Информация" }; get_field_description(128 = Pos) -> #iso_field{ pos = Pos, size = 16, header = 0, type = s, info = "MAC" }; get_field_description(_Pos) -> undefined.