@@ -1441,14 +1441,7 @@ namespace open_loop {
14411441 * @note The `explicit` keyword prevents implicit conversions (e.g., from an integer),
14421442 * which improves type safety.
14431443 */
1444- explicit container (const std::time_t card_effective_date_in_minutes) noexcept
1445- : card_effective_date_(card_effective_date_in_minutes) {
1446- // The constructor immediately propagates the effective date to its time-sensitive
1447- // child objects. This ensures that the container and its parts are always in a
1448- // consistent state from the moment of creation.
1449- validation_.set_card_effective_date (card_effective_date_in_minutes);
1450- history_.set_card_effective_date (card_effective_date_in_minutes);
1451- }
1444+ container () = default ;
14521445
14531446 /* *
14541447 * @brief Sets the `general` data block for the CSA.
@@ -1952,6 +1945,8 @@ namespace open_loop {
19521945 static constexpr uint32_t TERMINAL_ID_MAX = 0xFFFFFF ;
19531946 // ! The maximum value for the RFU field (stored in 4 bits: 2^4 - 1).
19541947 static constexpr uint8_t RFU_MAX = 0x0F ;
1948+ // ! The required length of a terminal ID hex string (2 chars per byte for 3 bytes).
1949+ static constexpr size_t TERMINAL_ID_HEX_LENGTH = 6 ;
19551950
19561951 /* *
19571952 * @brief Default constructor. Creates a record with all fields initialized to zero.
@@ -1995,13 +1990,32 @@ namespace open_loop {
19951990 }
19961991
19971992 /* *
1998- * @brief Sets the Terminal ID.
1999- * @param id The terminal's unique identifier. What to send: A value in the range [0, 0xFFFFFF].
2000- * @throws std::out_of_range if the ID exceeds the 24-bit limit.
1993+ * @brief Sets the Terminal ID from a hexadecimal string.
1994+ * @param hex_id The terminal's unique identifier as a hex string.
1995+ * What to send: A 6-character, case-insensitive string containing only
1996+ * hexadecimal characters (0-9, A-F). Example: "1122AA".
1997+ * @throws std::invalid_argument if `hex_id` is not exactly 6 characters long.
1998+ * @throws std::out_of_range if `hex_id` contains invalid characters or represents a value > 0xFFFFFF.
20011999 */
2002- void set_terminal_id (const uint32_t id) {
2003- if (id > TERMINAL_ID_MAX) throw std::out_of_range (" Terminal ID exceeds 24-bit limit." );
2004- terminal_id_ = id;
2000+ void set_terminal_id (const std::string& hex_id) {
2001+ // First, perform a quick and inexpensive check on the string length.
2002+ if (hex_id.size () != TERMINAL_ID_HEX_LENGTH)
2003+ throw std::invalid_argument (" Terminal ID hex string must be exactly 6 characters." );
2004+
2005+ // Use a stringstream for a robust conversion from hex string to an integer.
2006+ unsigned long long value = 0 ;
2007+ std::stringstream ss;
2008+ ss << std::hex << hex_id; // Tell the stream to interpret the input as hexadecimal.
2009+
2010+ // Try to extract the value and perform comprehensive validation.
2011+ // 1. `!(ss >> value)`: Fails if the string contains non-hex characters (e.g., "A1B2G3").
2012+ // 2. `ss.rdbuf()->in_avail() != 0`: Fails if there are leftover characters after a valid parse,
2013+ // which handles cases where the stream stops parsing early.
2014+ // 3. `value > TERMINAL_ID_MAX`: Fails if the parsed number exceeds the 24-bit limit.
2015+ if (!(ss >> value) || ss.rdbuf ()->in_avail () != 0 || value > TERMINAL_ID_MAX)
2016+ throw std::out_of_range (" Terminal ID string is invalid or its value is out of the 24-bit range." );
2017+ // If all checks pass, safely cast and assign the value.
2018+ terminal_id_ = static_cast <uint32_t >(value);
20052019 }
20062020
20072021 /* *
@@ -2745,12 +2759,7 @@ namespace open_loop {
27452759 * @param card_effective_date_in_minutes The card's effective date. What to send: A `std::time_t`
27462760 * value representing minutes since the Unix epoch.
27472761 */
2748- explicit container (const std::time_t card_effective_date_in_minutes) noexcept
2749- : card_effective_date_(card_effective_date_in_minutes) {
2750- // Immediately propagate the effective date to ensure a consistent state.
2751- validation_.set_card_effective_date (card_effective_date_in_minutes);
2752- history_.set_card_effective_date (card_effective_date_in_minutes);
2753- }
2762+ container () = default ;
27542763
27552764 void set_general (const general& gen) noexcept {
27562765 general_ = gen;
0 commit comments