@padosoft/react-native-ecr17 0.0.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/Ecr17.podspec +39 -39
  2. package/README.md +348 -348
  3. package/android/CMakeLists.txt +41 -41
  4. package/android/build.gradle +148 -148
  5. package/android/fix-prefab.gradle +50 -50
  6. package/android/gradle.properties +5 -5
  7. package/android/src/main/AndroidManifest.xml +2 -2
  8. package/android/src/main/cpp/cpp-adapter.cpp +8 -8
  9. package/android/src/main/java/com/margelo/nitro/ecr17/HybridEcr17Transport.kt +233 -233
  10. package/android/src/main/java/com/padosoft/ecr17/Ecr17Package.kt +30 -30
  11. package/cpp/Ecr17.hpp +1 -1
  12. package/cpp/Ecr17Client/HybridEcr17Client.cpp +598 -598
  13. package/cpp/Ecr17Client/HybridEcr17Client.hpp +85 -85
  14. package/cpp/Ecr17Protocol/Ecr17Protocol.cpp +277 -277
  15. package/cpp/Ecr17Protocol/Ecr17Protocol.hpp +103 -103
  16. package/cpp/Ecr17Response/Ecr17Response.cpp +155 -155
  17. package/cpp/Ecr17Response/Ecr17Response.hpp +113 -113
  18. package/cpp/Lcr/Lcr.cpp +42 -42
  19. package/cpp/Lcr/Lcr.hpp +21 -21
  20. package/cpp/PacketCodec/PacketCodec.cpp +145 -145
  21. package/cpp/PacketCodec/PacketCodec.hpp +47 -47
  22. package/cpp/Session/Ecr17Session.cpp +260 -260
  23. package/cpp/Session/Ecr17Session.hpp +97 -97
  24. package/cpp/Session/RetryPolicy.hpp +23 -23
  25. package/cpp/Transport/FakeTransport.hpp +95 -95
  26. package/cpp/Transport/NativeTransportAdapter.cpp +42 -42
  27. package/cpp/Transport/NativeTransportAdapter.hpp +32 -32
  28. package/cpp/Transport/Transport.hpp +30 -30
  29. package/cpp/tests/CMakeLists.txt +55 -55
  30. package/cpp/tests/PosixTcpTransport.hpp +105 -105
  31. package/cpp/tests/stubs/LrcMode.hpp +25 -25
  32. package/cpp/tests/test_flows.cpp +148 -148
  33. package/cpp/tests/test_integration_terminal.cpp +72 -72
  34. package/cpp/tests/test_lrc.cpp +66 -66
  35. package/cpp/tests/test_packet_codec.cpp +164 -164
  36. package/cpp/tests/test_protocol.cpp +102 -102
  37. package/cpp/tests/test_protocol_commands.cpp +190 -190
  38. package/cpp/tests/test_response.cpp +164 -164
  39. package/cpp/tests/test_retry_policy.cpp +28 -28
  40. package/cpp/tests/test_session.cpp +262 -262
  41. package/ios/HybridEcr17Transport.swift +103 -103
  42. package/lib/commonjs/index.js +50 -0
  43. package/lib/commonjs/index.js.map +1 -0
  44. package/lib/commonjs/package.json +1 -0
  45. package/lib/commonjs/specs/client.nitro.js +17 -0
  46. package/lib/commonjs/specs/client.nitro.js.map +1 -0
  47. package/lib/commonjs/specs/transport.nitro.js +6 -0
  48. package/lib/commonjs/specs/transport.nitro.js.map +1 -0
  49. package/lib/commonjs/types/client.js +2 -0
  50. package/lib/commonjs/types/client.js.map +1 -0
  51. package/lib/commonjs/utils/client.js +13 -0
  52. package/lib/commonjs/utils/client.js.map +1 -0
  53. package/lib/module/index.js +7 -0
  54. package/lib/module/index.js.map +1 -0
  55. package/lib/module/specs/client.nitro.js +13 -0
  56. package/lib/module/specs/client.nitro.js.map +1 -0
  57. package/lib/module/specs/transport.nitro.js +4 -0
  58. package/lib/module/specs/transport.nitro.js.map +1 -0
  59. package/lib/module/types/client.js +2 -0
  60. package/lib/module/types/client.js.map +1 -0
  61. package/lib/module/utils/client.js +9 -0
  62. package/lib/module/utils/client.js.map +1 -0
  63. package/lib/typescript/src/index.d.ts +5 -0
  64. package/lib/typescript/src/index.d.ts.map +1 -0
  65. package/lib/typescript/src/specs/client.nitro.d.ts +63 -0
  66. package/lib/typescript/src/specs/client.nitro.d.ts.map +1 -0
  67. package/lib/typescript/src/specs/transport.nitro.d.ts +13 -0
  68. package/lib/typescript/src/specs/transport.nitro.d.ts.map +1 -0
  69. package/lib/typescript/src/types/client.d.ts +138 -0
  70. package/lib/typescript/src/types/client.d.ts.map +1 -0
  71. package/lib/typescript/src/utils/client.d.ts +3 -0
  72. package/lib/typescript/src/utils/client.d.ts.map +1 -0
  73. package/nitro.json +30 -30
  74. package/package.json +4 -4
  75. package/react-native.config.js +18 -18
  76. package/src/index.ts +4 -4
  77. package/src/specs/client.nitro.ts +102 -102
  78. package/src/specs/transport.nitro.ts +25 -25
  79. package/src/types/client.ts +196 -196
  80. package/src/utils/client.ts +10 -10
@@ -1,102 +1,102 @@
1
- #include <gtest/gtest.h>
2
-
3
- #include <stdexcept>
4
- #include <string>
5
-
6
- #include "Ecr17Protocol/Ecr17Protocol.hpp"
7
-
8
- using margelo::nitro::ecr17::Ecr17Protocol;
9
-
10
- // Field layout reference (1-based positions from the Nexi ECR17 spec, Payment
11
- // request "P"):
12
- // 1 8 Terminal ID
13
- // 9 1 Reserved '0'
14
- // 10 1 Message code 'P'
15
- // 11 8 Cash register ID
16
- // 19 1 Presence of additional data for GT
17
- // 20 2 Reserved "00"
18
- // 22 1 Start transaction when card already present
19
- // 23 1 Payment type
20
- // 24 8 Transaction amount (right aligned, '0' filled)
21
- // 32 128 Text to print (' ' filled)
22
- // 160 8 Reserved '0' => total 167 bytes
23
-
24
- TEST(Protocol, StatusMessageLayout) {
25
- std::string m = Ecr17Protocol::buildStatusMessage("42");
26
- ASSERT_EQ(m.size(), 10u);
27
- EXPECT_EQ(m.substr(0, 8), "00000042"); // terminal id, left-padded
28
- EXPECT_EQ(m[8], '0'); // reserved
29
- EXPECT_EQ(m[9], 's'); // lowercase message code per spec
30
- }
31
-
32
- TEST(Protocol, StatusMessageKeepsFullWidthId) {
33
- std::string m = Ecr17Protocol::buildStatusMessage("12345678");
34
- EXPECT_EQ(m, "123456780s");
35
- }
36
-
37
- TEST(Protocol, PaymentMessageIs167Bytes) {
38
- std::string m = Ecr17Protocol::buildPaymentMessage("1", "2", 650);
39
- EXPECT_EQ(m.size(), 167u);
40
- }
41
-
42
- TEST(Protocol, PaymentMessageFieldLayout) {
43
- std::string m = Ecr17Protocol::buildPaymentMessage("12345678", "87654321", 650);
44
- ASSERT_EQ(m.size(), 167u);
45
- EXPECT_EQ(m.substr(0, 8), "12345678"); // terminal id
46
- EXPECT_EQ(m[8], '0'); // reserved
47
- EXPECT_EQ(m[9], 'P'); // message code
48
- EXPECT_EQ(m.substr(10, 8), "87654321"); // cash register id
49
- EXPECT_EQ(m[18], '0'); // presence of additional data
50
- EXPECT_EQ(m.substr(19, 2), "00"); // reserved
51
- EXPECT_EQ(m[21], '0'); // start-with-card
52
- EXPECT_EQ(m[22], '0'); // payment type
53
- EXPECT_EQ(m.substr(23, 8), "00000650"); // amount, right aligned
54
- EXPECT_EQ(m.substr(31, 128), std::string(128, ' ')); // text field
55
- EXPECT_EQ(m.substr(159, 8), "00000000"); // trailing reserved
56
- }
57
-
58
- TEST(Protocol, PaymentMessageAmountMaxFits) {
59
- std::string m = Ecr17Protocol::buildPaymentMessage("1", "2", 99999999);
60
- EXPECT_EQ(m.substr(23, 8), "99999999");
61
- }
62
-
63
- TEST(Protocol, PaymentRejectsNegativeAmount) {
64
- EXPECT_THROW(Ecr17Protocol::buildPaymentMessage("1", "2", -1), std::invalid_argument);
65
- }
66
-
67
- TEST(Protocol, PaymentRejectsAmountOverflowingField) {
68
- // 9 digits does not fit the 8-byte amount field.
69
- EXPECT_THROW(Ecr17Protocol::buildPaymentMessage("1", "2", 100000000), std::invalid_argument);
70
- }
71
-
72
- TEST(Protocol, PaymentRejectsOversizedTerminalId) {
73
- EXPECT_THROW(Ecr17Protocol::buildPaymentMessage("123456789", "2", 650), std::invalid_argument);
74
- }
75
-
76
- TEST(Protocol, StatusRejectsOversizedTerminalId) {
77
- EXPECT_THROW(Ecr17Protocol::buildStatusMessage("123456789"), std::invalid_argument);
78
- }
79
-
80
- // Reversal request "S" layout (1-based spec positions):
81
- // 1 8 Terminal ID · 9 1 Reserved · 10 1 'S' · 11 8 Cash register ID
82
- // 19 6 STAN · 25 1 Presence of GT data · 26 1 Reserved => 26 bytes
83
- TEST(Protocol, ReversalMessageLayout) {
84
- std::string m = Ecr17Protocol::buildReversalMessage("12345678", "87654321", "000123");
85
- ASSERT_EQ(m.size(), 26u);
86
- EXPECT_EQ(m.substr(0, 8), "12345678");
87
- EXPECT_EQ(m[8], '0');
88
- EXPECT_EQ(m[9], 'S');
89
- EXPECT_EQ(m.substr(10, 8), "87654321");
90
- EXPECT_EQ(m.substr(18, 6), "000123");
91
- EXPECT_EQ(m[24], '0');
92
- EXPECT_EQ(m[25], '0');
93
- }
94
-
95
- TEST(Protocol, ReversalDefaultStanIsNoCheck) {
96
- std::string m = Ecr17Protocol::buildReversalMessage("12345678", "87654321");
97
- EXPECT_EQ(m.substr(18, 6), "000000");
98
- }
99
-
100
- TEST(Protocol, ReversalRejectsOversizedStan) {
101
- EXPECT_THROW(Ecr17Protocol::buildReversalMessage("1", "2", "1234567"), std::invalid_argument);
102
- }
1
+ #include <gtest/gtest.h>
2
+
3
+ #include <stdexcept>
4
+ #include <string>
5
+
6
+ #include "Ecr17Protocol/Ecr17Protocol.hpp"
7
+
8
+ using margelo::nitro::ecr17::Ecr17Protocol;
9
+
10
+ // Field layout reference (1-based positions from the Nexi ECR17 spec, Payment
11
+ // request "P"):
12
+ // 1 8 Terminal ID
13
+ // 9 1 Reserved '0'
14
+ // 10 1 Message code 'P'
15
+ // 11 8 Cash register ID
16
+ // 19 1 Presence of additional data for GT
17
+ // 20 2 Reserved "00"
18
+ // 22 1 Start transaction when card already present
19
+ // 23 1 Payment type
20
+ // 24 8 Transaction amount (right aligned, '0' filled)
21
+ // 32 128 Text to print (' ' filled)
22
+ // 160 8 Reserved '0' => total 167 bytes
23
+
24
+ TEST(Protocol, StatusMessageLayout) {
25
+ std::string m = Ecr17Protocol::buildStatusMessage("42");
26
+ ASSERT_EQ(m.size(), 10u);
27
+ EXPECT_EQ(m.substr(0, 8), "00000042"); // terminal id, left-padded
28
+ EXPECT_EQ(m[8], '0'); // reserved
29
+ EXPECT_EQ(m[9], 's'); // lowercase message code per spec
30
+ }
31
+
32
+ TEST(Protocol, StatusMessageKeepsFullWidthId) {
33
+ std::string m = Ecr17Protocol::buildStatusMessage("12345678");
34
+ EXPECT_EQ(m, "123456780s");
35
+ }
36
+
37
+ TEST(Protocol, PaymentMessageIs167Bytes) {
38
+ std::string m = Ecr17Protocol::buildPaymentMessage("1", "2", 650);
39
+ EXPECT_EQ(m.size(), 167u);
40
+ }
41
+
42
+ TEST(Protocol, PaymentMessageFieldLayout) {
43
+ std::string m = Ecr17Protocol::buildPaymentMessage("12345678", "87654321", 650);
44
+ ASSERT_EQ(m.size(), 167u);
45
+ EXPECT_EQ(m.substr(0, 8), "12345678"); // terminal id
46
+ EXPECT_EQ(m[8], '0'); // reserved
47
+ EXPECT_EQ(m[9], 'P'); // message code
48
+ EXPECT_EQ(m.substr(10, 8), "87654321"); // cash register id
49
+ EXPECT_EQ(m[18], '0'); // presence of additional data
50
+ EXPECT_EQ(m.substr(19, 2), "00"); // reserved
51
+ EXPECT_EQ(m[21], '0'); // start-with-card
52
+ EXPECT_EQ(m[22], '0'); // payment type
53
+ EXPECT_EQ(m.substr(23, 8), "00000650"); // amount, right aligned
54
+ EXPECT_EQ(m.substr(31, 128), std::string(128, ' ')); // text field
55
+ EXPECT_EQ(m.substr(159, 8), "00000000"); // trailing reserved
56
+ }
57
+
58
+ TEST(Protocol, PaymentMessageAmountMaxFits) {
59
+ std::string m = Ecr17Protocol::buildPaymentMessage("1", "2", 99999999);
60
+ EXPECT_EQ(m.substr(23, 8), "99999999");
61
+ }
62
+
63
+ TEST(Protocol, PaymentRejectsNegativeAmount) {
64
+ EXPECT_THROW(Ecr17Protocol::buildPaymentMessage("1", "2", -1), std::invalid_argument);
65
+ }
66
+
67
+ TEST(Protocol, PaymentRejectsAmountOverflowingField) {
68
+ // 9 digits does not fit the 8-byte amount field.
69
+ EXPECT_THROW(Ecr17Protocol::buildPaymentMessage("1", "2", 100000000), std::invalid_argument);
70
+ }
71
+
72
+ TEST(Protocol, PaymentRejectsOversizedTerminalId) {
73
+ EXPECT_THROW(Ecr17Protocol::buildPaymentMessage("123456789", "2", 650), std::invalid_argument);
74
+ }
75
+
76
+ TEST(Protocol, StatusRejectsOversizedTerminalId) {
77
+ EXPECT_THROW(Ecr17Protocol::buildStatusMessage("123456789"), std::invalid_argument);
78
+ }
79
+
80
+ // Reversal request "S" layout (1-based spec positions):
81
+ // 1 8 Terminal ID · 9 1 Reserved · 10 1 'S' · 11 8 Cash register ID
82
+ // 19 6 STAN · 25 1 Presence of GT data · 26 1 Reserved => 26 bytes
83
+ TEST(Protocol, ReversalMessageLayout) {
84
+ std::string m = Ecr17Protocol::buildReversalMessage("12345678", "87654321", "000123");
85
+ ASSERT_EQ(m.size(), 26u);
86
+ EXPECT_EQ(m.substr(0, 8), "12345678");
87
+ EXPECT_EQ(m[8], '0');
88
+ EXPECT_EQ(m[9], 'S');
89
+ EXPECT_EQ(m.substr(10, 8), "87654321");
90
+ EXPECT_EQ(m.substr(18, 6), "000123");
91
+ EXPECT_EQ(m[24], '0');
92
+ EXPECT_EQ(m[25], '0');
93
+ }
94
+
95
+ TEST(Protocol, ReversalDefaultStanIsNoCheck) {
96
+ std::string m = Ecr17Protocol::buildReversalMessage("12345678", "87654321");
97
+ EXPECT_EQ(m.substr(18, 6), "000000");
98
+ }
99
+
100
+ TEST(Protocol, ReversalRejectsOversizedStan) {
101
+ EXPECT_THROW(Ecr17Protocol::buildReversalMessage("1", "2", "1234567"), std::invalid_argument);
102
+ }
@@ -1,190 +1,190 @@
1
- // Byte-layout tests for the full ECR17 command builder set (Phase 1).
2
- // Positions are checked against the Nexi ECR17 spec tables in docs/.
3
-
4
- #include <gtest/gtest.h>
5
-
6
- #include <stdexcept>
7
- #include <string>
8
-
9
- #include "Ecr17Protocol/Ecr17Protocol.hpp"
10
-
11
- using margelo::nitro::ecr17::Ecr17Protocol;
12
-
13
- namespace {
14
- constexpr char kFieldSep = 0x1B;
15
- const std::string T = "12345678"; // terminal id
16
- const std::string C = "87654321"; // cash register id
17
- } // namespace
18
-
19
- // --- Payment family (167 bytes) --------------------------------------------
20
-
21
- TEST(Commands, ExtendedPaymentLayoutAndFlags) {
22
- std::string m = Ecr17Protocol::buildExtendedPaymentMessage(T, C, 650, '2', true, true, "ABC");
23
- ASSERT_EQ(m.size(), 167u);
24
- EXPECT_EQ(m.substr(0, 8), T);
25
- EXPECT_EQ(m[8], '0');
26
- EXPECT_EQ(m[9], 'X');
27
- EXPECT_EQ(m.substr(10, 8), C);
28
- EXPECT_EQ(m[18], '1'); // withAdditionalData
29
- EXPECT_EQ(m.substr(19, 2), "00");
30
- EXPECT_EQ(m[21], '1'); // cardAlreadyPresent
31
- EXPECT_EQ(m[22], '2'); // payment type
32
- EXPECT_EQ(m.substr(23, 8), "00000650"); // amount
33
- EXPECT_EQ(m.substr(31, 125), std::string(125, ' ')); // text left-padding
34
- EXPECT_EQ(m.substr(156, 3), "ABC"); // text right-aligned
35
- EXPECT_EQ(m.substr(159, 8), "00000000");
36
- }
37
-
38
- TEST(Commands, PreAuthUsesCodeLowerP) {
39
- std::string m = Ecr17Protocol::buildPreAuthMessage(T, C, 1000);
40
- ASSERT_EQ(m.size(), 167u);
41
- EXPECT_EQ(m[9], 'p');
42
- EXPECT_EQ(m.substr(23, 8), "00001000");
43
- }
44
-
45
- TEST(Commands, PaymentDefaultsMatchBasicLayout) {
46
- std::string m = Ecr17Protocol::buildPaymentMessage(T, C, 650);
47
- ASSERT_EQ(m.size(), 167u);
48
- EXPECT_EQ(m[9], 'P');
49
- EXPECT_EQ(m[18], '0'); // no additional data by default
50
- EXPECT_EQ(m[21], '0'); // card not present by default
51
- EXPECT_EQ(m[22], '0'); // auto payment type by default
52
- EXPECT_EQ(m.substr(31, 128), std::string(128, ' '));
53
- }
54
-
55
- // --- Pre-auth integration / closure (176 bytes) ----------------------------
56
-
57
- TEST(Commands, IncrementalLayout) {
58
- std::string m = Ecr17Protocol::buildIncrementalMessage(T, C, 1000, "123456789");
59
- ASSERT_EQ(m.size(), 176u);
60
- EXPECT_EQ(m[9], 'i');
61
- EXPECT_EQ(m.substr(19, 4), "0000");
62
- EXPECT_EQ(m.substr(23, 8), "00001000");
63
- EXPECT_EQ(m.substr(159, 9), "123456789"); // original pre-auth code
64
- EXPECT_EQ(m.substr(168, 8), "00000000");
65
- }
66
-
67
- TEST(Commands, PreAuthClosureLayout) {
68
- std::string m = Ecr17Protocol::buildPreAuthClosureMessage(T, C, 500, "000000042");
69
- ASSERT_EQ(m.size(), 176u);
70
- EXPECT_EQ(m[9], 'c');
71
- EXPECT_EQ(m.substr(159, 9), "000000042");
72
- }
73
-
74
- // --- Card verification (39 bytes) ------------------------------------------
75
-
76
- TEST(Commands, CardVerificationLayout) {
77
- std::string m = Ecr17Protocol::buildCardVerificationMessage(T, C, '1');
78
- ASSERT_EQ(m.size(), 39u);
79
- EXPECT_EQ(m[9], 'H');
80
- EXPECT_EQ(m.substr(10, 8), C);
81
- EXPECT_EQ(m[18], '0'); // no additional data
82
- EXPECT_EQ(m.substr(19, 2), "00");
83
- EXPECT_EQ(m[21], '0'); // standard verification
84
- EXPECT_EQ(m[22], '1'); // payment type
85
- EXPECT_EQ(m.substr(23, 16), std::string(16, '0'));
86
- }
87
-
88
- // --- Session commands (26 bytes) -------------------------------------------
89
-
90
- TEST(Commands, CloseSessionLayout) {
91
- std::string m = Ecr17Protocol::buildCloseSessionMessage(T, C);
92
- ASSERT_EQ(m.size(), 26u);
93
- EXPECT_EQ(m[9], 'C');
94
- EXPECT_EQ(m.substr(10, 8), C);
95
- EXPECT_EQ(m[18], '0');
96
- EXPECT_EQ(m.substr(19, 7), std::string(7, '0'));
97
- }
98
-
99
- TEST(Commands, TotalsLayout) {
100
- std::string m = Ecr17Protocol::buildTotalsMessage(T, C);
101
- ASSERT_EQ(m.size(), 26u);
102
- EXPECT_EQ(m[9], 'T');
103
- }
104
-
105
- // --- Send last result (22 bytes) -------------------------------------------
106
-
107
- TEST(Commands, SendLastResultLayout) {
108
- std::string m = Ecr17Protocol::buildSendLastResultMessage(T, C);
109
- ASSERT_EQ(m.size(), 22u);
110
- EXPECT_EQ(m[9], 'G');
111
- EXPECT_EQ(m.substr(19, 3), "000");
112
- }
113
-
114
- // --- Enable/disable ECR printing (11 bytes) --------------------------------
115
-
116
- TEST(Commands, EnableEcrPrintLayout) {
117
- EXPECT_EQ(Ecr17Protocol::buildEnableEcrPrintMessage(T, true), "123456780E1");
118
- EXPECT_EQ(Ecr17Protocol::buildEnableEcrPrintMessage(T, false), "123456780E0");
119
- }
120
-
121
- // --- Reprint (22 bytes) -----------------------------------------------------
122
-
123
- TEST(Commands, ReprintLayout) {
124
- std::string m = Ecr17Protocol::buildReprintMessage(T, true);
125
- ASSERT_EQ(m.size(), 22u);
126
- EXPECT_EQ(m[9], 'R');
127
- EXPECT_EQ(m[10], '1'); // send to ECR
128
- EXPECT_EQ(m[11], '0'); // ticket type default
129
- EXPECT_EQ(m.substr(12, 10), std::string(10, '0'));
130
- }
131
-
132
- // --- VAS (variable, length-prefixed) ---------------------------------------
133
-
134
- TEST(Commands, VasLayoutAndLengthPrefix) {
135
- std::string m = Ecr17Protocol::buildVasMessage(T, C, "<x/>");
136
- ASSERT_EQ(m.size(), 30u);
137
- EXPECT_EQ(m[9], 'K');
138
- EXPECT_EQ(m.substr(10, 8), C);
139
- EXPECT_EQ(m.substr(18, 3), "000");
140
- EXPECT_EQ(m[21], '0');
141
- EXPECT_EQ(m.substr(22, 4), "0004"); // length of "<x/>"
142
- EXPECT_EQ(m.substr(26), "<x/>");
143
- }
144
-
145
- TEST(Commands, VasRejectsOversizedRequest) {
146
- EXPECT_THROW(Ecr17Protocol::buildVasMessage(T, C, std::string(1025, 'x')), std::invalid_argument);
147
- }
148
-
149
- // --- Additional data / tokenization 'U' ------------------------------------
150
-
151
- TEST(Commands, AdditionalTagsLayout) {
152
- const std::string content = "0COF0TRK123|0FNZ03"; // 18 chars
153
- std::string m = Ecr17Protocol::buildAdditionalTagsMessage(T, content);
154
- ASSERT_EQ(m.size(), 36u + content.size() + 1u);
155
- EXPECT_EQ(m[9], 'U');
156
- EXPECT_EQ(m.substr(10, 6), "000000");
157
- EXPECT_EQ(m.substr(16, 2), "62");
158
- EXPECT_EQ(m.substr(18, 8), "DF8D01 "); // left-justified, blank-filled
159
- EXPECT_EQ(m[26], '0');
160
- EXPECT_EQ(m.substr(27, 4), "0000");
161
- EXPECT_EQ(m.substr(31, 5), "00000");
162
- EXPECT_EQ(m.substr(36, content.size()), content);
163
- EXPECT_EQ(m.back(), kFieldSep);
164
- }
165
-
166
- TEST(Commands, AdditionalTagsRejectsBadContent) {
167
- EXPECT_THROW(Ecr17Protocol::buildAdditionalTagsMessage(T, ""), std::invalid_argument);
168
- EXPECT_THROW(Ecr17Protocol::buildAdditionalTagsMessage(T, std::string(101, 'x')),
169
- std::invalid_argument);
170
- }
171
-
172
- TEST(Commands, TokenizationTagFormat) {
173
- EXPECT_EQ(Ecr17Protocol::formatTokenizationTag(false, "1666354841608"),
174
- "0COF0TRK1666354841608|0FNZ03");
175
- EXPECT_EQ(Ecr17Protocol::formatTokenizationTag(true, "ABC"), "0REC0TRKABC|0FNZ03");
176
- EXPECT_THROW(Ecr17Protocol::formatTokenizationTag(false, ""), std::invalid_argument);
177
- EXPECT_THROW(Ecr17Protocol::formatTokenizationTag(false, std::string(19, 'x')),
178
- std::invalid_argument);
179
- }
180
-
181
- // --- Validation shared via leftPad -----------------------------------------
182
-
183
- TEST(Commands, IncrementalRejectsOversizedPreAuthCode) {
184
- EXPECT_THROW(Ecr17Protocol::buildIncrementalMessage(T, C, 100, "1234567890"),
185
- std::invalid_argument); // 10 digits > 9-byte field
186
- }
187
-
188
- TEST(Commands, PreAuthRejectsNegativeAmount) {
189
- EXPECT_THROW(Ecr17Protocol::buildPreAuthMessage(T, C, -1), std::invalid_argument);
190
- }
1
+ // Byte-layout tests for the full ECR17 command builder set (Phase 1).
2
+ // Positions are checked against the Nexi ECR17 spec tables in docs/.
3
+
4
+ #include <gtest/gtest.h>
5
+
6
+ #include <stdexcept>
7
+ #include <string>
8
+
9
+ #include "Ecr17Protocol/Ecr17Protocol.hpp"
10
+
11
+ using margelo::nitro::ecr17::Ecr17Protocol;
12
+
13
+ namespace {
14
+ constexpr char kFieldSep = 0x1B;
15
+ const std::string T = "12345678"; // terminal id
16
+ const std::string C = "87654321"; // cash register id
17
+ } // namespace
18
+
19
+ // --- Payment family (167 bytes) --------------------------------------------
20
+
21
+ TEST(Commands, ExtendedPaymentLayoutAndFlags) {
22
+ std::string m = Ecr17Protocol::buildExtendedPaymentMessage(T, C, 650, '2', true, true, "ABC");
23
+ ASSERT_EQ(m.size(), 167u);
24
+ EXPECT_EQ(m.substr(0, 8), T);
25
+ EXPECT_EQ(m[8], '0');
26
+ EXPECT_EQ(m[9], 'X');
27
+ EXPECT_EQ(m.substr(10, 8), C);
28
+ EXPECT_EQ(m[18], '1'); // withAdditionalData
29
+ EXPECT_EQ(m.substr(19, 2), "00");
30
+ EXPECT_EQ(m[21], '1'); // cardAlreadyPresent
31
+ EXPECT_EQ(m[22], '2'); // payment type
32
+ EXPECT_EQ(m.substr(23, 8), "00000650"); // amount
33
+ EXPECT_EQ(m.substr(31, 125), std::string(125, ' ')); // text left-padding
34
+ EXPECT_EQ(m.substr(156, 3), "ABC"); // text right-aligned
35
+ EXPECT_EQ(m.substr(159, 8), "00000000");
36
+ }
37
+
38
+ TEST(Commands, PreAuthUsesCodeLowerP) {
39
+ std::string m = Ecr17Protocol::buildPreAuthMessage(T, C, 1000);
40
+ ASSERT_EQ(m.size(), 167u);
41
+ EXPECT_EQ(m[9], 'p');
42
+ EXPECT_EQ(m.substr(23, 8), "00001000");
43
+ }
44
+
45
+ TEST(Commands, PaymentDefaultsMatchBasicLayout) {
46
+ std::string m = Ecr17Protocol::buildPaymentMessage(T, C, 650);
47
+ ASSERT_EQ(m.size(), 167u);
48
+ EXPECT_EQ(m[9], 'P');
49
+ EXPECT_EQ(m[18], '0'); // no additional data by default
50
+ EXPECT_EQ(m[21], '0'); // card not present by default
51
+ EXPECT_EQ(m[22], '0'); // auto payment type by default
52
+ EXPECT_EQ(m.substr(31, 128), std::string(128, ' '));
53
+ }
54
+
55
+ // --- Pre-auth integration / closure (176 bytes) ----------------------------
56
+
57
+ TEST(Commands, IncrementalLayout) {
58
+ std::string m = Ecr17Protocol::buildIncrementalMessage(T, C, 1000, "123456789");
59
+ ASSERT_EQ(m.size(), 176u);
60
+ EXPECT_EQ(m[9], 'i');
61
+ EXPECT_EQ(m.substr(19, 4), "0000");
62
+ EXPECT_EQ(m.substr(23, 8), "00001000");
63
+ EXPECT_EQ(m.substr(159, 9), "123456789"); // original pre-auth code
64
+ EXPECT_EQ(m.substr(168, 8), "00000000");
65
+ }
66
+
67
+ TEST(Commands, PreAuthClosureLayout) {
68
+ std::string m = Ecr17Protocol::buildPreAuthClosureMessage(T, C, 500, "000000042");
69
+ ASSERT_EQ(m.size(), 176u);
70
+ EXPECT_EQ(m[9], 'c');
71
+ EXPECT_EQ(m.substr(159, 9), "000000042");
72
+ }
73
+
74
+ // --- Card verification (39 bytes) ------------------------------------------
75
+
76
+ TEST(Commands, CardVerificationLayout) {
77
+ std::string m = Ecr17Protocol::buildCardVerificationMessage(T, C, '1');
78
+ ASSERT_EQ(m.size(), 39u);
79
+ EXPECT_EQ(m[9], 'H');
80
+ EXPECT_EQ(m.substr(10, 8), C);
81
+ EXPECT_EQ(m[18], '0'); // no additional data
82
+ EXPECT_EQ(m.substr(19, 2), "00");
83
+ EXPECT_EQ(m[21], '0'); // standard verification
84
+ EXPECT_EQ(m[22], '1'); // payment type
85
+ EXPECT_EQ(m.substr(23, 16), std::string(16, '0'));
86
+ }
87
+
88
+ // --- Session commands (26 bytes) -------------------------------------------
89
+
90
+ TEST(Commands, CloseSessionLayout) {
91
+ std::string m = Ecr17Protocol::buildCloseSessionMessage(T, C);
92
+ ASSERT_EQ(m.size(), 26u);
93
+ EXPECT_EQ(m[9], 'C');
94
+ EXPECT_EQ(m.substr(10, 8), C);
95
+ EXPECT_EQ(m[18], '0');
96
+ EXPECT_EQ(m.substr(19, 7), std::string(7, '0'));
97
+ }
98
+
99
+ TEST(Commands, TotalsLayout) {
100
+ std::string m = Ecr17Protocol::buildTotalsMessage(T, C);
101
+ ASSERT_EQ(m.size(), 26u);
102
+ EXPECT_EQ(m[9], 'T');
103
+ }
104
+
105
+ // --- Send last result (22 bytes) -------------------------------------------
106
+
107
+ TEST(Commands, SendLastResultLayout) {
108
+ std::string m = Ecr17Protocol::buildSendLastResultMessage(T, C);
109
+ ASSERT_EQ(m.size(), 22u);
110
+ EXPECT_EQ(m[9], 'G');
111
+ EXPECT_EQ(m.substr(19, 3), "000");
112
+ }
113
+
114
+ // --- Enable/disable ECR printing (11 bytes) --------------------------------
115
+
116
+ TEST(Commands, EnableEcrPrintLayout) {
117
+ EXPECT_EQ(Ecr17Protocol::buildEnableEcrPrintMessage(T, true), "123456780E1");
118
+ EXPECT_EQ(Ecr17Protocol::buildEnableEcrPrintMessage(T, false), "123456780E0");
119
+ }
120
+
121
+ // --- Reprint (22 bytes) -----------------------------------------------------
122
+
123
+ TEST(Commands, ReprintLayout) {
124
+ std::string m = Ecr17Protocol::buildReprintMessage(T, true);
125
+ ASSERT_EQ(m.size(), 22u);
126
+ EXPECT_EQ(m[9], 'R');
127
+ EXPECT_EQ(m[10], '1'); // send to ECR
128
+ EXPECT_EQ(m[11], '0'); // ticket type default
129
+ EXPECT_EQ(m.substr(12, 10), std::string(10, '0'));
130
+ }
131
+
132
+ // --- VAS (variable, length-prefixed) ---------------------------------------
133
+
134
+ TEST(Commands, VasLayoutAndLengthPrefix) {
135
+ std::string m = Ecr17Protocol::buildVasMessage(T, C, "<x/>");
136
+ ASSERT_EQ(m.size(), 30u);
137
+ EXPECT_EQ(m[9], 'K');
138
+ EXPECT_EQ(m.substr(10, 8), C);
139
+ EXPECT_EQ(m.substr(18, 3), "000");
140
+ EXPECT_EQ(m[21], '0');
141
+ EXPECT_EQ(m.substr(22, 4), "0004"); // length of "<x/>"
142
+ EXPECT_EQ(m.substr(26), "<x/>");
143
+ }
144
+
145
+ TEST(Commands, VasRejectsOversizedRequest) {
146
+ EXPECT_THROW(Ecr17Protocol::buildVasMessage(T, C, std::string(1025, 'x')), std::invalid_argument);
147
+ }
148
+
149
+ // --- Additional data / tokenization 'U' ------------------------------------
150
+
151
+ TEST(Commands, AdditionalTagsLayout) {
152
+ const std::string content = "0COF0TRK123|0FNZ03"; // 18 chars
153
+ std::string m = Ecr17Protocol::buildAdditionalTagsMessage(T, content);
154
+ ASSERT_EQ(m.size(), 36u + content.size() + 1u);
155
+ EXPECT_EQ(m[9], 'U');
156
+ EXPECT_EQ(m.substr(10, 6), "000000");
157
+ EXPECT_EQ(m.substr(16, 2), "62");
158
+ EXPECT_EQ(m.substr(18, 8), "DF8D01 "); // left-justified, blank-filled
159
+ EXPECT_EQ(m[26], '0');
160
+ EXPECT_EQ(m.substr(27, 4), "0000");
161
+ EXPECT_EQ(m.substr(31, 5), "00000");
162
+ EXPECT_EQ(m.substr(36, content.size()), content);
163
+ EXPECT_EQ(m.back(), kFieldSep);
164
+ }
165
+
166
+ TEST(Commands, AdditionalTagsRejectsBadContent) {
167
+ EXPECT_THROW(Ecr17Protocol::buildAdditionalTagsMessage(T, ""), std::invalid_argument);
168
+ EXPECT_THROW(Ecr17Protocol::buildAdditionalTagsMessage(T, std::string(101, 'x')),
169
+ std::invalid_argument);
170
+ }
171
+
172
+ TEST(Commands, TokenizationTagFormat) {
173
+ EXPECT_EQ(Ecr17Protocol::formatTokenizationTag(false, "1666354841608"),
174
+ "0COF0TRK1666354841608|0FNZ03");
175
+ EXPECT_EQ(Ecr17Protocol::formatTokenizationTag(true, "ABC"), "0REC0TRKABC|0FNZ03");
176
+ EXPECT_THROW(Ecr17Protocol::formatTokenizationTag(false, ""), std::invalid_argument);
177
+ EXPECT_THROW(Ecr17Protocol::formatTokenizationTag(false, std::string(19, 'x')),
178
+ std::invalid_argument);
179
+ }
180
+
181
+ // --- Validation shared via leftPad -----------------------------------------
182
+
183
+ TEST(Commands, IncrementalRejectsOversizedPreAuthCode) {
184
+ EXPECT_THROW(Ecr17Protocol::buildIncrementalMessage(T, C, 100, "1234567890"),
185
+ std::invalid_argument); // 10 digits > 9-byte field
186
+ }
187
+
188
+ TEST(Commands, PreAuthRejectsNegativeAmount) {
189
+ EXPECT_THROW(Ecr17Protocol::buildPreAuthMessage(T, C, -1), std::invalid_argument);
190
+ }