This repository has been archived on 2026-03-21. You can view files and clone it, but cannot push or open issues or pull requests.
payfrit-biz/api/auth/verifyLoginOTP.cfm
John Mizerek 6b66d2cef8 Fix normalized DB column names across all API files
Sweep of 26 API files to use prefixed column names matching the
database schema (e.g. BusinessID not ID, BusinessName not Name,
BusinessDeliveryFlatFee not DeliveryFlatFee, ServicePointName not Name).

Files fixed: auth, beacons, businesses, menu, orders, setup, stripe,
tasks, and workers endpoints.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 16:56:41 -08:00

97 lines
3 KiB
Text

<cfsetting showdebugoutput="false">
<cfsetting enablecfoutputonly="true">
<cfcontent type="application/json; charset=utf-8" reset="true">
<cfheader name="Cache-Control" value="no-store">
<cfscript>
/**
* Verify OTP for LOGIN (existing verified accounts)
*
* POST: { "uuid": "...", "otp": "123456" }
*
* Returns: { OK: true, UserID: 123, Token: "...", FirstName: "..." }
*/
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
// Magic OTP only bypasses Twilio SMS (in loginOTP.cfm), not OTP verification
qUser = queryExecute("
SELECT ID, FirstName, LastName
FROM Users
WHERE UUID = :uuid
AND MobileVerifyCode = :otp
AND IsContactVerified = 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 ID FROM Users WHERE UUID = :uuid AND IsContactVerified = 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 MobileVerifyCode = ''
WHERE ID = :userId
", { userId: { value: qUser.ID, 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.ID, cfsqltype: "cf_sql_integer" },
token: { value: token, cfsqltype: "cf_sql_varchar" }
}, { datasource: "payfrit" });
writeOutput(serializeJSON({
"OK": true,
"UserID": qUser.ID,
"Token": token,
"FirstName": qUser.FirstName ?: ""
}));
} catch (any e) {
apiAbort({
"OK": false,
"ERROR": "server_error",
"MESSAGE": e.message
});
}
</cfscript>