@padosoft/react-native-ecr17 0.0.0 → 2.0.0
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.
- package/Ecr17.podspec +39 -39
- package/README.md +348 -348
- package/android/CMakeLists.txt +41 -41
- package/android/build.gradle +148 -148
- package/android/fix-prefab.gradle +50 -50
- package/android/gradle.properties +5 -5
- package/android/src/main/AndroidManifest.xml +2 -2
- package/android/src/main/cpp/cpp-adapter.cpp +8 -8
- package/android/src/main/java/com/margelo/nitro/ecr17/HybridEcr17Transport.kt +233 -233
- package/android/src/main/java/com/padosoft/ecr17/Ecr17Package.kt +30 -30
- package/cpp/Ecr17.hpp +1 -1
- package/cpp/Ecr17Client/HybridEcr17Client.cpp +598 -598
- package/cpp/Ecr17Client/HybridEcr17Client.hpp +85 -85
- package/cpp/Ecr17Protocol/Ecr17Protocol.cpp +277 -277
- package/cpp/Ecr17Protocol/Ecr17Protocol.hpp +103 -103
- package/cpp/Ecr17Response/Ecr17Response.cpp +155 -155
- package/cpp/Ecr17Response/Ecr17Response.hpp +113 -113
- package/cpp/Lcr/Lcr.cpp +42 -42
- package/cpp/Lcr/Lcr.hpp +21 -21
- package/cpp/PacketCodec/PacketCodec.cpp +145 -145
- package/cpp/PacketCodec/PacketCodec.hpp +47 -47
- package/cpp/Session/Ecr17Session.cpp +260 -260
- package/cpp/Session/Ecr17Session.hpp +97 -97
- package/cpp/Session/RetryPolicy.hpp +23 -23
- package/cpp/Transport/FakeTransport.hpp +95 -95
- package/cpp/Transport/NativeTransportAdapter.cpp +42 -42
- package/cpp/Transport/NativeTransportAdapter.hpp +32 -32
- package/cpp/Transport/Transport.hpp +30 -30
- package/cpp/tests/CMakeLists.txt +55 -55
- package/cpp/tests/PosixTcpTransport.hpp +105 -105
- package/cpp/tests/stubs/LrcMode.hpp +25 -25
- package/cpp/tests/test_flows.cpp +148 -148
- package/cpp/tests/test_integration_terminal.cpp +72 -72
- package/cpp/tests/test_lrc.cpp +66 -66
- package/cpp/tests/test_packet_codec.cpp +164 -164
- package/cpp/tests/test_protocol.cpp +102 -102
- package/cpp/tests/test_protocol_commands.cpp +190 -190
- package/cpp/tests/test_response.cpp +164 -164
- package/cpp/tests/test_retry_policy.cpp +28 -28
- package/cpp/tests/test_session.cpp +262 -262
- package/ios/HybridEcr17Transport.swift +103 -103
- package/lib/commonjs/index.js +50 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/specs/client.nitro.js +17 -0
- package/lib/commonjs/specs/client.nitro.js.map +1 -0
- package/lib/commonjs/specs/transport.nitro.js +6 -0
- package/lib/commonjs/specs/transport.nitro.js.map +1 -0
- package/lib/commonjs/types/client.js +2 -0
- package/lib/commonjs/types/client.js.map +1 -0
- package/lib/commonjs/utils/client.js +13 -0
- package/lib/commonjs/utils/client.js.map +1 -0
- package/lib/module/index.js +7 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/specs/client.nitro.js +13 -0
- package/lib/module/specs/client.nitro.js.map +1 -0
- package/lib/module/specs/transport.nitro.js +4 -0
- package/lib/module/specs/transport.nitro.js.map +1 -0
- package/lib/module/types/client.js +2 -0
- package/lib/module/types/client.js.map +1 -0
- package/lib/module/utils/client.js +9 -0
- package/lib/module/utils/client.js.map +1 -0
- package/lib/typescript/src/index.d.ts +5 -0
- package/lib/typescript/src/index.d.ts.map +1 -0
- package/lib/typescript/src/specs/client.nitro.d.ts +63 -0
- package/lib/typescript/src/specs/client.nitro.d.ts.map +1 -0
- package/lib/typescript/src/specs/transport.nitro.d.ts +13 -0
- package/lib/typescript/src/specs/transport.nitro.d.ts.map +1 -0
- package/lib/typescript/src/types/client.d.ts +138 -0
- package/lib/typescript/src/types/client.d.ts.map +1 -0
- package/lib/typescript/src/utils/client.d.ts +3 -0
- package/lib/typescript/src/utils/client.d.ts.map +1 -0
- package/nitro.json +30 -30
- package/package.json +4 -4
- package/react-native.config.js +18 -18
- package/src/index.ts +4 -4
- package/src/specs/client.nitro.ts +102 -102
- package/src/specs/transport.nitro.ts +25 -25
- package/src/types/client.ts +196 -196
- 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
|