Bitrix24
Локальные приложения
APIStend играет роль портала Bitrix24: выдаёт OAuth-токены, отдаёт BX24.js и открывает приложение во фрейме — с обработчиком на localhost.
На этой странице · 4
Боевой Bitrix24 обращается к приложению со своей стороны, поэтому требует адрес, доступный ему из внешней сети, по HTTPS и с разрешённым фреймингом. Отсюда обычный цикл разработки: поднять туннель, вписать выданный адрес в карточку приложения, переустановить, повторить после каждого перезапуска туннеля — адрес сменился, а в карточке остался прежний.
APIStend снимает ровно это. Портал живёт на той же машине, что и приложение,
фрейм открывает браузер разработчика, http://localhost:3210/handler — законный
адрес обработчика. Всё остальное совпадает с боевым: набор полей POST-запроса
во фрейм, конверт OAuth, коды ошибок, правило «до installFinish событий и виджетов нет».
Что делает портал
- Выдаёт токены. Сервер авторизации на
/oauth/authorize/и/oauth/token/отвечает тем же конвертом, что боевойoauth.bitrix.info. - Отдаёт BX24.js. Библиотека лежит на
/api/v1/— том же пути, что у боевого//api.bitrix24.tech/api/v1/, отличается только хост. - Открывает приложение во фрейме. Раздел «Локальные приложения» кабинета показывает демонстрационный портал: левое меню, карточка сделки, слайдер. Приложение открывается там же, где откроется в бою.
- Отвечает на методы состояния.
app.info,scope,placement.bind,placement.get,event.bind,event.getотвечают про это конкретное приложение, а не примером из спецификации.
Всё, чего нет в этом списке, уходит в общий движок моков: crm.deal.list, вызванный
с токеном приложения, вернёт тот же ответ, что и с ключом песочницы.
Одна строка подмены
- <script src="//api.bitrix24.tech/api/v1/"></script>
+ <script src="http://localhost:8080/api/v1/"></script>
Хост берётся из APISTEND_PUBLIC_ORIGIN; всё, что портал сообщает приложению —
DOMAIN, client_endpoint, server_endpoint, адрес библиотеки — собирается из него.
Приложению, собранному правильно, подменять нечего
SERVER_ENDPOINT приходит в теле POST при открытии фрейма, client_endpoint — в ответе
сервера авторизации, хост библиотеки складывается из DOMAIN и PROTOCOL. Приложение,
которое читает эти поля, а не зашивает адреса, работает и здесь, и в бою без правок.
REST принимает токен там же, где боевой портал
Метод вызывается по /rest/<метод>, токен кладётся в поле auth тела запроса.
Суффикс .json и префикс входящего вебхука /rest/{user_id}/{code}/ отрезаются
до маршрутизации, поэтому оба варианта пути работают.
curl -s -X POST http://localhost:8080/rest/app.info \
-d "auth=$ACCESS_TOKEN"
{
"result": {
"ID": 565,
"CODE": "docprobe",
"VERSION": 1,
"STATUS": "L",
"INSTALLED": true,
"PAYMENT_EXPIRED": "N",
"DAYS": null,
"LANGUAGE_ID": "ru",
"LICENSE": "ru_demo",
"LICENSE_TYPE": "demo",
"LICENSE_FAMILY": "demo"
},
"time": { "start": 1788948933.472, "finish": 1788948933.473, "duration": 0.001, "processing": 0, "date_start": "2026-09-09T10:15:33+00:00", "date_finish": "2026-09-09T10:15:33+00:00", "operating": 0 }
}
Ответ в контексте приложения помечен заголовками X-APIStend-Source: app-context
и X-APIStend-App с client_id приложения — по ним видно, что отвечал портал,
а не движок моков. Обычные методы каталога отвечают как всегда,
с X-APIStend-Source: example.
Чего APIStend не воспроизводит
HTTPS. У localhost его нет, и портал честно сообщает PROTOCOL=0. Приложение,
которое зашило https://, здесь сломается — и это полезная поломка: она обнаружится
на несколько шагов раньше.
Проверку фрейминга. Её делает браузер, а не портал. Если приложение отдаёт
X-Frame-Options: SAMEORIGIN или CSP без нужного frame-ancestors, на месте виджета
останется пустая область, и портал об этом не узнает — ровно как в бою.
Секретность client_secret. В карточке приложения он показан открыто и копируется
кнопкой: это демонстрационный портал, смысл экрана в том, чтобы секрет было видно.
Полный перечень точек встраивания. Боевой список зависит от установленных модулей.
APIStend знает подмножество — те точки, у которых есть видимое место в демонстрационном
портале. На код вне списка placement.bind отвечает ERROR_PLACEMENT_NOT_FOUND,
как и боевой портал.
Связность данных между list и get. Ответы каталога отдаются ярусом «пример
из спецификации», поэтому crm.deal.get вернёт сделку из документации, а не ту, что
пришла в crm.deal.list. Это свойство всего каталога, а не раздела приложений;
контекст вызова при этом настоящий — ID в PLACEMENT_OPTIONS соответствует
открытой карточке.
Экран входа и согласия. /oauth/authorize/ выдаёт код сразу, без формы логина:
портал демонстрационный, и выбирать, кем войти, нужно в его шапке, а не в OAuth.