<!-- Адаптивная форма пожертвования для Tilda с CloudPayments, UniSender, Яндекс Метрикой и ecommerce. Источник: bulgkate.ru -->
<!-- =========================================================
НАСТРОЙКИ ИНТЕГРАЦИЙ
ЭТОТ БЛОК НУЖНО ВЫРЕЗАТЬ И ВСТАВИТЬ В HEAD САЙТА:
Настройки сайта → Ещё → HTML-код для вставки внутрь HEAD
Перед публикацией замените значения на свои.
========================================================= -->
<script src="https://widget.cloudpayments.ru/bundles/cloudpayments.js"></script>
<script>
window.CLOUD_WIDGET_KEY = 'YOUR_CLOUD_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: #f7f5fa;
--nf-card: #ffffff;
--nf-text: #302b36;
--nf-muted: #625b6b;
--nf-field: #f3f0f7;
--nf-border: #ddd7e5;
--nf-accent: #6750a4;
--nf-accent-hover: #57418f;
--nf-accent-soft: #eee9f7;
--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(54, 43, 73, 0.07),
0 3px 10px rgba(54, 43, 73, 0.035);
}
.nf-support .donation_form_wrapper {
width: 100%;
padding: 38px 40px 40px;
}
.nf-support .inputBlock {
width: 100%;
margin: 0;
padding: 0;
}
/* =========================================================
RADIO
========================================================= */
.nf-support input[type="radio"] {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
opacity: 0;
pointer-events: 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 16px;
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(54, 43, 73, 0.09);
}
/* =========================================================
ТЕКСТ ПРО ТИП ПОЖЕРТВОВАНИЯ
========================================================= */
.nf-support .recurrent_hint {
min-height: 42px;
margin: 0 0 28px;
padding: 0 2px;
color: #554e5e !important;
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;
}
@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.72;
}
.nf-support .custom-label.custom-active {
border-color: var(--nf-accent);
background: var(--nf-accent);
color: var(--nf-accent-text);
}
.nf-support .custom-label.custom-active .custom-input::placeholder {
color: var(--nf-accent-text);
opacity: 0.9;
}
/* =========================================================
ТЕКСТ ПОД СУММОЙ
========================================================= */
.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: #756d7d;
opacity: 1;
}
.nf-support .name_field input:focus,
.nf-support .email_field input:focus {
border-color: #8a75b8;
background: #ffffff;
box-shadow:
0 0 0 3px rgba(103, 80, 164, 0.12);
}
/* =========================================================
ЧЕКБОКСЫ
========================================================= */
.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: #59418e !important;
font-weight: 500;
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(72, 52, 118, 0.19);
}
}
.nf-support #donation_form .donate-button:focus-visible {
outline: 3px solid rgba(103, 80, 164, 0.25);
outline-offset: 3px;
}
.nf-support #donation_form .donate-button:active {
transform: scale(0.99);
}
/* =========================================================
TABLET
========================================================= */
@media (max-width: 767px) {
.nf-support {
padding: 54px 20px;
}
.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: 14px;
}
.nf-support .recurrent_field .label-for-check {
height: 46px;
padding: 0 8px;
font-size: 14px;
}
.nf-support .recurrent_hint {
min-height: 0;
margin-bottom: 24px;
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>
<script>
(function () {
/* =========================================================
ЭЛЕМЕНТЫ
========================================================= */
var form =
document.getElementById(
'donation_form'
);
if (!form) {
return;
}
var recurrentOneTime =
document.getElementById(
'recurrent_choice_2'
);
var recurrentMonthly =
document.getElementById(
'recurrent_choice_1'
);
var recurrentHint =
document.getElementById(
'recurrent_hint'
);
var subscribeBlock =
document.getElementById(
'subscribe_confirmation_block'
);
var subscribeConfirmation =
document.getElementById(
'subscribe_confirmation'
);
var nameInput =
document.getElementById(
'name'
);
var emailInput =
document.getElementById(
'email'
);
var privacyInput =
document.getElementById(
'privacy'
);
var customRadio =
document.getElementById(
'value_choice_custom'
);
var customInput =
document.getElementById(
'custom_price'
);
var customLabel =
form.querySelector(
'.custom-label'
);
var amountRadios =
form.querySelectorAll(
'input[name="price"]'
);
/* =========================================================
ВСПОМОГАТЕЛЬНЫЕ ФУНКЦИИ
========================================================= */
function sleep(ms) {
return new Promise(
function (resolve) {
setTimeout(
resolve,
ms
);
}
);
}
function uniqueID() {
return (
'donation_' +
Date.now() +
'_' +
Math.random()
.toString(36)
.slice(2, 9)
);
}
function isValidEmail(email) {
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(
email
);
}
function focusField(element) {
if (!element) {
return;
}
element.focus();
if (
typeof element.scrollIntoView ===
'function'
) {
element.scrollIntoView({
behavior: 'smooth',
block: 'center'
});
}
}
function sendMetrikaGoal(
goalName,
params
) {
if (
typeof window.ym ===
'function' &&
window.YANDEX_METRIKA_ID
) {
window.ym(
window.YANDEX_METRIKA_ID,
'reachGoal',
goalName,
params || {}
);
}
}
/* =========================================================
РАЗОВО / ЕЖЕМЕСЯЧНО
ЛОГИКА ТА ЖЕ, ЧТО В MIXPLAT:
читаем выбранный radio и сразу обновляем интерфейс.
========================================================= */
function updateRecurrentState() {
var isMonthly =
recurrentMonthly.checked;
if (isMonthly) {
recurrentHint.textContent =
'Вы выбрали регулярное пожертвование. Выбранная сумма будет автоматически списываться с карты один раз в месяц.';
subscribeBlock.style.display =
'flex';
subscribeConfirmation.required =
true;
} else {
recurrentHint.textContent =
'Вы выбрали разовое пожертвование. Выбранная сумма будет списана с карты один раз.';
subscribeBlock.style.display =
'none';
subscribeConfirmation.required =
false;
subscribeConfirmation.checked =
false;
}
}
recurrentOneTime.addEventListener(
'change',
updateRecurrentState
);
recurrentMonthly.addEventListener(
'change',
updateRecurrentState
);
/*
* Синхронизируем состояние при первой загрузке.
*/
updateRecurrentState();
/* =========================================================
ПРОИЗВОЛЬНАЯ СУММА
========================================================= */
function activateCustomAmount() {
amountRadios.forEach(
function (radio) {
if (
radio !==
customRadio
) {
radio.checked =
false;
}
}
);
customRadio.checked =
true;
customLabel
.classList
.add(
'custom-active'
);
}
function deactivateCustomAmount() {
customLabel
.classList
.remove(
'custom-active'
);
}
customInput.addEventListener(
'focus',
function () {
activateCustomAmount();
}
);
customInput.addEventListener(
'input',
function () {
if (
this.value.trim() !== ''
) {
activateCustomAmount();
}
}
);
amountRadios.forEach(
function (radio) {
radio.addEventListener(
'change',
function () {
if (
this ===
customRadio
) {
if (
this.checked
) {
customLabel
.classList
.add(
'custom-active'
);
}
return;
}
if (this.checked) {
customInput.value =
'';
deactivateCustomAmount();
}
}
);
}
);
/* =========================================================
ПОЛУЧЕНИЕ СУММЫ
========================================================= */
function getSelectedAmount() {
if (
customRadio.checked
) {
return parseFloat(
customInput.value
);
}
var checkedAmount =
form.querySelector(
'input[name="price"]:checked'
);
if (!checkedAmount) {
return NaN;
}
return parseFloat(
checkedAmount.value
);
}
/* =========================================================
UNISENDER
========================================================= */
function addToUnisender(
email,
listId,
name
) {
return new Promise(
function (
resolve,
reject
) {
var apiKey =
window.UNISENDER_API_KEY;
if (
!apiKey ||
!listId ||
!email
) {
resolve({
skipped: true
});
return;
}
var 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(
'https://api.unisender.com/ru/api/subscribe',
{
method: 'POST',
body: formData
}
)
.then(
function (response) {
return response.json();
}
)
.then(
function (data) {
if (data.result) {
resolve(data);
} else {
reject(
new Error(
data.error ||
'UniSender error'
)
);
}
}
)
.catch(
function (error) {
reject(error);
}
);
}
);
}
/* =========================================================
DONATION_VISIBLE
========================================================= */
var visibilitySent =
false;
function sendVisibilityGoal() {
if (visibilitySent) {
return;
}
visibilitySent =
true;
sendMetrikaGoal(
'donation_visible'
);
}
if (
'IntersectionObserver' in window
) {
var observer =
new IntersectionObserver(
function (entries) {
entries.forEach(
function (entry) {
if (
entry.isIntersecting &&
entry.intersectionRatio >=
0.25
) {
sendVisibilityGoal();
observer.disconnect();
}
}
);
},
{
threshold: 0.25
}
);
observer.observe(form);
} else {
sendVisibilityGoal();
}
/* =========================================================
ОТПРАВКА ФОРМЫ
========================================================= */
form.addEventListener(
'submit',
function (event) {
event.preventDefault();
var name =
nameInput.value.trim();
var email =
emailInput.value.trim();
var amount =
getSelectedAmount();
var isMonthly =
recurrentMonthly.checked;
var donationType =
isMonthly
? 'monthly'
: 'one-time';
/* =================================================
ВАЛИДАЦИЯ
================================================= */
if (!name) {
alert(
'Пожалуйста, укажите имя'
);
focusField(
nameInput
);
return;
}
if (!email) {
alert(
'Пожалуйста, укажите email'
);
focusField(
emailInput
);
return;
}
if (
!isValidEmail(email)
) {
alert(
'Пожалуйста, укажите корректный email'
);
focusField(
emailInput
);
return;
}
if (
!Number.isFinite(amount) ||
amount <= 0
) {
alert(
'Пожалуйста, укажите корректную сумму пожертвования'
);
if (
customRadio.checked
) {
focusField(
customInput
);
}
return;
}
if (
!privacyInput.checked
) {
alert(
'Пожалуйста, подтвердите согласие с политикой конфиденциальности и офертой'
);
focusField(
privacyInput
);
return;
}
if (
isMonthly &&
!subscribeConfirmation.checked
) {
alert(
'Пожалуйста, подтвердите, что пожертвование будет повторяться каждый месяц'
);
focusField(
subscribeConfirmation
);
return;
}
/* =================================================
ПРОВЕРКА CLOUDPAYMENTS
================================================= */
if (
!window.CLOUD_WIDGET_KEY
) {
console.error(
'[CloudPayments] Не указан CLOUD_WIDGET_KEY'
);
alert(
'Платёжная форма не настроена.'
);
return;
}
if (
typeof window.cp ===
'undefined' ||
typeof window.cp.CloudPayments ===
'undefined'
) {
console.error(
'[CloudPayments] Скрипт CloudPayments не загружен'
);
alert(
'Не удалось загрузить платёжную форму. Пожалуйста, попробуйте ещё раз.'
);
return;
}
/* =================================================
DONATION_ATTEMPT
================================================= */
sendMetrikaGoal(
'donation_attempt',
{
order_price:
amount.toFixed(2),
currency:
'RUB',
recurrent:
donationType
}
);
/* =================================================
CLOUDPAYMENTS
================================================= */
var orderId =
uniqueID();
var widget =
new window.cp.CloudPayments();
var intentParams = {
publicTerminalId:
window.CLOUD_WIDGET_KEY,
paymentSchema:
'Single',
description:
isMonthly
? 'Регулярное благотворительное пожертвование'
: 'Благотворительное пожертвование',
amount:
amount,
currency:
'RUB',
culture:
'ru-RU',
skin:
'classic',
externalId:
orderId,
receiptEmail:
email,
emailBehavior:
'Hidden',
userInfo: {
accountId:
email,
fullName:
name,
email:
email
},
metadata: {
donationType:
donationType,
source:
'tilda'
}
};
/*
* ВАЖНО:
*
* Только для ежемесячного пожертвования
* передаём recurrent.
*
* Для разового его вообще нет.
*/
if (isMonthly) {
intentParams.recurrent = {
interval:
'Month',
period:
1
};
}
var paymentState =
'processing';
/* =================================================
ЗАКРЫТИЕ CLOUDPAYMENTS БЕЗ ОПЛАТЫ
================================================= */
widget.oncomplete =
function (result) {
if (
!result ||
result.type !==
'cancel'
) {
return;
}
if (
paymentState !==
'processing'
) {
return;
}
paymentState =
'closed';
sendMetrikaGoal(
'donation_closed',
{
order_price:
amount.toFixed(2),
currency:
'RUB',
recurrent:
donationType
}
);
window.setTimeout(
function () {
window.location.href =
'/fail';
},
150
);
};
/* =================================================
ЗАПУСК CLOUDPAYMENTS
================================================= */
widget
.start(
intentParams
)
/* =================================================
УСПЕШНЫЙ ПЛАТЁЖ
================================================= */
.then(
async function (
widgetResult
) {
if (
paymentState !==
'processing'
) {
return;
}
paymentState =
'success';
var listId =
isMonthly
? window.UNISENDER_LIST_ID_MONTHLY
: window.UNISENDER_LIST_ID_ONETIME;
/*
* UniSender не влияет
* на статус уже успешного платежа.
*/
try {
await addToUnisender(
email,
listId,
name
);
} catch (
unisenderError
) {
console.error(
'[UniSender] Ошибка:',
unisenderError
);
}
/* =================================================
DONATION_SUCCESS
================================================= */
sendMetrikaGoal(
'donation_success',
{
order_price:
amount.toFixed(2),
currency:
'RUB',
recurrent:
donationType
}
);
/* =================================================
ECOMMERCE
================================================= */
window.dataLayer =
window.dataLayer ||
[];
window.dataLayer.push({
event:
'ecommerce_payment_success',
ecommerce: {
currencyCode:
'RUB',
purchase: {
actionField: {
id:
orderId,
revenue:
amount.toFixed(2),
affiliation:
isMonthly
? 'Пожертвование ежемесячное'
: 'Пожертвование разовое'
},
products: [
{
name:
isMonthly
? 'Регулярное пожертвование'
: 'Разовое пожертвование',
id:
isMonthly
? 'donation_monthly'
: 'donation_one_time',
price:
amount.toFixed(2),
quantity:
1,
variant:
email,
category:
donationType,
dimension1:
customRadio.checked
}
]
}
}
});
console.log(
'[CloudPayments] Успешный платёж:',
widgetResult
);
await sleep(
150
);
window.location.href =
'/success';
}
)
/* =================================================
НЕУСПЕШНЫЙ ПЛАТЁЖ
================================================= */
.catch(
function (error) {
/*
* Если пользователь просто
* закрыл виджет, событие уже
* обработано через oncomplete.
*/
if (
paymentState !==
'processing'
) {
return;
}
paymentState =
'fail';
console.error(
'[CloudPayments] Ошибка платежа:',
error
);
sendMetrikaGoal(
'donation_fail',
{
order_price:
amount.toFixed(2),
currency:
'RUB',
recurrent:
donationType
}
);
window.setTimeout(
function () {
window.location.href =
'/fail';
},
150
);
}
);
}
);
})();
</script>