Complete port of all 163 API endpoints from Lucee/CFML to PHP 8.3. Shared helpers in api/helpers.php (DB, auth, request/response, security). PDO prepared statements throughout. Same JSON response shapes as CFML.
139 lines
5.5 KiB
PHP
139 lines
5.5 KiB
PHP
<?php
|
|
require_once __DIR__ . '/../helpers.php';
|
|
require_once __DIR__ . '/_cartPayload.php';
|
|
require_once __DIR__ . '/../grants/_grantUtils.php';
|
|
runAuth();
|
|
|
|
/**
|
|
* Get or Create Cart
|
|
* POST: { BusinessID: int, UserID: int, ServicePointID?: int, OrderTypeID?: int }
|
|
* Always creates a fresh cart.
|
|
*/
|
|
|
|
$data = readJsonBody();
|
|
$BusinessID = (int) ($data['BusinessID'] ?? 0);
|
|
$ServicePointID = (int) ($data['ServicePointID'] ?? 0);
|
|
$OrderTypeID = (int) ($data['OrderTypeID'] ?? 0);
|
|
$UserID = (int) ($data['UserID'] ?? 0);
|
|
|
|
if ($BusinessID <= 0 || $UserID <= 0) {
|
|
apiAbort(['OK' => false, 'ERROR' => 'missing_params', 'MESSAGE' => 'BusinessID and UserID are required.']);
|
|
}
|
|
|
|
if ($OrderTypeID === 1 && $ServicePointID <= 0) {
|
|
apiAbort(['OK' => false, 'ERROR' => 'missing_service_point', 'MESSAGE' => 'ServicePointID is required for dine-in. Please scan a table beacon.']);
|
|
}
|
|
|
|
if ($OrderTypeID < 0 || $OrderTypeID > 3) {
|
|
apiAbort(['OK' => false, 'ERROR' => 'invalid_order_type', 'MESSAGE' => 'OrderTypeID must be 0-3 (0=undecided, 1=dine-in, 2=takeaway, 3=delivery).']);
|
|
}
|
|
|
|
try {
|
|
$qBiz = queryOne("SELECT DeliveryFlatFee FROM Businesses WHERE ID = ? LIMIT 1", [$BusinessID]);
|
|
if (!$qBiz) {
|
|
apiAbort(['OK' => false, 'ERROR' => 'bad_business', 'MESSAGE' => 'Business not found']);
|
|
}
|
|
|
|
// SP-SM: Resolve grant if ServicePoint doesn't belong to this business
|
|
$grantID = 0;
|
|
$grantOwnerBusinessID = 0;
|
|
$grantEconomicsType = '';
|
|
$grantEconomicsValue = 0;
|
|
|
|
if ($ServicePointID > 0) {
|
|
$qSPOwner = queryOne("SELECT BusinessID FROM ServicePoints WHERE ID = ? LIMIT 1", [$ServicePointID]);
|
|
if ($qSPOwner && (int) $qSPOwner['BusinessID'] !== $BusinessID) {
|
|
// SP belongs to another business
|
|
$qParent = queryOne("SELECT ParentBusinessID FROM Businesses WHERE ID = ? LIMIT 1", [$BusinessID]);
|
|
$isChildOfSPOwner = $qParent && (int) ($qParent['ParentBusinessID'] ?? 0) === (int) $qSPOwner['BusinessID'];
|
|
|
|
if (!$isChildOfSPOwner) {
|
|
// Check for active grant
|
|
$qGrant = queryOne(
|
|
"SELECT ID, OwnerBusinessID, EconomicsType, EconomicsValue, EligibilityScope, TimePolicyType, TimePolicyData
|
|
FROM ServicePointGrants
|
|
WHERE GuestBusinessID = ? AND ServicePointID = ? AND StatusID = 1
|
|
LIMIT 1",
|
|
[$BusinessID, $ServicePointID]
|
|
);
|
|
if (!$qGrant) {
|
|
apiAbort(['OK' => false, 'ERROR' => 'sp_not_accessible', 'MESSAGE' => 'Service point is not accessible to your business.']);
|
|
}
|
|
if (!isGrantTimeActive($qGrant['TimePolicyType'], $qGrant['TimePolicyData'])) {
|
|
apiAbort(['OK' => false, 'ERROR' => 'grant_time_inactive', 'MESSAGE' => 'Service point access is not available at this time.']);
|
|
}
|
|
if (!checkGrantEligibility($qGrant['EligibilityScope'], $UserID, (int) $qGrant['OwnerBusinessID'], $BusinessID)) {
|
|
apiAbort(['OK' => false, 'ERROR' => 'grant_eligibility_failed', 'MESSAGE' => 'You are not eligible to order at this service point.']);
|
|
}
|
|
$grantID = (int) $qGrant['ID'];
|
|
$grantOwnerBusinessID = (int) $qGrant['OwnerBusinessID'];
|
|
$grantEconomicsType = $qGrant['EconomicsType'];
|
|
$grantEconomicsValue = (float) $qGrant['EconomicsValue'];
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check if user is on an active tab at this business
|
|
$tabID = 0;
|
|
$qUserTab = queryOne("
|
|
SELECT t.ID
|
|
FROM TabMembers tm
|
|
JOIN Tabs t ON t.ID = tm.TabID
|
|
WHERE tm.UserID = ? AND tm.StatusID = 1 AND t.BusinessID = ? AND t.StatusID = 1
|
|
LIMIT 1
|
|
", [$UserID, $BusinessID]);
|
|
if ($qUserTab) {
|
|
$tabID = (int) $qUserTab['ID'];
|
|
}
|
|
|
|
$nowISO = gmdate('Y-m-d H:i:s');
|
|
$newUUID = generateUUID();
|
|
$deliveryFee = ($OrderTypeID === 3) ? (float) $qBiz['DeliveryFlatFee'] : 0;
|
|
|
|
// Generate new OrderID (table is not auto-inc)
|
|
$qNext = queryOne("SELECT IFNULL(MAX(ID),0) + 1 AS NextID FROM Orders", []);
|
|
$NewOrderID = (int) $qNext['NextID'];
|
|
|
|
queryTimed("
|
|
INSERT INTO Orders (
|
|
ID, UUID, UserID, BusinessID, DeliveryMultiplier, OrderTypeID, DeliveryFee,
|
|
StatusID, AddressID, PaymentID, Remarks, AddedOn, LastEditedOn, SubmittedOn,
|
|
ServicePointID, GrantID, GrantOwnerBusinessID, GrantEconomicsType,
|
|
GrantEconomicsValue, TabID
|
|
) VALUES (
|
|
?, ?, ?, ?, 1.0, ?, ?,
|
|
0, NULL, NULL, NULL, ?, ?, NULL,
|
|
?, ?, ?, ?, ?, ?
|
|
)
|
|
", [
|
|
$NewOrderID,
|
|
$newUUID,
|
|
$UserID,
|
|
$BusinessID,
|
|
$OrderTypeID,
|
|
$deliveryFee,
|
|
$nowISO,
|
|
$nowISO,
|
|
$ServicePointID,
|
|
$grantID > 0 ? $grantID : null,
|
|
$grantOwnerBusinessID > 0 ? $grantOwnerBusinessID : null,
|
|
strlen($grantEconomicsType) > 0 ? $grantEconomicsType : null,
|
|
($grantEconomicsType !== '' && $grantEconomicsType !== 'none') ? $grantEconomicsValue : null,
|
|
$tabID > 0 ? $tabID : null,
|
|
]);
|
|
|
|
// Get the final ID
|
|
$qLatest = queryOne("SELECT MAX(ID) AS NextID FROM Orders", []);
|
|
$FinalOrderID = (int) $qLatest['NextID'];
|
|
|
|
$payload = loadCartPayload($FinalOrderID);
|
|
jsonResponse($payload);
|
|
|
|
} catch (Exception $e) {
|
|
jsonResponse([
|
|
'OK' => false,
|
|
'ERROR' => 'server_error: ' . $e->getMessage(),
|
|
'MESSAGE' => 'DB error creating cart',
|
|
'DETAIL' => $e->getMessage(),
|
|
]);
|
|
}
|