function headerValue(name) { // Use servlet request object to get headers (CGI scope doesn't expose custom HTTP headers in Lucee) try { req = getPageContext().getRequest(); val = req.getHeader(arguments.name); if (!isNull(val)) return trim(val); } catch (any e) { // Fall back to CGI scope k = "HTTP_" & ucase(reReplace(arguments.name, "[^A-Za-z0-9]", "_", "all")); if (structKeyExists(cgi, k)) return trim(cgi[k]); } return ""; } userToken = headerValue("X-User-Token"); result = { "receivedToken": userToken, "tokenLength": len(userToken), "tokenPrefix": len(userToken) > 8 ? left(userToken, 8) : userToken }; if (len(userToken)) { try { qTok = queryExecute( "SELECT UserID, Token FROM UserTokens WHERE Token = ? LIMIT 1", [ { value = userToken, cfsqltype = "cf_sql_varchar" } ], { datasource = "payfrit" } ); result.dbLookupRecords = qTok.recordCount; if (qTok.recordCount > 0) { result.foundUserId = qTok.UserID; } // Also check with LIKE to see if partial match exists qPartial = queryExecute( "SELECT UserID, Token FROM UserTokens WHERE Token LIKE ? LIMIT 5", [ { value = left(userToken, 8) & "%", cfsqltype = "cf_sql_varchar" } ], { datasource = "payfrit" } ); result.partialMatches = qPartial.recordCount; } catch (any e) { result.error = e.message; } } // Also list recent tokens try { qRecent = queryExecute( "SELECT UserID, LEFT(Token, 8) as TokenPrefix, LENGTH(Token) as TokenLen FROM UserTokens ORDER BY UserID DESC LIMIT 5", [], { datasource = "payfrit" } ); result.recentTokens = []; for (row in qRecent) { arrayAppend(result.recentTokens, { "userId": row.UserID, "prefix": row.TokenPrefix, "length": row.TokenLen }); } } catch (any e) { result.recentTokensError = e.message; } writeOutput(serializeJSON(result));