Мини-плеер и live-виджет
Мини-плеер и live-виджет поддерживаются в двух форматах:
- Web SDK (рекомендуемый и простой путь) — копипастой скрипта в HTML-шаблон.
- Public API (дополнительный путь) — для кастомного UI, диагностики и серверного мониторинга.
- Нужен mini-player на web PDP: читайте раздел про
shopstory-pipв шаблоне карточки товара. - Нужен mini-player в mobile, где карточка товара уже открывается в WebView: читайте сценарий A.
- Нужен mini-player в mobile с нативной карточкой товара: читайте сценарий B и Deep links.
- Строите кастомный UI или backend-диагностику: API reference начинается только во второй половине страницы и нужен не всем.
Веб: mini-player в шаблоне карточки товара
Для большинства клиентов mini-player и live-виджет подключаются без ручной API-интеграции.
Достаточно вставить shopstory-pip в HTML-шаблон карточки товара (или в общий шаблон страниц, где нужен виджет):
<!-- Вставляем в конец тега <body> -->
<script
id="shopstory-pip"
type="application/javascript"
src="https://app.shopstory.live/sdk/shopstory-pip-sdk/shopstory-pip-sdk-v1.x.min.js"
crossorigin="anonymous"
charset="utf-8"
async
></script>
Что это даёт:
- скрипт подключает два виджета сразу: mini-player и live-виджет;
- mini-player появляется в карточке товара, если у товара есть стрим/запись;
- при наличии
startTimeв ответе API воспроизведение стартует с релевантного момента по товару; - live-виджет показывается только во время активного эфира;
- скрипт можно размещать на всех страницах, кроме корзины/checkout (рекомендация);
- один из виджетов можно отключить через менеджера/поддержку;
- скрипт загружается
async, без блокировки рендера страницы.
Успешный ответ /v2/mini-player/stream подтверждает, что backend нашёл релевантный стрим по товару. Он не подтверждает, что mini-player уже выведен в шаблоне карточки товара.
Для web-интеграции ShopStory отдельно включает mini-player под вашу верстку карточки товара: какой идентификатор товара использовать и куда вставлять виджет. Если API отвечает 200, а mini-player не появляется, проблема обычно в настройке под шаблон карточки товара, а не в Public API.
Минимальный web-flow:
- Вставьте
shopstory-pipв HTML-шаблон карточки товара. - Убедитесь, что идентификатор товара на странице стабилен и совпадает с данными товарного фида.
- Проверьте, что по этому товару
/v2/mini-player/streamвозвращает200. - После включения mini-player под ваш шаблон карточки товара виджет появится на PDP автоматически.

Когда нужен API-путь
API нужен не для базового SDK-сценария, а для:
- кастомной frontend-интеграции (без встроенных SDK-виджетов);
- технической проверки, почему виджет не появился;
- серверного мониторинга live/записей.
Если задача — быстро добавить mini-player в карточки товара и live-виджет на сайт, используйте только SDK-скрипт. API-путь нужен, когда вы сознательно строите свой интерфейс поверх ShopStory API.
Мобильные приложения: как добавить mini-player
Для мобильного приложения есть два корректных сценария.
Сценарий A. Карточка товара открывается как WebView вашей обычной PDP
Отдельная mobile-интеграция mini-player не нужна. Достаточно один раз встроить shopstory-pip в веб-шаблон карточки товара, и iOS/Android-приложение будет видеть тот же mini-player внутри WebView этой страницы.
Это самый быстрый путь, если продуктовая карточка в приложении уже рендерится через WebView.
Сценарий B. Карточка товара нативная
В этом случае mini-player как web-виджет в нативный экран не встраивается. Правильный сценарий:
- Ваш backend вызывает
/v2/mini-player/streamпоfeedProductIdилиproductCode. - Мобильное приложение получает от вашего backend только результат проверки: есть ли релевантный стрим,
streamId,startTime. - Если стрим найден, приложение показывает компактный CTA рядом с товаром: например, «Смотреть эфир».
- По тапу приложение открывает ShopStory player в WebView по контракту из Deep links.
Не вызывайте Public API mini-player напрямую из iOS/Android-клиента с публичным applicationId. Для production-сценария используйте свой backend/proxy.
iOS (Swift)
Пример загрузки доступности mini-player через ваш backend:
struct ShopStoryMiniPlayerEnvelope: Decodable {
let status: Int
let body: Body?
struct Body: Decodable {
let streamId: String
let startTime: Int?
let playbackLink: String
}
}
func loadMiniPlayer(feedProductId: String) async throws -> ShopStoryMiniPlayerEnvelope.Body? {
let url = URL(string: "https://api.example.ru/shopstory/mini-player?feedProductId=\(feedProductId)")!
let (data, response) = try await URLSession.shared.data(from: url)
guard let http = response as? HTTPURLResponse, http.statusCode == 200 else {
return nil
}
let payload = try JSONDecoder().decode(ShopStoryMiniPlayerEnvelope.self, from: data)
return payload.status == 200 ? payload.body : nil
}
Если streamId найден, покажите CTA на нативной карточке товара и откройте WebView-плеер:
let streamUrl = "https://example.ru/live/?wv=1#/translation/\(streamId)"
let controller = ShopstoryWebViewController(streamUrl: streamUrl)
present(controller, animated: true)
Полный WKWebView-контроллер и bridge-события: Deep links.
Android (Kotlin)
Пример той же проверки через backend:
@Serializable
data class ShopStoryMiniPlayerEnvelope(
val status: Int,
val body: Body? = null,
) {
@Serializable
data class Body(
val streamId: String,
val startTime: Int? = null,
val playbackLink: String,
)
}
suspend fun loadMiniPlayer(feedProductId: String): ShopStoryMiniPlayerEnvelope.Body? {
val request = Request.Builder()
.url("https://api.example.ru/shopstory/mini-player?feedProductId=$feedProductId")
.build()
OkHttpClient().newCall(request).execute().use { response ->
if (!response.isSuccessful) return null
val raw = response.body?.string() ?: return null
val payload = Json.decodeFromString<ShopStoryMiniPlayerEnvelope>(raw)
return if (payload.status == 200) payload.body else null
}
}
Если streamId найден, откройте WebView-плеер:
val streamUrl = "https://example.ru/live/?wv=1#/translation/$streamId"
startActivity(
Intent(this, ShopstoryWebViewActivity::class.java)
.putExtra("streamUrl", streamUrl)
)
Полный WebView-экран и bridge-события: Deep links.
Optional: API reference и диагностика
Если вы используете shopstory-pip на web или backend/proxy + WebView в mobile, вам не нужно вызывать эти эндпоинты прямо из клиентского приложения.
Ниже — reference для кастомного frontend/UI, backend-диагностики и серверного мониторинга. Для iOS/Android production-сценария запросы к mini-player должны идти через ваш backend/proxy.
Мини-плеер по товару — GET /v2/mini-player/stream
GET https://app.shopstory.live/v2/mini-player/stream?application=<appId>&productCode=<sku>
Параметры:
| Параметр | Обязательно | Описание |
|---|---|---|
application | ✅ | Идентификатор приложения |
productCode | Нет | SKU/артикул товара |
feedProductId | Нет | ID товара из фида (используйте либо productCode, либо feedProductId) |
feedProductGroupId | Нет | Группа товара из фида (опционально для уточнения) |
Примеры запросов:
GET /v2/mini-player/stream?application=<app-alias>&productCode=<sku>
GET /v2/mini-player/stream?application=<app-alias>&feedProductId=<feed-product-id>
Пример ответа:
{
"serverTime": "<timestamp>",
"status": 200,
"body": {
"streamId": "<stream-id>",
"streamStatus": "completed",
"productId": "<shopstory-product-id>",
"playbackLink": "https://cdn.example.com/path/to/playlist.m3u8",
"feedProductId": "<feed-product-id>"
}
}
playbackLink используйте для открытия плеера, streamStatus показывает состояние найденного стрима (online или completed).
Поле startTime (если присутствует) задаёт смещение в секундах для старта VOD с нужного момента. На практике это смещение считается от старта трансляции до момента, когда товар был показан в эфире, поэтому запись открывается сразу на релевантном эпизоде.
Реальные варианты ответа:
- HTTP
200+status: 200, когда стрим найден. - HTTP
400+status: 400, если не передан ниproductCode, ниfeedProductId. - HTTP
404+status: 404, если для товара нет подходящего стрима.
Любой live — GET /v2/mini-player/online
Получить любую актуальную live-трансляцию для виджета «Сейчас в эфире»:
GET https://app.shopstory.live/v2/mini-player/online?application=<appId>
Если live-стрим есть, в body.online приходит объект трансляции. Если live нет, API возвращает пустой body.
{
"serverTime": "<timestamp>",
"status": 200,
"body": {}
}
Быстрый статус — GET /v3/translation/quick-state
Используйте для дешёвого поллинга UI:
GET https://app.shopstory.live/v3/translation/quick-state?translationId=<streamId>
Ответ:
{
"status": 200,
"serverTime": "<timestamp>",
"body": {
"translationState": "completed",
"translationId": "<stream-id>",
"expires": "<expires-at>"
}
}
translationState может принимать значения: draft, confirmed, online, completed, ''.
Эндпоинт quick-state публичный, поэтому applicationId не требуется. Если вы участвуете в бете токенов, можно добавить заголовок Authorization: Bearer ... — клиент автоматически сопоставится.
Как интерпретировать диагностику
| Наблюдение | Что это значит | Следующее действие |
|---|---|---|
/v2/mini-player/stream возвращает HTTP 404 | Для товара нет связанного стрима или SKU/feedProductId не совпадает с фидом | Проверьте связку товара со стримом и значения в XML/YML |
/v2/mini-player/stream возвращает HTTP 200, но виджета на PDP нет | Backend здоров, но mini-player ещё не включён под ваш шаблон карточки товара или шаблон был изменён | Сверьте шаблон карточки товара и обратитесь в поддержку ShopStory |
/v2/mini-player/online возвращает 200 с пустым body | Прямо сейчас live-эфира нет | Тестируйте completed/VOD сценарий или дождитесь live |
quick-state возвращает completed | Для live-widget это уже не live, но запись доступна для мини-плеера/VOD-сценария | Используйте playbackLink и startTime из /v2/mini-player/stream |
Чек-лист проверки mini-player и live-виджета
- На странице товара подключён
shopstory-pipи отсутствуют блокирующие оверлеи. - SKU/ID в карточке товара совпадает с тем, что приходит в XML/YML фиде.
- Запрос
/v2/mini-player/streamпо этому SKU/ID возвращаетstatus: 200иbody.playbackLink. - Если API отвечает
200, но mini-player на PDP нет, значит mini-player ещё не включён под ваш шаблон карточки товара или шаблон был изменён. - Во время реального эфира
/v2/mini-player/onlineотдаётbody.online, и виджет появляется на странице. - Для мобильного сценария WebView события
add-productиopen-productобрабатываются нативным слоем.
Далее: