false, 'ERROR' => 'missing_hardware_id', 'MESSAGE' => 'HardwareId is required']); $beaconUUID = trim($data['UUID'] ?? ''); if ($beaconUUID === '') apiAbort(['OK' => false, 'ERROR' => 'missing_uuid', 'MESSAGE' => 'UUID is required']); $major = (int) ($data['Major'] ?? 0); $minor = (int) ($data['Minor'] ?? 0); $rssi = isset($data['RSSI']) && is_numeric($data['RSSI']) ? (int) $data['RSSI'] : null; $seenAt = date('Y-m-d H:i:s'); if (!empty($data['SeenAt'])) { try { $seenAt = (new DateTime($data['SeenAt']))->format('Y-m-d H:i:s'); } catch (Exception $e) {} } try { $qHW = queryOne(" SELECT ID, BusinessID, ServicePointID, ShardUUID, Major, Minor, Status FROM BeaconHardware WHERE HardwareId = ? LIMIT 1 ", [$hardwareId]); if (!$qHW) { apiAbort(['OK' => false, 'ERROR' => 'hardware_not_found', 'MESSAGE' => 'Beacon hardware not registered']); } if (strcasecmp($qHW['ShardUUID'], $beaconUUID) !== 0) { apiAbort(['OK' => false, 'ERROR' => 'uuid_mismatch', 'MESSAGE' => 'Beacon is broadcasting wrong UUID', 'ExpectedUUID' => $qHW['ShardUUID'], 'BroadcastUUID' => $beaconUUID]); } if ((int) $qHW['Major'] !== $major) { apiAbort(['OK' => false, 'ERROR' => 'major_mismatch', 'MESSAGE' => 'Beacon is broadcasting wrong Major', 'ExpectedMajor' => (int) $qHW['Major'], 'BroadcastMajor' => $major]); } if ((int) $qHW['Minor'] !== $minor) { apiAbort(['OK' => false, 'ERROR' => 'minor_mismatch', 'MESSAGE' => 'Beacon is broadcasting wrong Minor', 'ExpectedMinor' => (int) $qHW['Minor'], 'BroadcastMinor' => $minor]); } $setClauses = ["Status = 'verified'", "VerifiedAt = ?", "LastSeenAt = ?", "UpdatedAt = NOW()"]; $params = [$seenAt, $seenAt]; if ($rssi !== null) { $setClauses[] = "LastRssi = ?"; $params[] = $rssi; } $params[] = $hardwareId; queryTimed("UPDATE BeaconHardware SET " . implode(', ', $setClauses) . " WHERE HardwareId = ?", $params); jsonResponse([ 'OK' => true, 'BeaconHardwareID' => (int) $qHW['ID'], 'HardwareId' => $hardwareId, 'BusinessID' => (int) $qHW['BusinessID'], 'ServicePointID' => (int) $qHW['ServicePointID'], 'Status' => 'verified', 'VerifiedAt' => (new DateTime($seenAt))->format('Y-m-d\TH:i:s\Z'), ]); } catch (Exception $e) { jsonResponse(['OK' => false, 'ERROR' => 'server_error', 'MESSAGE' => $e->getMessage()]); }