Skip to content

Commit 4cfa70c

Browse files
committed
parse response and check field 32 for expiry
1 parent 63cb1bf commit 4cfa70c

File tree

5 files changed

+264
-9
lines changed

5 files changed

+264
-9
lines changed

.vscode/settings.json

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,61 @@
11
{
22
"files.associations": {
33
".fantomasignore": "ignore",
4-
"iomanip": "cpp"
4+
"iomanip": "cpp",
5+
"string": "cpp",
6+
"__bit_reference": "cpp",
7+
"__config": "cpp",
8+
"__hash_table": "cpp",
9+
"__locale": "cpp",
10+
"__node_handle": "cpp",
11+
"__split_buffer": "cpp",
12+
"__threading_support": "cpp",
13+
"__tree": "cpp",
14+
"__verbose_abort": "cpp",
15+
"any": "cpp",
16+
"array": "cpp",
17+
"bitset": "cpp",
18+
"cctype": "cpp",
19+
"charconv": "cpp",
20+
"clocale": "cpp",
21+
"cmath": "cpp",
22+
"complex": "cpp",
23+
"cstdarg": "cpp",
24+
"cstddef": "cpp",
25+
"cstdint": "cpp",
26+
"cstdio": "cpp",
27+
"cstdlib": "cpp",
28+
"cstring": "cpp",
29+
"ctime": "cpp",
30+
"cwchar": "cpp",
31+
"cwctype": "cpp",
32+
"execution": "cpp",
33+
"forward_list": "cpp",
34+
"fstream": "cpp",
35+
"initializer_list": "cpp",
36+
"ios": "cpp",
37+
"iosfwd": "cpp",
38+
"iostream": "cpp",
39+
"istream": "cpp",
40+
"limits": "cpp",
41+
"locale": "cpp",
42+
"map": "cpp",
43+
"mutex": "cpp",
44+
"new": "cpp",
45+
"optional": "cpp",
46+
"ostream": "cpp",
47+
"ratio": "cpp",
48+
"span": "cpp",
49+
"sstream": "cpp",
50+
"stdexcept": "cpp",
51+
"streambuf": "cpp",
52+
"string_view": "cpp",
53+
"tuple": "cpp",
54+
"typeinfo": "cpp",
55+
"unordered_map": "cpp",
56+
"valarray": "cpp",
57+
"variant": "cpp",
58+
"vector": "cpp",
59+
"algorithm": "cpp"
560
}
661
}

device_server

942 KB
Binary file not shown.

device_server_normal

854 KB
Binary file not shown.

src/pos_server/main.cpp

Lines changed: 208 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ using json = nlohmann::json;
8585

8686
//return the payload to the requestor
8787
void resendToRequestor(int socket, std::string data){
88+
log_to_file("resendToRequestor"+ data);
8889
send(socket,data.c_str(),data.size(), 0); // Send data back to client
8990

9091
}
@@ -160,6 +161,121 @@ json activateDevice(const std::string& secret) {
160161
}
161162

162163

164+
//Helper function to pad or trim a string to a specified fixed length
165+
std::string formatFixedField(const std::string& value, size_t length) {
166+
if (value.length() > length) {
167+
return value.substr(0, length); // Trim if too long
168+
} else {
169+
return value + std::string(length - value.length(), ' '); // Pad with spaces if too short
170+
}
171+
}
172+
173+
// Helper function to parse variable-length fields
174+
std::string parseVariableField(const std::string& data, size_t& start, int maxLength) {
175+
int length = std::stoi(data.substr(start, 2)); // Assuming 2-digit length indicator
176+
start += 2; // Move start past the length indicator
177+
178+
if (length > maxLength) {
179+
throw std::runtime_error("Field length exceeds maximum allowed length.");
180+
}
181+
182+
std::string field = data.substr(start, length);
183+
start += length; // Update start position to the end of the current field
184+
return field;
185+
}
186+
187+
// Function to modify fields 32 and 33 in an existing ISO 8583 message
188+
std::string modifyISO8583MessageForExpiredTokenAlert(std::string isoMessage) {
189+
// Parse and modify the message
190+
std::string modifiedMessage;
191+
size_t index = 0;
192+
193+
// Assuming fields are present and correctly formatted up to at least field 33
194+
// Parsing up to field 32
195+
modifiedMessage += isoMessage.substr(index, 85); // Sum of lengths of fields before field 32
196+
index += 85;
197+
198+
// Replace field 32 (length 12)
199+
modifiedMessage += formatFixedField("DECLINED", 12);
200+
index += 12; // Skip the original content of field 32
201+
202+
// Replace field 33 (length 40)
203+
modifiedMessage += formatFixedField("POS notauthorized to process this request", 40);
204+
index += 40; // Skip the original content of field 33
205+
206+
// Append the rest of the message unchanged
207+
modifiedMessage += isoMessage.substr(index);
208+
209+
return modifiedMessage;
210+
}
211+
212+
213+
214+
// Utility function to check if field 32 contains "TOKEN EXPIRY"
215+
bool hasResponseTokenExpiry(const std::map<int, std::string>& fields) {
216+
auto it = fields.find(32);
217+
if (it != fields.end() && it->second == "TOKEN EXPIRY") {
218+
return true;
219+
}
220+
return false;
221+
}
222+
223+
224+
225+
// Parses an ISO8583 response string into a map of fields.
226+
std::map<int, std::string> parseISO8583(const std::string& response) {
227+
std::map<int, std::string> fields;
228+
size_t index = 0;
229+
230+
// Fixed-length fields
231+
fields[0] = response.substr(index, 4);
232+
index += 4;
233+
234+
// Variable-length fields
235+
fields[2] = parseVariableField(response, index, 20); // Field 2 can be up to 20 characters long
236+
fields[4] = response.substr(index, 12); // Fixed length
237+
index += 12;
238+
fields[10] = response.substr(index, 12); // Fixed length
239+
index += 12;
240+
241+
// More variable-length fields
242+
fields[11] = parseVariableField(response, index, 40);
243+
fields[12] = parseVariableField(response, index, 40);
244+
fields[13] = parseVariableField(response, index, 40);
245+
fields[14] = parseVariableField(response, index, 256);
246+
fields[15] = parseVariableField(response, index, 256);
247+
fields[16] = parseVariableField(response, index, 256);
248+
fields[22] = response.substr(index, 4);
249+
index += 4;
250+
251+
fields[23] = parseVariableField(response, index, 12);
252+
fields[26] = parseVariableField(response, index, 20);
253+
fields[31] = parseVariableField(response, index, 40);
254+
fields[32] = response.substr(index, 12);
255+
index += 12;
256+
fields[33] = response.substr(index, 40);
257+
index += 40;
258+
259+
// More fields including very large variable fields
260+
fields[35] = parseVariableField(response, index, 660);
261+
fields[36] = parseVariableField(response, index, 128);
262+
fields[49] = response.substr(index, 3); // Assuming 3 characters for demo, adjust as needed
263+
index += 3;
264+
fields[50] = response.substr(index, 1);
265+
index += 1;
266+
267+
fields[55] = parseVariableField(response, index, 20);
268+
fields[57] = parseVariableField(response, index, 128);
269+
fields[59] = parseVariableField(response, index, 20);
270+
fields[60] = response.substr(index, 1);
271+
index += 1;
272+
fields[61] = response.substr(index, 1);
273+
index += 1;
274+
fields[62] = response.substr(index, 1);
275+
index += 1;
276+
277+
return fields;
278+
}
163279

164280
// Function to prompt user and get a string
165281
std::string getUserInput(const std::string& prompt) {
@@ -328,7 +444,7 @@ json sendSequenceHash(const std::string& deviceId, const std::string& deviceSequ
328444
* - This function is dependent on the libcurl library for handling HTTP communications.
329445
* - Assumes that the server endpoint, headers, and CURL error handling are correctly set.
330446
*/
331-
std::string sendPlainText(const std::string& accessToken, const std::string& payload) {
447+
std::string sendPlainText(const int requestorSocket, const std::string& accessToken, const std::string& payload) {
332448
std::string url = "https://dev.bit.cullinangroup.net:5443/bit-dps-webservices/posCommand";
333449
log_to_file("Sending plain text... Access Token: " + accessToken + ", Payload: " + payload);
334450
log_to_file("URL: " + url);
@@ -362,19 +478,99 @@ std::string sendPlainText(const std::string& accessToken, const std::string& pay
362478

363479
curl_slist_free_all(headers);
364480
curl_easy_cleanup(curl);
365-
return response_string;
481+
482+
483+
484+
//parse and check for token expiry
485+
std::string isoMessage=response_string;
486+
try {
487+
auto parsedFields = parseISO8583(isoMessage);
488+
489+
for (const auto& field : parsedFields) {
490+
std::cout << "Field " << field.first << ": " << field.second << std::endl;
491+
}
492+
//parse response for token expiry
493+
bool isTokenExpiry=hasResponseTokenExpiry(parsedFields);
494+
if (!isTokenExpiry){
495+
//If the ISO8583 response message does not contain a "TOKEN EXPIRY" response code
496+
//then the ISO8583 message response is to be returned to the requestor unmodified
497+
resendToRequestor(requestorSocket,payload);
498+
return response_string;
499+
}else{
500+
//If a "TOKEN EXPIRY" response is received the response code is to be modified to return
501+
//"DECLINED" in the ISO8583 message response located in field number 32, and “POS not
502+
//authorized to process this request” in field number 33.
503+
auto msg=modifyISO8583MessageForExpiredTokenAlert(payload);
504+
resendToRequestor(requestorSocket,payload);
505+
return "";
506+
}
507+
508+
} catch (const std::exception& e) {
509+
std::cerr << "Error parsing ISO8583 message: " << e.what() << std::endl;
510+
return "";
511+
}
512+
366513
}
367514
return "";
515+
368516
}
517+
/*
518+
// Generates an ISO 8583 response with specific values in fields 32 and 33
519+
std::string generateDummyISO8583ResponseForExpiredTokenAlert() {
520+
std::ostringstream message;
521+
522+
// Field 0: Message Type Indicator
523+
message << "0100"; // Assuming '0100' as a typical message type for a financial transaction request
524+
525+
// Adding some dummy and specific data for other fields
526+
message << formatFixedField("123456789012", 12); // Field 2: Primary Account Number (PAN), max length 20
527+
message << formatFixedField("000001234567", 12); // Field 4: Transaction Amount
369528
529+
// Skipping fields 10-11 for simplicity; add similar lines if needed
530+
message << formatFixedField("123", 3); // Field 10: Local Transaction Time (Fixed length)
531+
message << formatFixedField("RESPONSE", 40); // Field 11: Systems trace audit number (Variable length, up to 40)
532+
533+
// Field 12: Local Transaction Time
534+
message << formatFixedField("123456", 6); // Field 12: Local Transaction Time (Fixed length)
535+
message << formatFixedField("1206", 4); // Field 13: Local Transaction Date
536+
537+
// Assuming similar handling for fields 14-31 if necessary
538+
message << formatFixedField("FIELD14", 256); // Field 14: Extended data (Variable length, up to 256)
539+
540+
// Field 32: Acquiring Institution Identification Code
541+
message << formatFixedField("DECLINED", 12); // Field 32 with fixed length of 12
542+
543+
// Field 33: Forwarding Institution Identification Code
544+
message << formatFixedField("POS not authorized to process this request", 40); // Field 33 with fixed length of 40
545+
546+
// Fields 35-62: Dummy data; provide real values in actual implementation
547+
message << formatFixedField("ACCNO1234567890", 16); // Field 35: Card Sequence Number (Variable length, up to 660)
548+
message << formatFixedField("DATA", 4); // Field 36: Track 3 data (Variable length, up to 128)
549+
550+
// Continuing with similar placeholders for the rest of the fields
551+
message << formatFixedField("", 0); // Field 49: Currency Code, up to 128 characters
552+
message << formatFixedField("1", 1); // Field 50: Service Restriction Code
553+
message << formatFixedField("055", 3); // Field 55: ICC data (Variable, up to 20)
554+
555+
message << formatFixedField("1", 1); // Field 60: POS entry mode
556+
message << formatFixedField("1", 1); // Field 61: POS condition code
557+
message << formatFixedField("1", 1); // Field 62: POS capture code
558+
559+
return message.str();
560+
}
561+
*/
562+
563+
/*
564+
//Sends TOKEN EXPIRY as ISO8583 and manages its response
370565
void executeTokenExpiry(int requestorSocket, std::string accessToken){
371566
std::string payload = R"(
372567
<isomsg>
373568
<!-- org.jpos.iso.packager.XMLPackager -->
374569
<field id="32" value="TOKEN EXPIRY"/>
375570
</isomsg>
376571
)";
377-
auto response=sendPlainText(accessToken,payload);
572+
log_to_file("Send to ")
573+
auto response=sendPlainText(requestorSocket, accessToken,payload);
378574
if (response=="TOKEN EXPIRY"){
379575
std::string payload = R"(
380576
<isomsg>
@@ -388,7 +584,7 @@ void executeTokenExpiry(int requestorSocket, std::string accessToken){
388584
}else{
389585
resendToRequestor(requestorSocket,response);
390586
}
391-
}
587+
}*/
392588

393589
/**
394590
* Validates an access token based on its expiration time.
@@ -433,9 +629,13 @@ bool is_valid_access_token(std::optional<int> requestorSocket = std::nullopt) {
433629
if (current_time < expiration_time) {
434630
return true;
435631
}else{
436-
if (requestorSocket.has_value()) {
437-
executeTokenExpiry(*requestorSocket,access_token);
438-
}
632+
//optional:
633+
//the program checks token expiry at startup and when API returns TOKEN EXPIRY in field 32
634+
//if you want to check token expiration before the api, uncomment below
635+
//if expired, send TOKEN EXPIRY and manage its reponse
636+
//if (requestorSocket.has_value()) {
637+
// executeTokenExpiry(*requestorSocket,access_token);
638+
//}
439639
return false;
440640
}
441641
}
@@ -634,7 +834,7 @@ void checkTokenAndExecute(int requestorSocket, std::string sessionToken, std::st
634834
resendToRequestor( requestorSocket,payload);
635835
}else{
636836
// Sends the XML payload using the session's access token.
637-
sendPlainText(sessionToken, payload);
837+
sendPlainText(requestorSocket,sessionToken, payload);
638838

639839
}
640840
}

src/pos_server/pos

963 KB
Binary file not shown.

0 commit comments

Comments
 (0)