/** * Verify OTP for LOGIN (existing verified accounts) * * POST: { "uuid": "...", "otp": "123456" } * * Returns: { OK: true, UserID: 123, Token: "...", UserFirstName: "..." } */ function apiAbort(required struct payload) { writeOutput(serializeJSON(payload)); abort; } function readJsonBody() { var raw = getHttpRequestData().content; if (isNull(raw)) raw = ""; if (!len(trim(raw))) return {}; try { var data = deserializeJSON(raw); if (isStruct(data)) return data; } catch (any e) {} return {}; } try { data = readJsonBody(); userUUID = structKeyExists(data, "uuid") ? trim(data.uuid) : ""; otp = structKeyExists(data, "otp") ? trim(data.otp) : ""; if (!len(userUUID) || !len(otp)) { apiAbort({ "OK": false, "ERROR": "missing_fields", "MESSAGE": "UUID and OTP are required" }); } // Find verified user with matching UUID and OTP qUser = queryExecute(" SELECT UserID, UserFirstName, UserLastName FROM Users WHERE UserUUID = :uuid AND UserMobileVerifyCode = :otp AND UserIsContactVerified = 1 LIMIT 1 ", { uuid: { value: userUUID, cfsqltype: "cf_sql_varchar" }, otp: { value: otp, cfsqltype: "cf_sql_varchar" } }, { datasource: "payfrit" }); if (qUser.recordCount == 0) { // Check if UUID exists but OTP is wrong qCheck = queryExecute(" SELECT UserID FROM Users WHERE UserUUID = :uuid AND UserIsContactVerified = 1 ", { uuid: { value: userUUID, cfsqltype: "cf_sql_varchar" } }, { datasource: "payfrit" }); if (qCheck.recordCount > 0) { apiAbort({ "OK": false, "ERROR": "invalid_otp", "MESSAGE": "Invalid code. Please try again." }); } else { apiAbort({ "OK": false, "ERROR": "expired", "MESSAGE": "Session expired. Please request a new code." }); } } // Clear the OTP (one-time use) queryExecute(" UPDATE Users SET UserMobileVerifyCode = '' WHERE UserID = :userId ", { userId: { value: qUser.UserID, cfsqltype: "cf_sql_integer" } }, { datasource: "payfrit" }); // Create auth token token = replace(createUUID(), "-", "", "all"); queryExecute(" INSERT INTO UserTokens (UserID, Token) VALUES (:userId, :token) ", { userId: { value: qUser.UserID, cfsqltype: "cf_sql_integer" }, token: { value: token, cfsqltype: "cf_sql_varchar" } }, { datasource: "payfrit" }); writeOutput(serializeJSON({ "OK": true, "UserID": qUser.UserID, "Token": token, "UserFirstName": qUser.UserFirstName ?: "" })); } catch (any e) { apiAbort({ "OK": false, "ERROR": "server_error", "MESSAGE": e.message }); }