@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,277 +1,277 @@
1
- #include "Ecr17Protocol.hpp"
2
-
3
- #include <stdexcept>
4
-
5
- namespace margelo::nitro::ecr17 {
6
-
7
- namespace {
8
-
9
- constexpr char kReserved = '0'; // '0' (0x30) filler for reserved numeric fields
10
- constexpr char kFieldSep = 0x1B; // end-of-field for the privative TAG content
11
-
12
- // Right-aligns `value` into a fixed-width field of `size` bytes, padding on the
13
- // left with `ch`. ECR17 fields have a fixed length, so a value longer than the
14
- // field would shift every following field and corrupt the frame: reject it.
15
- std::string leftPad(const std::string& value, size_t size, char ch = kReserved) {
16
- if (value.size() > size) {
17
- throw std::invalid_argument("ECR17: value '" + value + "' exceeds fixed field width of " +
18
- std::to_string(size) + " bytes");
19
- }
20
- if (value.size() == size) {
21
- return value;
22
- }
23
- return std::string(size - value.size(), ch) + value;
24
- }
25
-
26
- // Left-aligns `value` into a fixed-width field, padding on the right with `ch`.
27
- std::string rightPad(const std::string& value, size_t size, char ch = ' ') {
28
- if (value.size() > size) {
29
- throw std::invalid_argument("ECR17: value '" + value + "' exceeds fixed field width of " +
30
- std::to_string(size) + " bytes");
31
- }
32
- return value + std::string(size - value.size(), ch);
33
- }
34
-
35
- std::string amountField(int amountCents) {
36
- if (amountCents < 0) {
37
- throw std::invalid_argument("ECR17: amount must be non-negative");
38
- }
39
- return leftPad(std::to_string(amountCents), 8);
40
- }
41
-
42
- char flag(bool on) { return on ? '1' : '0'; }
43
-
44
- // Shared 167-byte payment-family layout (codes 'P', 'X', 'p').
45
- std::string buildPaymentLike(char code, const std::string& terminalId,
46
- const std::string& cashRegisterId, int amountCents, char paymentType,
47
- bool cardAlreadyPresent, bool withAdditionalData,
48
- const std::string& receiptText) {
49
- std::string m;
50
- m += leftPad(terminalId, 8); // 1 : terminal id
51
- m += kReserved; // 9 : reserved
52
- m += code; // 10 : message code
53
- m += leftPad(cashRegisterId, 8); // 11 : cash register id
54
- m += flag(withAdditionalData); // 19 : presence of additional GT data
55
- m += "00"; // 20 : reserved
56
- m += flag(cardAlreadyPresent); // 22 : start-with-card-present
57
- m += paymentType; // 23 : payment type
58
- m += amountField(amountCents); // 24 : amount (8)
59
- m += leftPad(receiptText, 128, ' '); // 32 : receipt text (128)
60
- m += "00000000"; // 160: reserved (8)
61
- return m; // 167
62
- }
63
-
64
- // Shared 176-byte pre-auth integration/closure layout (codes 'i', 'c').
65
- std::string buildPreAuthFollowUp(char code, const std::string& terminalId,
66
- const std::string& cashRegisterId, int amountCents,
67
- const std::string& originalPreAuthCode, bool withAdditionalData,
68
- const std::string& receiptText) {
69
- std::string m;
70
- m += leftPad(terminalId, 8); // 1 : terminal id
71
- m += kReserved; // 9 : reserved
72
- m += code; // 10 : message code
73
- m += leftPad(cashRegisterId, 8); // 11 : cash register id
74
- m += flag(withAdditionalData); // 19 : presence of additional GT data
75
- m += "0000"; // 20 : reserved (4)
76
- m += amountField(amountCents); // 24 : amount (8)
77
- m += leftPad(receiptText, 128, ' '); // 32 : receipt text (128)
78
- m += leftPad(originalPreAuthCode, 9); // 160: original pre-auth code (9)
79
- m += "00000000"; // 169: reserved (8)
80
- return m; // 176
81
- }
82
-
83
- // Shared 26-byte session command layout (codes 'C', 'T').
84
- std::string buildSessionCommand(char code, const std::string& terminalId,
85
- const std::string& cashRegisterId, bool withAdditionalData) {
86
- std::string m;
87
- m += leftPad(terminalId, 8); // 1 : terminal id
88
- m += kReserved; // 9 : reserved
89
- m += code; // 10 : message code
90
- m += leftPad(cashRegisterId, 8); // 11 : cash register id
91
- m += flag(withAdditionalData); // 19 : presence of additional GT data
92
- m += "0000000"; // 20 : reserved (7)
93
- return m; // 26
94
- }
95
-
96
- } // namespace
97
-
98
- std::string Ecr17Protocol::buildPaymentMessage(const std::string& terminalId,
99
- const std::string& cashRegisterId, int amountCents,
100
- char paymentType, bool cardAlreadyPresent,
101
- bool withAdditionalData,
102
- const std::string& receiptText) {
103
- return buildPaymentLike('P', terminalId, cashRegisterId, amountCents, paymentType,
104
- cardAlreadyPresent, withAdditionalData, receiptText);
105
- }
106
-
107
- std::string Ecr17Protocol::buildExtendedPaymentMessage(const std::string& terminalId,
108
- const std::string& cashRegisterId,
109
- int amountCents, char paymentType,
110
- bool cardAlreadyPresent,
111
- bool withAdditionalData,
112
- const std::string& receiptText) {
113
- return buildPaymentLike('X', terminalId, cashRegisterId, amountCents, paymentType,
114
- cardAlreadyPresent, withAdditionalData, receiptText);
115
- }
116
-
117
- std::string Ecr17Protocol::buildPreAuthMessage(const std::string& terminalId,
118
- const std::string& cashRegisterId, int amountCents,
119
- char paymentType, bool cardAlreadyPresent,
120
- bool withAdditionalData,
121
- const std::string& receiptText) {
122
- return buildPaymentLike('p', terminalId, cashRegisterId, amountCents, paymentType,
123
- cardAlreadyPresent, withAdditionalData, receiptText);
124
- }
125
-
126
- std::string Ecr17Protocol::buildIncrementalMessage(const std::string& terminalId,
127
- const std::string& cashRegisterId,
128
- int amountCents,
129
- const std::string& originalPreAuthCode,
130
- bool withAdditionalData,
131
- const std::string& receiptText) {
132
- return buildPreAuthFollowUp('i', terminalId, cashRegisterId, amountCents, originalPreAuthCode,
133
- withAdditionalData, receiptText);
134
- }
135
-
136
- std::string Ecr17Protocol::buildPreAuthClosureMessage(const std::string& terminalId,
137
- const std::string& cashRegisterId,
138
- int amountCents,
139
- const std::string& originalPreAuthCode,
140
- bool withAdditionalData,
141
- const std::string& receiptText) {
142
- return buildPreAuthFollowUp('c', terminalId, cashRegisterId, amountCents, originalPreAuthCode,
143
- withAdditionalData, receiptText);
144
- }
145
-
146
- std::string Ecr17Protocol::buildCardVerificationMessage(const std::string& terminalId,
147
- const std::string& cashRegisterId,
148
- char paymentType, bool withAdditionalData) {
149
- std::string m;
150
- m += leftPad(terminalId, 8); // 1 : terminal id
151
- m += kReserved; // 9 : reserved
152
- m += 'H'; // 10 : message code
153
- m += leftPad(cashRegisterId, 8); // 11 : cash register id
154
- m += flag(withAdditionalData); // 19 : presence of additional GT data
155
- m += "00"; // 20 : reserved (2)
156
- m += '0'; // 22 : standard card verification
157
- m += paymentType; // 23 : payment type
158
- m += "0000000000000000"; // 24 : reserved (16)
159
- return m; // 39
160
- }
161
-
162
- std::string Ecr17Protocol::buildCloseSessionMessage(const std::string& terminalId,
163
- const std::string& cashRegisterId,
164
- bool withAdditionalData) {
165
- return buildSessionCommand('C', terminalId, cashRegisterId, withAdditionalData);
166
- }
167
-
168
- std::string Ecr17Protocol::buildTotalsMessage(const std::string& terminalId,
169
- const std::string& cashRegisterId,
170
- bool withAdditionalData) {
171
- return buildSessionCommand('T', terminalId, cashRegisterId, withAdditionalData);
172
- }
173
-
174
- std::string Ecr17Protocol::buildSendLastResultMessage(const std::string& terminalId,
175
- const std::string& cashRegisterId,
176
- bool withAdditionalData) {
177
- std::string m;
178
- m += leftPad(terminalId, 8); // 1 : terminal id
179
- m += kReserved; // 9 : reserved
180
- m += 'G'; // 10 : message code
181
- m += leftPad(cashRegisterId, 8); // 11 : cash register id
182
- m += flag(withAdditionalData); // 19 : presence of additional GT data
183
- m += "000"; // 20 : reserved (3)
184
- return m; // 22
185
- }
186
-
187
- std::string Ecr17Protocol::buildEnableEcrPrintMessage(const std::string& terminalId, bool enabled) {
188
- std::string m;
189
- m += leftPad(terminalId, 8); // 1 : terminal id
190
- m += kReserved; // 9 : reserved
191
- m += 'E'; // 10 : message code
192
- m += flag(enabled); // 11 : enable(1)/disable(0) printing on ECR
193
- return m; // 11
194
- }
195
-
196
- std::string Ecr17Protocol::buildReprintMessage(const std::string& terminalId, bool toEcr,
197
- char ticketType) {
198
- std::string m;
199
- m += leftPad(terminalId, 8); // 1 : terminal id
200
- m += kReserved; // 9 : reserved
201
- m += 'R'; // 10 : message code
202
- m += flag(toEcr); // 11 : 1 = send receipt to ECR, 0 = print on terminal
203
- m += ticketType; // 12 : ticket type flag
204
- m += "0000000000"; // 13 : reserved (10)
205
- return m; // 22
206
- }
207
-
208
- std::string Ecr17Protocol::buildStatusMessage(const std::string& terminalId) {
209
- std::string m;
210
- m += leftPad(terminalId, 8); // 1 : terminal id
211
- m += kReserved; // 9 : reserved
212
- m += 's'; // 10 : message code (lowercase per spec)
213
- return m; // 10
214
- }
215
-
216
- std::string Ecr17Protocol::buildReversalMessage(const std::string& terminalId,
217
- const std::string& cashRegisterId,
218
- const std::string& stan) {
219
- std::string m;
220
- m += leftPad(terminalId, 8); // 1 : terminal id
221
- m += kReserved; // 9 : reserved
222
- m += 'S'; // 10 : message code
223
- m += leftPad(cashRegisterId, 8); // 11 : cash register id
224
- m += leftPad(stan, 6); // 19 : STAN ("000000" = no check)
225
- m += kReserved; // 25 : presence of additional GT data
226
- m += kReserved; // 26 : reserved
227
- return m; // 26
228
- }
229
-
230
- std::string Ecr17Protocol::buildVasMessage(const std::string& terminalId, const std::string& ecrId,
231
- const std::string& xmlRequest) {
232
- if (xmlRequest.size() > 1024) {
233
- throw std::invalid_argument("ECR17: VAS request exceeds 1024 bytes");
234
- }
235
- std::string m;
236
- m += leftPad(terminalId, 8); // 1 : terminal id
237
- m += kReserved; // 9 : reserved
238
- m += 'K'; // 10 : message code
239
- m += leftPad(ecrId, 8); // 11 : ECR identifier
240
- m += "000"; // 19 : reserved (3)
241
- m += kReserved; // 22 : reserved (1)
242
- m += leftPad(std::to_string(xmlRequest.size()), 4);// 23 : VAS request length (4)
243
- m += xmlRequest; // 27 : VAS request (XML)
244
- return m;
245
- }
246
-
247
- std::string Ecr17Protocol::buildAdditionalTagsMessage(const std::string& terminalId,
248
- const std::string& tagContent,
249
- const std::string& isoField,
250
- const std::string& tagNumber) {
251
- if (tagContent.empty() || tagContent.size() > 100) {
252
- throw std::invalid_argument("ECR17: additional TAG content must be 1..100 chars");
253
- }
254
- std::string m;
255
- m += leftPad(terminalId, 8); // 1 : terminal id
256
- m += kReserved; // 9 : reserved
257
- m += 'U'; // 10 : message code
258
- m += "000000"; // 11 : payment type (6) -> standard payment
259
- m += leftPad(isoField, 2); // 17 : ISO field number (e.g. "62")
260
- m += rightPad(tagNumber, 8); // 19 : TAG number, left-justified, blank-filled
261
- m += kReserved; // 27 : reserved (1)
262
- m += "0000"; // 28 : exclusive TAG index bytemap (none to send to GT)
263
- m += "00000"; // 32 : reserved (5)
264
- m += tagContent; // 37 : privative TAG content (1..100)
265
- m += kFieldSep; // end-of-field (0x1B)
266
- return m;
267
- }
268
-
269
- std::string Ecr17Protocol::formatTokenizationTag(bool recurring, const std::string& contractCode) {
270
- if (contractCode.empty() || contractCode.size() > 18) {
271
- throw std::invalid_argument("ECR17: tokenization contract code must be 1..18 chars");
272
- }
273
- const std::string service = recurring ? "0REC" : "0COF";
274
- return service + "0TRK" + contractCode + "|0FNZ03";
275
- }
276
-
277
- } // namespace margelo::nitro::ecr17
1
+ #include "Ecr17Protocol.hpp"
2
+
3
+ #include <stdexcept>
4
+
5
+ namespace margelo::nitro::ecr17 {
6
+
7
+ namespace {
8
+
9
+ constexpr char kReserved = '0'; // '0' (0x30) filler for reserved numeric fields
10
+ constexpr char kFieldSep = 0x1B; // end-of-field for the privative TAG content
11
+
12
+ // Right-aligns `value` into a fixed-width field of `size` bytes, padding on the
13
+ // left with `ch`. ECR17 fields have a fixed length, so a value longer than the
14
+ // field would shift every following field and corrupt the frame: reject it.
15
+ std::string leftPad(const std::string& value, size_t size, char ch = kReserved) {
16
+ if (value.size() > size) {
17
+ throw std::invalid_argument("ECR17: value '" + value + "' exceeds fixed field width of " +
18
+ std::to_string(size) + " bytes");
19
+ }
20
+ if (value.size() == size) {
21
+ return value;
22
+ }
23
+ return std::string(size - value.size(), ch) + value;
24
+ }
25
+
26
+ // Left-aligns `value` into a fixed-width field, padding on the right with `ch`.
27
+ std::string rightPad(const std::string& value, size_t size, char ch = ' ') {
28
+ if (value.size() > size) {
29
+ throw std::invalid_argument("ECR17: value '" + value + "' exceeds fixed field width of " +
30
+ std::to_string(size) + " bytes");
31
+ }
32
+ return value + std::string(size - value.size(), ch);
33
+ }
34
+
35
+ std::string amountField(int amountCents) {
36
+ if (amountCents < 0) {
37
+ throw std::invalid_argument("ECR17: amount must be non-negative");
38
+ }
39
+ return leftPad(std::to_string(amountCents), 8);
40
+ }
41
+
42
+ char flag(bool on) { return on ? '1' : '0'; }
43
+
44
+ // Shared 167-byte payment-family layout (codes 'P', 'X', 'p').
45
+ std::string buildPaymentLike(char code, const std::string& terminalId,
46
+ const std::string& cashRegisterId, int amountCents, char paymentType,
47
+ bool cardAlreadyPresent, bool withAdditionalData,
48
+ const std::string& receiptText) {
49
+ std::string m;
50
+ m += leftPad(terminalId, 8); // 1 : terminal id
51
+ m += kReserved; // 9 : reserved
52
+ m += code; // 10 : message code
53
+ m += leftPad(cashRegisterId, 8); // 11 : cash register id
54
+ m += flag(withAdditionalData); // 19 : presence of additional GT data
55
+ m += "00"; // 20 : reserved
56
+ m += flag(cardAlreadyPresent); // 22 : start-with-card-present
57
+ m += paymentType; // 23 : payment type
58
+ m += amountField(amountCents); // 24 : amount (8)
59
+ m += leftPad(receiptText, 128, ' '); // 32 : receipt text (128)
60
+ m += "00000000"; // 160: reserved (8)
61
+ return m; // 167
62
+ }
63
+
64
+ // Shared 176-byte pre-auth integration/closure layout (codes 'i', 'c').
65
+ std::string buildPreAuthFollowUp(char code, const std::string& terminalId,
66
+ const std::string& cashRegisterId, int amountCents,
67
+ const std::string& originalPreAuthCode, bool withAdditionalData,
68
+ const std::string& receiptText) {
69
+ std::string m;
70
+ m += leftPad(terminalId, 8); // 1 : terminal id
71
+ m += kReserved; // 9 : reserved
72
+ m += code; // 10 : message code
73
+ m += leftPad(cashRegisterId, 8); // 11 : cash register id
74
+ m += flag(withAdditionalData); // 19 : presence of additional GT data
75
+ m += "0000"; // 20 : reserved (4)
76
+ m += amountField(amountCents); // 24 : amount (8)
77
+ m += leftPad(receiptText, 128, ' '); // 32 : receipt text (128)
78
+ m += leftPad(originalPreAuthCode, 9); // 160: original pre-auth code (9)
79
+ m += "00000000"; // 169: reserved (8)
80
+ return m; // 176
81
+ }
82
+
83
+ // Shared 26-byte session command layout (codes 'C', 'T').
84
+ std::string buildSessionCommand(char code, const std::string& terminalId,
85
+ const std::string& cashRegisterId, bool withAdditionalData) {
86
+ std::string m;
87
+ m += leftPad(terminalId, 8); // 1 : terminal id
88
+ m += kReserved; // 9 : reserved
89
+ m += code; // 10 : message code
90
+ m += leftPad(cashRegisterId, 8); // 11 : cash register id
91
+ m += flag(withAdditionalData); // 19 : presence of additional GT data
92
+ m += "0000000"; // 20 : reserved (7)
93
+ return m; // 26
94
+ }
95
+
96
+ } // namespace
97
+
98
+ std::string Ecr17Protocol::buildPaymentMessage(const std::string& terminalId,
99
+ const std::string& cashRegisterId, int amountCents,
100
+ char paymentType, bool cardAlreadyPresent,
101
+ bool withAdditionalData,
102
+ const std::string& receiptText) {
103
+ return buildPaymentLike('P', terminalId, cashRegisterId, amountCents, paymentType,
104
+ cardAlreadyPresent, withAdditionalData, receiptText);
105
+ }
106
+
107
+ std::string Ecr17Protocol::buildExtendedPaymentMessage(const std::string& terminalId,
108
+ const std::string& cashRegisterId,
109
+ int amountCents, char paymentType,
110
+ bool cardAlreadyPresent,
111
+ bool withAdditionalData,
112
+ const std::string& receiptText) {
113
+ return buildPaymentLike('X', terminalId, cashRegisterId, amountCents, paymentType,
114
+ cardAlreadyPresent, withAdditionalData, receiptText);
115
+ }
116
+
117
+ std::string Ecr17Protocol::buildPreAuthMessage(const std::string& terminalId,
118
+ const std::string& cashRegisterId, int amountCents,
119
+ char paymentType, bool cardAlreadyPresent,
120
+ bool withAdditionalData,
121
+ const std::string& receiptText) {
122
+ return buildPaymentLike('p', terminalId, cashRegisterId, amountCents, paymentType,
123
+ cardAlreadyPresent, withAdditionalData, receiptText);
124
+ }
125
+
126
+ std::string Ecr17Protocol::buildIncrementalMessage(const std::string& terminalId,
127
+ const std::string& cashRegisterId,
128
+ int amountCents,
129
+ const std::string& originalPreAuthCode,
130
+ bool withAdditionalData,
131
+ const std::string& receiptText) {
132
+ return buildPreAuthFollowUp('i', terminalId, cashRegisterId, amountCents, originalPreAuthCode,
133
+ withAdditionalData, receiptText);
134
+ }
135
+
136
+ std::string Ecr17Protocol::buildPreAuthClosureMessage(const std::string& terminalId,
137
+ const std::string& cashRegisterId,
138
+ int amountCents,
139
+ const std::string& originalPreAuthCode,
140
+ bool withAdditionalData,
141
+ const std::string& receiptText) {
142
+ return buildPreAuthFollowUp('c', terminalId, cashRegisterId, amountCents, originalPreAuthCode,
143
+ withAdditionalData, receiptText);
144
+ }
145
+
146
+ std::string Ecr17Protocol::buildCardVerificationMessage(const std::string& terminalId,
147
+ const std::string& cashRegisterId,
148
+ char paymentType, bool withAdditionalData) {
149
+ std::string m;
150
+ m += leftPad(terminalId, 8); // 1 : terminal id
151
+ m += kReserved; // 9 : reserved
152
+ m += 'H'; // 10 : message code
153
+ m += leftPad(cashRegisterId, 8); // 11 : cash register id
154
+ m += flag(withAdditionalData); // 19 : presence of additional GT data
155
+ m += "00"; // 20 : reserved (2)
156
+ m += '0'; // 22 : standard card verification
157
+ m += paymentType; // 23 : payment type
158
+ m += "0000000000000000"; // 24 : reserved (16)
159
+ return m; // 39
160
+ }
161
+
162
+ std::string Ecr17Protocol::buildCloseSessionMessage(const std::string& terminalId,
163
+ const std::string& cashRegisterId,
164
+ bool withAdditionalData) {
165
+ return buildSessionCommand('C', terminalId, cashRegisterId, withAdditionalData);
166
+ }
167
+
168
+ std::string Ecr17Protocol::buildTotalsMessage(const std::string& terminalId,
169
+ const std::string& cashRegisterId,
170
+ bool withAdditionalData) {
171
+ return buildSessionCommand('T', terminalId, cashRegisterId, withAdditionalData);
172
+ }
173
+
174
+ std::string Ecr17Protocol::buildSendLastResultMessage(const std::string& terminalId,
175
+ const std::string& cashRegisterId,
176
+ bool withAdditionalData) {
177
+ std::string m;
178
+ m += leftPad(terminalId, 8); // 1 : terminal id
179
+ m += kReserved; // 9 : reserved
180
+ m += 'G'; // 10 : message code
181
+ m += leftPad(cashRegisterId, 8); // 11 : cash register id
182
+ m += flag(withAdditionalData); // 19 : presence of additional GT data
183
+ m += "000"; // 20 : reserved (3)
184
+ return m; // 22
185
+ }
186
+
187
+ std::string Ecr17Protocol::buildEnableEcrPrintMessage(const std::string& terminalId, bool enabled) {
188
+ std::string m;
189
+ m += leftPad(terminalId, 8); // 1 : terminal id
190
+ m += kReserved; // 9 : reserved
191
+ m += 'E'; // 10 : message code
192
+ m += flag(enabled); // 11 : enable(1)/disable(0) printing on ECR
193
+ return m; // 11
194
+ }
195
+
196
+ std::string Ecr17Protocol::buildReprintMessage(const std::string& terminalId, bool toEcr,
197
+ char ticketType) {
198
+ std::string m;
199
+ m += leftPad(terminalId, 8); // 1 : terminal id
200
+ m += kReserved; // 9 : reserved
201
+ m += 'R'; // 10 : message code
202
+ m += flag(toEcr); // 11 : 1 = send receipt to ECR, 0 = print on terminal
203
+ m += ticketType; // 12 : ticket type flag
204
+ m += "0000000000"; // 13 : reserved (10)
205
+ return m; // 22
206
+ }
207
+
208
+ std::string Ecr17Protocol::buildStatusMessage(const std::string& terminalId) {
209
+ std::string m;
210
+ m += leftPad(terminalId, 8); // 1 : terminal id
211
+ m += kReserved; // 9 : reserved
212
+ m += 's'; // 10 : message code (lowercase per spec)
213
+ return m; // 10
214
+ }
215
+
216
+ std::string Ecr17Protocol::buildReversalMessage(const std::string& terminalId,
217
+ const std::string& cashRegisterId,
218
+ const std::string& stan) {
219
+ std::string m;
220
+ m += leftPad(terminalId, 8); // 1 : terminal id
221
+ m += kReserved; // 9 : reserved
222
+ m += 'S'; // 10 : message code
223
+ m += leftPad(cashRegisterId, 8); // 11 : cash register id
224
+ m += leftPad(stan, 6); // 19 : STAN ("000000" = no check)
225
+ m += kReserved; // 25 : presence of additional GT data
226
+ m += kReserved; // 26 : reserved
227
+ return m; // 26
228
+ }
229
+
230
+ std::string Ecr17Protocol::buildVasMessage(const std::string& terminalId, const std::string& ecrId,
231
+ const std::string& xmlRequest) {
232
+ if (xmlRequest.size() > 1024) {
233
+ throw std::invalid_argument("ECR17: VAS request exceeds 1024 bytes");
234
+ }
235
+ std::string m;
236
+ m += leftPad(terminalId, 8); // 1 : terminal id
237
+ m += kReserved; // 9 : reserved
238
+ m += 'K'; // 10 : message code
239
+ m += leftPad(ecrId, 8); // 11 : ECR identifier
240
+ m += "000"; // 19 : reserved (3)
241
+ m += kReserved; // 22 : reserved (1)
242
+ m += leftPad(std::to_string(xmlRequest.size()), 4);// 23 : VAS request length (4)
243
+ m += xmlRequest; // 27 : VAS request (XML)
244
+ return m;
245
+ }
246
+
247
+ std::string Ecr17Protocol::buildAdditionalTagsMessage(const std::string& terminalId,
248
+ const std::string& tagContent,
249
+ const std::string& isoField,
250
+ const std::string& tagNumber) {
251
+ if (tagContent.empty() || tagContent.size() > 100) {
252
+ throw std::invalid_argument("ECR17: additional TAG content must be 1..100 chars");
253
+ }
254
+ std::string m;
255
+ m += leftPad(terminalId, 8); // 1 : terminal id
256
+ m += kReserved; // 9 : reserved
257
+ m += 'U'; // 10 : message code
258
+ m += "000000"; // 11 : payment type (6) -> standard payment
259
+ m += leftPad(isoField, 2); // 17 : ISO field number (e.g. "62")
260
+ m += rightPad(tagNumber, 8); // 19 : TAG number, left-justified, blank-filled
261
+ m += kReserved; // 27 : reserved (1)
262
+ m += "0000"; // 28 : exclusive TAG index bytemap (none to send to GT)
263
+ m += "00000"; // 32 : reserved (5)
264
+ m += tagContent; // 37 : privative TAG content (1..100)
265
+ m += kFieldSep; // end-of-field (0x1B)
266
+ return m;
267
+ }
268
+
269
+ std::string Ecr17Protocol::formatTokenizationTag(bool recurring, const std::string& contractCode) {
270
+ if (contractCode.empty() || contractCode.size() > 18) {
271
+ throw std::invalid_argument("ECR17: tokenization contract code must be 1..18 chars");
272
+ }
273
+ const std::string service = recurring ? "0REC" : "0COF";
274
+ return service + "0TRK" + contractCode + "|0FNZ03";
275
+ }
276
+
277
+ } // namespace margelo::nitro::ecr17