Банковская карта на сайте (через удобные платежные системы)
Другие доступные способы
В данное время оплата наличными не возможна
Все платежи защищены и осуществляются через проверенные платежные системы
Время обработки заказа
Условия возврата и обмена при необходимости
Контактные данные для уточнений по доставке и оплате
Доставка
Доставка почтой или транспортной компанией (с указанием возможных служб)
Бесплатно при заказе от 5 000 рублей суммы
Стоимость по тарифам службы доставки (с указанием тарифа)
Обычно от 1 до 7 рабочих дней
Конкретные сроки могут зависеть от региона и выбранного способа доставки
Возможность доставки в выходные/праздничные дни
Возможность отслеживания посылки
Доставка осуществляется только по 100% предоплате, отправка заказа наложенным платежом невозможна! Заказ можно оплатить на сайте онлайн банковской картой, электронными деньгами или любым другим удобным для вас способом в течение 3-х суток с момента его размещения. Если оплата не была произведена в данные сроки, заказ аннулируется.
(function () {
// =========================
// НАСТРОЙКИ (заполняет мерчант)
// =========================
const DesignVariant = {
BLUE: 'blue',
GREY: 'grey',
WHITE: 'white',
};
const MaceLoyaltySettings = {
// ОБЯЗАТЕЛЬНО: id компании в Mace Loyalty
clientId: '',
// ОБЯЗАТЕЛЬНО: секрет для доступа к API
secret: '',
// ОБЯЗАТЕЛЬНО: URL оформления карты (куда отправляем клиента, если карты нет)
cardIssueURL: '',
// Опционально: идентификатор оплаты наличными "cash" при котором карта не отображается, можно дополнить другими видами оплаты или оставить переменную пустой
forbiddenPayment: ['cash'],
// Опционально: BLUE (по умолчанию), GREY, WHITE
designVariant: DesignVariant.BLUE,
};
const API_HOST = 'https://maceloyalty.ru/tilda/api/v1';
let detectedFontFamily = 'sans-serif';
let promoField = null;
let hasPromoField = false; // есть ли блок с промокодом в корзине
const HiddenFields = {
cardInstanceId: null,
amount: null,
type: null,
companyId: null,
useBonusAmount: null,
};
const MaceLoyaltyState = {
card: null,
phone: null,
mode: null,
bonusesToDeposit: 0,
bonusesToWithdrawal: 0,
totalCashback: 0,
useBonusAmount: 0,
};
// =========================
// ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ
// =========================
function logError(msg) {
console.error('[MaceLoyalty]', msg);
}
function logInfo(msg) {
console.log('[MaceLoyalty]', msg);
}
function validateSettings() {
const { clientId, secret, cardIssueURL } = MaceLoyaltySettings;
if (!clientId || !secret || !cardIssueURL) {
logError(
'Интеграция не активна: отсутствуют обязательные настройки (clientId, secret, cardIssueURL).'
);
return false;
}
return true;
}
// =========================
// ПРОВЕРКА ГОТОВНОСТИ DOM / TILDA
// =========================
function isMaceLoyaltyEnvReady() {
const hasForm = !!document.querySelector('.t706 form');
const hasDeliveryGroup = !!document.querySelector('.t-input-group_dl');
const hasPromoWrapper = !!document.querySelector('.t-inputpromocode__wrapper'); // просто фиксируем, но не требуем
const hasPhoneInput = !!document.querySelector('.t706 .t-input-group_ph');
const hasTcart = typeof window.tcart === 'object' && window.tcart !== null;
const hasRedrawTotal = typeof window.tcart__reDrawTotal === 'function';
return (
hasForm &&
hasDeliveryGroup &&
hasPhoneInput &&
hasTcart &&
hasRedrawTotal
);
}
function bootstrapMaceLoyalty(attempt) {
attempt = attempt || 0;
if (!validateSettings()) {
return;
}
if (isMaceLoyaltyEnvReady()) {
logInfo('Окружение готово, запускаем initMaceLoyalty');
initMaceLoyalty();
return;
}
if (attempt > 120) {
logError('Окружение так и не подготовилось после 60 секунд, прекращаем попытки.');
return;
}
logInfo('Окружение ещё не готово, пробуем снова через 500ms (попытка ' + attempt + ')');
setTimeout(function () {
bootstrapMaceLoyalty(attempt + 1);
}, 500);
}
function getSelectedPaymentSystem() {
const checked = document.querySelector('input[name="paymentsystem"]:checked');
return checked ? checked.value : null;
}
function isPaymentForbidden() {
const current = getSelectedPaymentSystem();
const forbidden = Array.isArray(MaceLoyaltySettings.forbiddenPayment)
? MaceLoyaltySettings.forbiddenPayment
: [];
if (!current) return false;
return forbidden.indexOf(current) !== -1;
}
function getDesignClass() {
const variant = MaceLoyaltySettings.designVariant || DesignVariant.BLUE;
switch (variant) {
case DesignVariant.GREY:
return 'maceloyalty--grey';
case DesignVariant.WHITE:
return 'maceloyalty--white';
case DesignVariant.BLUE:
default:
return 'maceloyalty--blue';
}
}
function detectTildaFont() {
const el = document.querySelector('.t-name');
if (!el) return 'sans-serif';
const style = window.getComputedStyle(el);
const font = style.fontFamily || '';
if (!font || font.trim() === '') return 'sans-serif';
return font;
}
function getBonusWord(n) {
const abs = Math.abs(n);
const lastTwo = abs % 100;
const last = abs % 10;
if (lastTwo >= 11 && lastTwo <= 14) return 'бонусов';
if (last === 1) return 'бонус';
if (last >= 2 && last <= 4) return 'бонуса';
return 'бонусов';
}
function formatBonuses(n) {
const number = Number.isFinite(n) ? n : 0;
const formatted = number.toLocaleString('ru-RU');
return `${formatted} ${getBonusWord(number)}`;
}
// считает общее кол-во бонусов, сколько начислить и сколько можно списать
function calculateCashbackMetrics(card) {
const cartValue = window.tcart.prodamount;
const totalCashback =
card.balance && typeof card.balance.totalCashback === 'number'
? card.balance.totalCashback
: 0;
const modifier =
card.level && typeof card.level.modifier === 'number'
? card.level.modifier
: 0;
const maxUsePercent =
card.balance && typeof card.balance.maxUsePercent === 'number'
? card.balance.maxUsePercent
: 0;
const bonusesToDeposit = Math.floor(modifier * cartValue / 100);
const bonusesToWithdrawal = Math.min(maxUsePercent * cartValue / 100, totalCashback);
return {
totalCashback,
bonusesToDeposit,
bonusesToWithdrawal,
};
}
// Меняем подписи в итоговом блоке корзины
function updateCartTotalsLabels() {
const totalInfo = document.querySelector('.t706__cartwin-totalamount-info');
if (!totalInfo) return;
// есть ли наш промокод
const hasPersonalPromo =
!!(window.tcart &&
window.tcart.promocode &&
window.tcart.promocode.promocode === 'PERSONALCODE');
if (!hasPersonalPromo) return;
const cardType = (
MaceLoyaltyState.card &&
MaceLoyaltyState.card.balance &&
(MaceLoyaltyState.card.balance.type || '')
).toLowerCase();
if (!cardType) return;
const labels = totalInfo.querySelectorAll('.t706__cartwin-totalamount-info_label');
labels.forEach(label => {
const text = (label.textContent || '').trim();
if (cardType === 'cashback') {
// карта бонусная
if (text === 'Скидка:') {
label.textContent = 'Скидка бонусами:';
}
} else if (cardType === 'discount') {
// карта с процентной скидкой
if (text === 'Промокод:') {
label.textContent = 'Скидка по карте лояльности:';
} else if (text === 'Скидка:') {
label.textContent = 'Сумма скидки:';
}
}
});
}
// Пересчитать состояние лояльности после изменения корзины
function recalcAfterCartChange() {
if (!MaceLoyaltyState.card || !window.tcart) return;
const card = MaceLoyaltyState.card;
const type = (card.balance.type || '').toLowerCase();
// ---- КАРТА CASHBACK ----
if (type === 'cashback') {
const { totalCashback, bonusesToDeposit, bonusesToWithdrawal } =
calculateCashbackMetrics(card);
const spendFloor = Math.floor(bonusesToWithdrawal);
const canUse = spendFloor > 0;
// Обновляем state
MaceLoyaltyState.totalCashback = totalCashback;
MaceLoyaltyState.bonusesToDeposit = bonusesToDeposit;
MaceLoyaltyState.bonusesToWithdrawal = bonusesToWithdrawal;
// Обновляем подписи на кнопках
const container = document.getElementById('maceloyalty');
if (container) {
const btnAdd = container.querySelector('[data-ml-mode="add"]');
const btnUse = container.querySelector('[data-ml-mode="use"]');
if (btnAdd) {
btnAdd.textContent = 'Накопить ' + formatBonuses(bonusesToDeposit);
}
if (btnUse) {
btnUse.textContent = 'Списать ' + formatBonuses(spendFloor);
if (!canUse) {
btnUse.classList.add('maceloyalty__btn--disabled');
btnUse.setAttribute('aria-disabled', 'true');
btnUse.setAttribute('tabindex', '-1');
} else {
btnUse.classList.remove('maceloyalty__btn--disabled');
btnUse.removeAttribute('aria-disabled');
btnUse.removeAttribute('tabindex');
}
}
}
// Если пользователь выбрал режим "Списать", нужно пересчитать скидку
if (MaceLoyaltyState.mode === 'useCashback') {
if (!canUse) {
// Больше нечего списывать — убираем промокод и обнуляем списание
removePromo();
MaceLoyaltyState.useBonusAmount = 0;
updateHiddenFields();
} else {
// Пересчитываем сумму списания
removePromo(); // убираем старый промокод/скидку
MaceLoyaltyState.useBonusAmount = spendFloor;
updateHiddenFields();
applyUseCashbak(card.balance.type, spendFloor); // применяем заново
}
}
}
// ---- КАРТА С ПРОЦЕНТНОЙ СКИДКОЙ ----
else if (type === 'discount') {
if (MaceLoyaltyState.mode === 'addPurchase') {
const modifier =
card.level && typeof card.level.modifier === 'number'
? card.level.modifier
: 0;
const percent = Math.round(modifier);
// Просто пересчитать скидку по проценту
removePromo();
applyUseCashbak(card.balance.type, percent);
}
}
}
function ensureHiddenFields() {
const form = document.querySelector('.t706 form');
if (!form) {
logError('Форма корзины ".t706 form" не найдена, скрытые поля не будут созданы.');
return null;
}
function ensureInput(name) {
let input = form.querySelector('input[name="' + name + '"]');
if (!input) {
input = document.createElement('input');
input.type = 'hidden';
input.name = name;
form.insertBefore(input, form.firstChild);
}
return input;
}
HiddenFields.cardInstanceId = ensureInput('maceloyalty_cardInstanceId');
HiddenFields.amount = ensureInput('maceloyalty_amount');
HiddenFields.type = ensureInput('maceloyalty_type');
HiddenFields.companyId = ensureInput('maceloyalty_companyId');
HiddenFields.useBonusAmount = ensureInput('maceloyalty_useBonusAmount');
return HiddenFields;
}
function updateHiddenFields() {
if (!MaceLoyaltyState.card) {
clearHiddenFields();
return;
}
const fields = HiddenFields.cardInstanceId ? HiddenFields : ensureHiddenFields();
if (!fields) return;
const cardInstanceId =
MaceLoyaltyState.card && MaceLoyaltyState.card.cardInstanceId
? MaceLoyaltyState.card.cardInstanceId
: '';
let amount = '';
if (window.tcart) {
// если применён наш промокод (useCashback или addPurchase),
// то берём итоговую сумму с учётом скидки/списания
if (
(MaceLoyaltyState.mode === 'useCashback' ||
MaceLoyaltyState.mode === 'addPurchase') &&
typeof window.tcart.prodamount_withdiscount === 'number'
) {
amount = window.tcart.prodamount_withdiscount;
} else if (typeof window.tcart.prodamount === 'number') {
// во всех остальных случаях — базовая сумма товаров
amount = window.tcart.prodamount;
}
}
const useBonusAmount =
MaceLoyaltyState.mode === 'useCashback' &&
typeof MaceLoyaltyState.useBonusAmount === 'number'
? MaceLoyaltyState.useBonusAmount
: '';
fields.cardInstanceId.value = cardInstanceId;
fields.amount.value = amount;
fields.type.value = MaceLoyaltyState.mode || '';
fields.companyId.value = MaceLoyaltySettings.clientId || '';
fields.useBonusAmount.value = useBonusAmount === '' ? '' : String(useBonusAmount);
}
function clearHiddenFields() {
const fields = HiddenFields.cardInstanceId ? HiddenFields : ensureHiddenFields();
if (!fields) return;
// очищаем значения hidden-полей
fields.cardInstanceId.value = '';
fields.amount.value = '';
fields.type.value = '';
fields.companyId.value = '';
if (fields.useBonusAmount) {
fields.useBonusAmount.value = '';
}
// сбрасываем состояние карты лояльности
MaceLoyaltyState.card = null;
MaceLoyaltyState.phone = null;
MaceLoyaltyState.mode = null;
MaceLoyaltyState.bonusesToDeposit = 0;
MaceLoyaltyState.bonusesToWithdrawal = 0;
MaceLoyaltyState.totalCashback = 0;
MaceLoyaltyState.useBonusAmount = 0;
}
// удаляем промокоды из корзины
// удаляем промокоды из корзины
function removePromo() {
if (!window.tcart) return;
const cartID = document
.querySelector('div[data-record-type="706"]')
?.getAttribute('id')
?.replace(/[^0-9]/g, '');
const promoGroup = document.querySelector('.t-input-group_pc');
const promoID = promoGroup?.getAttribute('data-input-lid');
// очищаем данные скидок в tcart
if (window.tcart.promocode) {
delete window.tcart.promocode;
}
if (window.tcart.prodamount_discountsum) {
delete window.tcart.prodamount_discountsum;
}
if (window.tcart.prodamount_withdiscount) {
delete window.tcart.prodamount_withdiscount;
}
if (window.tcart.delivery && window.tcart.delivery.price) {
window.tcart.amount = window.tcart.prodamount + window.tcart.delivery.price;
} else {
window.tcart.amount = window.tcart.prodamount;
}
// если промо-блок вообще есть на странице — восстанавливаем его
if (hasPromoField && promoField !== null) {
const promoWrapper = document.querySelector('.t-inputpromocode__wrapper');
if (promoWrapper) {
promoWrapper.innerHTML = promoField;
}
if (promoGroup) {
promoGroup.style.display = 'block';
}
if (
typeof window.t_input_promocode_init === 'function' &&
cartID &&
promoID
) {
window.t_input_promocode_init(cartID, promoID);
}
}
if (typeof window.tcart__saveLocalObj === 'function') window.tcart__saveLocalObj();
if (typeof window.tcart__reDrawProducts === 'function') window.tcart__reDrawProducts();
if (typeof window.tcart__updateTotalProductsinCartObj === 'function') window.tcart__updateTotalProductsinCartObj();
if (typeof window.tcart__reDrawTotal === 'function') window.tcart__reDrawTotal();
}
// применение промокода (списывание бонусов)
function applyUseCashbak(cardType, amountToSpend) {
// проводим проверки перед списыванием
if (amountToSpend == 0) {
logError(
'Попытка списать 0 рублей.'
);
return;
} else if (!amountToSpend || !cardType) {
logError(
'Не передано значение для списывания или тип карты.'
);
return;
}
// проверяем нет ли промокода в корзине
if (window.tcart && window.tcart.promocode) {
if (window.tcart.promocode.promocode && window.tcart.promocode.promocode == "PERSONALCODE") {
logInfo('Бонусы уже списаны, дополнительные действия не требуются.');
return;
}
// удаляем любые промокоды из корзины
removePromo();
}
let bonuspromo;
if (cardType == 'cashback') {
bonuspromo = {
"promocode": "PERSONALCODE",
"discountsum": String(amountToSpend),
"prodamount_discountsum": String(amountToSpend)
}
logInfo('Рассчитаны бонусы для карты типа "кэшбек"');
} else if (cardType == 'discount') {
bonuspromo = {
"promocode": "PERSONALCODE",
"discountpercent": String(amountToSpend)
}
logInfo('Рассчитаны бонусы для карты типа "дискаунт"');
} else {
logError('Не найден типа карты ' + cardType);
return;
}
window.t_input_promocode__addPromocode(bonuspromo);
if (hasPromoField) {
let promoBlock = document.querySelector('.t-input-group_pc');
if (promoBlock) {
promoBlock.style.display = 'none';
}
}
logInfo('Списаны бонусы: ' + amountToSpend);
}
// Вставляем простые базовые стили (дефолтный светло-серый + разные темы)
function injectBaseStylesOnce() {
if (document.getElementById('maceloyalty-base-styles')) return;
const style = document.createElement('style');
style.id = 'maceloyalty-base-styles';
style.textContent = `
/* цветовые темы (акцентные цвета для кнопок и текста) */
.maceloyalty--blue {
--ml-accent-bg: #2D68F8;
--ml-accent-border: none;
--ml-accent-text: #ffffff;
--ml-button-border: #ffffff;
--ml-button-active-text-color: #000000;
--ml-button-inactive-text-color: #ffffff;
--ml-loader-color: rgba(255,255,255,0.15);
--ml-loader-top-color: rgba(255,255,255,0.45);
background-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20345%20165'%3E%3Cdefs%3E%3ClinearGradient%20id='mlg'%20x1='0'%20y1='1'%20x2='1'%20y2='0'%3E%3Cstop%20stop-color='%23F5F7F8'/%3E%3Cstop%20offset='1'%20stop-color='%23DCE1E4'/%3E%3C/linearGradient%3E%3C/defs%3E%3Cg%20opacity='.4'%3E%3Crect%20opacity='.3'%20x='209.231'%20y='298.462'%20width='295.897'%20height='295.897'%20rx='5'%20transform='rotate(-135%20209.231%20298.462)'%20fill='url(%23mlg)'/%3E%3Crect%20opacity='.3'%20x='282.096'%20y='298.462'%20width='295.897'%20height='295.897'%20rx='5'%20transform='rotate(-135%20282.096%20298.462)'%20fill='url(%23mlg)'/%3E%3Crect%20opacity='.4'%20x='359.371'%20y='298.462'%20width='295.897'%20height='295.897'%20rx='5'%20transform='rotate(-135%20359.371%20298.462)'%20fill='url(%23mlg)'/%3E%3Crect%20opacity='.6'%20x='431.231'%20y='298.462'%20width='295.897'%20height='295.897'%20rx='5'%20transform='rotate(-135%20431.231%20298.462)'%20fill='url(%23mlg)'/%3E%3C/g%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: calc(100% + 1px);
background-size: cover;
}
.maceloyalty--grey {
--ml-accent-bg: #F5F5F6;
--ml-accent-border: none;
--ml-accent-text: #000000;
--ml-button-border: #000000;
--ml-button-active-text-color: #ffffff;
--ml-button-inactive-text-color: #000000;
--ml-loader-color: rgba(0,0,0,0.15);
--ml-loader-top-color: rgba(0,0,0,0.45);
background-image: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20130%20131'%3E%3Cpath%20d='M100.159%2026.8376%20103.13%2050.449%20125.168%2059.4296%20110.573%2078.226%20119.806%20100.159%2096.1946%20103.13%2087.214%20125.168%2068.4176%20110.573%2046.4841%20119.806%2043.5134%2096.1946%2021.4755%2087.214%2036.0705%2068.4176%2026.8376%2046.4841%2050.449%2043.5134%2059.4296%2021.4755%2078.226%2036.0705%20100.159%2026.8376Z'%20fill='%23EBEBEB'/%3E%3Cg%20fill='none'%20stroke='%23F5F5F6'%20stroke-width='4'%20stroke-linecap='round'%20stroke-linejoin='round'%3E%3Cpath%20d='M58%2088%2088%2058'/%3E%3Cpath%20d='M62%2066c2.21%200%204-1.79%204-4s-1.79-4-4-4-4%201.79-4%204%201.79%204%204%204Z'/%3E%3Cpath%20d='M86%2090c2.21%200%204-1.79%204-4s-1.79-4-4-4-4%201.79-4%204%201.79%204%204%204Z'/%3E%3C/g%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: calc(100% - 6px) calc(100% - 6px);
}
.maceloyalty--white {
--ml-accent-bg: #ffffff;
--ml-accent-border: #DFE4EA;
--ml-accent-text: #000000;
--ml-button-border: #000000;
--ml-loader-color: rgba(0,0,0,0.15);
--ml-loader-top-color: rgba(0,0,0,0.45);
--ml-button-active-text-color: #ffffff;
--ml-button-inactive-text-color: #000000;
}
#maceloyalty {
box-sizing: border-box;
width: 100%;
padding: 37px 30px;
border-radius: 16px;
min-height: 120px;
font-family: ${detectedFontFamily};
}
.maceloyalty {
position: relative;
background-color: var(--ml-accent-bg, #2D68F8);
border: 1px solid var(--ml-accent-border, none);
}
/* лоадер */
.maceloyalty__loader {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
}
.maceloyalty__loader-spinner {
width: 20px;
height: 20px;
border-radius: 50%;
border: 2px solid var(--ml-loader-color, rgba(255,255,255,0.15));
border-top-color: var(--ml-loader-top-color, rgba(255,255,255,0.45));
animation: maceloyalty-spin 0.8s linear infinite;
}
/* типографика */
.maceloyalty__header {
display: flex;
column-gap: 4px;
align-items: center;
font-size: 20px;
font-weight: 400;
color: var(--ml-accent-text, #ffffff);
margin-bottom: 24px;
}
.maceloyalty__header.maceloyalty__discount {
font-size: 30px;
line-height: 30px;
font-weight: 600;
}
.maceloyalty__header b {
font-size: 22px;
font-weight: 700;
}
.maceloyalty__nouser__text {
width: 70%;
}
.maceloyalty__nouser {
display: flex;
justify-content: space-between;
align-items: center;
}
.maceloyalty__nouser .maceloyalty__header {
font-size: 24px;
font-weight: 700;
}
.maceloyalty__subheader {
font-size: 18px;
line-height: 18px;
color: var(--ml-accent-text, #ffffff);
}
.maceloyalty__row {
display: flex;
justify-content: left;
align-items: center;
gap: 19px;
flex-wrap: wrap;
}
/* кнопки */
.maceloyalty__btn {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 12px 16px;
border: 1px solid var(--ml-button-border, #ffffff);
cursor: pointer;
font-size: 14px;
font-weight: 500;
line-height: 20px;
white-space: nowrap;
background-color: var(--ml-button-border, #ffffff);
color: var(--ml-button-active-text-color, #000000);
}
.maceloyalty__btn--secondary {
background-color: transparent;
color: var(--ml-button-inactive-text-color, #ffffff);
}
.maceloyalty__btn--disabled {
opacity: 0.4;
cursor: default;
transition: opacity 0.3s ease-in-out;
}
.maceloyalty__btn--disabled:hover {
opacity: 0.2;
}
/* блоки опций / подсказок для cashback */
.maceloyalty__options {
margin-top: 16px;
font-size: 14px;
}
.maceloyalty__option {
margin-top: 8px;
}
.maceloyalty__option input {
margin-right: 6px;
}
@media screen and (max-width: 480px) {
#maceloyalty {
padding: 24px 18px;
}
.maceloyalty__btn {
padding: 6px 8px;
flex: 1;
}
.maceloyalty__header {
margin-bottom: 16px;
font-weight: 600;
span {
font-size: 16px;
b {
font-size: 18px;
}
}
}
.maceloyalty__nouser {
flex-wrap: wrap;
.maceloyalty__nouser__text {
width: 100%;
margin-bottom: 16px;
}
.maceloyalty__header {
font-size: 18px;
magin-bottom: 12px;
}
.maceloyalty__subheader {
font-size: 16px;
}
}
.maceloyalty__header.maceloyalty__discount {
font-size: 25px;
line-height: 25px;
}
}
@keyframes maceloyalty-spin {
to { transform: rotate(360deg); }
}
`;
document.head.appendChild(style);
}
function findPhoneInput() {
// Ищем группу телефона по стандартному классу Tilda для телефона в корзине
const group = document.querySelector('.t706 .t-input-group_ph');
if (!group) {
logError(
'Не найден блок .t-input-group_ph в корзине. Интеграция не запущена.'
);
return null;
}
// Находим видимый input с маской телефона
const visibleInput =
group.querySelector('input.t-input-phonemask') ||
group.querySelector('input[type="tel"]');
if (!visibleInput) {
logError(
'Не найдено поле телефона внутри .t-input-group_ph. Интеграция не запущена.'
);
return null;
}
return visibleInput;
}
function ensureMaceloyaltyContainer() {
let container = document.getElementById('maceloyalty');
if (container) return container;
const deliveryGroup = document.querySelector('.t-input-group_dl');
if (!deliveryGroup || !deliveryGroup.parentNode) {
logError(
'Блок доставки ".t-input-group_dl" не найден. Невозможно запустить интеграцию.'
);
return null;
}
container = document.createElement('div');
container.id = 'maceloyalty';
container.className = 'maceloyalty ' + getDesignClass();
// вставляем ПЕРЕД блоком доставки
deliveryGroup.parentNode.insertBefore(container, deliveryGroup);
return container;
}
function showLoader() {
const container = ensureMaceloyaltyContainer();
if (!container) return;
container.innerHTML = `
`;
}
// =========================
// ВЫЗОВ API ПО ТЕЛЕФОНУ (пока просто логируем ответ)
// =========================
function renderNoCardState(phoneDigits) {
const container = ensureMaceloyaltyContainer();
clearHiddenFields();
if (!container) return;
container.innerHTML = `
Станьте участником нашей бонусной программы
Скидки • Предложения • Бонусы
`;
const btn = container.querySelector('[data-ml-action]');
if (!btn) return;
btn.addEventListener('click', function () {
const mode = btn.getAttribute('data-ml-action');
if (mode === 'issue') {
// открыть страницу оформления карты
window.open(MaceLoyaltySettings.cardIssueURL, '_blank');
// поменять кнопку на "Обновить"
btn.textContent = 'Обновить';
btn.setAttribute('data-ml-action', 'refresh');
} else if (mode === 'refresh') {
// повторный запрос карты по тому же номеру
showLoader();
fetchCardByPhone(phoneDigits);
}
});
}
function renderCashbackCard(card, phoneDigits) {
const container = ensureMaceloyaltyContainer();
if (!container) return;
const { totalCashback, bonusesToDeposit, bonusesToWithdrawal } =
calculateCashbackMetrics(card);
const spendFloor = Math.floor(bonusesToWithdrawal);
const canUse = spendFloor > 0;
MaceLoyaltyState.card = card;
MaceLoyaltyState.phone = phoneDigits;
MaceLoyaltyState.mode = 'addCashback';
MaceLoyaltyState.bonusesToDeposit = bonusesToDeposit;
MaceLoyaltyState.bonusesToWithdrawal = bonusesToWithdrawal;
MaceLoyaltyState.totalCashback = totalCashback;
MaceLoyaltyState.useBonusAmount = 0;
updateHiddenFields();
container.innerHTML = `
На вашей карте лояльности ${formatBonuses(totalCashback)}
`;
const btnAdd = container.querySelector('[data-ml-mode="add"]');
const btnUse = container.querySelector('[data-ml-mode="use"]');
if (!btnAdd || !btnUse) return;
if (!canUse) {
btnUse.classList.add('maceloyalty__btn--disabled');
btnUse.setAttribute('aria-disabled', 'true');
btnUse.setAttribute('tabindex', '-1');
}
function setActive(mode) {
if (mode === 'add') {
// выбран "Накопить"
btnAdd.classList.remove('maceloyalty__btn--secondary');
btnUse.classList.add('maceloyalty__btn--secondary');
MaceLoyaltyState.mode = 'addCashback';
MaceLoyaltyState.useBonusAmount = 0;
removePromo();
updateHiddenFields();
} else if (mode === 'use') {
if (!canUse) return; // нельзя списывать, просто игнорируем
// выбран "Списать"
btnUse.classList.remove('maceloyalty__btn--secondary');
btnAdd.classList.add('maceloyalty__btn--secondary');
MaceLoyaltyState.mode = 'useCashback';
MaceLoyaltyState.useBonusAmount = spendFloor;
updateHiddenFields();
applyUseCashbak(card.balance.type, spendFloor);
}
}
// по умолчанию: "Накопить" активно (чёрная)
setActive('add');
btnAdd.addEventListener('click', function () {
setActive('add');
});
btnUse.addEventListener('click', function () {
setActive('use');
});
}
function renderDiscountCard(card, phoneDigits) {
const container = ensureMaceloyaltyContainer();
if (!container) return;
const modifier =
card.level && typeof card.level.modifier === 'number'
? card.level.modifier
: 0;
const percent = Math.round(modifier);
MaceLoyaltyState.card = card;
MaceLoyaltyState.phone = phoneDigits;
MaceLoyaltyState.mode = 'addPurchase';
MaceLoyaltyState.bonusesToDeposit = 0;
MaceLoyaltyState.bonusesToWithdrawal = 0;
MaceLoyaltyState.useBonusAmount = 0;
updateHiddenFields();
container.innerHTML = `
Мы применили скидку ${percent}%
по вашей карте лояльности
`;
applyUseCashbak(card.balance.type, percent);
}
function renderCardState(card, phoneDigits) {
const type = (card.balance.type || '').toLowerCase();
if (type === 'cashback') {
renderCashbackCard(card, phoneDigits);
} else if (type === 'discount') {
renderDiscountCard(card, phoneDigits);
} else {
// на всякий случай считаем неизвестный тип как cashback
logInfo('Неизвестный тип карты, используем поведение cashback:', type);
renderCashbackCard(card, phoneDigits);
}
}
async function fetchCardByPhone(phoneDigits) {
const { clientId, secret } = MaceLoyaltySettings;
const url =
API_HOST +
'/cards?customer.phoneNumber.like=' +
encodeURIComponent(phoneDigits) +
'&pageSize=5';
logInfo('Получаем карту по номеру телефона: ' + phoneDigits);
try {
const response = await fetch(url, {
method: 'GET',
headers: {
Authorization: 'Basic ' + btoa(clientId + ':' + secret),
},
});
if (!response.ok) {
const text = await response.text();
logError(
'Ошибка: ' + response.status + ' ' + response.statusText
);
console.log(text);
return;
}
const data = await response.json();
logInfo('Ответ сервера:');
console.log(data);
if (!data.items || !Array.isArray(data.items) || data.items.length === 0) {
logInfo('Карта по этому номеру не найдена.');
renderNoCardState(phoneDigits);
return;
}
const card = data.items[0];
logInfo('Используем первую найденную карту: ' + card.serialNumber);
renderCardState(card, phoneDigits);
} catch (e) {
logError('Ошибка при получении карты: ' + e);
}
}
// =========================
// ОСНОВНАЯ ЛОГИКА: отслеживаем телефон
// =========================
function initMaceLoyalty() {
if (!validateSettings()) {
return; // без настроек не работаем, на верстке тишина
}
detectedFontFamily = detectTildaFont();
const promoWrapper = document.querySelector('.t-inputpromocode__wrapper');
if (promoWrapper) {
promoField = promoWrapper.innerHTML;
hasPromoField = true;
} else {
promoField = null;
hasPromoField = false;
}
injectBaseStylesOnce();
ensureHiddenFields();
clearHiddenFields();
const phoneInput = findPhoneInput();
if (!phoneInput) {
return; // не нашли поле телефона — выходим тихо
}
logInfo('Интеграция запущена. Ожидание ввода номера телефона');
let lastValidPhone = null;
const onPhoneInput = function (event) {
const rawValue = event.target.value || '';
const digits = rawValue.replace(/\D/g, ''); // только цифры
if (digits.length === 10) {
// полный номер без кода страны, например 916 555 55 55
const phoneForApi = digits;
// Если выбран несовместимый способ оплаты – вообще не грузим карту
if (isPaymentForbidden()) {
logInfo('Выбран несовместимый способ оплаты, карту лояльности не загружаем.');
// не запоминаем lastValidPhone, чтобы после смены способа оплаты
// можно было подтянуть карту для уже введённого номера
clearHiddenFields();
removePromo();
const container = document.getElementById('maceloyalty');
if (container) container.remove();
return;
}
// Проверяем, не тот же номер уже обрабатывали при совместимом способе оплаты
if (phoneForApi === lastValidPhone) {
return;
}
lastValidPhone = phoneForApi;
// 1) показываем loader
showLoader();
// 2) вызываем API уже с номером БЕЗ кода страны
fetchCardByPhone(phoneForApi);
} else {
// номер введен не полностью -> сбрасываем state и убираем блок
if (lastValidPhone !== null) {
logInfo('Номер телефона стал неполным. Сбрасываем статус.');
}
removePromo();
clearHiddenFields();
lastValidPhone = null;
const container = document.getElementById('maceloyalty');
if (container) container.remove();
}
};
phoneInput.addEventListener('input', onPhoneInput);
// следим за сменой способа оплаты
const paymentInputs = document.querySelectorAll('input[name="paymentsystem"]');
paymentInputs.forEach(function (input) {
input.addEventListener('change', function () {
const current = getSelectedPaymentSystem();
logInfo('Сменился способ оплаты: ' + current);
if (isPaymentForbidden()) {
// Запрещённый способ оплаты — очищаем всё, скрываем блок
logInfo('Выбран способ оплаты из forbiddenPayment, очищаем данные лояльности.');
removePromo();
clearHiddenFields();
const container = document.getElementById('maceloyalty');
if (container) container.remove();
return;
}
// Способ оплаты совместим:
// если телефон уже введён полностью — подгружаем карту автоматически
const rawValue = phoneInput.value || '';
const digits = rawValue.replace(/\D/g, '');
if (digits.length === 10) {
const phoneForApi = digits;
logInfo('Совместимый способ оплаты и уже введён телефон, загружаем карту лояльности.');
showLoader();
lastValidPhone = phoneForApi;
fetchCardByPhone(phoneForApi);
} else {
// телефон ещё не полный — просто очищаем UI лояльности
removePromo();
clearHiddenFields();
const container = document.getElementById('maceloyalty');
if (container) container.remove();
}
});
});
// следим за изменением общего итога
var reDrawTotalFunction = window.tcart__reDrawTotal;
window.tcart__reDrawTotal = function (d) {
reDrawTotalFunction(d);
setTimeout(function () {
updateCartTotalsLabels();
updateHiddenFields();
}, 10);
};
var updatetCart__reDrawCartIcon = window.tcart__reDrawCartIcon;
window.tcart__reDrawCartIcon = function () {
updatetCart__reDrawCartIcon();
setTimeout(function () {
recalcAfterCartChange();
}, 10);
};
}
// =========================
// СТАРТ
// =========================
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', function () {
bootstrapMaceLoyalty();
});
} else {
bootstrapMaceLoyalty();
}
})();