Инструкция
Эта модификация создаёт полноценную форму пожертвования для Tilda. Она позволяет принимать разовые и ежемесячные пожертвования через Mixplat, передавать контакты успешных доноров в UniSender, фиксировать действия пользователя в Яндекс Метрике и отправлять данные об успешном пожертвовании в ecommerce.
Перед публикацией форму обязательно нужно настроить под свой проект.
Перед публикацией форму обязательно нужно настроить под свой проект.
- В самом начале кода найдите блок «НАСТРОЙКИ ИНТЕГРАЦИЙ», вырежьте его из основного кода и вставьте в Tilda в «Настройки сайта → Ещё → HTML-код для вставки внутрь HEAD». Остальной код целиком вставьте на страницу в отдельный блок T123 «HTML-код».
- В блоке для HEAD замените MIXPLAT_WIDGET_KEY на ключ виджета Mixplat, UNISENDER_API_KEY на API-ключ UniSender, UNISENDER_LIST_ID_MONTHLY и UNISENDER_LIST_ID_ONETIME на ID списков для регулярных и разовых доноров, а YANDEX_METRIKA_ID — на номер своего счётчика Яндекс Метрики.
- В начале CSS настройте внешний вид формы через переменные --nf-bg, --nf-card, --nf-text, --nf-muted, --nf-field, --nf-border, --nf-accent и --nf-accent-hover. Скругления карточки и полей задаются через --nf-radius и --nf-field-radius.
- В .nf-support__title замените заголовок формы, в .nf-support__subtitle — короткое описание, а в .nf-support__amount-note — пояснение под выбором суммы.
- В блоке .value_field укажите нужные готовые суммы. Для каждой кнопки обязательно одновременно меняйте value у input и видимую сумму внутри label: например, для 700 ₽ должно быть value="700" и текст 700 ₽. Атрибут checked оставьте только у той суммы, которая должна быть выбрана по умолчанию.
- Произвольная сумма вводится через поле #custom_price. Значение min="1" задаёт минимальное значение самого HTML-поля; если нужен обязательный минимальный размер пожертвования выше 1 ₽, потребуется также скорректировать JavaScript-валидацию.
- Поля #name и #email используются платёжной логикой и UniSender, поэтому их ID не меняйте. Тексты внутри placeholder можно заменить на свои.
- В функции updateRecurrentState() замените тексты, которые показываются после выбора разового или ежемесячного пожертвования. Саму конструкцию if (isMonthly) и блок subscribe_confirmation не удаляйте — они отвечают за подтверждение регулярного списания.
- В ссылках href="/policy" и href="/oferta" укажите адреса своих юридических документов, а текст основной кнопки при необходимости измените в value="ПОМОЧЬ".
- В объекте const options при необходимости замените description: 'Благотворительное пожертвование' на своё описание платежа. Остальные параметры Mixplat без необходимости не изменяйте.
- После успешной оплаты разовый донор автоматически отправляется в список UNISENDER_LIST_ID_ONETIME, а регулярный — в UNISENDER_LIST_ID_MONTHLY. Убедитесь, что в HEAD указаны существующие ID нужных списков UniSender.
- После успешного платежа пользователь перенаправляется на /success, а после ошибки или закрытия платёжного окна — на /fail. Замените эти адреса на страницы успешного и неуспешного платежа своего сайта.
- Не меняйте без необходимости технические значения name="price", name="recurrent", value="one-time", value="monthly", ID элементов формы и callbacks Mixplat — на них завязана работа JavaScript.
- Перед публикацией протестируйте разовое и ежемесячное пожертвование, все готовые суммы, произвольную сумму, валидацию обязательных полей, успешную оплату, ошибку и закрытие Mixplat. После успешной оплаты отдельно проверьте попадание контакта в нужный список UniSender, цели Метрики, ecommerce и переход на страницу /success.
Рекомендации:
Настройки в HEAD:
Цели Яндекс Метрики:
Электронная коммерция: после успешного платежа отправляется событие ecommerce_payment_success с суммой и типом пожертвования. Если на сайте уже настроена собственная ecommerce-логика, проверьте совместимость с существующим dataLayer.
Что можно менять свободно: цвета, шрифты, размеры, скругления, отступы, тексты, подписи, готовые суммы, ссылки на документы, описание платежа и страницы /success и /fail.
Что лучше не трогать: технические ID, name и value radio-полей, создание объекта Mixplat, recurrent_payment, функции UniSender, callbacks успешного/неуспешного платежа и последовательность отправки аналитики.
- MIXPLAT_WIDGET_KEY — ключ Mixplat.
- UNISENDER_API_KEY — API-ключ UniSender.
- UNISENDER_LIST_ID_MONTHLY — список регулярных доноров.
- UNISENDER_LIST_ID_ONETIME — список разовых доноров.
- YANDEX_METRIKA_ID — ID счётчика Метрики.
Цели Яндекс Метрики:
- donation_visible — форма появилась в области видимости.
- donation_attempt — пользователь попытался перейти к оплате.
- donation_success — успешный платёж.
- donation_closed — пользователь закрыл виджет.
- donation_fail — ошибка платежа.
Электронная коммерция: после успешного платежа отправляется событие ecommerce_payment_success с суммой и типом пожертвования. Если на сайте уже настроена собственная ecommerce-логика, проверьте совместимость с существующим dataLayer.
Что можно менять свободно: цвета, шрифты, размеры, скругления, отступы, тексты, подписи, готовые суммы, ссылки на документы, описание платежа и страницы /success и /fail.
Что лучше не трогать: технические ID, name и value radio-полей, создание объекта Mixplat, recurrent_payment, функции UniSender, callbacks успешного/неуспешного платежа и последовательность отправки аналитики.
<!-- Адаптивная форма пожертвования для Tilda с Mixplat, UniSender, Яндекс Метрикой и ecommerce. Источник: bulgkate.ru -->
<!-- =========================================================
НАСТРОЙКИ ИНТЕГРАЦИЙ
ЭТОТ БЛОК НУЖНО ВСТАВИТЬ В HEAD САЙТА:
Настройки сайта → Ещё → HTML-код для вставки внутрь HEAD
Перед публикацией замените значения на свои.
========================================================= -->
<script>
window.MIXPLAT_WIDGET_KEY = 'YOUR_MIXPLAT_WIDGET_KEY';
window.UNISENDER_API_KEY = 'YOUR_UNISENDER_API_KEY';
window.UNISENDER_LIST_ID_MONTHLY = 4;
window.UNISENDER_LIST_ID_ONETIME = 3;
window.YANDEX_METRIKA_ID = 11;
</script>
<!-- =========================================================
КОНЕЦ БЛОКА ДЛЯ HEAD
Основной код формы начинается ниже.
========================================================= -->
<style>
/* =========================================================
ОСНОВНЫЕ НАСТРОЙКИ
========================================================= */
.nf-support {
--nf-bg: #f4f3ee;
--nf-card: #fbfbf8;
--nf-text: #29322d;
--nf-muted: #707a74;
--nf-field: #eeefe9;
--nf-border: #d9ddd6;
--nf-accent: #687c70;
--nf-accent-hover: #596c61;
--nf-accent-soft: #e4e9e3;
--nf-accent-text: #ffffff;
--nf-radius: 24px;
--nf-field-radius: 14px;
width: 100%;
padding: 88px 24px;
color: var(--nf-text);
background: var(--nf-bg);
font-family:
var(--nf-font, "TildaSans"),
Arial,
sans-serif;
}
.nf-support,
.nf-support *,
.nf-support *::before,
.nf-support *::after {
box-sizing: border-box;
}
.nf-support input,
.nf-support button,
.nf-support label,
.nf-support a {
font-family: inherit;
}
/* =========================================================
КОНТЕЙНЕР
========================================================= */
.nf-support__inner {
width: min(590px, 100%);
margin: 0 auto;
}
/* =========================================================
ФОРМА
========================================================= */
.nf-support .donation {
width: 100%;
margin: 0;
padding: 0;
}
.nf-support .donation-form {
width: 100%;
margin: 0;
overflow: hidden;
border: 1px solid var(--nf-border);
border-radius: var(--nf-radius);
background: var(--nf-card);
color: var(--nf-text);
box-shadow:
0 18px 50px rgba(51, 61, 55, 0.07),
0 3px 10px rgba(51, 61, 55, 0.035);
}
.nf-support .donation_form_wrapper {
width: 100%;
padding: 38px 40px 40px;
}
.nf-support .inputBlock {
width: 100%;
margin: 0;
padding: 0;
}
.nf-support input[type="radio"] {
display: none;
}
/* =========================================================
ЗАГОЛОВОК
========================================================= */
.nf-support__heading {
margin: 0 0 30px;
}
.nf-support__title {
margin: 0;
color: var(--nf-text);
font-size: clamp(30px, 4vw, 38px);
font-weight: 600;
line-height: 1.08;
letter-spacing: -0.025em;
}
.nf-support__subtitle {
max-width: 470px;
margin: 12px 0 0;
color: var(--nf-muted);
font-size: 16px;
font-weight: 400;
line-height: 1.5;
}
/* =========================================================
РАЗОВО / ЕЖЕМЕСЯЧНО
========================================================= */
.nf-support .recurrent_field {
display: grid;
grid-template-columns: 1fr 1fr;
width: 100%;
margin: 0 0 18px;
padding: 4px;
border: 1px solid var(--nf-border);
border-radius: 999px;
background: var(--nf-field);
}
.nf-support .recurrent_field .label-for-check {
display: flex;
align-items: center;
justify-content: center;
min-width: 0;
height: 50px;
margin: 0;
padding: 0 16px;
border: 0;
border-radius: 999px;
background: transparent;
color: var(--nf-muted);
font-size: 15px;
font-weight: 500;
line-height: 1.2;
text-align: center;
cursor: pointer;
transition:
background-color 0.18s ease,
color 0.18s ease,
box-shadow 0.18s ease;
}
.nf-support #recurrent_choice_2:checked + .one-time,
.nf-support #recurrent_choice_1:checked + .monthly {
background: var(--nf-card);
color: var(--nf-text);
box-shadow:
0 2px 9px rgba(47, 58, 51, 0.08);
}
/* =========================================================
ПОЯСНЕНИЕ ТИПА ПЛАТЕЖА
========================================================= */
.nf-support .recurrent_hint {
min-height: 68px;
margin: 0 0 30px;
padding: 0 2px;
color: var(--nf-muted);
font-size: 14px;
font-weight: 400;
line-height: 1.5;
}
/* =========================================================
СУММА
========================================================= */
.nf-support .descr {
margin: 0 0 13px;
color: var(--nf-text);
font-size: 17px;
font-weight: 500;
line-height: 1.3;
}
.nf-support .value_field {
display: grid;
grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 9px;
width: 100%;
margin: 0;
padding: 0;
}
.nf-support .value_field > .label-for-check {
display: flex;
align-items: center;
justify-content: center;
min-width: 0;
height: 48px;
margin: 0;
padding: 0 8px;
border: 1px solid var(--nf-border);
border-radius: 999px;
background: var(--nf-card);
color: var(--nf-text);
font-size: 15px;
font-weight: 500;
line-height: 1;
white-space: nowrap;
cursor: pointer;
transition:
background-color 0.18s ease,
border-color 0.18s ease,
color 0.18s ease,
transform 0.18s ease;
}
@media (hover: hover) {
.nf-support .value_field > .label-for-check:hover {
border-color: var(--nf-accent);
background: var(--nf-accent-soft);
}
}
.nf-support
.value_field
.check-with-label:checked
+ .label-for-check {
border-color: var(--nf-accent);
background: var(--nf-accent);
color: var(--nf-accent-text);
}
/* =========================================================
ДРУГАЯ СУММА
========================================================= */
.nf-support .value_field > .custom-label {
padding: 0;
overflow: hidden;
}
.nf-support .custom-label .custom-input {
display: block;
width: 100%;
height: 46px;
margin: 0;
padding: 0 8px;
border: 0;
outline: 0;
background: transparent;
color: inherit;
font-size: 15px;
font-weight: 500;
line-height: 46px;
text-align: center;
appearance: textfield;
-moz-appearance: textfield;
}
.nf-support
.custom-label
.custom-input::-webkit-inner-spin-button,
.nf-support
.custom-label
.custom-input::-webkit-outer-spin-button {
margin: 0;
-webkit-appearance: none;
}
.nf-support .custom-input::placeholder {
color: inherit;
opacity: 0.7;
}
.nf-support .custom-label.custom-active {
border-color: var(--nf-accent);
background: var(--nf-accent);
color: var(--nf-accent-text);
}
/* =========================================================
ТЕКСТ ПОД СУММОЙ
========================================================= */
.nf-support__amount-note {
margin: 14px 2px 29px;
color: var(--nf-muted);
font-size: 13px;
font-weight: 400;
line-height: 1.5;
}
/* =========================================================
ИМЯ / EMAIL
========================================================= */
.nf-support .name_field,
.nf-support .email_field {
display: block;
width: 100%;
}
.nf-support .name_field {
margin-bottom: 10px;
}
.nf-support .email_field {
margin-bottom: 15px;
}
.nf-support .name_field input[type="text"],
.nf-support .email_field input[type="email"] {
display: block;
width: 100%;
height: 52px;
margin: 0;
padding: 0 18px;
border: 1px solid transparent;
border-radius: var(--nf-field-radius);
outline: none;
background: var(--nf-field);
color: var(--nf-text);
font-size: 15px;
font-weight: 400;
line-height: 1;
transition:
background-color 0.18s ease,
border-color 0.18s ease,
box-shadow 0.18s ease;
}
.nf-support .name_field input::placeholder,
.nf-support .email_field input::placeholder {
color: #8a938d;
opacity: 1;
}
.nf-support .name_field input:focus,
.nf-support .email_field input:focus {
border-color: #abb8b0;
background: #ffffff;
box-shadow:
0 0 0 3px rgba(104, 124, 112, 0.09);
}
/* =========================================================
ЧЕКБОКСЫ
========================================================= */
.nf-support .subscribe_confirmation,
.nf-support .checkbox-row {
display: flex;
align-items: flex-start;
width: 100%;
padding: 0;
color: var(--nf-muted);
font-size: 12px;
font-weight: 400;
line-height: 1.45;
text-align: left;
}
.nf-support .subscribe_confirmation {
margin: 2px 0 12px;
}
.nf-support .checkbox-row {
margin: 0 0 24px;
}
.nf-support .subscribe_confirmation input,
.nf-support .checkbox-row input {
flex: 0 0 auto;
width: 16px;
height: 16px;
margin: 1px 9px 0 0;
padding: 0;
accent-color: var(--nf-accent);
}
.nf-support .subscribe_confirmation label,
.nf-support .checkbox-row label {
margin: 0;
padding: 0;
color: inherit;
font-size: inherit;
font-weight: inherit;
line-height: inherit;
}
.nf-support .checkbox-row a {
color: inherit !important;
text-decoration: underline !important;
text-decoration-thickness: 1px !important;
text-underline-offset: 2px;
}
/* =========================================================
КНОПКА
========================================================= */
.nf-support .submitBlock {
width: 100%;
margin: 0;
padding: 0;
}
.nf-support #donation_form .donate-button {
display: block;
width: 100%;
min-width: 0;
height: 56px;
margin: 0;
padding: 0 28px;
border: 0;
border-radius: var(--nf-field-radius);
background: var(--nf-accent);
color: var(--nf-accent-text);
font-size: 16px;
font-weight: 600;
line-height: 1;
cursor: pointer;
transition:
background-color 0.18s ease,
transform 0.18s ease,
box-shadow 0.18s ease;
}
@media (hover: hover) {
.nf-support #donation_form .donate-button:hover {
background: var(--nf-accent-hover);
box-shadow:
0 7px 18px rgba(74, 94, 82, 0.16);
}
}
.nf-support #donation_form .donate-button:active {
transform: scale(0.99);
}
/* =========================================================
TABLET
========================================================= */
@media (max-width: 767px) {
.nf-support {
padding: 54px 20px;
}
.nf-support__inner {
width: min(580px, 100%);
}
.nf-support .donation_form_wrapper {
padding: 32px 30px 34px;
}
.nf-support__title {
font-size: clamp(29px, 6vw, 35px);
}
}
/* =========================================================
MOBILE
========================================================= */
@media (max-width: 599px) {
.nf-support {
padding: 28px 12px;
--nf-radius: 20px;
--nf-field-radius: 13px;
}
.nf-support .donation_form_wrapper {
padding: 26px 18px 28px;
}
.nf-support__heading {
margin-bottom: 25px;
}
.nf-support__title {
font-size: 29px;
line-height: 1.1;
}
.nf-support__subtitle {
margin-top: 9px;
font-size: 15px;
line-height: 1.45;
}
.nf-support .recurrent_field {
margin-bottom: 16px;
}
.nf-support .recurrent_field .label-for-check {
height: 46px;
padding: 0 8px;
font-size: 14px;
}
.nf-support .recurrent_hint {
min-height: 0;
margin-bottom: 26px;
font-size: 13px;
line-height: 1.48;
}
.nf-support .descr {
margin-bottom: 11px;
font-size: 16px;
}
.nf-support .value_field {
grid-template-columns:
repeat(3, minmax(0, 1fr));
gap: 8px;
}
.nf-support .value_field > .label-for-check {
height: 46px;
padding: 0 5px;
font-size: 14px;
}
.nf-support .value_field > .custom-label {
grid-column: 1 / -1;
}
.nf-support .custom-label .custom-input {
height: 44px;
font-size: 14px;
line-height: 44px;
}
.nf-support__amount-note {
margin:
13px 2px 25px;
font-size: 12px;
}
.nf-support .name_field input[type="text"],
.nf-support .email_field input[type="email"] {
height: 50px;
padding: 0 16px;
font-size: 15px;
}
.nf-support .subscribe_confirmation,
.nf-support .checkbox-row {
font-size: 11px;
line-height: 1.45;
}
.nf-support .checkbox-row {
margin-bottom: 21px;
}
.nf-support #donation_form .donate-button {
height: 54px;
font-size: 16px;
}
}
/* =========================================================
VERY SMALL
========================================================= */
@media (max-width: 359px) {
.nf-support {
padding-right: 8px;
padding-left: 8px;
}
.nf-support .donation_form_wrapper {
padding-right: 14px;
padding-left: 14px;
}
.nf-support .recurrent_field .label-for-check {
font-size: 13px;
}
.nf-support .value_field {
gap: 6px;
}
.nf-support .value_field > .label-for-check {
font-size: 13px;
}
}
</style>
<section class="nf-support">
<div class="nf-support__inner">
<div class="donation" id="help">
<form
id="donation_form"
class="donation-form"
method="post"
action=""
novalidate
>
<div class="donation_form_wrapper">
<!-- ЗАГОЛОВОК -->
<div class="nf-support__heading">
<h2 class="nf-support__title">
Сделать пожертвование
</h2>
<p class="nf-support__subtitle">
Выберите удобный формат и сумму поддержки.
</p>
</div>
<!-- РАЗОВО / ЕЖЕМЕСЯЧНО -->
<div class="inputBlock recurrent_field">
<input
type="radio"
id="recurrent_choice_2"
name="recurrent"
value="one-time"
class="check-with-label"
checked
>
<label
for="recurrent_choice_2"
class="label-for-check one-time"
>
Помочь разово
</label>
<input
type="radio"
id="recurrent_choice_1"
name="recurrent"
value="monthly"
class="check-with-label"
>
<label
for="recurrent_choice_1"
class="label-for-check monthly"
>
Помогать ежемесячно
</label>
</div>
<div
id="recurrent_hint"
class="recurrent_hint"
></div>
<!-- СУММА -->
<div class="descr">
Сумма пожертвования
</div>
<div class="inputBlock value_field">
<input
type="radio"
id="value_choice_1"
name="price"
value="500"
class="check-with-label"
>
<label
for="value_choice_1"
class="label-for-check"
>
500 ₽
</label>
<input
type="radio"
id="value_choice_2"
name="price"
value="1500"
class="check-with-label"
checked
>
<label
for="value_choice_2"
class="label-for-check"
>
1500 ₽
</label>
<input
type="radio"
id="value_choice_3"
name="price"
value="3000"
class="check-with-label"
>
<label
for="value_choice_3"
class="label-for-check"
>
3000 ₽
</label>
<input
type="radio"
id="value_choice_custom"
name="price"
value="custom"
class="check-with-label"
>
<label
for="value_choice_custom"
class="label-for-check custom-label"
>
<input
type="number"
id="custom_price"
name="custom_price"
placeholder="Другая сумма"
class="custom-input"
min="1"
inputmode="numeric"
>
</label>
</div>
<div class="nf-support__amount-note">
Любая сумма имеет значение и помогает проекту продолжать работу.
</div>
<!-- ИМЯ -->
<div class="inputBlock name_field">
<input
type="text"
id="name"
name="name"
placeholder="Ваше имя"
required
>
</div>
<!-- EMAIL -->
<div class="inputBlock email_field">
<input
type="email"
id="email"
name="email"
placeholder="Ваш email"
required
>
</div>
<!-- ПОДТВЕРЖДЕНИЕ РЕГУЛЯРНОГО ПОЖЕРТВОВАНИЯ -->
<div
class="subscribe_confirmation"
id="subscribe_confirmation_block"
style="display:none;"
>
<input
type="checkbox"
id="subscribe_confirmation"
name="subscribe_confirmation"
value="1"
>
<label for="subscribe_confirmation">
Я понимаю, что пожертвование будет повторяться каждый месяц
</label>
</div>
<!-- СОГЛАСИЯ -->
<div class="checkbox-row">
<input
type="checkbox"
id="privacy"
name="privacy"
value="1"
required
>
<label for="privacy">
Я соглашаюсь с
<a href="/policy" target="_blank">политикой конфиденциальности</a>
и с
<a href="/oferta" target="_blank">офертой</a>.
</label>
</div>
<!-- ОПЛАТА -->
<div class="inputBlock submitBlock">
<input
type="submit"
value="ПОМОЧЬ"
class="donate-button"
>
</div>
</div>
</form>
</div>
</div>
</section>
<!-- MIXPLAT + JQUERY -->
<script src="https://cdn.mixplat.ru/widget/v3/widget.js"></script>
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<script>
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function isInViewport(el) {
const rect = el.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <=
(window.innerHeight ||
document.documentElement.clientHeight) &&
rect.right <=
(window.innerWidth ||
document.documentElement.clientWidth)
);
}
function sendMetrikaGoal(goalName, params) {
if (
typeof ym === 'function' &&
window.YANDEX_METRIKA_ID
) {
ym(
window.YANDEX_METRIKA_ID,
'reachGoal',
goalName,
params || {}
);
}
}
document.addEventListener(
'scroll',
function () {
const donationForm =
document.querySelector('#donation_form');
if (
donationForm &&
isInViewport(donationForm)
) {
if (
!window.__donation_form_visible_logged
) {
sendMetrikaGoal(
'donation_visible'
);
window.__donation_form_visible_logged =
true;
}
}
},
{
passive: true
}
);
function addToUnisender(email, listId) {
return new Promise(
(resolve, reject) => {
const name =
document
.getElementById('name')
.value
.trim();
const apiKey =
window.UNISENDER_API_KEY;
const apiEndpoint =
'https://api.unisender.com/ru/api/subscribe';
if (
!email ||
!listId ||
!apiKey
) {
resolve({
skipped: true
});
return;
}
const formData =
new FormData();
formData.append(
'api_key',
apiKey
);
formData.append(
'list_ids',
listId
);
formData.append(
'fields[email]',
email
);
formData.append(
'fields[Name]',
name
);
formData.append(
'double_optin',
'3'
);
fetch(
apiEndpoint,
{
method: 'POST',
body: formData
}
)
.then(
response =>
response.json()
)
.then(
data => {
if (
data.result ||
data.skipped
) {
resolve(data);
} else {
reject(
new Error(
data.error ||
'Unknown Unisender error'
)
);
}
}
)
.catch(
error => {
reject(error);
}
);
}
);
}
function isValidEmail(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(
email
);
}
function getSelectedAmount() {
const customRadio =
document.getElementById(
'value_choice_custom'
);
const customPriceValue =
document.getElementById(
'custom_price'
).value.trim();
const checkedRadio =
document.querySelector(
"input[name='price']:checked"
);
if (customRadio.checked) {
return parseFloat(
customPriceValue
);
}
if (checkedRadio) {
return parseFloat(
checkedRadio.value
);
}
return NaN;
}
function updateRecurrentState() {
const isMonthly =
document
.getElementById(
'recurrent_choice_1'
)
.checked;
const emailBlock =
$('.email_field');
const emailInput =
document.getElementById(
'email'
);
const confirmationBlock =
$('#subscribe_confirmation_block');
const confirmationInput =
document.getElementById(
'subscribe_confirmation'
);
const recurrentHint =
$('#recurrent_hint');
emailBlock.show();
emailInput.required = true;
if (isMonthly) {
confirmationBlock
.stop(true, true)
.fadeIn(300);
confirmationInput.required =
true;
recurrentHint
.html(
'Вы выбрали регулярное пожертвование. Выбранная сумма будет автоматически списываться с карты один раз в месяц.'
)
.fadeIn(300);
} else {
confirmationInput.required =
false;
confirmationInput.checked =
false;
confirmationBlock
.stop(true, true)
.hide();
recurrentHint
.html(
'Вы выбрали разовое пожертвование. Выбранная сумма будет списана с карты один раз.'
)
.fadeIn(300);
}
}
$(document).ready(function () {
const amountRadios =
document.querySelectorAll(
'input[name="price"]'
);
const customInput =
document.getElementById(
'custom_price'
);
const customLabel =
document.querySelector(
'.custom-label'
);
const customRadio =
document.getElementById(
'value_choice_custom'
);
function activateCustom() {
amountRadios.forEach(
radio => {
radio.checked = false;
}
);
customRadio.checked = true;
customLabel
.classList
.add('custom-active');
}
function deactivateCustom() {
customLabel
.classList
.remove('custom-active');
}
function focusField(el) {
if (!el) return;
el.focus();
if (
typeof el.scrollIntoView ===
'function'
) {
el.scrollIntoView({
behavior: 'smooth',
block: 'center'
});
}
}
$(".recurrent_field input")
.on(
'change',
function () {
updateRecurrentState();
}
);
customInput
.addEventListener(
'input',
function () {
const value =
this.value.trim();
if (
value &&
parseFloat(value) > 0
) {
activateCustom();
} else if (
customRadio.checked
) {
deactivateCustom();
}
}
);
customInput
.addEventListener(
'focus',
function () {
activateCustom();
}
);
amountRadios.forEach(
radio => {
radio.addEventListener(
'change',
function () {
if (
this.id !==
'value_choice_custom' &&
this.checked
) {
deactivateCustom();
customInput.value =
'';
}
if (
this.id ===
'value_choice_custom' &&
this.checked
) {
customLabel
.classList
.add(
'custom-active'
);
}
}
);
}
);
updateRecurrentState();
$('#donation_form')
.on(
'submit',
function (e) {
e.preventDefault();
const name =
$("#name")
.val()
.trim();
const email =
$("#email")
.val()
.trim();
const recurrent =
$(
"input[name='recurrent']:checked"
).val();
const isMonthly =
recurrent ===
'monthly';
const privacyChecked =
$("#privacy")
.is(':checked');
const subscribeChecked =
$("#subscribe_confirmation")
.is(':checked');
const amount =
getSelectedAmount();
if (!name) {
alert(
'Пожалуйста, укажите имя'
);
focusField(
document.getElementById(
'name'
)
);
return;
}
if (!email) {
alert(
'Пожалуйста, укажите email'
);
focusField(
document.getElementById(
'email'
)
);
return;
}
if (
!isValidEmail(email)
) {
alert(
'Пожалуйста, укажите корректный email'
);
focusField(
document.getElementById(
'email'
)
);
return;
}
if (
!amount ||
isNaN(amount) ||
amount <= 0
) {
alert(
'Пожалуйста, введите корректную сумму пожертвования'
);
focusField(
document.getElementById(
'custom_price'
)
);
return;
}
if (
!privacyChecked
) {
alert(
'Пожалуйста, подтвердите согласие с политикой конфиденциальности и офертой'
);
focusField(
document.getElementById(
'privacy'
)
);
return;
}
if (
isMonthly &&
!subscribeChecked
) {
alert(
'Пожалуйста, подтвердите, что пожертвование будет повторяться каждый месяц'
);
focusField(
document.getElementById(
'subscribe_confirmation'
)
);
return;
}
sendMetrikaGoal(
'donation_attempt',
{
order_price:
amount.toFixed(2),
currency:
'RUB',
recurrent:
recurrent
}
);
const options = {
widget_key:
window.MIXPLAT_WIDGET_KEY,
amount:
Math.round(
amount * 100
),
description:
'Благотворительное пожертвование',
user_email:
email,
user_name:
name,
recurrent_payment:
isMonthly
? 1
: 0
};
const M =
new Mixplat(options);
M.build();
M.setSuccessCallback(
async () => {
const finalAmount =
getSelectedAmount();
const finalRecurrent =
$(
"input[name='recurrent']:checked"
).val();
const finalIsMonthly =
finalRecurrent ===
'monthly';
const finalEmail =
$("#email")
.val()
.trim();
const listId =
finalIsMonthly
? window.UNISENDER_LIST_ID_MONTHLY
: window.UNISENDER_LIST_ID_ONETIME;
try {
await addToUnisender(
finalEmail,
listId
);
} catch (err) {
console.error(
'[Unisender] Ошибка:',
err
);
}
sendMetrikaGoal(
'donation_success',
{
order_price:
finalAmount
.toFixed(2),
currency:
'RUB',
recurrent:
finalRecurrent
}
);
window.dataLayer =
window.dataLayer || [];
window.dataLayer.push({
event:
'ecommerce_payment_success',
ecommerce: {
currencyCode:
'RUB',
purchase: {
actionField: {
id:
'donation_' +
Date.now(),
revenue:
finalAmount
.toFixed(2),
affiliation:
finalIsMonthly
? 'Пожертвование ежемесячное'
: 'Пожертвование разовое'
},
products: [{
name:
finalIsMonthly
? 'Регулярное пожертвование'
: 'Разовое пожертвование',
id:
finalIsMonthly
? 'donation_monthly'
: 'donation_one_time',
price:
finalAmount
.toFixed(2),
quantity:
1,
variant:
finalEmail,
category:
finalRecurrent,
dimension1:
document
.getElementById(
'value_choice_custom'
)
.checked
}]
}
}
});
await sleep(150);
window.location.href =
'/success';
}
);
M.setCloseCallback(
() => {
const finalRecurrent =
$(
"input[name='recurrent']:checked"
).val();
const finalAmount =
getSelectedAmount();
if (
!isNaN(finalAmount) &&
finalAmount > 0
) {
sendMetrikaGoal(
'donation_closed',
{
order_price:
finalAmount
.toFixed(2),
currency:
'RUB',
recurrent:
finalRecurrent
}
);
}
window.location.href =
'/fail';
}
);
M.setFailCallback(
() => {
const finalRecurrent =
$(
"input[name='recurrent']:checked"
).val();
const finalAmount =
getSelectedAmount();
if (
!isNaN(finalAmount) &&
finalAmount > 0
) {
sendMetrikaGoal(
'donation_fail',
{
order_price:
finalAmount
.toFixed(2),
currency:
'RUB',
recurrent:
finalRecurrent
}
);
}
window.location.href =
'/fail';
}
);
}
);
});
</script>