false, 'ERROR' => 'missing_params', 'MESSAGE' => 'GrantID or InviteToken is required.']); } if ($userId <= 0) { apiAbort(['OK' => false, 'ERROR' => 'not_authenticated']); } // Load grant by ID or token if ($grantID > 0) { $qGrant = queryOne( "SELECT g.*, b.UserID AS GuestOwnerUserID FROM ServicePointGrants g JOIN Businesses b ON b.ID = g.GuestBusinessID WHERE g.ID = ? LIMIT 1", [$grantID] ); } else { $qGrant = queryOne( "SELECT g.*, b.UserID AS GuestOwnerUserID FROM ServicePointGrants g JOIN Businesses b ON b.ID = g.GuestBusinessID WHERE g.InviteToken = ? LIMIT 1", [$inviteToken] ); } if (!$qGrant) { apiAbort(['OK' => false, 'ERROR' => 'not_found', 'MESSAGE' => 'Grant not found.']); } if ((int) $qGrant['GuestOwnerUserID'] !== $userId) { apiAbort(['OK' => false, 'ERROR' => 'not_guest_owner', 'MESSAGE' => 'Only the guest business owner can accept this invite.']); } $statusID = (int) $qGrant['StatusID']; if ($statusID !== 0) { $statusLabels = [1 => 'already active', 2 => 'declined', 3 => 'revoked']; $label = $statusLabels[$statusID] ?? 'not pending'; apiAbort(['OK' => false, 'ERROR' => 'bad_state', 'MESSAGE' => "This grant is $label and cannot be accepted."]); } // Accept: activate grant queryTimed( "UPDATE ServicePointGrants SET StatusID = 1, AcceptedOn = NOW(), AcceptedByUserID = ?, InviteToken = NULL WHERE ID = ?", [$userId, $qGrant['ID']] ); recordGrantHistory( (int) $qGrant['ID'], 'accepted', $userId, (int) $qGrant['GuestBusinessID'], ['StatusID' => 0], ['StatusID' => 1] ); jsonResponse([ 'OK' => true, 'GrantID' => (int) $qGrant['ID'], 'MESSAGE' => 'Grant accepted. Service point access is now active.', ]);