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>
This commit is contained in:
John Mizerek 2026-01-31 16:56:41 -08:00
parent 62c61c13cd
commit 6b66d2cef8
26 changed files with 222 additions and 227 deletions

View file

@ -37,36 +37,19 @@ try {
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 verified user with matching UUID and OTP (or magic OTP)
if (isMagicOTP) {
qUser = queryExecute("
SELECT ID, FirstName, LastName
FROM Users
WHERE UUID = :uuid
AND IsContactVerified = 1
LIMIT 1
", {
uuid: { value: userUUID, cfsqltype: "cf_sql_varchar" }
}, { datasource: "payfrit" });
} else {
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" });
}
// 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

View file

@ -40,36 +40,19 @@ try {
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" });
}
// Find unverified user with matching UUID and OTP
// Magic OTP only bypasses Twilio SMS (in sendOTP.cfm), not OTP verification
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

View file

@ -50,11 +50,11 @@ beaconId = int(data.BeaconID);
SELECT
sp.BusinessID,
sp.ID AS ServicePointID,
biz.Name AS BusinessName,
biz.ParentBusinessID,
biz.BusinessName AS BusinessName,
biz.BusinessParentBusinessID,
sp.Name AS ServicePointName
FROM ServicePoints sp
INNER JOIN Businesses biz ON biz.ID = sp.BusinessID
INNER JOIN Businesses biz ON biz.BusinessID = sp.BusinessID
WHERE sp.BeaconID = <cfqueryparam cfsqltype="cf_sql_integer" value="#beaconId#">
AND sp.IsActive = 1
@ -63,18 +63,18 @@ beaconId = int(data.BeaconID);
SELECT
lt.BusinessID,
0 AS ServicePointID,
biz.Name AS BusinessName,
biz.ParentBusinessID,
biz.BusinessName AS BusinessName,
biz.BusinessParentBusinessID,
'' AS ServicePointName
FROM lt_BeaconsID_BusinessesID lt
INNER JOIN Businesses biz ON biz.ID = lt.BusinessID
INNER JOIN Businesses biz ON biz.BusinessID = lt.BusinessID
WHERE lt.BeaconID = <cfqueryparam cfsqltype="cf_sql_integer" value="#beaconId#">
AND lt.BusinessID NOT IN (
SELECT sp2.BusinessID FROM ServicePoints sp2
WHERE sp2.BeaconID = <cfqueryparam cfsqltype="cf_sql_integer" value="#beaconId#"> AND sp2.IsActive = 1
)
ORDER BY ParentBusinessID IS NULL DESC, BusinessName ASC
ORDER BY BusinessParentBusinessID IS NULL DESC, BusinessName ASC
</cfquery>
<!--- Check if any assigned business is a parent (has children) --->
@ -83,7 +83,7 @@ beaconId = int(data.BeaconID);
<!--- Check if this business has children --->
<cfquery name="qChildren" datasource="payfrit">
SELECT COUNT(*) as cnt FROM Businesses
WHERE ParentBusinessID = <cfqueryparam cfsqltype="cf_sql_integer" value="#qAssignments.BusinessID#">
WHERE BusinessParentBusinessID = <cfqueryparam cfsqltype="cf_sql_integer" value="#qAssignments.BusinessID#">
</cfquery>
<cfif qChildren.cnt GT 0>
<cfset parentBusinessID = qAssignments.BusinessID>
@ -98,24 +98,24 @@ beaconId = int(data.BeaconID);
<cfif parentBusinessID GT 0>
<!--- Get parent business info for header image --->
<cfquery name="qParent" datasource="payfrit">
SELECT Name, HeaderImageExtension
SELECT BusinessName, BusinessHeaderImageExtension
FROM Businesses
WHERE ID = <cfqueryparam cfsqltype="cf_sql_integer" value="#parentBusinessID#">
WHERE BusinessID = <cfqueryparam cfsqltype="cf_sql_integer" value="#parentBusinessID#">
</cfquery>
<cfquery name="qChildBusinesses" datasource="payfrit">
SELECT
ID,
Name,
ParentBusinessID,
HeaderImageExtension
BusinessID,
BusinessName,
BusinessParentBusinessID,
BusinessHeaderImageExtension
FROM Businesses
WHERE ParentBusinessID = <cfqueryparam cfsqltype="cf_sql_integer" value="#parentBusinessID#">
WHERE BusinessParentBusinessID = <cfqueryparam cfsqltype="cf_sql_integer" value="#parentBusinessID#">
ORDER BY Name ASC
</cfquery>
<cfloop query="qChildBusinesses">
<cfset arrayAppend(businesses, {
"BusinessID" = qChildBusinesses.ID,
"Name" = qChildBusinesses.Name,
"BusinessID" = qChildBusinesses.BusinessID,
"Name" = qChildBusinesses.BusinessName,
"ServicePointID" = qAssignments.ServicePointID,
"ServicePointName" = qAssignments.ServicePointName,
"IsParent" = false,
@ -156,8 +156,8 @@ beaconId = int(data.BeaconID);
<cfif parentBusinessID GT 0>
<cfset response["PARENT"] = {
"BusinessID" = parentBusinessID,
"Name" = qParent.Name,
"HeaderImageExtension" = len(trim(qParent.HeaderImageExtension)) ? qParent.HeaderImageExtension : ""
"Name" = qParent.BusinessName,
"HeaderImageExtension" = len(trim(qParent.BusinessHeaderImageExtension)) ? qParent.BusinessHeaderImageExtension : ""
}>
</cfif>

View file

@ -35,17 +35,17 @@ try {
// Get business details
q = queryExecute("
SELECT
ID,
Name,
Phone,
StripeAccountID,
StripeOnboardingComplete,
IsHiring,
HeaderImageExtension,
TaxRate,
BrandColor
BusinessID,
BusinessName,
BusinessPhone,
BusinessStripeAccountID,
BusinessStripeOnboardingComplete,
BusinessIsHiring,
BusinessHeaderImageExtension,
BusinessTaxRate,
BusinessBrandColor
FROM Businesses
WHERE ID = :businessID
WHERE BusinessID = :businessID
", { businessID: businessID }, { datasource: "payfrit" });
if (q.recordCount == 0) {
@ -54,13 +54,13 @@ try {
abort;
}
// Get address from Addresses table (either linked via BusinessID or via Businesses.AddressID)
// Get address from Addresses table (either linked via AddressBusinessID or via Businesses.BusinessAddressID)
qAddr = queryExecute("
SELECT a.Line1, a.Line2, a.City, a.ZIPCode, s.Abbreviation
SELECT a.AddressLine1, a.AddressLine2, a.AddressCity, a.AddressZIPCode, s.Abbreviation
FROM Addresses a
LEFT JOIN tt_States s ON s.ID = a.StateID
WHERE (a.BusinessID = :businessID OR a.ID = (SELECT AddressID FROM Businesses WHERE ID = :businessID))
AND a.IsDeleted = 0
LEFT JOIN tt_States s ON s.ID = a.AddressStateID
WHERE (a.AddressBusinessID = :businessID OR a.AddressID = (SELECT BusinessAddressID FROM Businesses WHERE BusinessID = :businessID))
AND a.AddressIsDeleted = 0
LIMIT 1
", { businessID: businessID }, { datasource: "payfrit" });
@ -70,29 +70,29 @@ try {
addressState = "";
addressZip = "";
if (qAddr.recordCount > 0) {
addressLine1 = qAddr.Line1;
addressCity = qAddr.City;
addressLine1 = qAddr.AddressLine1;
addressCity = qAddr.AddressCity;
addressState = qAddr.Abbreviation;
addressZip = qAddr.ZIPCode;
addressZip = qAddr.AddressZIPCode;
addressParts = [];
if (len(qAddr.Line1)) arrayAppend(addressParts, qAddr.Line1);
if (len(qAddr.Line2)) arrayAppend(addressParts, qAddr.Line2);
if (len(qAddr.AddressLine1)) arrayAppend(addressParts, qAddr.AddressLine1);
if (len(qAddr.AddressLine2)) arrayAppend(addressParts, qAddr.AddressLine2);
cityStateZip = [];
if (len(qAddr.City)) arrayAppend(cityStateZip, qAddr.City);
if (len(qAddr.AddressCity)) arrayAppend(cityStateZip, qAddr.AddressCity);
if (len(qAddr.Abbreviation)) arrayAppend(cityStateZip, qAddr.Abbreviation);
if (len(qAddr.ZIPCode)) arrayAppend(cityStateZip, qAddr.ZIPCode);
if (len(qAddr.AddressZIPCode)) arrayAppend(cityStateZip, qAddr.AddressZIPCode);
if (arrayLen(cityStateZip) > 0) arrayAppend(addressParts, arrayToList(cityStateZip, ", "));
addressStr = arrayToList(addressParts, ", ");
}
// Get hours from Hours table
qHours = queryExecute("
SELECT h.DayID, h.OpenTime, h.ClosingTime, d.Abbrev
SELECT h.HoursDayID, h.HoursOpenTime, h.HoursClosingTime, d.Abbrev
FROM Hours h
JOIN tt_Days d ON d.ID = h.DayID
WHERE h.BusinessID = :businessID
ORDER BY h.DayID
JOIN tt_Days d ON d.ID = h.HoursDayID
WHERE h.HoursBusinessID = :businessID
ORDER BY h.HoursDayID
", { businessID: businessID }, { datasource: "payfrit" });
hoursArr = [];
@ -101,13 +101,12 @@ try {
for (h in qHours) {
arrayAppend(hoursArr, {
"day": h.Abbrev,
"dayId": h.DayID,
"open": timeFormat(h.OpenTime, "h:mm tt"),
"close": timeFormat(h.ClosingTime, "h:mm tt")
"dayId": h.HoursDayID,
"open": timeFormat(h.HoursOpenTime, "h:mm tt"),
"close": timeFormat(h.HoursClosingTime, "h:mm tt")
});
}
// Build readable hours string (group similar days)
// Mon-Thu: 11am-10pm, Fri-Sat: 11am-11pm, Sun: 11am-10pm
hourGroups = {};
for (h in hoursArr) {
key = h.open & "-" & h.close;
@ -125,28 +124,28 @@ try {
}
// Build business object
taxRate = isNumeric(q.TaxRate) ? q.TaxRate : 0;
taxRate = isNumeric(q.BusinessTaxRate) ? q.BusinessTaxRate : 0;
business = {
"BusinessID": q.ID,
"Name": q.Name,
"BusinessID": q.BusinessID,
"Name": q.BusinessName,
"Address": addressStr,
"Line1": addressLine1,
"City": addressCity,
"AddressState": addressState,
"AddressZip": addressZip,
"Phone": q.Phone,
"Phone": q.BusinessPhone,
"Hours": hoursStr,
"HoursDetail": hoursArr,
"StripeConnected": (len(q.StripeAccountID) > 0 && q.StripeOnboardingComplete == 1),
"IsHiring": q.IsHiring == 1,
"StripeConnected": (len(q.BusinessStripeAccountID) > 0 && q.BusinessStripeOnboardingComplete == 1),
"IsHiring": q.BusinessIsHiring == 1,
"TaxRate": taxRate,
"TaxRatePercent": taxRate * 100,
"BrandColor": len(q.BrandColor) ? (left(q.BrandColor, 1) == chr(35) ? q.BrandColor : chr(35) & q.BrandColor) : ""
"BrandColor": len(q.BusinessBrandColor) ? (left(q.BusinessBrandColor, 1) == chr(35) ? q.BusinessBrandColor : chr(35) & q.BusinessBrandColor) : ""
};
// Add header image URL if extension exists
if (len(q.HeaderImageExtension)) {
business["HeaderImageURL"] = "https://biz.payfrit.com/uploads/headers/" & q.ID & "." & q.HeaderImageExtension;
if (len(q.BusinessHeaderImageExtension)) {
business["HeaderImageURL"] = "https://biz.payfrit.com/uploads/headers/" & q.BusinessID & "." & q.BusinessHeaderImageExtension;
}
response["OK"] = true;

View file

@ -36,10 +36,10 @@ try {
q = queryExecute(
"
SELECT
ID,
Name
BusinessID,
BusinessName
FROM Businesses
WHERE ParentBusinessID = :parentId
WHERE BusinessParentBusinessID = :parentId
ORDER BY Name
",
{ parentId = { value = parentBusinessId, cfsqltype = "cf_sql_integer" } },
@ -49,8 +49,8 @@ try {
rows = [];
for (i = 1; i <= q.recordCount; i++) {
arrayAppend(rows, {
"BusinessID": q.ID[i],
"Name": q.Name[i]
"BusinessID": q.BusinessID[i],
"Name": q.BusinessName[i]
});
}

View file

@ -43,17 +43,17 @@ try {
q = queryExecute(
"
SELECT
b.ID,
b.Name,
a.Latitude,
a.Longitude,
a.City,
a.Line1
b.BusinessID,
b.BusinessName,
a.AddressLat,
a.AddressLng,
a.AddressCity,
a.AddressLine1
FROM Businesses b
LEFT JOIN Addresses a ON b.AddressID = a.ID
WHERE (b.IsDemo = 0 OR b.IsDemo IS NULL)
AND (b.IsPrivate = 0 OR b.IsPrivate IS NULL)
ORDER BY b.Name
LEFT JOIN Addresses a ON b.BusinessAddressID = a.AddressID
WHERE (b.BusinessIsDemo = 0 OR b.BusinessIsDemo IS NULL)
AND (b.BusinessIsPrivate = 0 OR b.BusinessIsPrivate IS NULL)
ORDER BY b.BusinessName
",
[],
{ datasource = "payfrit" }
@ -63,15 +63,15 @@ try {
rows = [];
for (i = 1; i <= q.recordCount; i++) {
row = {
"BusinessID": q.ID[i],
"Name": q.Name[i],
"City": isNull(q.City[i]) ? "" : q.City[i],
"Line1": isNull(q.Line1[i]) ? "" : q.Line1[i]
"BusinessID": q.BusinessID[i],
"Name": q.BusinessName[i],
"City": isNull(q.AddressCity[i]) ? "" : q.AddressCity[i],
"Line1": isNull(q.AddressLine1[i]) ? "" : q.AddressLine1[i]
};
// Calculate distance if we have both user location and business location
bizLat = isNull(q.Latitude[i]) ? 0 : val(q.Latitude[i]);
bizLng = isNull(q.Longitude[i]) ? 0 : val(q.Longitude[i]);
bizLat = isNull(q.AddressLat[i]) ? 0 : val(q.AddressLat[i]);
bizLng = isNull(q.AddressLng[i]) ? 0 : val(q.AddressLng[i]);
if (hasUserLocation AND bizLat != 0 AND bizLng != 0) {
row["DistanceMiles"] = haversineDistance(userLat, userLng, bizLat, bizLng);

View file

@ -45,7 +45,7 @@ if (isHiring == -1) {
try {
queryExecute("
UPDATE Businesses
SET IsHiring = ?
SET BusinessIsHiring = ?
WHERE BusinessID = ?
", [
{ value: isHiring, cfsqltype: "cf_sql_tinyint" },

View file

@ -48,7 +48,7 @@ try {
if (len(bizName)) {
if (isNumeric(taxRate)) {
queryExecute("
UPDATE Businesses SET Name = :name, Phone = :phone, TaxRate = :taxRate
UPDATE Businesses SET BusinessName = :name, BusinessPhone = :phone, BusinessTaxRate = :taxRate
WHERE BusinessID = :id
", {
name: bizName,
@ -58,7 +58,7 @@ try {
}, { datasource: "payfrit" });
} else {
queryExecute("
UPDATE Businesses SET Name = :name, Phone = :phone
UPDATE Businesses SET BusinessName = :name, BusinessPhone = :phone
WHERE BusinessID = :id
", {
name: bizName,
@ -90,8 +90,8 @@ try {
// Check if business has an address
qAddr = queryExecute("
SELECT ID FROM Addresses
WHERE BusinessID = :bizID AND UserID = 0 AND IsDeleted = 0
SELECT AddressID FROM Addresses
WHERE AddressBusinessID = :bizID AND AddressUserID = 0 AND AddressIsDeleted = 0
LIMIT 1
", { bizID: businessId }, { datasource: "payfrit" });
@ -99,11 +99,11 @@ try {
// Update existing address
queryExecute("
UPDATE Addresses SET
Line1 = :line1,
City = :city,
StateID = :stateID,
ZIPCode = :zip
WHERE ID = :addrID
AddressLine1 = :line1,
AddressCity = :city,
AddressStateID = :stateID,
AddressZIPCode = :zip
WHERE AddressID = :addrID
", {
line1: addressLine1,
city: city,
@ -114,7 +114,7 @@ try {
} else {
// Create new address
queryExecute("
INSERT INTO Addresses (Line1, City, StateID, ZIPCode, BusinessID, UserID, AddressTypeID, AddedOn)
INSERT INTO Addresses (AddressLine1, AddressCity, AddressStateID, AddressZIPCode, AddressBusinessID, AddressUserID, AddressTypeID, AddressAddedOn)
VALUES (:line1, :city, :stateID, :zip, :bizID, 0, 2, NOW())
", {
line1: addressLine1,

View file

@ -38,7 +38,7 @@ try {
// Delete all existing hours for this business
queryExecute("
DELETE FROM Hours WHERE BusinessID = :bizID
DELETE FROM Hours WHERE HoursBusinessID = :bizID
", { bizID: businessId }, { datasource: "payfrit" });
// Insert new hours
@ -55,7 +55,7 @@ try {
if (len(closeTime) == 5) closeTime = closeTime & ":00";
queryExecute("
INSERT INTO Hours (BusinessID, DayID, OpenTime, ClosingTime)
INSERT INTO Hours (HoursBusinessID, HoursDayID, HoursOpenTime, HoursClosingTime)
VALUES (:bizID, :dayID, :openTime, :closeTime)
", {
bizID: businessId,

View file

@ -22,14 +22,14 @@ try {
// Get counts before deletion
qOrderLineItems = queryExecute("SELECT COUNT(*) as cnt FROM OrderLineItems", {}, { datasource: "payfrit" });
qOrders = queryExecute("SELECT COUNT(*) as cnt FROM Orders", {}, { datasource: "payfrit" });
qAddresses = queryExecute("SELECT COUNT(*) as cnt FROM Addresses", {}, { datasource: "payfrit" });
qAddresses = queryExecute("SELECT COUNT(*) as cnt FROM Addresses WHERE AddressTypeID != 2", {}, { datasource: "payfrit" });
qTasks = queryExecute("SELECT COUNT(*) as cnt FROM Tasks", {}, { datasource: "payfrit" });
// Delete in correct order (foreign key constraints)
queryExecute("DELETE FROM Tasks", {}, { datasource: "payfrit" });
queryExecute("DELETE FROM OrderLineItems", {}, { datasource: "payfrit" });
queryExecute("DELETE FROM Orders", {}, { datasource: "payfrit" });
queryExecute("DELETE FROM Addresses", {}, { datasource: "payfrit" });
queryExecute("DELETE FROM Addresses WHERE AddressTypeID != 2", {}, { datasource: "payfrit" });
response = {
"OK": true,

View file

@ -442,10 +442,10 @@ try {
brandColor = "";
try {
qBrand = queryExecute("
SELECT BrandColor FROM Businesses WHERE ID = :bizId
SELECT BusinessBrandColor FROM Businesses WHERE BusinessID = :bizId
", { bizId: businessID }, { datasource: "payfrit" });
if (qBrand.recordCount > 0 && len(trim(qBrand.BrandColor))) {
brandColor = left(qBrand.BrandColor, 1) == chr(35) ? qBrand.BrandColor : chr(35) & qBrand.BrandColor;
if (qBrand.recordCount > 0 && len(trim(qBrand.BusinessBrandColor))) {
brandColor = left(qBrand.BusinessBrandColor, 1) == chr(35) ? qBrand.BusinessBrandColor : chr(35) & qBrand.BusinessBrandColor;
}
} catch (any e) {
// Column may not exist yet, ignore

View file

@ -533,12 +533,12 @@
<cfset brandColor = "">
<cftry>
<cfset qBrand = queryExecute(
"SELECT BrandColor FROM Businesses WHERE ID = ?",
"SELECT BusinessBrandColor FROM Businesses WHERE BusinessID = ?",
[ { value = BusinessID, cfsqltype = "cf_sql_integer" } ],
{ datasource = "payfrit" }
)>
<cfif qBrand.recordCount GT 0 AND len(trim(qBrand.BrandColor))>
<cfset brandColor = left(qBrand.BrandColor, 1) EQ chr(35) ? qBrand.BrandColor : chr(35) & qBrand.BrandColor>
<cfif qBrand.recordCount GT 0 AND len(trim(qBrand.BusinessBrandColor))>
<cfset brandColor = left(qBrand.BusinessBrandColor, 1) EQ chr(35) ? qBrand.BusinessBrandColor : chr(35) & qBrand.BusinessBrandColor>
</cfif>
<cfcatch>
<!--- Column may not exist yet, ignore --->

View file

@ -36,17 +36,17 @@ try {
o.StatusID,
o.ServicePointID,
o.AddedOn,
b.Name,
b.OrderTypes,
sp.Name,
b.BusinessName,
b.BusinessOrderTypes,
sp.ServicePointName,
(SELECT COUNT(*)
FROM OrderLineItems oli
WHERE oli.OrderID = o.ID
AND oli.IsDeleted = 0
AND oli.ParentOrderLineItemID = 0) as ItemCount
FROM Orders o
LEFT JOIN Businesses b ON b.ID = o.BusinessID
LEFT JOIN ServicePoints sp ON sp.ID = o.ServicePointID
LEFT JOIN Businesses b ON b.BusinessID = o.BusinessID
LEFT JOIN ServicePoints sp ON sp.ServicePointID = o.ServicePointID
WHERE o.UserID = :userId
AND o.StatusID = 0
ORDER BY o.ID DESC
@ -65,7 +65,7 @@ try {
}
// Parse business order types (e.g., "1,2,3" -> array of ints)
businessOrderTypes = len(trim(qCart.OrderTypes)) ? qCart.OrderTypes : "1,2,3";
businessOrderTypes = len(trim(qCart.BusinessOrderTypes)) ? qCart.BusinessOrderTypes : "1,2,3";
orderTypesArray = listToArray(businessOrderTypes, ",");
response["OK"] = true;
@ -74,12 +74,12 @@ try {
"OrderID": val(qCart.OrderID),
"UUID": qCart.UUID ?: "",
"BusinessID": val(qCart.BusinessID),
"Name": len(trim(qCart.Name)) ? qCart.Name : "",
"Name": len(trim(qCart.BusinessName)) ? qCart.BusinessName : "",
"OrderTypes": orderTypesArray,
"OrderTypeID": val(qCart.OrderTypeID),
"OrderTypeName": orderTypeName,
"ServicePointID": val(qCart.ServicePointID),
"Name": len(trim(qCart.Name)) ? qCart.Name : "",
"ServicePointName": len(trim(qCart.ServicePointName)) ? qCart.ServicePointName : "",
"ItemCount": val(qCart.ItemCount),
"AddedOn": dateTimeFormat(qCart.AddedOn, "yyyy-mm-dd HH:nn:ss")
};

View file

@ -67,12 +67,12 @@
<!--- Get business info for display in cart --->
<cfset qBusiness = queryExecute(
"SELECT DeliveryFlatFee, OrderTypes FROM Businesses WHERE ID = ? LIMIT 1",
"SELECT BusinessDeliveryFlatFee, BusinessOrderTypes FROM Businesses WHERE BusinessID = ? LIMIT 1",
[ { value = qOrder.BusinessID, cfsqltype = "cf_sql_integer" } ],
{ datasource = "payfrit" }
)>
<cfset businessDeliveryFee = qBusiness.recordCount GT 0 ? qBusiness.DeliveryFlatFee : 0>
<cfset businessOrderTypes = qBusiness.recordCount GT 0 AND len(trim(qBusiness.OrderTypes)) ? qBusiness.OrderTypes : "1,2,3">
<cfset businessDeliveryFee = qBusiness.recordCount GT 0 ? qBusiness.BusinessDeliveryFlatFee : 0>
<cfset businessOrderTypes = qBusiness.recordCount GT 0 AND len(trim(qBusiness.BusinessOrderTypes)) ? qBusiness.BusinessOrderTypes : "1,2,3">
<cfset businessOrderTypesArray = listToArray(businessOrderTypes, ",")>
<cfset qLI = queryExecute(

View file

@ -62,11 +62,11 @@
<!--- Get business delivery fee for display in cart --->
<cfset var qBusiness = queryExecute(
"SELECT DeliveryFlatFee FROM Businesses WHERE ID = ? LIMIT 1",
"SELECT BusinessDeliveryFlatFee FROM Businesses WHERE BusinessID = ? LIMIT 1",
[ { value = qOrder.BusinessID, cfsqltype = "cf_sql_integer" } ],
{ datasource = "payfrit" }
)>
<cfset var businessDeliveryFee = qBusiness.recordCount GT 0 ? qBusiness.DeliveryFlatFee : 0>
<cfset var businessDeliveryFee = qBusiness.recordCount GT 0 ? qBusiness.BusinessDeliveryFlatFee : 0>
<cfset out.Order = {
"OrderID": val(qOrder.ID),
@ -229,9 +229,9 @@
<!--- Create new cart order --->
<cfset qBiz = queryExecute(
"
SELECT DeliveryMultiplier, DeliveryFlatFee
SELECT BusinessDeliveryMultiplier, BusinessDeliveryFlatFee
FROM Businesses
WHERE ID = ?
WHERE BusinessID = ?
LIMIT 1
",
[ { value = BusinessID, cfsqltype = "cf_sql_integer" } ],
@ -249,7 +249,7 @@
OrderTypeID: 0=undecided, 1=dine-in, 2=takeaway, 3=delivery
Only delivery (3) should have delivery fee.
Note: For undecided orders (0), fee is set later via setOrderType.cfm --->
<cfset deliveryFee = (OrderTypeID EQ 3) ? qBiz.DeliveryFlatFee : 0>
<cfset deliveryFee = (OrderTypeID EQ 3) ? qBiz.BusinessDeliveryFlatFee : 0>
<!--- Generate new OrderID (table is not auto-inc in SSOT) --->
<cfset qNext = queryExecute(
@ -300,7 +300,7 @@
{ value = newUUID, cfsqltype = "cf_sql_varchar" },
{ value = UserID, cfsqltype = "cf_sql_integer" },
{ value = BusinessID, cfsqltype = "cf_sql_integer" },
{ value = qBiz.DeliveryMultiplier, cfsqltype = "cf_sql_decimal" },
{ value = qBiz.BusinessDeliveryMultiplier, cfsqltype = "cf_sql_decimal" },
{ value = OrderTypeID, cfsqltype = "cf_sql_integer" },
{ value = deliveryFee, cfsqltype = "cf_sql_decimal" },
{ value = nowDt, cfsqltype = "cf_sql_timestamp" },

View file

@ -72,10 +72,10 @@ try {
o.OrderTypeID,
o.AddedOn,
o.LastEditedOn,
b.Name,
b.BusinessName,
COALESCE(ot.Name, 'Unknown') as OrderTypeName
FROM Orders o
LEFT JOIN Businesses b ON b.ID = o.BusinessID
LEFT JOIN Businesses b ON b.BusinessID = o.BusinessID
LEFT JOIN tt_OrderTypes ot ON ot.ID = o.OrderTypeID
WHERE o.UserID = :userId
AND o.StatusID > 0
@ -144,7 +144,7 @@ try {
"OrderID": val(row.ID),
"UUID": row.UUID ?: "",
"BusinessID": val(row.BusinessID),
"Name": row.Name ?: "Unknown",
"Name": row.BusinessName ?: "Unknown",
"OrderTotal": round(val(total) * 100) / 100,
"StatusID": val(row.StatusID),
"StatusName": statusText,

View file

@ -191,9 +191,9 @@
o.LastEditedOn,
o.SubmittedOn,
o.ServicePointID,
COALESCE(b.DeliveryFlatFee, 0) AS BusinessDeliveryFee
COALESCE(b.BusinessDeliveryFlatFee, 0) AS BusinessDeliveryFee
FROM Orders o
LEFT JOIN Businesses b ON b.ID = o.BusinessID
LEFT JOIN Businesses b ON b.BusinessID = o.BusinessID
WHERE o.ID = ?
LIMIT 1
",

View file

@ -62,11 +62,11 @@
<!--- Get business delivery fee for display in cart --->
<cfset var qBusiness = queryExecute(
"SELECT DeliveryFlatFee FROM Businesses WHERE ID = ? LIMIT 1",
"SELECT BusinessDeliveryFlatFee FROM Businesses WHERE BusinessID = ? LIMIT 1",
[ { value = qOrder.BusinessID, cfsqltype = "cf_sql_integer" } ],
{ datasource = "payfrit" }
)>
<cfset var businessDeliveryFee = qBusiness.recordCount GT 0 ? qBusiness.DeliveryFlatFee : 0>
<cfset var businessDeliveryFee = qBusiness.recordCount GT 0 ? qBusiness.BusinessDeliveryFlatFee : 0>
<cfset out.Order = {
"OrderID": val(qOrder.ID),
@ -201,12 +201,12 @@
<cfset deliveryFee = 0>
<cfif OrderTypeID EQ 3>
<cfset qBiz = queryExecute(
"SELECT DeliveryFlatFee FROM Businesses WHERE ID = ? LIMIT 1",
"SELECT BusinessDeliveryFlatFee FROM Businesses WHERE BusinessID = ? LIMIT 1",
[ { value = qOrder.BusinessID, cfsqltype = "cf_sql_integer" } ],
{ datasource = "payfrit" }
)>
<cfif qBiz.recordCount GT 0>
<cfset deliveryFee = qBiz.DeliveryFlatFee>
<cfset deliveryFee = qBiz.BusinessDeliveryFlatFee>
</cfif>
</cfif>

View file

@ -82,7 +82,7 @@ try {
}
queryExecute("
INSERT INTO Addresses (Line1, City, StateID, ZIPCode, UserID, AddressTypeID, AddedOn)
INSERT INTO Addresses (AddressLine1, AddressCity, AddressStateID, AddressZIPCode, AddressUserID, AddressTypeID, AddressAddedOn)
VALUES (:line1, :city, :stateID, :zip, :userID, :typeID, NOW())
", {
line1: len(addressLine1) ? addressLine1 : "Address pending",
@ -103,7 +103,7 @@ try {
// Create new business with address link and phone
queryExecute("
INSERT INTO Businesses (Name, Phone, UserID, AddressID, BusinessDeliveryZipCodes, CommunityMealType, TaxRate, AddedOn)
INSERT INTO Businesses (BusinessName, BusinessPhone, BusinessUserID, BusinessAddressID, BusinessDeliveryZipCodes, BusinessCommunityMealType, BusinessTaxRate, BusinessAddedOn)
VALUES (:name, :phone, :userId, :addressId, :deliveryZips, :communityMealType, :taxRate, NOW())
", {
name: bizName,
@ -121,7 +121,7 @@ try {
// Update address with business ID link
queryExecute("
UPDATE Addresses SET BusinessID = :businessID WHERE ID = :addressID
UPDATE Addresses SET AddressBusinessID = :businessID WHERE AddressID = :addressID
", {
businessID: businessId,
addressID: addressId
@ -188,14 +188,14 @@ try {
} else {
// Verify existing business exists
qBiz = queryExecute("
SELECT ID, Name FROM Businesses WHERE ID = :id
SELECT BusinessID, BusinessName FROM Businesses WHERE BusinessID = :id
", { id: businessId }, { datasource: "payfrit" });
if (qBiz.recordCount == 0) {
throw(message="Business not found: " & businessId);
}
response.steps.append("Found existing business: " & qBiz.Name);
response.steps.append("Found existing business: " & qBiz.BusinessName);
}
// Build modifier template map

View file

@ -62,9 +62,9 @@ try {
// Get business Stripe account
qBusiness = queryExecute("
SELECT StripeAccountID, StripeOnboardingComplete, Name
SELECT BusinessStripeAccountID, BusinessStripeOnboardingComplete, BusinessName
FROM Businesses
WHERE ID = :businessID
WHERE BusinessID = :businessID
", { businessID: businessID }, { datasource: "payfrit" });
if (qBusiness.recordCount == 0) {
@ -75,18 +75,18 @@ try {
// Get order's delivery fee (if delivery order)
qOrder = queryExecute("
SELECT DeliveryFee, OrderTypeID
SELECT OrderDeliveryFee, OrderTypeID
FROM Orders
WHERE ID = :orderID
WHERE OrderID = :orderID
", { orderID: orderID }, { datasource: "payfrit" });
deliveryFee = 0;
if (qOrder.recordCount > 0 && qOrder.OrderTypeID == 3) {
deliveryFee = val(qOrder.DeliveryFee);
deliveryFee = val(qOrder.OrderDeliveryFee);
}
// For testing, allow orders even without Stripe Connect setup
hasStripeConnect = qBusiness.StripeOnboardingComplete == 1 && len(trim(qBusiness.StripeAccountID)) > 0;
hasStripeConnect = qBusiness.BusinessStripeOnboardingComplete == 1 && len(trim(qBusiness.BusinessStripeAccountID)) > 0;
// ============================================================
// FEE CALCULATION
@ -119,12 +119,12 @@ try {
if (hasStripeConnect) {
httpService.addParam(type="formfield", name="application_fee_amount", value=totalPlatformFeeCents);
httpService.addParam(type="formfield", name="transfer_data[destination]", value=qBusiness.StripeAccountID);
httpService.addParam(type="formfield", name="transfer_data[destination]", value=qBusiness.BusinessStripeAccountID);
}
httpService.addParam(type="formfield", name="metadata[order_id]", value=orderID);
httpService.addParam(type="formfield", name="metadata[business_id]", value=businessID);
httpService.addParam(type="formfield", name="description", value="Order ###orderID# at #qBusiness.Name#");
httpService.addParam(type="formfield", name="description", value="Order ###orderID# at #qBusiness.BusinessName#");
if (customerEmail != "") {
httpService.addParam(type="formfield", name="receipt_email", value=customerEmail);

View file

@ -32,9 +32,9 @@ try {
// Check if business already has a Stripe account
qBusiness = queryExecute("
SELECT StripeAccountID, Name, BusinessEmail
SELECT BusinessStripeAccountID, BusinessName
FROM Businesses
WHERE ID = :businessID
WHERE BusinessID = :businessID
", { businessID: businessID });
if (qBusiness.recordCount == 0) {
@ -43,7 +43,7 @@ try {
abort;
}
stripeAccountID = qBusiness.StripeAccountID;
stripeAccountID = qBusiness.BusinessStripeAccountID;
// Create new connected account if none exists
if (stripeAccountID == "" || isNull(stripeAccountID)) {
@ -55,10 +55,9 @@ try {
httpService.setPassword("");
httpService.addParam(type="formfield", name="type", value="express");
httpService.addParam(type="formfield", name="country", value="US");
httpService.addParam(type="formfield", name="email", value=qBusiness.BusinessEmail);
httpService.addParam(type="formfield", name="capabilities[card_payments][requested]", value="true");
httpService.addParam(type="formfield", name="capabilities[transfers][requested]", value="true");
httpService.addParam(type="formfield", name="business_profile[name]", value=qBusiness.Name);
httpService.addParam(type="formfield", name="business_profile[name]", value=qBusiness.BusinessName);
result = httpService.send().getPrefix();
accountData = deserializeJSON(result.fileContent);
@ -74,8 +73,8 @@ try {
// Save to database
queryExecute("
UPDATE Businesses
SET StripeAccountID = :stripeAccountID,
StripeOnboardingStarted = NOW()
SET BusinessStripeAccountID = :stripeAccountID,
BusinessStripeOnboardingStarted = NOW()
WHERE BusinessID = :businessID
", {
stripeAccountID: stripeAccountID,

View file

@ -23,9 +23,9 @@ try {
// Get business Stripe info
qBusiness = queryExecute("
SELECT StripeAccountID, StripeOnboardingComplete
SELECT BusinessStripeAccountID, BusinessStripeOnboardingComplete
FROM Businesses
WHERE ID = :businessID
WHERE BusinessID = :businessID
", { businessID: businessID });
if (qBusiness.recordCount == 0) {
@ -34,7 +34,7 @@ try {
abort;
}
stripeAccountID = qBusiness.StripeAccountID;
stripeAccountID = qBusiness.BusinessStripeAccountID;
if (stripeAccountID == "" || isNull(stripeAccountID)) {
response["OK"] = true;
@ -73,10 +73,10 @@ try {
accountStatus = "active";
// Mark as complete in database if not already
if (!qBusiness.StripeOnboardingComplete) {
if (!qBusiness.BusinessStripeOnboardingComplete) {
queryExecute("
UPDATE Businesses
SET StripeOnboardingComplete = 1
SET BusinessStripeOnboardingComplete = 1
WHERE BusinessID = :businessID
", { businessID: businessID });
}
@ -96,8 +96,8 @@ try {
} else {
// No Stripe key, just return what we have in DB
response["OK"] = true;
response["CONNECTED"] = qBusiness.StripeOnboardingComplete == 1;
response["ACCOUNT_STATUS"] = qBusiness.StripeOnboardingComplete == 1 ? "active" : "unknown";
response["CONNECTED"] = qBusiness.BusinessStripeOnboardingComplete == 1;
response["ACCOUNT_STATUS"] = qBusiness.BusinessStripeOnboardingComplete == 1 ? "active" : "unknown";
response["STRIPE_ACCOUNT_ID"] = stripeAccountID;
}

View file

@ -22,15 +22,43 @@ try {
// Webhook secret (set in Stripe dashboard)
webhookSecret = application.stripeWebhookSecret ?: "";
// For now, skip signature verification in development
// In production, uncomment and implement signature verification:
/*
if (webhookSecret != "" && sigHeader != "") {
// Verify webhook signature
// This requires computing HMAC-SHA256 and comparing
// See: https://stripe.com/docs/webhooks/signatures
// Verify webhook signature (Stripe HMAC-SHA256)
if (len(trim(webhookSecret)) > 0 && len(trim(sigHeader)) > 0) {
// Parse signature header: t=timestamp,v1=signature
sigParts = {};
for (part in listToArray(sigHeader, ",")) {
kv = listToArray(trim(part), "=");
if (arrayLen(kv) >= 2) sigParts[trim(kv[1])] = trim(kv[2]);
}
sigTimestamp = sigParts["t"] ?: "";
sigV1 = sigParts["v1"] ?: "";
if (len(sigTimestamp) == 0 || len(sigV1) == 0) {
response["ERROR"] = "invalid_signature";
writeOutput(serializeJSON(response));
abort;
}
// Check timestamp tolerance (5 minutes)
timestampAge = dateDiff("s", dateAdd("s", val(sigTimestamp), createDate(1970,1,1)), now());
if (abs(timestampAge) > 300) {
response["ERROR"] = "timestamp_expired";
writeOutput(serializeJSON(response));
abort;
}
// Compute expected signature: HMAC-SHA256(timestamp + "." + payload, secret)
signedPayload = sigTimestamp & "." & payload;
expectedSig = lcase(hmac(signedPayload, webhookSecret, "HmacSHA256"));
if (expectedSig != lcase(sigV1)) {
writeLog(file="stripe_webhooks", text="Signature mismatch! Expected=#left(expectedSig,16)#... Got=#left(sigV1,16)#...");
response["ERROR"] = "signature_mismatch";
writeOutput(serializeJSON(response));
abort;
}
}
*/
// Parse event
event = deserializeJSON(payload);
@ -101,6 +129,7 @@ try {
httpTransfer.addParam(type="formfield", name="metadata[task_id]", value=qLedger.TaskID);
httpTransfer.addParam(type="formfield", name="metadata[ledger_id]", value=qLedger.ID);
httpTransfer.addParam(type="formfield", name="metadata[activation_withheld_cents]", value=0);
httpTransfer.addParam(type="header", name="Idempotency-Key", value="transfer-ledger-#qLedger.ID#");
transferResult = httpTransfer.send().getPrefix();
transferData = deserializeJSON(transferResult.fileContent);

View file

@ -105,7 +105,7 @@
<cfset queryExecute("
UPDATE Tasks
SET CompletedOn = NOW()
WHERE TaskID = ?
WHERE ID = ?
", [ { value = TaskID, cfsqltype = "cf_sql_integer" } ], { datasource = "payfrit" })>
<!--- If task has an associated order, mark it as Delivered/Complete (status 5) --->
@ -126,7 +126,7 @@
<cfif workerUserID_for_payout GT 0>
<!--- Get PayCents from the task --->
<cfset qTaskPay = queryExecute("
SELECT PayCents FROM Tasks WHERE TaskID = ?
SELECT PayCents FROM Tasks WHERE ID = ?
", [ { value = TaskID, cfsqltype = "cf_sql_integer" } ], { datasource = "payfrit" })>
<cfset grossCents = val(qTaskPay.PayCents)>

View file

@ -59,8 +59,9 @@ try {
httpService.addParam(type="formfield", name="line_items[0][price_data][currency]", value="usd");
httpService.addParam(type="formfield", name="line_items[0][price_data][product_data][name]", value="Payfrit Activation");
httpService.addParam(type="formfield", name="line_items[0][quantity]", value="1");
httpService.addParam(type="formfield", name="success_url", value="https://biz.payfrit.com/works/activation?success=1");
httpService.addParam(type="formfield", name="cancel_url", value="https://biz.payfrit.com/works/activation?cancel=1");
baseUrl = application.isDevEnvironment ? "https://dev.payfrit.com" : "https://biz.payfrit.com";
httpService.addParam(type="formfield", name="success_url", value=baseUrl & "/works/stripe-return.cfm?status=success");
httpService.addParam(type="formfield", name="cancel_url", value=baseUrl & "/works/stripe-return.cfm?status=cancel");
httpService.addParam(type="formfield", name="metadata[user_id]", value=userID);
httpService.addParam(type="formfield", name="metadata[type]", value="activation_unlock");

View file

@ -53,8 +53,9 @@ try {
httpService.setPassword("");
httpService.addParam(type="formfield", name="account", value=accountID);
httpService.addParam(type="formfield", name="refresh_url", value="https://biz.payfrit.com/works/tier?refresh=1");
httpService.addParam(type="formfield", name="return_url", value="https://biz.payfrit.com/works/tier?return=1");
baseUrl = application.isDevEnvironment ? "https://dev.payfrit.com" : "https://biz.payfrit.com";
httpService.addParam(type="formfield", name="refresh_url", value=baseUrl & "/works/stripe-return.cfm?status=refresh");
httpService.addParam(type="formfield", name="return_url", value=baseUrl & "/works/stripe-return.cfm?status=complete");
httpService.addParam(type="formfield", name="type", value="account_onboarding");
result = httpService.send().getPrefix();