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/beacon-sharding/resolve_business.cfm
John Mizerek 084e815c6c Stripe and beacon API updates
- createPaymentIntent: improved error handling
- webhook: updated payment processing
- resolve_business: minor fix

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-16 12:53:05 -08:00

174 lines
4.6 KiB
Text

<cfsetting showdebugoutput="false">
<cfsetting enablecfoutputonly="true">
<cfcontent type="application/json; charset=utf-8" reset="true">
<cfheader name="Cache-Control" value="no-store">
<!---
ResolveBusiness API
===================
Resolves a beacon's (UUID, Major) to a Business.
Used by customer apps to identify which business a beacon belongs to.
Request:
POST /api/beacon-sharding/resolve_business.cfm
{ "UUID": "f7826da6-4fa2-4e98-8024-bc5b71e0893e", "Major": 42 }
Or for batch resolution:
{ "Beacons": [
{ "UUID": "...", "Major": 42 },
{ "UUID": "...", "Major": 43 }
]
}
Response (single):
{
"OK": true,
"BusinessID": 123,
"BusinessName": "Joe's Diner",
"BrandColor": "#FF5722",
"HeaderImageURL": "/uploads/businesses/123/header.jpg"
}
Response (batch):
{
"OK": true,
"Results": [
{ "UUID": "...", "Major": 42, "BusinessID": 123, "BusinessName": "..." },
{ "UUID": "...", "Major": 43, "BusinessID": null, "Error": "not_found" }
]
}
--->
<cftry>
<cfscript>
function apiAbort(obj) {
writeOutput(serializeJSON(obj));
abort;
}
function readJsonBody() {
raw = toString(getHttpRequestData().content);
if (isNull(raw) || len(trim(raw)) EQ 0) return {};
try {
parsed = deserializeJSON(raw);
} catch(any e) {
apiAbort({ OK=false, ERROR="bad_json", MESSAGE="Invalid JSON body" });
}
if (!isStruct(parsed)) return {};
return parsed;
}
function normStr(v) {
if (isNull(v)) return "";
return trim(toString(v));
}
function resolveSingleBusiness(uuid, major) {
var qBiz = queryExecute(
"SELECT b.ID, b.Name, b.BrandColor, b.HeaderImageExtension
FROM Businesses b
JOIN BeaconShards bs ON b.BeaconShardID = bs.ID
WHERE bs.UUID = ? AND b.BeaconMajor = ?
LIMIT 1",
[
{ value=uuid, cfsqltype="cf_sql_varchar" },
{ value=major, cfsqltype="cf_sql_smallint" }
],
{ datasource="payfrit" }
);
if (qBiz.recordCount EQ 0) {
return { Found=false, Error="not_found" };
}
var headerImageURL = "";
if (len(qBiz.HeaderImageExtension)) {
headerImageURL = "/uploads/headers/#qBiz.ID#.#qBiz.HeaderImageExtension#";
}
return {
Found = true,
BusinessID = qBiz.ID,
BusinessName = qBiz.Name,
BrandColor = qBiz.BrandColor,
HeaderImageURL = headerImageURL
};
}
data = readJsonBody();
// Check for batch request
if (structKeyExists(data, "Beacons") && isArray(data.Beacons)) {
results = [];
for (beacon in data.Beacons) {
uuid = normStr(structKeyExists(beacon, "UUID") ? beacon.UUID : "");
major = structKeyExists(beacon, "Major") && isNumeric(beacon.Major) ? int(beacon.Major) : 0;
if (len(uuid) EQ 0 || major LTE 0) {
arrayAppend(results, { UUID=uuid, Major=major, BusinessID=javaCast("null",""), Error="invalid_params" });
continue;
}
resolved = resolveSingleBusiness(uuid, major);
if (resolved.Found) {
arrayAppend(results, {
UUID = uuid,
Major = major,
BusinessID = resolved.BusinessID,
BusinessName = resolved.BusinessName,
BrandColor = resolved.BrandColor,
HeaderImageURL = resolved.HeaderImageURL
});
} else {
arrayAppend(results, { UUID=uuid, Major=major, BusinessID=javaCast("null",""), Error="not_found" });
}
}
writeOutput(serializeJSON({ OK=true, COUNT=arrayLen(results), Results=results }));
abort;
}
// Single request
uuid = normStr(structKeyExists(data, "UUID") ? data.UUID : "");
major = 0;
if (structKeyExists(data, "Major") && isNumeric(data.Major)) {
major = int(data.Major);
}
// Also check URL params
if (len(uuid) EQ 0 && structKeyExists(url, "UUID")) {
uuid = normStr(url.UUID);
}
if (major LTE 0 && structKeyExists(url, "Major") && isNumeric(url.Major)) {
major = int(url.Major);
}
if (len(uuid) EQ 0) {
apiAbort({ OK=false, ERROR="missing_uuid", MESSAGE="UUID is required" });
}
if (major LTE 0) {
apiAbort({ OK=false, ERROR="missing_major", MESSAGE="Major is required" });
}
resolved = resolveSingleBusiness(uuid, major);
if (!resolved.Found) {
apiAbort({ OK=false, ERROR="not_found", MESSAGE="No business found for this beacon" });
}
</cfscript>
<cfoutput>#serializeJSON({
OK = true,
BusinessID = resolved.BusinessID,
BusinessName = resolved.BusinessName,
BrandColor = resolved.BrandColor,
HeaderImageURL = resolved.HeaderImageURL
})#</cfoutput>
<cfcatch type="any">
<cfheader statuscode="200" statustext="OK">
<cfcontent type="application/json; charset=utf-8" reset="true">
<cfoutput>#serializeJSON({ OK=false, ERROR="server_error", MESSAGE=cfcatch.message, DETAIL=cfcatch.detail })#</cfoutput>
</cfcatch>
</cftry>