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/verifyOTP.cfm
John Mizerek 1210249f54 Normalize database column and table names across entire codebase
Update all SQL queries, query result references, and ColdFusion code to match
the renamed database schema. Tables use plural CamelCase, PKs are all `ID`,
column prefixes stripped (e.g. BusinessName→Name, UserFirstName→FirstName).

Key changes:
- Strip table-name prefixes from all column references (Businesses, Users,
  Addresses, Hours, Menus, Categories, Items, Stations, Orders,
  OrderLineItems, Tasks, TaskCategories, TaskRatings, QuickTaskTemplates,
  ScheduledTaskDefinitions, ChatMessages, Beacons, ServicePoints, Employees,
  VisitorTrackings, ApiPerfLogs, tt_States, tt_Days, tt_AddressTypes,
  tt_OrderTypes, tt_TaskTypes)
- Rename PK references from {TableName}ID to ID in all queries
- Rewrite 7 admin beacon files to use ServicePoints.BeaconID instead of
  dropped lt_Beacon_Businesses_ServicePoints link table
- Rewrite beacon assignment files (list, save, delete) for new schema
- Fix FK references incorrectly changed to ID (OrderLineItems.OrderID,
  Categories.MenuID, Tasks.CategoryID, ServicePoints.BeaconID)
- Update Addresses: AddressLat→Latitude, AddressLng→Longitude
- Update Users: UserPassword→Password, UserIsEmailVerified→IsEmailVerified,
  UserIsActive→IsActive, UserBalance→Balance, etc.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 15:39:12 -08:00

124 lines
4.1 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 and activate user account
*
* POST: { "uuid": "...", "otp": "123456" }
*
* Returns: { OK: true, UserID: 123, Token: "...", NeedsProfile: true/false }
*
* On success, marks phone as verified and returns auth token.
* NeedsProfile indicates if user still needs to provide name/email.
*/
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" });
}
// Check for magic OTP bypass (for App Store review)
isMagicOTP = structKeyExists(application, "MAGIC_OTP_ENABLED")
&& application.MAGIC_OTP_ENABLED
&& structKeyExists(application, "MAGIC_OTP_CODE")
&& otp == application.MAGIC_OTP_CODE;
// Find unverified user with matching UUID and OTP (or magic OTP)
if (isMagicOTP) {
qUser = queryExecute("
SELECT ID, FirstName, LastName, EmailAddress, IsEmailVerified
FROM Users
WHERE UUID = :uuid
AND IsContactVerified = 0
LIMIT 1
", {
uuid: { value: userUUID, cfsqltype: "cf_sql_varchar" }
}, { datasource: "payfrit" });
} else {
qUser = queryExecute("
SELECT ID, FirstName, LastName, EmailAddress, IsEmailVerified
FROM Users
WHERE UUID = :uuid
AND MobileVerifyCode = :otp
AND IsContactVerified = 0
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 = 0
", { uuid: { value: userUUID, cfsqltype: "cf_sql_varchar" } }, { datasource: "payfrit" });
if (qCheck.recordCount > 0) {
apiAbort({ "OK": false, "ERROR": "invalid_otp", "MESSAGE": "Invalid verification code. Please try again." });
} else {
apiAbort({ "OK": false, "ERROR": "expired", "MESSAGE": "Verification expired. Please request a new code." });
}
}
// Clear the OTP code (one-time use) but DON'T mark as verified yet
// Account will be marked verified after profile completion
queryExecute("
UPDATE Users
SET MobileVerifyCode = ''
WHERE UserID = :userId
", { userId: { value: qUser.ID, cfsqltype: "cf_sql_integer" } }, { datasource: "payfrit" });
// Create auth token (needed for completeProfile call)
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" });
// Check if profile is complete (has first name)
// For new signups, this will always be true
needsProfile = !len(trim(qUser.FirstName));
writeOutput(serializeJSON({
"OK": true,
"UserID": qUser.ID,
"Token": token,
"NeedsProfile": needsProfile,
"FirstName": qUser.FirstName ?: "",
"IsEmailVerified": qUser.IsEmailVerified == 1
}));
} catch (any e) {
apiAbort({
"OK": false,
"ERROR": "server_error",
"MESSAGE": e.message
});
}
</cfscript>