Виджеты и бейджи Яндекс Пэй для Tilda

Вступление

На этой странице Вы найдете инструкцию по размещению готовых виджетов и бейджей Яндекс Пэй / Сплит на сайте вашего интернет-магазина, отображающих информацию об оплате в рассрочку или размере кешбэка баллами Плюса.
Если вам требуется полная документация по работе виджетов и бейджиков, ее можно найти по кнопке ниже ↓
Инструкция предназначена для опытного веб-мастера, и описывает обязательные этапы подключения, а также наши рекомендации.
Перед началом установки кода, убедитесь что Яндекс включил для Вас боевой режим приема платежей.

Какие виджеты можно установить?

Команда Яндекс подготовила несколько готовых решений Виджетов, которые являются частью большого SDK фронтенда.
Список рекомендуемых к установке типов виджетов, их описание и интерактивное демо расположены ниже.
Simple - виджет содержит бейджи Сплита и кешбэка, при нажатии на которые, вызывается всплывающее окно с подробной информацией.

SPLIT + CARD
BnplPreview - виджет показывает информацию только про Сплит, есть возможность переключить для изучения сроки оплаты на 2/4/6 месяцев.

SPLIT
Info - самый подробный виджет, содержит информацию об оплате через Сплит на 2 месяца и/или размер кешбэка баллами Плюса.

SPLIT + CARD / SPLIT / CARD
Для установки на магазин в Tilda подойдут виджеты без отображения кнопки покупки, при этом все остальные настройки доступны в полном объеме.

Функция добавления виджета

Функция принимает имя селектора элемента в качестве аргумента, с его помощью мы определим, в каком контейнере будем размещать виджет.
Для того, чтобы сформировать данные платежа, а именно в последствии создать платежную сессию, нам необходимо получить сумму корзины (для отображения виджета на странице корзины/чекаута) или цену товара (для отображения виджета на странице/попапе с товаром).

Логика работы

Далее мы формируем данные платежа и создаем платежную сессию с их использованием. Внутри созданной сессии мы добавляем отображение виджета, а также вешаем обработчик события на закрытие всплывающего окна, для последующего удаления созданного виджета.
function addWidget(selector){
	const YaPay = window.YaPay;
	// Получаем сумму/цену - для точного поиска используем переданный селектор
	let priceNode; // объявляем переменную в которой будем хранить элемент содержащий сумму/цену
	// Ниже мы последовательно пытаемся найти существующий элемент с суммой/ценой
	priceNode = document.querySelector(`${selector} .js-product-price`); // селектор для страницы/попапа с товаром в большинстве стандартных блоков Tilda
	if (priceNode === null){
		priceNode = document.querySelector(`${selector} .t706__cartwin-prodamount-wrap .t706__cartwin-prodamount-price`); // стандартный селектор для корзины/чекаута блока ST100
		if (priceNode === null){
			priceNode = document.querySelector(`${selector} .t706__sidebar-prodamount .t706__cartwin-prodamount-price`); // стандартный селектор для корзины в сайдбаре в блоке ST100
			if (priceNode === null){
				priceNode = document.querySelector(`${selector} .t1025__price-wrapper .t1025__price-value`); // стандартный селектор для страницы/попапа с товаром в блоке ST330
			}
		}
	}
	let amount = priceNode.textContent.replace(/\s/g, ''); // удаляем в полученной сумме/цене разделитель числового разряда (пробелы)
	// Формируем данные платежа
	const paymentData = {
		env: YaPay.PaymentEnv,
		version: 4,
		currencyCode: YaPay.CurrencyCode.Rub,
		merchantId: '<YOUR_MERCHANT_ID>', // Merchant ID (идентификатор клиента из раздела настройки в кабинете Яндекс Пэй)
		totalAmount: amount,
		availablePaymentMethods: ['SPLIT', 'CARD'] // влияет на отображение информации про кешбэк (также за это отвечает настройка в кабинете Яндекс Пэй)
	};

	function onPayButtonClick(){}
	// Создаем платежную сессию
	YaPay.createSession(paymentData, {
		onPayButtonClick: onPayButtonClick
	})
	.then(function(paymentSession){
		// Добавляем виджет и указываем для него настройки
		paymentSession.mountWidget(document.querySelector(`${selector} .omni-widget`), {
			widgetType: YaPay.WidgetType.Simple,
			widgetTheme: YaPay.WidgetTheme.Light,
			borderRadius: 0
		});
		// Удаляем виджет по событию закрытия всплывающего окна
		document.addEventListener('popupHidden', function(){
			paymentSession.unmountWidget(document.querySelector(`${selector} .omni-widget`),{});
		});
	})
	.catch(function(err){
		console.error(err);
	});
}

Настройка функции

Функция, для поиска цены и контейнера размещения, использует стандартные классы следующих блоков Tilda (это значит, что функция будет работать с этими блоками, сразу после корректного размещения на сайте):
ST300 - Карточки продуктов + Popup с подробной информацией

ST305N - Карточки продуктов с кнопками + Popup с подробной информацией

ST310N - Карточки продуктов с кнопками и фоном + Popup с подробной информацией

ST315N - Колонки с продуктами на всю ширину экрана + Popup с подробной информацией

ST320N - Карточки продуктов с кнопками и фильтрами сбоку + Popup с подробной информацией

ST330 - Список товаров в одну колонку + Popup с подробной информацией

В блоках, указанных выше, поддерживается отображение виджета во всплывающем окне и на странице товара.
ST100 - Корзина с формой заказа

В данном блоке поддерживается отображение виджета на странице оформления заказа, а также во всех представлениях корзины, при включенном режиме "Оформление заказов в несколько этапов").
Если вам требуется разместить виджеты в другом месте/блоке, необходимо доработать функцию, добавив дополнительное условие для поиска цены и изменив селекторы контейнера для размещения, находящиеся в передаваемом в аргументах элементе.

Создание контейнеров для размещения виджета

Пример кода, расположенный ниже, включает в себя два основных этапа: создание и размещение контейнеров для последующего запуска в них виджетов Яндекс Пэй/Сплит; подписку на события, в какой момент запускать размещение виджетов.
В момент, когда модуль магазина полностью прогрузится, в каждый требуемый блок Tilda, добавляем контейнер, в который, с помощью функции, разместим виджеты Яндекс Пэй/Сплит. Размещать виджеты следует после того, как всплывающее окно полностью откроется, для этого используем задержку выполнения функции.

В комментариях к коду можно найти, как определить, какое окно отобразится по событию открытия попапа.

Логика работы

// Код ниже отвечает за размещение виджета на странице товара
document.addEventListener('DOMContentLoaded', function(){ // выполняем операции по событию загрузки страницы
	let widgetSnippetWrapper = document.createElement('div'); // создаем контейнер для размещения виджета на странице товара
	widgetSnippetWrapper.classList = 'omni-widget'; // присваиваем контейнеру общий класс для работы со стилями
	let snippetWrapper = document.querySelector('.t-store__product-snippet .js-store-price-wrapper'); // получаем элемент в который будем помещать наш контейнер
	// если получили элемент для размещения виджета - запускаем нашу функцию
	if (snippetWrapper !== null){
		snippetWrapper.appendChild(widgetSnippetWrapper); // перед запуском, размещаем наш контейнер в неободимое место в блоке
		addWidget('.t-store__product-snippet'); // передаем функции имя селектора элемента в котором разместиили контейнер для виджета, и где содержится цена товара для данных платежной сессии
	}
});
// Код ниже отвечает за размещение виджета во всплывающих окнах с товаром, а также корзине/чекауте
document.addEventListener('tStoreRendered', function(){ // выполняем операции по событию загрузки модуля интернет-магазина
	let widgetPopupWrapper = document.createElement('div'); // создаем контейнер для размещения виджета на попапе товара
	widgetPopupWrapper.classList = 'omni-widget'; // присваиваем контейнеру общий класс для работы со стилями
	let popupWrapper = document.querySelector('.t-store__product-popup .js-store-price-wrapper');
	if (popupWrapper !== null){
		popupWrapper.appendChild(widgetPopupWrapper); // если блок размещен на странице, вставляем в него наш контейнер
	}
	let widgetPopupProdWrapper = document.createElement('div'); // создаем контейнер для размещения виджета на странице товара в блоке ST330
	widgetPopupProdWrapper.classList = 'omni-widget'; // присваиваем контейнеру общий класс для работы со стилями
	let popupProdWrapper = document.querySelector('.t-store__product-popup .t1025__price-wrapper');
	if (popupProdWrapper !== null){
		popupWrapper.appendChild(widgetPopupProdWrapper); // если блок размещен на странице, вставляем в него наш контейнер
	}
	let widgetCartWrapper = document.createElement('div'); // создаем контейнер для размещения виджета в корзине
	widgetCartWrapper.classList = 'omni-widget'; // присваиваем контейнеру общий класс для работы со стилями
	let cartPageWrapper = document.querySelector('.t706__cartpage-content .t706__cartpage-info-wrapper');
	if (cartPageWrapper !== null){
		cartPageWrapper.appendChild(widgetCartWrapper);
	}
	let widgetSidebarWrapper = document.createElement('div'); // создаем контейнер для размещения виджета в сайдбаре корзины
	widgetSidebarWrapper.classList = 'omni-widget'; // присваиваем контейнеру общий класс для работы со стилями
	let cartSidebarWrapper = document.querySelector('.t706__sidebar-content .t706__sidebar-prodamount-wrap');
	if (cartSidebarWrapper !== null){
		cartSidebarWrapper.appendChild(widgetSidebarWrapper);
	}
	let widgetCheckoutWrapper = document.createElement('div'); // создаем контейнер для размещения виджета в чекауте
	widgetCheckoutWrapper.classList = 'omni-widget'; // присваиваем контейнеру общий класс для работы со стилями
	let checkoutWrapper = document.querySelector('.t706__cartwin-content .t706__cartwin-prodamount-wrap');
	if (checkoutWrapper !== null){
		checkoutWrapper.appendChild(widgetCheckoutWrapper);
	}
});
// Так как, во всплывающих окнах, данные имеют динамический характер, то мы запускаем добавление виджета в момент открытия попапа
document.addEventListener('popupShowed', function(){ // подписываемся на событие открытия всплывающего окна
	setTimeout(function(){
		// Ниже мы определяем, какое окно было открыто, и передаем функции имя селектора элемента в котором хотим разместить виджет
		let popupNode = document.querySelector('.t-body_popupshowed'); // окно с информацией о товаре
		let cartPageNode = document.querySelector('.t706__body_cartpageshowed'); // окно с корзиной
		let cartSidebarNode = document.querySelector('.t706__body_cartsidebarshowed'); // окно с сайдбаром корзины
		let checkoutNode = document.querySelector('.t706__body_cartwinshowed'); // окно с чекаутом (без этапа корзины)
		if (popupNode !== null){
			addWidget('.t-store__product-popup');
		}
		if (cartPageNode !== null){
			addWidget('.t706__cartpage-content');
		}
		if (cartSidebarNode !== null){
			addWidget('.t706__sidebar-content');
		}
		if (checkoutNode !== null){
			addWidget('.t706__cartwin-content');
		}
	}, 200);
});

Установка универсального скрипта

Так как скрипт взаимодействует с динамическим контентом, а именно со страницами товаров, его необходимо устанавливать в разделе настроек сайта: Настройки сайта Еще HTML-код для вставки внутрь HEAD Редактировать код Вставьте код универсального скрипта Сохранить

Не забудьте установить ваш Merchant ID
и переопубликовать все страницы.
Если представленный код не заработал, Вы можете обратиться за помощью к нашим специалистам.
<script src="https://pay.yandex.ru/sdk/v1/pay.js"></script>
<script>
// Version 1.4
const MID = '<YOUR_MERCHANT_ID>'; // Merchant ID (идентификатор клиента из настроек Яндекс Пэй)
const METHODS = ['SPLIT', 'CARD']; // ['SPLIT', 'CARD'] - для отображения кешбэк + сплит, ['SPLIT'] - для отображения только сплит
const BNPL_BADGES = true; // true - показать бейджи сплита, false - скрыть бейджи сплита
const CASHBACK_BADGE = true; // true - показать бейджи кешбэка, false - скрыть бейджи кешбэка
const THEME = 'light'; // dark - темная тема оформления, light - светлая тема оформления
function addBadges(){
    let productsNodes = [];
        productsNodes = document.querySelectorAll('.t-store__card');
    if (productsNodes.length == 0){
        productsNodes = document.querySelectorAll('.t776__col');
    }
    productsNodes.forEach((product, index) => {
        if (product.querySelector('.omni-badge') === null){
            let badgeWrapper = document.createElement("div");
            let omniBadge = `omni-badge-${index}`;
            badgeWrapper.id = omniBadge;
            badgeWrapper.classList = 'omni-badge';
            let priceWrapper = null;
            priceWrapper = product.querySelector('.js-store-price-wrapper');
            if (priceWrapper === null){
                priceWrapper = product.querySelector('.t776__btn-wrapper');
            }
            priceWrapper.appendChild(badgeWrapper);
            let priceNode = null;
            priceNode = product.querySelector('.js-product-price');
            if (priceNode === null){
                priceNode = product.querySelector('.t776__btn_second td');
            }
            let price = priceNode.textContent.replace(/\s/g, '').match(/\d+/g);
            if (BNPL_BADGES){
                YaPay.mountBadge(document.querySelector(`#${omniBadge}`),
                {
                    type: 'bnpl',
                    theme: THEME,
                    amount: price[0],
                    size: 's', // размер бейджика (s, m, l)
                    variant: 'simple', // simple - простой вариант бейджа, detailed - детальный вариант бейджа
                    color: 'grey', // цветовое оформление (primary, green, grey, transparent)
                    align: 'center', // выравнивание информации (left, center, right)
                    merchantId: MID
                });
            }
            if (CASHBACK_BADGE){
                YaPay.mountBadge(document.querySelector(`#${omniBadge}`),
                {
                    type: 'cashback',
                    theme: THEME,
                    amount: price[0], // передаем цену для расчета
                    size: 's', // размер бейджика (s, m, l)
                    variant: 'default', // default - вариант бейджа по умолчанию, compact - компактный вариант бейджа
                    color: 'primary', // цветовое оформление (primary, grey, transparent)
                    align: 'center', // выравнивание информации (left, center, right)
                    merchantId: MID
                });
            }
        }
    });
}
document.addEventListener('tStoreRendered', addBadges);
document.addEventListener('DOMContentLoaded', addBadges);
function addWidget(selector, popupWidget = false){
    const YaPay = window.YaPay;
    let activeSession = null;
    let priceNode = null;
    priceNode = document.querySelector(`${selector} .js-product-price`);
    if (priceNode === null){
        priceNode = document.querySelector(`${selector} .t706__cartwin-prodamount-wrap .t706__cartwin-prodamount-price`);
    }
    if (priceNode === null){
        priceNode = document.querySelector(`${selector} .t706__sidebar-prodamount .t706__cartwin-prodamount-price`);
    }
    if (priceNode === null){
        priceNode = document.querySelector(`${selector} .t1025__price-wrapper .t1025__price-value`);
    }
    let amount = priceNode.textContent.replace(/\s/g, '').match(/\d+/g);
    const paymentData = {
        env: YaPay.PaymentEnv,
        version: 4,
        currencyCode: YaPay.CurrencyCode.Rub,
        merchantId: MID,
        totalAmount: amount[0],
        availablePaymentMethods: METHODS
    };
    function onPayButtonClick(){}
    YaPay.createSession(paymentData, {
        onPayButtonClick: onPayButtonClick
    })
    .then(function(paymentSession){
        activeSession = paymentSession;
        paymentSession.mountWidget(document.querySelector(`${selector} .omni-widget`), {
            //какой тип виджеты выводим?
            widgetType: YaPay.WidgetType.BnplPreview,
            //widgetTheme: YaPay.WidgetTheme.Light,
            //padding: YaPay.WidgetPaddingType.None,
            //borderRadius: 5
        });
        if (popupWidget){
            document.addEventListener('popupHidden', function(){
                paymentSession.unmountWidget(document.querySelector(`${selector} .omni-widget`),{});
            });
        }
    })
    .catch(function(err){
        console.error(err);
    });
    let observer = new MutationObserver(mutationRecords => {
        let amountContent = null;
        try {
            amountContent = mutationRecords[0].addedNodes[0].textContent;
            if (!amountContent){
                amountContent = mutationRecords[0].addedNodes[0].innerText;
            }
        } catch (err) {
            console.log(err);
        }
        let newAmount = amountContent.replace(/\s/g, '');
        activeSession.update(async function(){
            return {
                totalAmount: newAmount,
            };
        });
    });
    observer.observe(priceNode.parentNode, {
        childList: true, subtree: true
    });
}
document.addEventListener('DOMContentLoaded', function(){
    let widgetSnippetWrapper = document.createElement('div');
    widgetSnippetWrapper.classList = 'omni-widget';
    let snippetWrapper = null;
    snippetWrapper = document.querySelector('.t-store__product-snippet .js-store-price-wrapper');
    if (snippetWrapper === null){
        snippetWrapper = document.querySelector('.t744 .js-store-price-wrapper');
        if (snippetWrapper !== null){
            snippetWrapper.appendChild(widgetSnippetWrapper); 
            addWidget('.t744');
        }
    } else {
        snippetWrapper.appendChild(widgetSnippetWrapper); 
        addWidget('.t-store__product-snippet');
    }
    let widgetCheckoutWrapper = document.createElement('div');
    widgetCheckoutWrapper.classList = 'omni-widget';
    let checkoutWrapper = document.querySelector('.t706__cartwin-content .t706__cartwin-prodamount-wrap');
    if (checkoutWrapper !== null){
        checkoutWrapper.appendChild(widgetCheckoutWrapper);
    }
});
document.addEventListener('tStoreRendered', function(){
    let widgetPopupWrapper = document.createElement('div');
    widgetPopupWrapper.classList = 'omni-widget';
    let popupWrapper = document.querySelector('.t-store__product-popup .js-store-price-wrapper');
    if (popupWrapper !== null){
        popupWrapper.appendChild(widgetPopupWrapper);
    }
    let widgetPopupProdWrapper = document.createElement('div');
    widgetPopupProdWrapper.classList = 'omni-widget';
    let popupProdWrapper = document.querySelector('.t-store__product-popup .t1025__price-wrapper');
    if (popupProdWrapper !== null){
        popupWrapper.appendChild(widgetPopupProdWrapper);
    }
    let widgetCartWrapper = document.createElement('div');
    widgetCartWrapper.classList = 'omni-widget';
    let cartPageWrapper = document.querySelector('.t706__cartpage-content .t706__cartpage-info-wrapper');
    if (cartPageWrapper !== null){
        cartPageWrapper.appendChild(widgetCartWrapper);
    }
    let widgetSidebarWrapper = document.createElement('div');
    widgetSidebarWrapper.classList = 'omni-widget';
    let cartSidebarWrapper = document.querySelector('.t706__sidebar-content .t706__sidebar-prodamount-wrap');
    if (cartSidebarWrapper !== null){
        cartSidebarWrapper.appendChild(widgetSidebarWrapper);
    }
    let widgetCheckoutWrapper = document.createElement('div');
    widgetCheckoutWrapper.classList = 'omni-widget';
    let checkoutWrapper = document.querySelector('.t706__cartwin-content .t706__cartwin-prodamount-wrap');
    if (checkoutWrapper !== null){
        checkoutWrapper.appendChild(widgetCheckoutWrapper);
    }
});
document.addEventListener('popupShowed', function(){
    setTimeout(function(){
        let popupNode = document.querySelector('.t-body_popupshowed');
        let cartPageNode = document.querySelector('.t706__body_cartpageshowed');
        let cartSidebarNode = document.querySelector('.t706__body_cartsidebarshowed');
        let checkoutNode = document.querySelector('.t706__body_cartwinshowed');
        if (popupNode !== null){
            addWidget('.t-store__product-popup', true);
        }
        if (cartPageNode !== null){
            addWidget('.t706__cartpage-content', true);
        }
        if (cartSidebarNode !== null){
            addWidget('.t706__sidebar-content', true);
        }
        if (checkoutNode !== null){
            addWidget('.t706__cartwin-content', true);
        }
    }, 200);
});
</script>
<style>
.omni-badge {
    display: flex;
    justify-content: center;
    flex-wrap: wrap;
}
.omni-badge yandex-pay-badge {
    margin-top: 0.75rem;
}
.omni-widget .ya-pay-widget {
    margin-top: 20px!important;
    margin-bottom: 0!important;
}
.t706__sidebar-prodamount-wrap {
    padding-top: 20px!important;
    padding-bottom: 20px!important;
}
.t706__cartpage-info-wrapper .omni-widget .ya-pay-widget {
    margin-top: -30px!important;
    margin-bottom: 20px!important;
}
.t706__cartpage-totals {
    padding-top: 20px!important;
}
</style>

Демонстрация работы

Для демонстрации работы виджетов и бейджей, мы создали демо-магазин, там всегда установлена самая последняя версия универсального скрипта.