Динамический бейдж
Данный документ описывает требования к интеграции динамического бейджа в мобильное или веб-приложение Партнера.
Ключевые принципы
- Самостоятельная реализация. Визуальная часть и логика отображения динамического бейджа реализуются Партнером собственными силами на основании дизайн-гайда .
- Использование SDK. SDK используется исключительно для:
- Настройки бесшовного перехода.
- Передачи событий (метрик) об отображении и взаимодействии с бейджем.
Подключение бесшовного перехода
Для обеспечения плавного пользовательского опыта необходимо настроить бесшовный переход из приложения или сайта Партнера на веб-интерфейс без повторной авторизации.
Предварительные требования
Перед началом подключения к SSO ознакомьтесь с общими требованиями:
Инструкции по платформам
| Платформа | Ссылка на документацию |
|---|---|
| Android | Настройка перехода на веб-интерфейс Партнера |
| iOS | Настройка перехода на веб-интерфейс Партнера |
| Web | Web SSO |
Отправка метрик в SDK
Для дальнейшей транспортировки данных в ClickStream партнеру необходимо уведомлять SDK о событиях, происходящих с динамическим бейджем.
Мобильные приложения (Android и iOS)
Для отправки событий по бейджу используйте новый метод sendBadgeEvent.
Метод отправки событий
SID.Events.sendBadgeEvent(
eventType: BadgeEventType, // SHOW, CLICK, ERROR
widgetType: BadgeWidgetType, // BADGE, POPUP (POPUP зарезервировано)
widgetContent: List<String>?, // ["Прайм", "Спасибо"] или null
status: List<String>? // ["ACTIVE", "INACTIVE"] или null
)
SID.Events.sendBadgeEvent(
eventType: BadgeEventType, // .show, .click, .error
widgetType: BadgeWidgetType, // .badge, .popup (.popup зарезервировано)
widgetContent: [String?], // ["Прайм", "Спасибо"] или [nil]
status: [String?] // ["ACTIVE", "INACTIVE"] или [nil]
)
- Максимальный размер любой строки — 128 символов.
- Метод вызывается столько раз, сколько событий произошло.
Параметры метода
| Параметр | Тип | Описание и правила передачи |
|---|---|---|
eventType | Enum | SHOW (показ), CLICK (клик), ERROR (ошибка). |
widgetType | Enum | Всегда BADGE (значение POPUP зарезервировано на будущее). |
widgetContent | List / Array | Массив названий бейджей. Обязателен для SHOW и CLICK. Для глобальной ошибки (ERROR) может быть null (Android) или [] / [nil] (iOS). |
status | List / Array | Массив статусов бейджей. Порядок элементов должен строго соответствовать порядку в widgetContent. |
Правила и ограничения:
- Элемент с индексом
0вwidgetContentотносится к элементу с индексом0вstatus. Если длины массивов не совпадают, SDK обрежет их по минимальной длине. - Особенности iOS - массивы
widgetContentиstatusпередаются всегда (не могут бытьnilкак весь объект), но могут содержать элементыnil. SDK автоматически отфильтруетnilзначения и пустые массивы перед отправкой. - Пустая строка
""внутри массива допустима и будет передана как есть.
Логика отправки событий
Партнер отправляет события по факту намерения показать бейджи, а не по факту их физическо го появления на экране. Это ключевой принцип, обеспечивающий корректную сквозную аналитику. Правило одного вызова: Независимо от количества бейджей, запланированных к показу (один, два или больше), Партнер отправляет один вызов SHOW с массивом всех бейджей.
Таблица сценариев
| № | Ситуация | Действия Партнера | Какие события отправлять |
|---|---|---|---|
| 1 | Штатный показ. Партнер успешно отобразил все запланированные бейджи. | Отправить один SHOW с массивом всех показанных бейджей и их статусов. | SHOW |
| 2 | Намерение показать, но пользователь ушел. Партнер начал рендер, но пользователь закрыл приложение / ушел со страницы до завершения показа. | Событие все равно отправляется — факт намерения состоялся. | SHOW (с полным массивом запланированных бейджей) |
| 3 | Глобальная системная ошибка. Из-за ошибки (сетевой, серверной и т.п.), не связанной с действиями пользователя, Партнер не может отобразить ни один бейдж. | SHOW не отправляется. Отправляется только ERROR без данных о бейджах. | ERROR (глобальная, widgetContent = null / []) |
| 4 | Частичная ошибка при рендере. Планировался показ нескольких бейджей, но один (или несколько) не отобразился из-за ошибки. | Сначала отправить SHOW со всеми запланированными бейджами. Затем следом отправить ERROR с данными конкретного сбойного бейджа. | SHOW (полный массив) → ERROR (данные проблемного бейджа) |
| 5 | Полная ошибка при рендере. Все запланированные бейджи не отобразились из-за ошибки в процессе показа. | Сначала отправить SHOW со всеми запланированными бейджами. Затем следом отправить ERROR без данных (глобальная ошибка в процессе показа). | SHOW (полный массив) → ERROR (глобальная) |
Важно:
- Пользовательские действия (уход со страницы, закрытие приложения) не являются ошиб кой. Если намерение показать бейджи было — отправляйте SHOW.
- ERROR без данных (widgetContent = null) означает, что Партнер не смог отобразить бейджи до отправки SHOW (сценарий №3).
- ERROR с данными конкретного бейджа означает, что SHOW уже был отправлен, но в процессе рендера возникла проблема с одним из бейджей (сценарий №4).
Примеры вызовов
// 1. Штатный показ нескольких бейджей
SID.Events.sendBadgeEvent(BadgeEventType.SHOW, BadgeWidgetType.BADGE, listOf("Прайм", "Спасибо"), listOf("ACTIVE", "INACTIVE"))
// 2. Клик по конкретному бейджу
SID.Events.sendBadgeEvent(BadgeEventType.CLICK, BadgeWidgetType.BADGE, listOf("Прайм"), listOf("ACTIVE"))
// 3. Глобальная ошибка ДО показа (ни один бейдж не загрузился)
SID.Events.sendBadgeEvent(BadgeEventType.ERROR, BadgeWidgetType.BADGE, null, null)
// 4. Ошибка конкретного бейджа в процессе рендера
SID.Events.sendBadgeEvent(BadgeEventType.ERROR, BadgeWidgetType.BADGE, listOf("Прайм"), null)
// 1. Штатный показ нескольких бейджей
SID.Events.sendBadgeEvent(.show, .badge, ["Прайм", "Спасибо"], ["ACTIVE", "INACTIVE"])
// 2. Клик по конкретному бейджу
SID.Events.sendBadgeEvent(.click, .badge, ["Прайм"], ["ACTIVE"])
// 3. Глобальная ошибка ДО показа (ни один бейдж не загрузился)
SID.Events.sendBadgeEvent(.error, .badge, [], [])
// 4. Ошибка конкретного бейджа в процессе рендера
SID.Events.sendBadgeEvent(.error, .badge, ["Прайм"], [nil])
Web и mWeb
Приложение партнера самостоятельно отправляет метрики в ClickStream Сбер ID по следующим событиям:
- Отображение бейджа
- Клик на бейдж
- Ошибка показа бейдж
Инициализация ClickStream
Для передачи метрик необходимо инициализировать clickstream:
export const clickstreamParams = {
url: 'https://ext.clickstream.sbrf.ru/metrics/inner-partners/sber-id',
apiKey: '5b63c7457b46f2e2c0c81062da0efbd628c62940db7590f9e97aecac87a43f05',
applicationName: '[clientId партнера]'
};
Формат передаваемого события
| Параметр | Показ бейджа Прайм/Спасибо | Ошибка отображения бейджа | Клик по бейджу Прайм/Спасибо |
|---|---|---|---|
| eventCategory | ELK | ELK | ELK |
| eventAction | SID_ELK_Badge screen | SID_ELK_Badge error | SID_ELK_Badge button |
| eventType | business (обычные события: клики, изменения, открытие страниц, диалоговых окон и пр.) | business (обычные события: клики, изменения, открытие страниц, диалоговых окон и пр.) | business (обычные события: клики, изменения, открытие страниц, диалоговых окон и пр.) |
| value | timeStamp (время формирования метрик) | timeStamp (время формирования метрик) | timeStamp (время формирования метрик) |
Свойства (properties)
| Ключ | Показ бейджа Прайм/Спасибо | Ошибка отображения бейджа | Клик по бейджу Прайм/Спасибо |
|---|---|---|---|
widget_content | Если бонусы Спасибо: "Баланс"Если значок Прайма: "Прайм" | — | Если бонусы Спасибо: "Баланс"Если значок Прайма: "Прайм" |
event_type | Show (отображение экрана пользователю) | Error (отображение ошибки пользователю) | Click (нажатие на кнопку/чекбокс/подсказку) |
source | Если бонусы Спасибо: "Баланс"Если значок Прайма: "Прайм" | Код ошибки (не совпадает с текстом ошибки на экране, http-код ошибки) | Если бонусы Спасибо: "Баланс"Если значок Прайма: "Прайм" |
channel | web / mWeb | web / mWeb | web / mWeb |
partner_name | Наименование партнера | Наименование партнера | Наименование партнера |
logId | Идентификатор сессии пользователя | Идентификатор сессии пользователя | Идентификатор сессии пользователя |
Пример запроса:
{
"meta": {
"apiKey": "5b63c7457b46f2e2c0c81062da0efbd628c62940db7590f9e97aecac87a43f05",
"applicationName": "40c1d5da-1532-11eb-adc1-0242ac-disk",
"platform": "WEB",
"browser": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36 SberBrowser/39.0.0.0",
"timeStamp": "2026-08-24T12:51:12.842+03:00",
"screenSize": "1680x1050",
"systemLanguage": "ru-RU"
},
"profile": {
"applicationLanguage": "en",
"deviceId": "SA1.3590e0b5-5301-4c46-9f89-e3e2737f578f.1786517005",
"sessionId": "SA1.3590e0b5-5301-4c46-9f89-e3e2737f578f.1786517005.1787565072",
"cookie": [
{
"key": "_ym_uid",
"value": "1783693885483729367"
}
],
"hashUserLoginId": ""
},
"data": [
{
"url": "https://ci02440297-ift-sberid-elk-ext-static.apps.a9s6tc15.k8s.delta.sbrf.ru/profile/external_partners/storybook/iframe.html",
"pageName": "partners-disk--disk",
"connectionType": "UNKNOWN",
"timeStamp": "2026-08-24T12:51:12.843+03:00",
"eventAction": "SID_ELK_Badge screen",
"eventCategory": "ELK",
"eventType": "business",
"value": "",
"properties": [
{
"key": "event_type",
"value": "Show"
},
{
"key": "source",
"value": "Баланс"
},
{
"key": "channel",
"value": "web"
},
{
"key": "partner_name",
"value": "Диск"
},
{
"key": "logId",
"value": "6b5174dcb14a48aaacd96d2e58102f45"
},
{
"key": "widget_content",
"value": "Баланс"
}
]
}
]
}
Пример реализации:
import {
logDir,
logUidSessionStorageName,
} from '@front-static-partners/shared';
import { sendEventClickStream } from '@sberid/click-stream';
import { getSessionStorageItem } from '../utils/getSessionStorageItem';
type DefaultParamsProps = {
eventCategory: string;
system: string;
metricEventType?: 'technical' | 'business';
};
export type EventParamsProps = {
eventAction: string;
value: string;
event_type: string;
partner_name: string;
source: string;
templateId?: string;
error_type?: string;
abName?: string;
widget_content?: string;
widget_download_start_time?: number;
widget_download_end_time?: number;
popupType?: string;
actionPlace?: string;
actionType?: string;
};
export function setDefaultParams(defaultProps: DefaultParamsProps) {
const isMobile = window.innerWidth < 480;
const channel = isMobile ? 'mWeb' : 'web';
return function (eventProps: EventParamsProps) {
const { eventCategory, metricEventType } = defaultProps;
const {
eventAction,
value,
event_type,
partner_name,
source,
templateId,
error_type,
abName,
widget_content,
widget_download_start_time,
widget_download_end_time,
popupType,
actionPlace,
actionType,
} = eventProps;
let ab;
if (abName) {
ab = window.elkAB?.[abName];
}
const analyticData = {
eventAction,
eventCategory,
value,
eventType: metricEventType || 'business',
properties: [
{
key: 'event_type',
value: event_type,
},
{
key: 'source',
value: source,
},
{
key: 'channel',
value: channel,
},
{
key: 'partner_name',
value: partner_name,
},
],
};
const hashUserLog = getSessionStorageItem(logUidSessionStorageName);
const logId = hashUserLog?.logId;
if (logId) {
analyticData.properties.push({
key: 'logId',
value: logId,
});
}
if (ab) {
analyticData.properties.push({
key: 'ab_experiment',
value: ab
.map((experiment: { name: string; group: string }) => experiment.name)
.join('|'),
});
analyticData.properties.push({
key: 'ab_variant',
value: ab
.map(
(experiment: { name: string; group: string }) => experiment.group,
)
.join('|'),
});
}
if (widget_download_start_time) {
analyticData.properties.push({
key: 'widget_download_start_time',
value: Math.trunc(widget_download_start_time).toString(),
});
}
if (widget_download_end_time) {
analyticData.properties.push({
key: 'widget_download_end_time',
value: Math.trunc(widget_download_end_time).toString(),
});
}
if (widget_content) {
analyticData.properties.push({
key: 'widget_content',
value: widget_content,
});
}
if (templateId) {
analyticData.properties.push({
key: 'templateId',
value: templateId,
});
}
if (error_type) {
analyticData.properties.push({
key: 'error_type',
value: error_type,
});
}
if (popupType) {
analyticData.properties.push({
key: 'popupType',
value: popupType,
});
}
if (actionPlace) {
analyticData.properties.push({
key: 'actionPlace',
value: actionPlace,
});
}
if (actionType) {
analyticData.properties.push({
key: 'actionType',
value: actionType,
});
}
return analyticData;
};
}
export const sendAnalytics = setDefaultParams({
eventCategory: 'ELK',
metricEventType: 'business',
system: 'SBERID',
});
export const sendTechAnalytics = setDefaultParams({
eventCategory: 'ELK',
metricEventType: 'technical',
system: 'SBERID',
});