Виджет оплаты подписок
Виджет позволяет оформить подписку на странице партнёра через СБП, Alfa Pay или SberPay — в зависимости от настроек проекта и условий подписки. T-Pay временно отключён.
Если оплата первого периода предусмотрена при оформлении, выбранный способ запускает первый платёж с сохранением привязки для последующих списаний. Если первая оплата отложена, в этой версии доступен только СБП: плательщик подтверждает привязку без списания, а первый платёж впоследствии запускает платформа по расписанию. Бесплатная привязка должна быть подключена и поддерживаться платёжным провайдером для проекта.
Событие success сообщает об успешном завершении начального шага: первого платежа либо привязки без списания при отложенной оплате. Оно не заменяет серверные уведомления об активации подписки и тарификации. Учёт платежей на стороне партнёра должен опираться на серверные уведомления, даже если плательщик закрыл страницу и не получил success в браузере.
Виджет работает и в песочнице — embed. публикуется на её домене, токен берётся из AuthorizeUrl так же, как в бою. На демо-проекте песочницы подключены СБП, Alfa Pay и SberPay, и экран выбора виден сразу; у остальных проектов по умолчанию только СБП, набор меняется по запросу. Отложенную первую оплату в песочнице включает BillingStartTimeUtc в будущем — тогда в виджете остаётся только СБП. Настоящих банков в тестовой среде нет: кнопка Alfa Pay или SberPay открывает имитацию приложения банка с кнопкой подтверждения, а внутри виджета есть кнопка симуляции — она и доводит сценарий до success. Пошаговая проверка — Виджет оплаты подписок в песочнице.
6.1 Как встроить
- Вызвать
CreateSubscriptionсо своего сервера. В ответе придётAuthorizeUrl, внутри которого лежит токен — 32 шестнадцатеричных символа. Этот токен и нужен виджету. - Подключить
embed.с платёжного домена и вызватьjs SbpWidget.mount()илиSbpWidget.open().
<div id="pay"></div>
<script src="https://subs.digitalspb.com/Cards/Sbp/embed.js"></script>
<script>
const widget = SbpWidget.mount('#pay', {
token: 'TOKEN_FROM_CREATE_SUBSCRIPTION',
brand: {
name: 'CoinKeeper',
logo: 'https://partner.example/logo.png',
primary: '#1756ef',
background: '#ffffff',
textColor: '#182045'
},
onSuccess: function () { location.href = '/thanks'; },
onFail: function () {},
onExpired: function () {},
onError: function (error) { console.error(error.message); }
});
</script>Здесь TOKEN_FROM_CREATE_SUBSCRIPTION — обозначение места для настоящего токена из ответа CreateSubscription, а не допустимое значение токена. Подпись запроса и пароль проекта остаются только на backend партнёра.
mount() вставляет <iframe src=".../Cards/Sbp/widget.html?...">. Сам виджет живёт внутри iframe — стили и скрипты страницы партнёра на него не влияют и наоборот — и работает same-origin с платёжным доменом, поэтому его запросы идут без CORS. С родительской страницей виджет общается через postMessage: авторесайз высоты iframe плюс события, которые приходят в колбэки mount().
Модальное окно — SbpWidget.open(). Второй режим показа, по возможностям такой же, как mount(): виджет появляется поверх страницы, а не в её вёрстке. Принимает те же параметры, только без селектора, и дополнительно колбэк onClose. Открыт всегда один экран — новое открытие закрывает предыдущее. Окно закрывается крестиком, клавишей Esc или кликом по фону; SbpWidget.close() закрывает его из кода.
SDK передаёт токен в query-параметре token адреса iframe: /Cards/Sbp/widget.html?token=…. Запросы виджета к API способов оплаты, создания платежа и проверки состояния передают этот токен заголовком Authorization: Bearer. Токен следует считать чувствительными данными: не публиковать его и не включать в открытые журналы. Поскольку он присутствует в URL iframe, его отсутствие в журналах веб-сервера не гарантируется.
Если встраивается виджет, AuthorizeUrl открывать не нужно. Это отдельный сценарий оплаты на нашей странице, и он конфликтует с виджетом по тому же токену.
Создание подписки остаётся на backend партнёра — и здесь, и в сценарии с кнопкой ниже. Пароль проекта и подпись в браузер не попадают.
6.2 Параметры mount() и open()
| Параметр | Обязателен | Описание |
|---|---|---|
token | Да | Токен из AuthorizeUrl ответа CreateSubscription. Используется для начального шага выбранным способом и проверки состояния |
apiBase | Нет | Адрес платёжного домена. По умолчанию — origin, с которого загружен embed. |
brand | Нет | Объект брендирования (см. ниже) |
poll | Нет | false отключает автопроверку состояния; по умолчанию включена |
methods | Нет | Фильтр способов: массив кодов или строка кодов через запятую. Коды: sbp, alfapay, sberpay. По умолчанию отображается набор сервера. Фильтр не включает способы, запрещённые сервером. Порядок остаётся серверным |
webview | Нет | Булево значение. true включает режим WebView, false отключает его; без параметра виджет определяет режим автоматически. В режиме WebView банковская оплата открывается по кнопке, без автоматического открытия новой вкладки |
onSuccess | Нет | Начальный шаг завершён успешно: первый платёж либо привязка без списания при отложенной оплате. Не заменяет серверные уведомления |
onFail | Нет | Начальный шаг завершился отказом |
onExpired | Нет | Истёк срок ожидания начального шага в виджете. Само событие не является подтверждением отмены платежа или отсутствия списания |
onError | Нет | Ошибка загрузки или работы виджета. Получает объект Error; сообщение доступно в error.. Для некоторых событий доступен error.; его наличие не гарантируется |
onClose | Нет | Только для open(): модальное окно закрыто. Закрытие окна не отменяет подписку или платёж (§6.11) |
Метод destroy() у результата mount() снимает обработчик postMessage и удаляет iframe со страницы.
Фильтр способов:
SbpWidget.mount('#pay', {
token: 'TOKEN_FROM_CREATE_SUBSCRIPTION',
methods: ['sbp', 'sberpay'],
onError: function (error) {
console.error(error.message, error.code);
}
});Значение токена в примере — обозначение места для настоящих 32 hex-символов из AuthorizeUrl, не рабочий токен.
6.3 Внешний вид — объект brand
| Поле | Описание | По умолчанию |
|---|---|---|
name | Название магазина или продукта | «Оплата» |
logo | URL логотипа — только http(s):// или data:image/ | нет |
primary | Акцентный цвет: hex, именованный цвет или rgb() / rgba(). На кнопки способов оплаты не действует | #1a1f36 |
background | Цвет фона виджета | #ffffff |
textColor | Цвет текста | Подбирается автоматически по цвету фона, если партнёр не задал textColor |
Значения проходят валидацию на стороне виджета: недопустимый цвет молча заменяется значением по умолчанию, логотип с посторонней схемой не принимается. Параметры приходят через query публичной страницы виджета — без этой проверки получилась бы подстановка разметки.
При задании собственного textColor партнёр отвечает за контраст текста и фона. QR-код сохраняет белый фон независимо от оформления виджета. Фирменные кнопки способов оплаты не перекрашиваются цветом brand..
До начала оплаты виджет показывает сумму с НДС, период подписки и юридическую информацию, настроенную платформой.
6.4 События
| Событие | Когда приходит |
|---|---|
success | Успешно завершён первый платёж либо привязка без списания при отложенной оплате. Не заменяет серверные уведомления |
fail | Начальный шаг завершился отказом |
expired | Истёк срок ожидания начального шага. Не подтверждает отмену платежа или отсутствие списания |
error | Сбой виджета, в том числе устойчивый отказ проверки состояния |
resize | Изменилась высота содержимого; обрабатывается обёрткой автоматически |
В onError передаётся объект Error. Текст доступен в error.; для некоторых событий передаётся error.. Код method_unavailable означает, что фильтр methods не содержит ни одного способа из доступного серверного набора. Это нефатальное событие: виджет продолжает работу и показывает доступные серверные способы. Если iframe не загрузился или не ответил SDK при установлении связи, также вызывается onError.
Не следует автоматически создавать новую подписку при любой ошибке: предыдущая платёжная операция могла быть начата, а её результат ещё не получен.
6.5 Готовая кнопка СБП — renderButton()
Надстройка над mount(): SDK сам рисует фирменную кнопку СБП, а создание подписки остаётся на стороне партнёра. Удобно, когда своего чекаута нет.
<div id="pay"></div>
<script src="https://subs.digitalspb.com/Cards/Sbp/embed.js"></script>
<script>
SbpWidget.renderButton('#pay', {
createInvoice: async function () { // на backend партнёра: CreateSubscription
const r = await fetch('/api/create-subscription', { method: 'POST' });
return r.json(); // должен вернуть { token: '...' }
},
text: 'Оформить подписку',
brand: { name: 'CoinKeeper', primary: '#1756ef' },
onSuccess: function () { location.href = '/thanks'; },
onFail: function () {},
onExpired: function () {},
onError: function (e) {}
});
</script>| Параметр | Обязателен | Описание |
|---|---|---|
createInvoice | Да | Async-колбэк: создаёт подписку на backend партнёра и возвращает объект с полем token |
text | Нет | Текст кнопки; по умолчанию «Оплатить» |
brand | Нет | Брендирование виджета (таблица выше). На саму кнопку не влияет — она фирменная |
apiBase, methods, webview, poll, onSuccess, onFail, onExpired, onError | Нет | То же, что у mount() |
У результата два метода: reset() возвращает кнопку на место, destroy() убирает со страницы и кнопку, и виджет.
Кнопка выглядит одинаково у всех партнёров: тёмно-синий фон #1D1346, слева текст, справа официальный знак СБП без перекраски. Построено по брендбуку НСПК.
renderButton() создаёт готовую кнопку, которая после получения токена открывает виджет. Это не маршрут отдельной кнопки конкретного способа оплаты. Для собственных кнопок магазина, открывающих выбранный способ, используйте §6.12. Название и оформление готовой кнопки набор способов не ограничивают — для этого есть methods, а при пустом пересечении виджет покажет серверный набор и вызовет onError с кодом method_unavailable.
6.6 Что видит плательщик
Способы оплаты. Доступные способы: sbp — СБП, alfapay — Alfa Pay, sberpay — SberPay. T-Pay временно отключён: он не отображается в виджете и не принимается маршрутом собственных кнопок.
Набор и порядок способов задаются на стороне платформы и дополнительно зависят от условий подписки. При оплате первого периода во время оформления доступны подключённые способы. При отложенной первой оплате в этой версии доступен только СБП.
Если доступен один способ, отдельного экрана выбора нет. Если способов несколько, виджет показывает их фирменные кнопки. Нажатие кнопки запускает начальную операцию выбранным способом, после чего плательщик подтверждает её в банке.
Партнёр может сузить отображаемый набор параметром methods. Виджет использует пересечение фильтра с доступными серверными способами, сохраняя серверный порядок. Регистр кодов и пробелы по краям несущественны. Параметр не включает запрещённые способы и не обходит временное отключение T-Pay.
Если пересечение пустое, виджет показывает серверный набор и вызывает onError с кодом method_unavailable. Для изменения подключённых способов или их серверного порядка обратитесь к менеджеру.
Смена способа. Способ выбирается до начала операции. После её запуска изменить способ для той же подписки нельзя: сервер возвращает canChangeMethod=false, и виджет скрывает переключение способов.
При повторном открытии виджета с тем же токеном и несколькими доступными способами экран выбора может появиться снова. Для продолжения следует выбрать тот же способ. В пределах срока действия сервер использует уже начатую операцию; выбор другого способа для неё будет отклонён.
Для другого способа требуется новая подписка с новым токеном. Перед её созданием необходимо проверить результат предыдущей операции, чтобы не оформить две подписки или два начальных платежа.
Собственные кнопки магазина. Для них доступен маршрут /Cards/Sbp/pay: СБП открывает экран оплаты без выбора способа, Alfa Pay и SberPay перенаправляют на страницу оплаты банка. Формат и ограничения приведены в §6.12.
Экран СБП виджет подбирает под устройство:
| Устройство | Что показывается |
|---|---|
| Настольный браузер | QR-код, обратный отсчёт и надпись об ожидании оплаты |
| Телефон или узкое окно (сенсорный ввод либо ширина меньше 600 пикселей) | Кнопка перехода в приложение банка. Если банк не передал ссылку для перехода, показывается ссылка на страницу оплаты СБП |
После подтверждения в приложении банка плательщик возвращается на страницу партнёра; виджет продолжает проверять состояние и сам покажет результат.
6.7 Сроки
| Что | Значение |
|---|---|
Срок ожидания начальной операции (QR-код, ссылка на оплату, переход в банк); по его истечении приходит expired | 20 минут |
| Частота проверки состояния | раз в 5 секунд, интервал задаёт сервер |
| Задержка подтверждения начального шага | до полутора минут |
Истечение таймера в виджете не означает отмену банковского платежа.
При повторном открытии в пределах срока действия используется уже начатая операция того же способа. При наличии сохранённых данных сервер возвращает прежний QR-код или ссылку.
Для бесплатной СБП-привязки после истечения срока сервер может запросить новую привязку. Для уже начатого платного первого платежа повторная авторизация не отправляется — это относится и к СБП, и к Alfa Pay, и к SberPay. Таймаут или отсутствие ссылки не доказывают, что платёж не был проведён.
До создания новой подписки необходимо проверить результат предыдущей операции по серверным данным и уведомлениям. Не вызывайте CreateSubscription автоматически после ошибки или истечения таймера.
Перезагрузка страницы партнёра, если она заново вызывает CreateSubscription, создаёт новую подписку с новым токеном — при любом способе оплаты. Прежняя операция при этом не отменяется: QR СБП остаётся действующим, начатый платёж может завершиться, и подтверждение по ним относится к прежней подписке, а не к той, что на экране. При проверках это главный источник путаницы.
6.8 Первое списание и пробный период
Момент первой оплаты определяется условиями созданной подписки с учётом BillingStartTimeUtc и настроек проекта. BillingStartTimeUtc передаётся в CreateSubscription в формате гггг-мм-дд чч:мм, время UTC.
| Условия подписки | Доступные способы | Начальный шаг |
|---|---|---|
| Первый период оплачивается при оформлении | Подключённые СБП, Alfa Pay, SberPay | Первый платёж с сохранением привязки для последующих списаний |
| Первая оплата отложена | В этой версии только СБП | Привязка без списания; первый платёж впоследствии запускает платформа по расписанию |
Бесплатный пробный период — промежуток до первой запланированной оплаты, в течение которого при оформлении выполняется только привязка. Минимальная длительность может быть ограничена настройками проекта.
Для отложенной оплаты нужна поддержка бесплатной СБП-привязки у подключённого платёжного провайдера. Само наличие BillingStartTimeUtc не гарантирует, что этот сценарий доступен для проекта. Alfa Pay и SberPay при отложенной первой оплате не предлагаются.
Тип уже начатой операции не меняется при повторном открытии: позднее открытие подписки с отложенной оплатой не превращает начальную бесплатную привязку в платный первый шаг.
6.9 Требования к стороне партнёра
CreateSubscriptionвызывается только со своего сервера: пароль проекта и подпись не должны попадать в браузер. Формула подписи — Подпись запросов.- Адреса, с которых партнёр вызывает API, должны быть в списке разрешённых на нашей стороне.
- Страница с виджетом должна работать по
https, иначе браузер заблокирует встраивание.
6.10 Порядок проверки
Первый платёж при оформлении — СБП, Alfa Pay или SberPay
- Создайте отдельную подписку с оплатой первого периода при оформлении.
- Откройте виджет и выберите проверяемый способ.
- Для СБП проверьте QR-код или переход в банк; для Alfa Pay и SberPay — переход на страницу оплаты банка.
- Подтвердите платёж в банке.
- Проверьте результат виджета, серверное подтверждение активации, фактическое первое списание и сохранённую привязку.
- Убедитесь, что первый период учтён один раз и следующая оплата запланирована корректно.
- Отдельно проверьте регулярное списание и отмену подписки.
Отложенная первая оплата — СБП
- Создайте отдельную подписку с отложенной первой оплатой на проекте с поддержкой бесплатной привязки.
- Убедитесь, что Alfa Pay и SberPay не предлагаются.
- Подтвердите привязку через СБП.
- Проверьте отсутствие списания при оформлении, результат виджета, активацию и сохранённую привязку.
- Проверьте первое списание в запланированное время.
- Отдельно проверьте отмену подписки.
Собственные кнопки магазина
- Создайте отдельную подписку и получите её токен.
- Откройте маршрут из §6.12 с конкретным
method. - Убедитесь, что СБП открывает экран оплаты без выбора способа, а Alfa Pay и SberPay перенаправляют на банк.
- Подтвердите операцию и проверьте её результат по серверным данным.
- Проверьте повторное открытие тем же способом: оно не должно создавать повторный платный первый платёж.
Для каждого способа используйте отдельную подписку. Перед созданием новой проверьте результат предыдущей операции. Открытие QR-кода или страницы банка ещё не подтверждает полный платёжный цикл.
6.11 Закрытие виджета и отмена подписки
Закрытие модального окна через крестик, Escape или SbpWidget.close() и удаление виджета через destroy() прекращают показ интерфейса. Они не отменяют подписку, не отзывают привязку и не отменяют уже отправленный платёж. Колбэк onClose уведомляет только о закрытии окна.
Для отмены подписки используйте серверный API отмены подписки — метод CancelSubscription в разделе Методы управления подпиской. Для YP платформа при обработке отмены отзывает сохранённый платёжный токен или привязку у провайдера до сохранения локального статуса Cancelled. Если привязки нет, внешний отзыв не требуется. Если отзыв не подтверждён из-за ошибки провайдера или связи, обработка отмены останавливается до изменения статуса.
Принятие запроса API к обработке не следует считать самостоятельным подтверждением завершённой отмены: итог нужно проверять по состоянию подписки и серверным уведомлениям. Отмена подписки не является возвратом денег за ранее проведённые платежи.
6.12 Собственные кнопки магазина — redirect-маршрут
Партнёр может разместить свои кнопки способов оплаты. Подписка создаётся на backend партнёра вызовом CreateSubscription; в ссылки передаётся токен из AuthorizeUrl.
GET https://subs.digitalspb.com/Cards/Sbp/pay?token=TOKEN&method=sbp
GET https://subs.digitalspb.com/Cards/Sbp/pay?token=TOKEN&method=alfapay
GET https://subs.digitalspb.com/Cards/Sbp/pay?token=TOKEN&method=sberpay| Параметр | Обязателен | Описание |
|---|---|---|
token | Да | 32 шестнадцатеричных символа из AuthorizeUrl, без дефисов |
method | Да | sbp, alfapay или sberpay; способ должен быть доступен для этой подписки |
Маршрут проверяет доступность метода и возвращает HTTP 302:
sbp— на экран СБП-виджета с выбранным способом, без экрана выбора;alfapayиsberpay— на HTTP(S)-страницу оплаты банка.
При выборе банковского способа обращение к маршруту запускает начальную платёжную операцию. Это не диагностический запрос и не запрос списка способов. СБП запускает операцию при загрузке целевого экрана.
<a href="https://subs.digitalspb.com/Cards/Sbp/pay?token=TOKEN&method=sbp">Оплатить через СБП</a>
<a href="https://subs.digitalspb.com/Cards/Sbp/pay?token=TOKEN&method=alfapay">Оплатить через Alfa Pay</a>
<a href="https://subs.digitalspb.com/Cards/Sbp/pay?token=TOKEN&method=sberpay">Оплатить через SberPay</a>В примере TOKEN нужно заменить настоящим токеном. При программном формировании URL используйте encodeURIComponent для значений параметров. Токен является чувствительными данными и присутствует в URL; его отсутствие в журналах не гарантируется.
Не открывайте эти ссылки автоматически, не запускайте предварительную загрузку и не обращайтесь к ним для проверки доступности. Переход должен следовать явному действию плательщика. Список методов получают через GET /Cards/Sbp/methods с заголовком Authorization: Bearer TOKEN; этот запрос не создаёт платёж.
После начала операции смена метода по тому же токену запрещена. Повторное открытие платной операции не должно создавать новую авторизацию. Для другого способа нужен новый токен, но прежде необходимо проверить исход предыдущей операции.
Неверный токен или неизвестный/недоступный метод приводят к отказу HTTP 400. Ошибка обращения к платёжному провайдеру может приводить к HTTP 500. Маршрут предназначен для навигации браузера и при отказе возвращает HTML-страницу, а не JSON-контракт.
Переход к банку через этот маршрут не создаёт экземпляр SDK: для Alfa Pay и SberPay не следует ожидать колбэков onSuccess / onFail от SbpWidget. Результат операции проверяется по серверным уведомлениям и состоянию подписки. Закрытие страницы не отменяет платёж или подписку.