@@ -85,6 +85,7 @@ using json = nlohmann::json;
8585
8686// return the payload to the requestor
8787void 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
165281std::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
370565void 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}
0 commit comments