Migrate beacon APIs to shard-only system

- save.cfm: Auto-allocates shard+Major to business, creates ServicePoint with Minor
- list.cfm: Lists ServicePoints with BeaconMinor (removed legacy Beacons table)
- list_all.cfm: Returns shard UUIDs instead of legacy beacon UUIDs
- lookup.cfm: Removed legacy UUID lookup, shard-only resolution

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
John Mizerek 2026-02-14 19:33:49 -08:00
parent b360284e56
commit be29352b21
4 changed files with 256 additions and 375 deletions

View file

@ -42,7 +42,7 @@ if (bizId LTE 0) {
apiAbort({ OK=false, ERROR="no_business_selected" });
}
// Default behavior: only active beacons unless onlyActive is explicitly false/0
// Default behavior: only active unless onlyActive is explicitly false/0
onlyActive = true;
if (structKeyExists(data, "onlyActive")) {
if (isBoolean(data.onlyActive)) {
@ -55,71 +55,35 @@ if (structKeyExists(data, "onlyActive")) {
}
</cfscript>
<!--- Check if this business uses beacon sharding --->
<!--- Get business shard info --->
<cfquery name="qBiz" datasource="payfrit">
SELECT BeaconShardID, BeaconMajor
FROM Businesses
WHERE ID = <cfqueryparam cfsqltype="cf_sql_integer" value="#bizId#">
SELECT b.ID, b.Name, b.BeaconShardID, b.BeaconMajor, bs.UUID AS ShardUUID
FROM Businesses b
LEFT JOIN BeaconShards bs ON bs.ID = b.BeaconShardID
WHERE b.ID = <cfqueryparam cfsqltype="cf_sql_integer" value="#bizId#">
LIMIT 1
</cfquery>
<cfset usesSharding = qBiz.recordCount GT 0 AND val(qBiz.BeaconShardID) GT 0>
<cfset shardingInfo = {}>
<cfif usesSharding>
<!--- Get shard UUID for display --->
<cfquery name="qShard" datasource="payfrit">
SELECT UUID FROM BeaconShards WHERE ID = <cfqueryparam cfsqltype="cf_sql_integer" value="#qBiz.BeaconShardID#">
</cfquery>
<cfset shardingInfo = {
"ShardID" = qBiz.BeaconShardID,
"ShardUUID" = qShard.recordCount GT 0 ? qShard.UUID : "",
"Major" = qBiz.BeaconMajor
}>
<cfif qBiz.recordCount EQ 0>
<cfoutput>#serializeJSON({ OK=false, ERROR="business_not_found" })#</cfoutput>
<cfabort>
</cfif>
<!--- Legacy beacons from Beacons table --->
<cfquery name="q" datasource="payfrit">
SELECT DISTINCT
b.ID,
b.BusinessID,
b.Name,
b.UUID,
b.IsActive
FROM Beacons b
WHERE (
b.BusinessID = <cfqueryparam cfsqltype="cf_sql_integer" value="#bizId#">
OR b.ID IN (
SELECT lt.BeaconID FROM lt_BeaconsID_BusinessesID lt
WHERE lt.BusinessID = <cfqueryparam cfsqltype="cf_sql_integer" value="#bizId#">
)
)
<cfif onlyActive>
AND b.IsActive = 1
</cfif>
ORDER BY b.Name, b.ID
</cfquery>
<cfset hasShard = val(qBiz.BeaconShardID) GT 0>
<cfset shardInfo = {
"ShardID" = hasShard ? qBiz.BeaconShardID : 0,
"ShardUUID" = hasShard ? qBiz.ShardUUID : "",
"Major" = hasShard ? qBiz.BeaconMajor : 0
}>
<cfset beacons = []>
<cfloop query="q">
<cfset arrayAppend(beacons, {
"BeaconID" = q.ID,
"BusinessID" = q.BusinessID,
"Name" = q.Name,
"UUID" = q.UUID,
"IsActive" = q.IsActive,
"IsSharding" = false
})>
</cfloop>
<!--- If using sharding, also show service points with BeaconMinor as "beacons" --->
<cfif usesSharding>
<cfquery name="qShardBeacons" datasource="payfrit">
<!--- Get service points with beacon minor assignments --->
<cfquery name="qSP" datasource="payfrit">
SELECT
sp.ID AS ServicePointID,
sp.Name,
sp.BeaconMinor,
sp.IsActive
sp.IsActive,
sp.TypeID
FROM ServicePoints sp
WHERE sp.BusinessID = <cfqueryparam cfsqltype="cf_sql_integer" value="#bizId#">
AND sp.BeaconMinor IS NOT NULL
@ -127,30 +91,30 @@ if (structKeyExists(data, "onlyActive")) {
AND sp.IsActive = 1
</cfif>
ORDER BY sp.BeaconMinor, sp.Name
</cfquery>
</cfquery>
<cfloop query="qShardBeacons">
<cfset beacons = []>
<cfloop query="qSP">
<cfset arrayAppend(beacons, {
"BeaconID" = 0,
"ServicePointID" = qShardBeacons.ServicePointID,
"ServicePointID" = qSP.ServicePointID,
"BusinessID" = bizId,
"Name" = qShardBeacons.Name,
"UUID" = shardingInfo.ShardUUID,
"Major" = shardingInfo.Major,
"Minor" = qShardBeacons.BeaconMinor,
"IsActive" = qShardBeacons.IsActive,
"IsSharding" = true
"Name" = qSP.Name,
"UUID" = shardInfo.ShardUUID,
"Major" = shardInfo.Major,
"Minor" = qSP.BeaconMinor,
"IsActive" = qSP.IsActive ? true : false,
"TypeID" = qSP.TypeID
})>
</cfloop>
</cfif>
</cfloop>
<cfscript>try{logPerf(0);}catch(any e){}</cfscript>
<cfoutput>#serializeJSON({
OK=true,
ERROR="",
BusinessID=bizId,
COUNT=arrayLen(beacons),
BEACONS=beacons,
USES_SHARDING=usesSharding,
SHARDING_INFO=shardingInfo
OK = true,
ERROR = "",
BusinessID = bizId,
BusinessName = qBiz.Name,
COUNT = arrayLen(beacons),
BEACONS = beacons,
HAS_SHARD = hasShard,
SHARD_INFO = shardInfo
})#</cfoutput>

View file

@ -4,20 +4,11 @@
<cfcontent type="application/json; charset=utf-8" reset="true">
<cfheader name="Cache-Control" value="no-store">
<cfscript>
function apiAbort(obj) {
writeOutput(serializeJSON(obj));
abort;
}
</cfscript>
<!--- No auth required - this is public for beacon scanning before login --->
<!--- No auth required - returns all Payfrit shard UUIDs for beacon scanning --->
<cfquery name="q" datasource="payfrit">
SELECT
ID,
UUID
FROM Beacons
SELECT ID, UUID
FROM BeaconShards
WHERE IsActive = 1
ORDER BY ID
</cfquery>
@ -25,7 +16,7 @@ function apiAbort(obj) {
<cfset items = []>
<cfloop query="q">
<cfset arrayAppend(items, {
"BeaconID" = q.ID,
"ShardID" = q.ID,
"UUID" = q.UUID
})>
</cfloop>
@ -33,5 +24,6 @@ function apiAbort(obj) {
<cfoutput>#serializeJSON({
"OK" = true,
"ERROR" = "",
"SHARDS" = items,
"ITEMS" = items
})#</cfoutput>

View file

@ -6,14 +6,21 @@
<cfset requestData = deserializeJSON(toString(getHttpRequestData().content))>
<!--- ============================================================================
SHARDING FORMAT: { "Beacons": [{ "UUID": "...", "Major": 49, "Minor": 15 }] }
Resolves via BeaconShards -> Businesses (BeaconMajor) -> ServicePoints (BeaconMinor)
============================================================================ --->
<cfif structKeyExists(requestData, "Beacons") AND isArray(requestData.Beacons) AND arrayLen(requestData.Beacons) GT 0>
<cfset beacons = []>
<!--- Beacon lookup via sharding: { "Beacons": [{ "UUID": "...", "Major": 49, "Minor": 15 }] }
Resolves via BeaconShards -> Businesses (BeaconMajor) -> ServicePoints (BeaconMinor) --->
<cfloop array="#requestData.Beacons#" index="beaconData">
<cfif NOT structKeyExists(requestData, "Beacons") OR NOT isArray(requestData.Beacons) OR arrayLen(requestData.Beacons) EQ 0>
<cfoutput>#serializeJSON({
"OK" = true,
"ERROR" = "",
"BEACONS" = []
})#</cfoutput>
<cfabort>
</cfif>
<cfset beacons = []>
<cfloop array="#requestData.Beacons#" index="beaconData">
<cfset uuid = "">
<cfset major = 0>
<cfset minor = -1>
@ -39,10 +46,7 @@
<cfcontinue>
</cfif>
<!--- Resolve via sharding: Namespace (first 20 chars of UUID) -> Shard -> Business (Major) -> ServicePoint (Minor) --->
<!--- Extract namespace (first 20 hex chars) from beacon UUID for matching --->
<cfset namespace = lCase(left(reReplace(uuid, "-", "", "all"), 20))>
<!--- Resolve via sharding: UUID -> Shard -> Business (Major) -> ServicePoint (Minor) --->
<cfquery name="qShard" datasource="payfrit">
SELECT
biz.ID AS BusinessID,
@ -51,13 +55,12 @@
COALESCE(parent.Name, '') AS ParentBusinessName,
sp.ID AS ServicePointID,
sp.Name AS ServicePointName,
bs.UUID AS ShardUUID,
(SELECT COUNT(*) FROM Businesses WHERE ParentBusinessID = biz.ID) AS ChildCount
FROM BeaconShards bs
JOIN Businesses biz ON biz.BeaconShardID = bs.ID AND biz.BeaconMajor = <cfqueryparam cfsqltype="cf_sql_smallint" value="#major#">
LEFT JOIN ServicePoints sp ON sp.BusinessID = biz.ID AND sp.BeaconMinor = <cfqueryparam cfsqltype="cf_sql_smallint" value="#minor#"> AND sp.IsActive = 1
LEFT JOIN Businesses parent ON biz.ParentBusinessID = parent.ID
WHERE LEFT(REPLACE(bs.UUID, '-', ''), 20) = <cfqueryparam cfsqltype="cf_sql_varchar" value="#namespace#">
WHERE bs.UUID = <cfqueryparam cfsqltype="cf_sql_varchar" value="#uuid#">
AND bs.IsActive = 1
AND biz.IsDemo = 0
AND biz.IsPrivate = 0
@ -69,8 +72,6 @@
"UUID" = uCase(reReplace(uuid, "-", "", "all")),
"Major" = major,
"Minor" = minor,
"BeaconID" = 0,
"BeaconName" = qShard.ServicePointName,
"BusinessID" = qShard.BusinessID,
"BusinessName" = qShard.BusinessName,
"ServicePointID" = val(qShard.ServicePointID),
@ -80,85 +81,6 @@
"HasChildren" = qShard.ChildCount GT 0
})>
</cfif>
</cfloop>
<cfoutput>#serializeJSON({
"OK" = true,
"ERROR" = "",
"BEACONS" = beacons
})#</cfoutput>
<cfabort>
</cfif>
<!--- ============================================================================
LEGACY FORMAT: { "UUIDs": ["uuid1", "uuid2", ...] }
Resolves via old Beacons table
============================================================================ --->
<cfif NOT structKeyExists(requestData, "UUIDs") OR NOT isArray(requestData.UUIDs) OR arrayLen(requestData.UUIDs) EQ 0>
<cfoutput>#serializeJSON({
"OK" = true,
"ERROR" = "",
"BEACONS" = []
})#</cfoutput>
<cfabort>
</cfif>
<!--- Clean and normalize UUIDs (remove dashes, uppercase) --->
<cfset cleanUUIDs = []>
<cfloop array="#requestData.UUIDs#" index="uuid">
<cfset cleanUUID = uCase(reReplace(uuid, "-", "", "all"))>
<cfif len(cleanUUID) EQ 32>
<cfset arrayAppend(cleanUUIDs, cleanUUID)>
</cfif>
</cfloop>
<cfif arrayLen(cleanUUIDs) EQ 0>
<cfoutput>#serializeJSON({
"OK" = true,
"ERROR" = "",
"BEACONS" = []
})#</cfoutput>
<cfabort>
</cfif>
<!--- Query for matching beacons with business info --->
<cfquery name="qBeacons" datasource="payfrit">
SELECT
b.ID AS BeaconID,
b.Name AS BeaconName,
b.UUID,
COALESCE(sp.ID, 0) AS ServicePointID,
COALESCE(sp.Name, '') AS ServicePointName,
COALESCE(sp.BusinessID, lt.BusinessID, b.BusinessID) AS BusinessID,
biz.Name AS BusinessName,
biz.ParentBusinessID AS ParentBusinessID,
COALESCE(parent.Name, '') AS ParentBusinessName,
(SELECT COUNT(*) FROM Businesses WHERE ParentBusinessID = biz.ID) AS ChildCount
FROM Beacons b
LEFT JOIN ServicePoints sp ON sp.BeaconID = b.ID
LEFT JOIN lt_BeaconsID_BusinessesID lt ON lt.BeaconID = b.ID
INNER JOIN Businesses biz ON COALESCE(sp.BusinessID, lt.BusinessID, b.BusinessID) = biz.ID
LEFT JOIN Businesses parent ON biz.ParentBusinessID = parent.ID
WHERE b.UUID IN (<cfqueryparam value="#arrayToList(cleanUUIDs)#" cfsqltype="cf_sql_varchar" list="true">)
AND b.IsActive = 1
AND biz.IsDemo = 0
AND biz.IsPrivate = 0
</cfquery>
<cfset beacons = []>
<cfloop query="qBeacons">
<cfset arrayAppend(beacons, {
"UUID" = qBeacons.UUID,
"BeaconID" = qBeacons.BeaconID,
"BeaconName" = qBeacons.BeaconName,
"BusinessID" = qBeacons.BusinessID,
"BusinessName" = qBeacons.BusinessName,
"ServicePointID" = qBeacons.ServicePointID,
"ServicePointName" = qBeacons.ServicePointName,
"ParentBusinessID" = val(qBeacons.ParentBusinessID),
"ParentBusinessName" = qBeacons.ParentBusinessName,
"HasChildren" = qBeacons.ChildCount GT 0
})>
</cfloop>
<cfoutput>#serializeJSON({

View file

@ -34,155 +34,158 @@ if (!structKeyExists(request, "BusinessID") || !isNumeric(request.BusinessID) ||
apiAbort({ OK=false, ERROR="no_business_selected" });
}
// Verify the business exists
qBiz = queryTimed(
"SELECT ID FROM Businesses WHERE ID = ? LIMIT 1",
[ { value=request.BusinessID, cfsqltype="cf_sql_integer" } ],
businessID = request.BusinessID;
// Get business and check if it has a shard assigned
qBiz = queryExecute(
"SELECT ID, Name, BeaconShardID, BeaconMajor FROM Businesses WHERE ID = ? LIMIT 1",
[ { value=businessID, cfsqltype="cf_sql_integer" } ],
{ datasource="payfrit" }
);
if (qBiz.recordCount EQ 0) {
apiAbort({ OK=false, ERROR="invalid_business", MESSAGE="Business ID #request.BusinessID# does not exist. Please log out and log back in." });
apiAbort({ OK=false, ERROR="invalid_business", MESSAGE="Business not found" });
}
if (!structKeyExists(data, "Name") || len(normStr(data.Name)) EQ 0) {
apiAbort({ OK=false, ERROR="missing_beacon_name", MESSAGE="Name is required" });
}
// If business doesn't have a shard, allocate one
shardID = qBiz.BeaconShardID;
major = qBiz.BeaconMajor;
beaconId = 0;
if (structKeyExists(data, "BeaconID") && isNumeric(data.BeaconID) && int(data.BeaconID) GT 0) {
beaconId = int(data.BeaconID);
}
beaconName = normStr(data.Name);
uuid = structKeyExists(data, "UUID") ? normStr(data.UUID) : "";
isActive = 1;
if (structKeyExists(data, "IsActive")) {
if (isBoolean(data.IsActive)) isActive = (data.IsActive ? 1 : 0);
else if (isNumeric(data.IsActive)) isActive = int(data.IsActive);
else if (isSimpleValue(data.IsActive)) isActive = (lcase(trim(toString(data.IsActive))) EQ "true" ? 1 : 0);
}
// App is authoritative: if UUID exists, treat as update (overwrite)
if (beaconId EQ 0 && len(uuid) GT 0) {
qExisting = queryTimed(
"SELECT ID FROM Beacons WHERE UUID = ? LIMIT 1",
[ { value=uuid, cfsqltype="cf_sql_varchar" } ],
if (isNull(shardID) || val(shardID) EQ 0) {
// Find an unassigned shard
qFreeShard = queryExecute(
"SELECT bs.ID FROM BeaconShards bs
WHERE bs.IsActive = 1
AND bs.ID NOT IN (SELECT BeaconShardID FROM Businesses WHERE BeaconShardID IS NOT NULL)
ORDER BY bs.ID
LIMIT 1",
[],
{ datasource="payfrit" }
);
if (qExisting.recordCount GT 0) {
beaconId = qExisting.ID;
if (qFreeShard.recordCount EQ 0) {
apiAbort({ OK=false, ERROR="no_shards_available", MESSAGE="No beacon shards available" });
}
shardID = qFreeShard.ID;
// Find next available major for this shard (in case multiple businesses share a shard in future)
qMaxMajor = queryExecute(
"SELECT COALESCE(MAX(BeaconMajor), 0) AS MaxMajor FROM Businesses WHERE BeaconShardID = ?",
[ { value=shardID, cfsqltype="cf_sql_integer" } ],
{ datasource="payfrit" }
);
major = val(qMaxMajor.MaxMajor) + 1;
// Assign shard to business
queryExecute(
"UPDATE Businesses SET BeaconShardID = ?, BeaconMajor = ? WHERE ID = ?",
[
{ value=shardID, cfsqltype="cf_sql_integer" },
{ value=major, cfsqltype="cf_sql_smallint" },
{ value=businessID, cfsqltype="cf_sql_integer" }
],
{ datasource="payfrit" }
);
}
// Get the shard UUID
qShard = queryExecute(
"SELECT UUID FROM BeaconShards WHERE ID = ?",
[ { value=shardID, cfsqltype="cf_sql_integer" } ],
{ datasource="payfrit" }
);
shardUUID = qShard.UUID;
// Service point handling
spName = structKeyExists(data, "Name") ? normStr(data.Name) : "";
if (len(spName) EQ 0) {
apiAbort({ OK=false, ERROR="missing_name", MESSAGE="Service point name is required" });
}
servicePointID = 0;
if (structKeyExists(data, "ServicePointID") && isNumeric(data.ServicePointID) && int(data.ServicePointID) GT 0) {
servicePointID = int(data.ServicePointID);
}
// Check if we're updating an existing service point or creating new
if (servicePointID GT 0) {
// Update existing service point
qSP = queryExecute(
"SELECT ID, BeaconMinor FROM ServicePoints WHERE ID = ? AND BusinessID = ? LIMIT 1",
[
{ value=servicePointID, cfsqltype="cf_sql_integer" },
{ value=businessID, cfsqltype="cf_sql_integer" }
],
{ datasource="payfrit" }
);
if (qSP.recordCount EQ 0) {
apiAbort({ OK=false, ERROR="invalid_service_point", MESSAGE="Service point not found" });
}
minor = qSP.BeaconMinor;
// If no minor assigned yet, get next available
if (isNull(minor)) {
qMaxMinor = queryExecute(
"SELECT COALESCE(MAX(BeaconMinor), 0) AS MaxMinor FROM ServicePoints WHERE BusinessID = ?",
[ { value=businessID, cfsqltype="cf_sql_integer" } ],
{ datasource="payfrit" }
);
minor = val(qMaxMinor.MaxMinor) + 1;
}
// Update service point
queryExecute(
"UPDATE ServicePoints SET Name = ?, BeaconMinor = ?, IsActive = 1 WHERE ID = ?",
[
{ value=spName, cfsqltype="cf_sql_varchar" },
{ value=minor, cfsqltype="cf_sql_smallint" },
{ value=servicePointID, cfsqltype="cf_sql_integer" }
],
{ datasource="payfrit" }
);
} else {
// Create new service point with next available minor
qMaxMinor = queryExecute(
"SELECT COALESCE(MAX(BeaconMinor), 0) AS MaxMinor FROM ServicePoints WHERE BusinessID = ?",
[ { value=businessID, cfsqltype="cf_sql_integer" } ],
{ datasource="payfrit" }
);
minor = val(qMaxMinor.MaxMinor) + 1;
queryExecute(
"INSERT INTO ServicePoints (BusinessID, Name, TypeID, IsActive, BeaconMinor, SortOrder)
VALUES (?, ?, 1, 1, ?, ?)",
[
{ value=businessID, cfsqltype="cf_sql_integer" },
{ value=spName, cfsqltype="cf_sql_varchar" },
{ value=minor, cfsqltype="cf_sql_smallint" },
{ value=minor, cfsqltype="cf_sql_integer" }
],
{ datasource="payfrit" }
);
qNewSP = queryExecute("SELECT LAST_INSERT_ID() AS ID", [], { datasource="payfrit" });
servicePointID = qNewSP.ID;
}
// Return the beacon configuration for programming
writeOutput(serializeJSON({
OK = true,
ERROR = "",
ServicePointID = servicePointID,
ServicePointName = spName,
BusinessID = businessID,
BusinessName = qBiz.Name,
ShardID = shardID,
UUID = shardUUID,
Major = major,
Minor = minor
}));
</cfscript>
<cfif beaconId GT 0>
<!--- Update - app is authoritative, reassign to current business --->
<cfquery datasource="payfrit">
UPDATE Beacons
SET
BusinessID = <cfqueryparam cfsqltype="cf_sql_integer" value="#request.BusinessID#">,
Name = <cfqueryparam cfsqltype="cf_sql_varchar" value="#beaconName#">,
UUID = <cfqueryparam cfsqltype="cf_sql_varchar" value="#uuid#" null="#(len(uuid) EQ 0)#">,
IsActive = <cfqueryparam cfsqltype="cf_sql_tinyint" value="#isActive#">
WHERE ID = <cfqueryparam cfsqltype="cf_sql_integer" value="#beaconId#">
</cfquery>
<!--- Update associated service point if it exists, also reassign to current business --->
<cfquery name="qLink" datasource="payfrit">
SELECT ID FROM ServicePoints
WHERE BeaconID = <cfqueryparam cfsqltype="cf_sql_integer" value="#beaconId#">
LIMIT 1
</cfquery>
<cfif qLink.recordCount GT 0>
<cfquery datasource="payfrit">
UPDATE ServicePoints
SET Name = <cfqueryparam cfsqltype="cf_sql_varchar" value="#beaconName#">,
BusinessID = <cfqueryparam cfsqltype="cf_sql_integer" value="#request.BusinessID#">
WHERE BeaconID = <cfqueryparam cfsqltype="cf_sql_integer" value="#beaconId#">
</cfquery>
<cfelse>
<!--- No service point exists yet, create one linked to this beacon --->
<cfquery datasource="payfrit">
INSERT INTO ServicePoints (
BusinessID,
Name,
TypeID,
IsActive,
BeaconID
) VALUES (
<cfqueryparam cfsqltype="cf_sql_integer" value="#request.BusinessID#">,
<cfqueryparam cfsqltype="cf_sql_varchar" value="#beaconName#">,
1,
1,
<cfqueryparam cfsqltype="cf_sql_integer" value="#beaconId#">
)
</cfquery>
</cfif>
<cfelse>
<!--- Insert beacon --->
<cfquery datasource="payfrit">
INSERT INTO Beacons (
BusinessID,
Name,
UUID,
IsActive
) VALUES (
<cfqueryparam cfsqltype="cf_sql_integer" value="#request.BusinessID#">,
<cfqueryparam cfsqltype="cf_sql_varchar" value="#beaconName#">,
<cfqueryparam cfsqltype="cf_sql_varchar" value="#uuid#" null="#(len(uuid) EQ 0)#">,
<cfqueryparam cfsqltype="cf_sql_tinyint" value="#isActive#">
)
</cfquery>
<cfquery name="qId" datasource="payfrit">
SELECT LAST_INSERT_ID() AS ID
</cfquery>
<cfset beaconId = qId.ID>
<!--- Auto-create service point with same name, linked to beacon --->
<cfquery datasource="payfrit">
INSERT INTO ServicePoints (
BusinessID,
Name,
TypeID,
IsActive,
BeaconID
) VALUES (
<cfqueryparam cfsqltype="cf_sql_integer" value="#request.BusinessID#">,
<cfqueryparam cfsqltype="cf_sql_varchar" value="#beaconName#">,
1,
1,
<cfqueryparam cfsqltype="cf_sql_integer" value="#beaconId#">
)
</cfquery>
</cfif>
<!--- Return saved row --->
<cfquery name="qOut" datasource="payfrit">
SELECT
ID,
BusinessID,
Name,
UUID,
IsActive
FROM Beacons
WHERE ID = <cfqueryparam cfsqltype="cf_sql_integer" value="#beaconId#">
AND BusinessID = <cfqueryparam cfsqltype="cf_sql_integer" value="#request.BusinessID#">
LIMIT 1
</cfquery>
<cfset beacon = {
"BeaconID" = qOut.BeaconID,
"BusinessID" = qOut.BusinessID,
"Name" = qOut.Name,
"UUID" = qOut.UUID,
"IsActive" = qOut.IsActive
}>
<cfoutput>#serializeJSON({ OK=true, ERROR="", BEACON=beacon })#</cfoutput>
<cfcatch type="any">
<cfheader statuscode="200" statustext="OK">
<cfcontent type="application/json; charset=utf-8" reset="true">