Расширения
Приложения Bazex могут расширять платформу через четыре типа расширений: Блоки (визуальные компоненты в конструкторе сайта), Вставки (скрипты и виджеты, встраиваемые в витрину), Хуки (синхронные точки расширения для логики оформления заказа) и Страницы администратора (полностраничные iframe в боковой панели админки).
Блоки
Блоки — это визуальные компоненты на базе iframe, которые продавцы могут перетаскивать на страницы сайта с помощью конструктора Bazex. Каждый блок имеет собственную панель настроек, отображаемую на основе определённой вами JSON Schema.
Формат манифеста
Определите блоки в массиве blocks манифеста вашего приложения:
{
"blocks": [
{
"blockType": "countdown_timer",
"name": "Countdown Timer",
"description": "Displays a countdown to a target date",
"iconName": "Clock",
"category": "marketing",
"renderUrl": "https://your-app.com/blocks/countdown",
"settingsSchema": {
"type": "object",
"properties": {
"targetDate": {
"type": "string",
"format": "date-time",
"title": "Target Date"
},
"title": {
"type": "string",
"title": "Heading Text"
},
"backgroundColor": {
"type": "string",
"format": "color",
"title": "Background Color"
}
},
"required": ["targetDate"]
},
"defaultConfig": {
"title": "Coming Soon!",
"backgroundColor": "#1a1a2e"
}
}
]
}Поля определения блока
| Name | Type | Description |
|---|---|---|
| blockType | string | Уникальный идентификатор внутри приложения (например, "countdown_timer") |
| name | string | Отображаемое название в палитре блоков |
| description | string | Краткое описание назначения блока |
| iconName | string | Имя иконки Lucide для палитры блоков (например, "Clock", "BarChart") |
| category | string | Категория для группировки в палитре |
| renderUrl | string | URL, обслуживающий содержимое iframe для этого блока |
| settingsSchema | object | JSON Schema, определяющая настройки блока (отображается в боковой панели) |
| defaultConfig | object | Значения по умолчанию при первом добавлении блока |
Схема настроек
settingsSchema использует формат JSON Schema. Админ-панель конструктора сайта автоматически генерирует поля формы на основе схемы. Поддерживаемые специальные форматы:
| Формат | Отображается как |
|---|---|
| color | Палитра цветов |
| date-time | Выбор даты и времени |
| uri | Текстовое поле для URL |
| (нет) | Стандартный элемент ввода по type (string → текст, number → число, boolean → переключатель) |
Коммуникация с iframe
Используйте клиент BazexBlock из SDK для взаимодействия с конструктором сайта:
import { BazexBlock } from '@bazex/app-sdk';
// Wait for initialization — receives settings and blockId from the site builder
const { settings, blockId } = await BazexBlock.ready();
// Render your UI with the current settings
renderCountdown(settings.targetDate, settings.title, settings.backgroundColor);
// Listen for real-time settings updates (user changes in sidebar panel)
BazexBlock.onSettingsUpdate((newSettings) => {
renderCountdown(newSettings.targetDate, newSettings.title, newSettings.backgroundColor);
});
// Request iframe resize if your content height changes (max: 400px)
BazexBlock.resize(300);Коммуникация использует window.postMessage со следующим протоколом:
| Сообщение | Направление | Назначение |
|---|---|---|
| BAZEX_BLOCK_READY | iframe → parent | Блок загружен, запрашивает инициализацию |
| BAZEX_BLOCK_INIT | parent → iframe | Начальные настройки и blockId переданы блоку |
| BAZEX_BLOCK_UPDATE | parent → iframe | Пользователь изменил настройки в боковой панели |
| BAZEX_BLOCK_RESIZE | iframe → parent | Запрос на изменение высоты iframe |
CORS
renderUrl вашего блока должен отдавать HTML, который можно загрузить в iframe. Убедитесь, что ваш сервер не отправляет заголовок X-Frame-Options: DENY. Конструктор сайта загружает iframe блоков с вашего домена.Вставки
Вставки внедряются на каждую страницу витрины продавца. Существует два вида:
GLOBAL_SCRIPT
JavaScript-файл, встраиваемый на страницу. Используйте для пикселей аналитики, чат-виджетов, загружаемых через JS, трекинговых скриптов или любого кода, работающего глобально.
Позиции: head или body_end
FLOATING_WIDGET
iframe-виджет, отображаемый в фиксированной позиции на странице. Используйте для чат-кнопок, кнопок обратной связи или любого постоянного плавающего элемента интерфейса.
Позиции: bottom_right или bottom_left
Формат манифеста
{
"embeds": [
{
"embedType": "analytics_pixel",
"name": "Analytics Pixel",
"description": "Tracks page views and conversions",
"kind": "GLOBAL_SCRIPT",
"position": "head",
"scriptUrl": "https://your-app.com/scripts/pixel.js",
"settingsSchema": {
"type": "object",
"properties": {
"trackingId": {
"type": "string",
"title": "Tracking ID"
}
},
"required": ["trackingId"]
}
},
{
"embedType": "chat_widget",
"name": "Live Chat",
"description": "Floating chat bubble for customer support",
"kind": "FLOATING_WIDGET",
"position": "bottom_right",
"renderUrl": "https://your-app.com/widgets/chat",
"settingsSchema": {
"type": "object",
"properties": {
"primaryColor": {
"type": "string",
"format": "color",
"title": "Bubble Color"
},
"greeting": {
"type": "string",
"title": "Welcome Message"
}
}
},
"defaultConfig": {
"primaryColor": "#4f46e5",
"greeting": "How can we help?"
}
}
]
}Поля определения вставки
| Name | Type | Description |
|---|---|---|
| embedType | string | Уникальный идентификатор внутри приложения (например, "analytics_pixel") |
| name | string | Отображаемое название |
| description | string | Краткое описание назначения вставки |
| kind | string | "GLOBAL_SCRIPT" или "FLOATING_WIDGET" |
| position | string | "head", "body_end", "bottom_right" или "bottom_left" |
| scriptUrl | string | URL JS-файла для внедрения (обязателен для GLOBAL_SCRIPT) |
| renderUrl | string | URL iframe для отображения (обязателен для FLOATING_WIDGET) |
| settingsSchema | object | JSON Schema для настроек вставки (настраивается в админ-панели) |
| defaultConfig | object | Значения настроек по умолчанию |
Включение/отключение
Хуки
Хуки — это синхронные точки расширения, которые позволяют вашему приложению участвовать в оформлении заказа и обработке заказов. Когда срабатывает точка расширения, Bazex отправляет HTTP POST-запрос вашему приложению и ожидает ответа. Данные ответа встраиваются в процесс платформы.
Тайм-аут
Формат манифеста
{
"hooks": [
{
"hookPoint": "checkout.payment_methods",
"url": "/hooks/payment-methods",
"timeout": 5000,
"priority": 100
},
{
"hookPoint": "checkout.shipping_rates",
"url": "/hooks/shipping",
"timeout": 5000,
"priority": 50
},
{
"hookPoint": "order.validate",
"url": "/hooks/validate-order"
}
]
}Поля определения хука
| Name | Type | Description |
|---|---|---|
| hookPoint | string | Одна из 5 поддерживаемых точек расширения (см. ниже) |
| url | string | Относительный путь, добавляемый к webhook URL вашего приложения |
| timeout | number | Тайм-аут в миллисекундах (по умолчанию: 5000) |
| priority | number | Порядок выполнения — меньшие значения выполняются первыми (по умолчанию: 100) |
Формат запроса хука
Каждый вызов хука — это POST-запрос со следующей структурой:
{
"hookPoint": "checkout.payment_methods",
"businessId": "clx1business456",
"timestamp": "2026-02-20T10:30:45.123Z",
"data": { ... }
}Запрос включает те же заголовки подписи, что и вебхуки:X-Webhook-Signature иX-Webhook-Timestamp. Всегда проверяйте подпись перед обработкой.
Справочник точек расширения
checkout.payment_methods
Приложение предоставляет доступные способы оплаты для текущего заказа
Входные данные:
interface PaymentMethodsPayload {
businessId: string;
}Ожидаемый ответ:
interface PaymentMethodsResponse {
methods: Array<{
id: string; // Unique method ID (e.g. "card", "crypto")
name: string; // Display name ("Credit Card")
description?: string;
icon?: string; // URL to icon image
}>;
}Пример:
{
"methods": [
{ "id": "card", "name": "Credit Card", "description": "Visa/Mastercard" },
{ "id": "crypto", "name": "Crypto", "description": "BTC, ETH, USDT" }
]
}checkout.create_payment
Приложение создаёт платёжную сессию для выбранного способа оплаты
Входные данные:
interface CreatePaymentPayload {
orderId: string;
amount: string; // Amount in minor units
currency: string; // e.g. "RUB"
paymentMethodId: string; // ID from payment_methods response
businessId: string;
description: string; // Human-readable order description
}Ожидаемый ответ:
interface CreatePaymentResponse {
paymentUrl?: string; // Redirect URL for payment page
qrCode?: string; // QR code data for in-person payment
invoiceId?: string; // External payment provider invoice ID
expiresAt?: string; // ISO 8601 — when the payment link expires
}checkout.shipping_rates
Приложение предоставляет пользовательские тарифы на доставку для текущего заказа
Входные данные:
interface ShippingRatesPayload {
deliveryMethod: string; // "DELIVERY", "PICKUP", etc.
locality?: string; // Delivery area/district
subtotal: number; // Order subtotal in minor units
builtInFee: number; // Platform's default delivery fee
}Ожидаемый ответ:
interface ShippingRatesResponse {
fee: number; // Delivery fee in minor units (overrides builtInFee)
}Пример — бесплатная доставка от 2000:
async function handleShipping(data: ShippingRatesPayload) {
return { fee: data.subtotal > 2000 ? 0 : 300 };
}order.validate
Приложение проверяет заказ перед созданием — может отклонить с указанием причины
Входные данные:
interface OrderValidatePayload {
items: Array<{ productId: string; quantity: number }>;
subtotal: number;
deliveryMethod: string;
}Ожидаемый ответ:
interface OrderValidateResponse {
valid: boolean;
reason?: string; // Shown to customer when valid=false
}Пример — минимальная сумма заказа:
async function validateOrder(data: OrderValidatePayload) {
if (data.subtotal < 500) {
return { valid: false, reason: 'Minimum order is 500₽' };
}
return { valid: true };
}order.calculate_discounts
Приложение предоставляет дополнительные скидки помимо промокодов
Входные данные:
interface CalculateDiscountsPayload {
items: Array<{ productId: string; quantity: number }>;
subtotal: number;
promoDiscount: number; // Discount already applied from promo codes
deliveryFee: number;
}Ожидаемый ответ:
interface CalculateDiscountsResponse {
discount: number; // Discount amount in minor units
reason?: string; // Shown to customer (e.g. "Loyalty discount: 10%")
}Обработка хуков с помощью SDK
Утилита createHookHandler автоматически выполняет проверку подписи, валидацию временных меток и маршрутизацию:
import { createHookHandler, HookPoints } from '@bazex/app-sdk';
const handleHook = createHookHandler({
secret: process.env.BAZEX_ACCESS_TOKEN!,
handlers: {
[HookPoints.CHECKOUT_PAYMENT_METHODS]: async (data, ctx) => {
return {
methods: [
{ id: 'card', name: 'Credit Card', description: 'Visa/Mastercard' },
{ id: 'cash', name: 'Cash on Delivery' },
],
};
},
[HookPoints.CHECKOUT_SHIPPING_RATES]: async (data) => {
return { fee: data.subtotal > 2000 ? 0 : 300 };
},
[HookPoints.ORDER_VALIDATE]: async (data) => {
if (data.subtotal < 500) {
return { valid: false, reason: 'Minimum order is 500₽' };
}
return { valid: true };
},
},
});
// In your Express route:
app.post('/hooks', async (req, res) => {
const result = await handleHook({
headers: req.headers as Record<string, string>,
body: JSON.stringify(req.body),
});
res.status(result.status).json(result.body);
});Контекст обработчика
(data, ctx), где ctx содержитhookPoint, businessId и timestamp.Страницы администратора
Страницы администратора позволяют вашему приложению отображать полноэкранные интерфейсы внутри панели управления продавца. При установке приложения каждая зарегистрированная страница появляется как элемент бокового меню. Страница загружается внутри iframe, а платформа передаёт контекст тенанта через postMessage.
Формат манифеста
{
"adminPages": [
{
"pageId": "dashboard",
"title": "Дашборд бота",
"iconName": "BarChart3",
"renderUrl": "https://your-app.com/admin/dashboard"
},
{
"pageId": "settings",
"title": "Настройки бота",
"iconName": "Settings",
"renderUrl": "https://your-app.com/admin/settings"
}
]
}Поля страницы администратора
| Name | Type | Description |
|---|---|---|
| pageId | string | Уникальный идентификатор внутри приложения (например, "dashboard", "settings") |
| title | string | Отображаемое имя в боковой панели |
| iconName | string | Имя иконки Lucide (например, "Settings", "BarChart3"). При отсутствии используется иконка приложения. |
| renderUrl | string | Полный URL страницы для отображения в iframe |
PostMessage-мост
При загрузке iframe панель управления отправляет сообщение bazex:init с контекстом тенанта. Прослушивайте его в вашем приложении для идентификации бизнеса:
window.addEventListener('message', (event) => {
if (event.data?.type === 'bazex:init') {
const { businessId, businessName, role, locale } = event.data;
// Инициализируйте приложение с контекстом тенанта
console.log('Бизнес:', businessId, businessName);
console.log('Роль пользователя:', role);
console.log('Локаль:', locale);
}
});URL-параметры
Помимо postMessage, src iframe содержит query-параметры для удобства:
businessId— ID бизнеса продавцаlocale— локаль панели управления (например, "ru")
Политика песочницы
allow-scripts allow-same-origin allow-forms allow-popups. Ваша страница может выполнять JavaScript, делать API-запросы, отправлять формы и открывать всплывающие окна.Полный пример манифеста
Ниже представлен полный манифест приложения, использующий все типы расширений и глобальные настройки:
{
"settingsSchema": {
"type": "object",
"properties": {
"apiKey": { "type": "string", "title": "API Key" },
"enableNotifications": { "type": "boolean", "title": "Send Notifications" }
}
},
"blocks": [
{
"blockType": "promo_banner",
"name": "Promo Banner",
"renderUrl": "https://your-app.com/blocks/promo",
"settingsSchema": {
"type": "object",
"properties": {
"text": { "type": "string", "title": "Banner Text" },
"bgColor": { "type": "string", "format": "color", "title": "Background" }
}
},
"defaultConfig": { "text": "Free delivery!", "bgColor": "#10b981" }
}
],
"embeds": [
{
"embedType": "tracking_script",
"name": "Order Tracking",
"kind": "GLOBAL_SCRIPT",
"position": "body_end",
"scriptUrl": "https://your-app.com/scripts/tracking.js",
"settingsSchema": {
"type": "object",
"properties": {
"trackingId": { "type": "string", "title": "Tracking ID" }
}
}
}
],
"hooks": [
{
"hookPoint": "checkout.shipping_rates",
"url": "/hooks/shipping",
"timeout": 5000
},
{
"hookPoint": "order.validate",
"url": "/hooks/validate"
}
],
"adminPages": [
{
"pageId": "dashboard",
"title": "Analytics Dashboard",
"iconName": "BarChart3",
"renderUrl": "https://your-app.com/admin/dashboard"
}
]
}Синхронизация манифеста
При обновлении манифеста приложения (через портал разработчика или API) Bazex автоматически синхронизирует определения расширений:
- Новые расширения создаются и сразу становятся активными
- Обновлённые расширения (с тем же
blockType/embedType/hookPoint/pageId) обновляются на месте - Удалённые расширения деактивируются (не удаляются) для сохранения данных продавца
Валидация хуков
hookPoint, обновление манифеста будет отклонено с ошибкой 400.