Market 26.150.0: лимит приложений Vibe+, который в коробке не включается
market 26.150.0 вышел в коробку 22 сентября 2026 года (по данным официального канала «Битрикс24 changelog») и почти целиком посвящён лимиту числа приложений Маркета на тарифе Vibe+. Механика завязана на модуль bitrix24, которого в коробке нет, поэтому лимит не включается и попап о превышении не появляется. Из всего релиза в коробке видна только кнопка «Установить» для пользователя без права на установку приложений. Раньше она была заблокирована, а теперь активна. По клику она открывает справку, если карточка получила код в ACCESS_HELPER_CODE (например, у подписочного приложения без подписки), а без кода клик ничего не делает.
Кнопка «Установить» для пользователя без прав стала активной
В JS-компоненте карточки (market/detail-component) кнопка для пользователя без права на установку (showNoAccessInstallButton) потеряла класс ui-btn-disabled и по клику вызывает installApp(). Этот метод стал async. Сначала он пробует показать попап лимита, потом открывает BX.UI.InfoHelper с кодом из ACCESS_HELPER_CODE, а окно установки открывает, только если выставлен showInstallButton. В коробке попап не покажется, поэтому клик открывает справку, только если ACCESS_HELPER_CODE не пуст, а иначе ничего не происходит.
Код справки выбирает bitrix:market.detail. Прежняя ветка выбора по REST_ACCESS и подписке пропускается, только когда сервис тарифов модуля rest возвращает лимит. В коробке getMarketApplicationLimit() отдаёт null, так что эта ветка работает как раньше. Новая ветка срабатывает, если ACCESS_HELPER_CODE пуст, приложение не установлено и Access::canInstallApp() вернул false. В этом случае подставляется код limit_free_rest_hold (константа MARKET_APPLICATION_LIMIT_HELPER_CODE).
Во всех редакциях поменялся аргумент Access::isAvailable() в той же карточке. Если Installed::getByCode() нашёл запись приложения (фильтр только по CODE, без ACTIVE), туда теперь передаётся её CLIENT_ID, а раньше всегда шёл APP_CODE. Если записи нет, по-прежнему передаётся код.
Как лимит работает там, где есть bitrix24
Сам лимит и ошибку MARKET_APPLICATION_LIMIT_EXCEEDED отдаёт модуль rest, тарифные ограничения Vibe+ мы разбирали в rest 26.650.0. В market к ним добавился интерфейс:
bitrix:market.detailиbitrix:market.listкладут проекцию лимита вarResult['VIBE_PLUS_APPLICATION_LIMIT']. В списке она считается до$template->getInfo(), а записывается после, потому что шаблон перезаписываетarResult.- Список установленных получил режим
vibe_plus_limit=Y(параметр запроса илиrequestParams). В нёмListTemplates\Installedфильтрует по'=INSTALLED'вместо'=ACTIVE'и по кодам изgetCountedApplicationCodes(), не применяет фильтр «есть обновления» и выставляетarResult['VIBE_PLUS_LIMIT_MODE']. Если засчитанных кодов нет, в фильтр уходит'=ID' => 0, и список заведомо пуст. installFinish()вmarket.install-storeсталasyncи на ответ сerror === 'MARKET_APPLICATION_LIMIT_EXCEEDED'показывает попап поresult.vibePlusApplicationLimit.market.detailиmarket.marketвmounted()планируют автопоказ попапа черезscheduleVibePlusApplicationLimitPopup(), аmarket.marketещё и после загрузки контента. Попап встаёт в очередьBannerDispatcher.normal, автопоказ срабатывает один раз на пользователя в каждом браузере, потому что отметка хранится вlocalStorageпод ключомmarket-vibe-plus-application-limit-popup:shown:<USER_ID>.
Попап бывает двух видов. transition показывается, пока лимит ещё не действует (state: unlimited), но приложений больше configuredLimit и задана дата окончания переходного периода. active показывается, когда лимит finite и превышен. Для показа в actions нужны сразу list и buy, а buy приходит только от провайдера модуля bitrix24. Это вторая причина, по которой в коробке попапа нет.
Спросите лимит у VibePlusApplicationLimit
Удалённых и изменённых публичных сигнатур нет, api.diff только добавляет final-класс Bitrix\Market\Application\VibePlusApplicationLimit. Он собирает данные о лимите для фронтенда. getProjection() возвращает массив из трёх блоков: limit (state, count, limit, configuredLimit, exceeded, installationBlocked), transition (active, endsAt) и actions. В actions бывают list (ссылка на список установленных, только для админа по InstalledListAccess::isAdmin) и buy (апселл). Адрес списка лежит в константе INSTALLED_LIST_URL, это /market/installed/?vibe_plus_limit=Y. getCountedApplicationCodes() возвращает коды приложений, которые засчитываются в лимит.
Конструктор принимает пять необязательных \Closure-провайдеров. По умолчанию данные берутся через ServiceLocator из Bitrix\Rest\Public\Service\VibePlusMarketApplicationLimitProvider и сервисов Bitrix\Bitrix24\Public\Service\VibePlus\*. Исключения класс перехватывает (catch (\Throwable)) и тогда отдаёт state: unknown или неактивный переходный период.
Если читаете проекцию сами, начинайте с state. В коробке он unknown, и такой код ничего не покажет:
<?php declare(strict_types=1);
namespace Vendor\Portal\Market;
use Bitrix\Main\Loader;
use Bitrix\Market\Application\VibePlusApplicationLimit;
final class ApplicationLimitNotice
{
public function build(): ?string
{
if (!Loader::includeModule('market'))
{
return null;
}
$limit = (new VibePlusApplicationLimit())->getProjection()['limit'] ?? [];
if (($limit['state'] ?? 'unknown') !== 'finite' || empty($limit['installationBlocked']))
{
return null;
}
return sprintf(
'Приложений установлено: %d из %d, новое из Маркета не встанет',
(int)($limit['count'] ?? 0),
(int)($limit['limit'] ?? 0),
);
}
}
На фронтенде с лимитом работает расширение market.vibe-plus-application-limit-popup (TypeScript и Vue 3). Оно экспортирует showVibePlusApplicationLimitPopup, showVibePlusApplicationLimitPopupPreview, scheduleVibePlusApplicationLimitPopup, isApplicationLimitExceeded и isApplicationInstallationBlocked. Ссылку на список установленных в режиме лимита возвращает MarketLinks.installedVibePlusLimitLink() из market.market-links.
Мелочи и находки
- Расширения
market.detail-component,market.detail,market.install-storeиmarket.marketпереведены сmain.polyfill.coreнаmain.core('skip_core' => false), и это касается всех редакций. - Расширение попапа тянет
ui.vue3,ui.banner-dispatcherи ещё восемь зависимостей, а в его папку положили картинкиcosmos4k.jpg(396 439 байт),promo-bg-int.jpg(196 420 байт) и дваlimit-mascot*.webp. - В TS-типе
ApplicationLimitActionTypeесть'trial', и под него заведён test-id...-trial-btn. PHP-класс такое действие не отдаёт, потому чтоgetUpsellAction()пропускает толькоcta === 'buy'. VibePlusApplicationLimitимпортирует класс из внутреннего пространства имён чужого модуля,Bitrix\Bitrix24\Internal\Service\VibePlus\Communication\ValueObject\Cta, радиCta::isTargetValid(). Проверкиmethod_exists()наgetConfiguredLimitиisTransitionPeriodActiveрассчитаны на разные версии зависимостей.- Ссылка «Подробнее» в попапе вызывает
BX.Helper.show('redirect=detail&code=26027119'). - Бандл
market-linksпересобран без транспиляции. Вdist/market-links.bundle.jsтеперь нативныеstatic-поля класса и??, а раньше были присваиванияMarketLinks.siteTemplateUri = ''после класса и временные переменные вроде_appItem$CODE.bundle.configрасширения в диффе не менялся.