(function () { 'use strict'; var CFG = { publicId: 'pk_833a9f5fcd0b80066c8718bf13e03', publicIdInstallment: 'pk_7685791892d716cdfc9355fa3f84a', publicIdDolyame: 'pk_5cc290ae26f086db06598c09769c7', publicIdSbp: '', publicIdForeign: '', taxSystem: 1, defaultVat: 5, calculationPlace: 'school.olesyamaterova.com', successUrl: 'https://school.olesyamaterova.com/sales/shop/dealPaid/id/config/hash/', itemsWebhookUrl: '', showCardButton: true, showInstallmentButton: true, showDolyameButton: true, showSbpButton: false, showForeignButton: false, minInstallmentRub: 3500, hideGcCardMethods: true, showGcForeignCards: true, showGcInstallmentMethods: true, hideGcAlternativeMethods: true, buttonImageCard: '/saas/images/card-logo-triple.png?v=1', buttonImageInstallment: '/saas/images/t-credit.png', buttonImageDolyame: 'https://fs26.getcourse.ru/fileservice/file/download/a/125071/sc/368/h/9ff417c8d370e94d9be4fff85cc18de5.svg', buttonImageSbp: '', buttonImageForeign: '/saas/images/card-logo-triple.png?v=1', buttonOrder: ['sbp', 'foreign', 'card', 'installment', 'dolyame'], cardPaymentMethods: ['Sbp', 'Card', 'ForeignCard', 'TinkoffPay', 'SberPay', 'MirPay'], cardPaymentRestricted: ['TcsInstallment', 'Dolyame', 'ApplePay', 'GooglePay', 'YandexPay', 'Som'], cardPaymentNotfoldedCount: 1, cardPaymentSbpFirst: true, installmentPaymentMethods: ['TcsInstallment'], installmentPaymentRestricted: ['Sbp', 'TinkoffPay', 'SberPay', 'MirPay', 'Dolyame', 'ForeignCard', 'YandexPay', 'ApplePay', 'GooglePay', 'Card'], dolyamePaymentMethods: ['Dolyame'], dolyamePaymentRestricted: ['Card', 'Sbp', 'TinkoffPay', 'SberPay', 'MirPay', 'TcsInstallment', 'ForeignCard', 'YandexPay', 'ApplePay', 'GooglePay'], sbpPaymentMethods: ['Sbp'], sbpPaymentRestricted: ['Card', 'ForeignCard', 'TcsInstallment', 'Dolyame', 'TinkoffPay', 'SberPay', 'MirPay', 'ApplePay', 'GooglePay', 'YandexPay', 'Som'], foreignPaymentMethods: ['ForeignCard'], foreignPaymentRestricted: ['Card', 'Sbp', 'TcsInstallment', 'Dolyame', 'TinkoffPay', 'SberPay', 'MirPay', 'ApplePay', 'GooglePay', 'YandexPay', 'Som'], subscriptionEnabled: false, subscriptionOfferIds: [], subscriptionKeywords: ['клуб'], subscriptionExcludeKeywords: ['демо'], defaultSubscriptionInterval: 'Month', defaultSubscriptionPeriod: 1, embedSchoolId: 333, fiscalCpHintsEnabled: false }; var dealId, description, amountNumber, email, currency; /** Кэш названий/позиций после «Информация о заказе» или из формы */ var orderCache = { titles: [], items: null, loaded: false, loading: false }; var spin = document.createElement('style'); spin.textContent = '.cp-btn-loading{position:relative;pointer-events:none;opacity:.7}' + '.cp-btn-loading::after{content:"";position:absolute;top:50%;left:50%;width:24px;height:24px;margin:-12px 0 0 -12px;border:3px solid rgba(0,0,0,.15);border-top-color:#333;border-radius:50%;animation:cp-spin .6s linear infinite}' + '@keyframes cp-spin{to{transform:rotate(360deg)}}' + '.oi-gc-pay-hidden{display:none!important}'; document.head.appendChild(spin); function $(sel) { return document.querySelector(sel); } function money(n) { return Math.round((+n + Number.EPSILON) * 100) / 100; } function buildFiscalCpHints(cfg, originalAmount, finalAmount) { if (!cfg || !cfg.fiscalCpHintsEnabled || !cfg.embedSchoolId) return null; function m(n) { return Math.round((+n + Number.EPSILON) * 100) / 100; } var orderRef = m(Math.max(Number(originalAmount) || 0, Number(finalAmount) || 0)); var paidRef = m(Number(finalAmount) || 0); var isFullPay = paidRef >= m(orderRef - 0.02); return { v: 1, schoolId: cfg.embedSchoolId, orderTotalRub: orderRef, paidRub: paidRef, atolPaymentMethod: isFullPay ? 'full_prepayment' : 'prepayment' }; } function attachFiscalCpHints(extraObj, metaObj, cfg, originalAmount, finalAmount) { var hints = buildFiscalCpHints(cfg, originalAmount, finalAmount); if (!hints) return null; extraObj.fiscalCpHints = hints; if (metaObj && typeof metaObj === 'object') metaObj.fiscalCpHints = hints; return hints; } function rublNum(s) { if (!s) return 0; var m = String(s).replace(/руб\.?/gi, '').match(/(\d[\d\s]*[.,]?\d*)/g); if (!m) return 0; var v = parseFloat(m[m.length - 1].replace(/\s/g, '').replace(',', '.')); return isNaN(v) ? 0 : money(v); } function nodeAmountRub(node) { if (!node) return 0; var t = node.innerText || node.textContent || ''; if (node.childNodes && node.childNodes.length) { for (var i = 0; i < node.childNodes.length; i++) { var cn = node.childNodes[i]; if (cn.nodeType === 3 && String(cn.textContent || '').trim()) { t = cn.textContent; break; } } } return rublNum(t); } function setButtonLoading(buttonEl, loading) { if (!buttonEl) return; buttonEl.classList.toggle('cp-btn-loading', !!loading); } function cfgButtonImage(key, fallback) { var v = CFG[key]; v = v != null ? String(v).trim() : ''; return v || fallback; } function escapeCssUrl(url) { return String(url || '') .replace(/\\/g, '%5C') .replace(/'/g, '%27') .replace(/"/g, '%22'); } /** Фоновая картинка с кнопки GetPay (например .pay-method.sbp) */ function gcPayMethodBackgroundImage(scope, selector) { var root = scope || getGcpayWidgetRoot() || document; var btn = root.querySelector(selector); if (!btn) return ''; var bg = window.getComputedStyle(btn).backgroundImage || ''; if (!bg || bg === 'none') return ''; var m = bg.match(/url\(["']?([^"')]+)["']?\)/i); return m ? m[1] : ''; } function sbpButtonImageUrl(container) { var fromCfg = CFG.buttonImageSbp != null ? String(CFG.buttonImageSbp).trim() : ''; if (fromCfg) return fromCfg; var fromGc = gcPayMethodBackgroundImage(container, '.gcpay-widget-payment-method-btn.pay-method.sbp') || gcPayMethodBackgroundImage(container, '.gcpay-widget-payment-method-btn.sbp'); if (fromGc) return fromGc; return 'https://fs24.getcourse.ru/fileservice/file/download/a/125071/sc/94/h/58a3b6625b9518f25ea416441d54dea1.jpeg'; } function createPaymentBlock(blockId, imglink, buttonText) { var img = escapeCssUrl(imglink); var div = document.createElement('div'); div.innerHTML = '
'; return div.firstElementChild; } function isInstallmentListTitle(text) { var t = String(text || '').toLowerCase(); return t.indexOf('рассроч') >= 0 || t.indexOf('кредит') >= 0 || t.indexOf('частями') >= 0 || t.indexOf('долями') >= 0; } function isForeignCardListTitle(text) { var t = String(text || '').toLowerCase(); return ( t.indexOf('не рф') >= 0 || t.indexOf('не из россии') >= 0 || t.indexOf('банка не из') >= 0 || t.indexOf('банк не из') >= 0 || t.indexOf('банк не из россии') >= 0 || t.indexOf('иностран') >= 0 || t.indexOf('загран') >= 0 || t.indexOf('foreign') >= 0 || t.indexOf('world') >= 0 || t.indexOf('made-world') >= 0 || t.indexOf('других стран') >= 0 || t.indexOf('другая страна') >= 0 ); } function listHasForeignGcpayMethod(list) { return !!list.querySelector( '.gcpay-widget-payment-method-btn.made-world, .gcpay-widget-payment-method .made-world' ); } function unhideGcAncestors(el, stopAt) { var node = el && el.parentElement; while (node && node !== document.body) { if (node.classList && node.classList.contains('oi-gc-pay-hidden')) { node.classList.remove('oi-gc-pay-hidden'); } if (stopAt && node === stopAt) break; if (node.classList && node.classList.contains('gcpay-widget')) break; node = node.parentElement; } } function isOurCpPaymentBlock(el) { if (!el) return false; return !!( el.closest('#pay-widget-cloudRF, #pay-widget-cloudINST, #pay-widget-cloudDOLY, #pay-widget-cloudSBP, #pay-widget-cloudFOREIGN') || el.closest('.cloudpayments-widget-payment-method') ); } function getGcpayMethodButtonLabel(btn) { if (!btn) return ''; var span = btn.querySelector('span'); return String((span && span.textContent) || btn.textContent || btn.innerText || '') .trim() .toLowerCase(); } function isForeignGcpayMethod(methodEl, btn) { btn = btn || (methodEl && methodEl.querySelector('.gcpay-widget-payment-method-btn')); if (!btn) return false; if (btn.classList.contains('made-world')) return true; return isForeignCardListTitle(getGcpayMethodButtonLabel(btn)); } function isRfGcpayMethod(methodEl, btn) { btn = btn || (methodEl && methodEl.querySelector('.gcpay-widget-payment-method-btn')); if (!btn) return false; if (btn.classList.contains('made-RF')) return true; var label = getGcpayMethodButtonLabel(btn); if (isForeignCardListTitle(label) || isInstallmentListTitle(label)) return false; return label.indexOf('карт') >= 0 || label.indexOf('оплатить') >= 0; } function canShowCpInstallment() { return CFG.showInstallmentButton && amountNumber >= CFG.minInstallmentRub; } function canShowCpDolyame() { return CFG.showDolyameButton && amountNumber > 0; } function canShowCpSbp() { return CFG.showSbpButton && !!(CFG.publicIdSbp && String(CFG.publicIdSbp).trim()); } function canShowCpForeign() { return CFG.showForeignButton && !!(CFG.publicIdForeign && String(CFG.publicIdForeign).trim()); } function terminalIdForMethod(method) { if (method === 'installment') { return CFG.publicIdInstallment || CFG.publicId; } if (method === 'dolyame') { return CFG.publicIdDolyame || CFG.publicId; } if (method === 'sbp') { return CFG.publicIdSbp; } if (method === 'foreign') { return CFG.publicIdForeign; } return CFG.publicId; } /** Виджет CP: СБП первым, карты/T-Pay/… под «Ещё способы» (paymentMethodsNotfoldedCount: 1) */ function cardMethodsForCpIntent() { var list = (CFG.cardPaymentMethods || []).slice(); if (!CFG.cardPaymentSbpFirst) return list; var rest = []; for (var i = 0; i < list.length; i++) { if (list[i] !== 'Sbp') rest.push(list[i]); } return ['Sbp'].concat(rest); } function cardNotfoldedForCpIntent() { if (CFG.cardPaymentSbpFirst) return 1; var n = parseInt(CFG.cardPaymentNotfoldedCount, 10); return Number.isFinite(n) && n > 0 ? n : 0; } function setGcMethodHidden(methodEl, hide) { if (!methodEl) return; if (hide) methodEl.classList.add('oi-gc-pay-hidden'); else { methodEl.classList.remove('oi-gc-pay-hidden'); if (CFG.showGcForeignCards) unhideGcAncestors(methodEl, getGcpayWidgetRoot()); } } function applyGcGcpayPaymentMethods(container) { var scope = container || getGcpayWidgetRoot(); if (!scope) return; scope.querySelectorAll('.gcpay-widget-payment-method').forEach(function (methodEl) { if (isOurCpPaymentBlock(methodEl)) return; var btn = methodEl.querySelector('.gcpay-widget-payment-method-btn'); if (!btn) return; var foreign = isForeignGcpayMethod(methodEl, btn); var installment = isInstallmentListTitle(getGcpayMethodButtonLabel(btn)); var rf = isRfGcpayMethod(methodEl, btn); var hide = false; if (!CFG.hideGcCardMethods) { hide = false; } else if (foreign) { hide = !CFG.showGcForeignCards; } else if (installment) { hide = !CFG.showGcInstallmentMethods; } else if (rf) { hide = true; } else { hide = !!CFG.hideGcCardMethods; } setGcMethodHidden(methodEl, hide); }); if (!CFG.hideGcCardMethods) return; scope.querySelectorAll('.gcpay-widget-payment-method-btn.made-world').forEach(function (btn) { if (isOurCpPaymentBlock(btn)) return; var block = btn.closest('.gcpay-widget-payment-method,.gc-payment-method-card') || btn; if (CFG.showGcForeignCards) { setGcMethodHidden(block, false); } else { setGcMethodHidden(block, true); } }); scope.querySelectorAll('.gc-payment-method-card.made-world,#stripe_slave_card_gc,#payAnyWay').forEach( function (el) { if (isOurCpPaymentBlock(el)) return; var block = el.closest('.gcpay-widget-payment-method,.gc-payment-method-card') || el; setGcMethodHidden(block, !CFG.showGcForeignCards); } ); scope.querySelectorAll('.gcpay-widget-payment-method-btn.made-RF,.gc-payment-method-card.made-RF').forEach( function (el) { if (isOurCpPaymentBlock(el)) return; if (el.classList.contains('made-world')) return; var block = el.closest('.gcpay-widget-payment-method,.gc-payment-method-card') || el; setGcMethodHidden(block, true); } ); } function applyGcListVisibility(list) { var titleEl = list.querySelector('.gcpay-widget-payment-methods-list-title'); var titleText = titleEl && titleEl.textContent; var hasForeignInside = listHasForeignGcpayMethod(list); if (!CFG.hideGcCardMethods) { list.classList.remove('oi-gc-pay-hidden'); return; } if (CFG.showGcInstallmentMethods && isInstallmentListTitle(titleText)) { list.classList.remove('oi-gc-pay-hidden'); return; } if (CFG.showGcForeignCards && (isForeignCardListTitle(titleText) || hasForeignInside)) { list.classList.remove('oi-gc-pay-hidden'); list.querySelectorAll('.gcpay-widget-payment-method').forEach(function (methodEl) { if (isOurCpPaymentBlock(methodEl)) return; var btn = methodEl.querySelector('.gcpay-widget-payment-method-btn'); if (!btn) return; var foreign = isForeignGcpayMethod(methodEl, btn); var installment = isInstallmentListTitle(getGcpayMethodButtonLabel(btn)); if (foreign && CFG.showGcForeignCards) setGcMethodHidden(methodEl, false); else if (installment) setGcMethodHidden(methodEl, !CFG.showGcInstallmentMethods); else setGcMethodHidden(methodEl, true); }); return; } if (isInstallmentListTitle(titleText)) { if (CFG.showGcInstallmentMethods) list.classList.remove('oi-gc-pay-hidden'); else list.classList.add('oi-gc-pay-hidden'); return; } list.classList.add('oi-gc-pay-hidden'); } function reapplyGcPaymentUi(container) { if (!container) return; var root = getGcpayWidgetRoot(); if (root) { if (CFG.showGcForeignCards && CFG.hideGcCardMethods) root.setAttribute('data-oi-foreign-mode', '1'); else root.removeAttribute('data-oi-foreign-mode'); } var lists = container.querySelectorAll('.gcpay-widget-payment-methods-list'); for (var i = 0; i < lists.length; i++) applyGcListVisibility(lists[i]); applyGcGcpayPaymentMethods(container); if (root && root !== container) applyGcGcpayPaymentMethods(root); var alt = (root || container).querySelector('.gcpay-widget-payment-alternative-methods'); if (alt) { var hideAlt = CFG.hideGcAlternativeMethods && !(CFG.showGcForeignCards && listHasForeignGcpayMethod(alt)); if (hideAlt) alt.classList.add('oi-gc-pay-hidden'); else { alt.classList.remove('oi-gc-pay-hidden'); if (CFG.showGcForeignCards && CFG.hideGcCardMethods) applyGcGcpayPaymentMethods(alt); } } } function applyGcVisibility(container) { reapplyGcPaymentUi(container); } function watchGcpayPaymentMethodsDom(container) { if (!container) return; reapplyGcPaymentUi(container); if (watchGcpayPaymentMethodsDom._obs) { watchGcpayPaymentMethodsDom._el = container; return; } watchGcpayPaymentMethodsDom._el = container; watchGcpayPaymentMethodsDom._obs = new MutationObserver(function () { if (watchGcpayPaymentMethodsDom._el) reapplyGcPaymentUi(watchGcpayPaymentMethodsDom._el); }); watchGcpayPaymentMethodsDom._obs.observe(container, { childList: true, subtree: true }); } function mountCustomPaymentButtons(container) { if (!container) return; var cpButtonIds = { sbp: 'pay-widget-cloudSBP', foreign: 'pay-widget-cloudFOREIGN', card: 'pay-widget-cloudRF', installment: 'pay-widget-cloudINST', dolyame: 'pay-widget-cloudDOLY' }; Object.keys(cpButtonIds).forEach(function (key) { var node = container.querySelector('#' + cpButtonIds[key]); if (node) node.remove(); }); function canShowCpButton(key) { if (key === 'card') return CFG.showCardButton; if (key === 'sbp') return canShowCpSbp(); if (key === 'foreign') return canShowCpForeign(); if (key === 'installment') return canShowCpInstallment(); if (key === 'dolyame') return canShowCpDolyame(); return false; } function createCpButton(key) { if (key === 'sbp') { return createPaymentBlock( cpButtonIds.sbp, sbpButtonImageUrl(container), 'СБП' ); } if (key === 'foreign') { return createPaymentBlock( cpButtonIds.foreign, cfgButtonImage('buttonImageForeign', '/saas/images/card-logo-triple.png?v=1'), 'Картой банка не из России' ); } if (key === 'card') { return createPaymentBlock( cpButtonIds.card, cfgButtonImage('buttonImageCard', '/saas/images/card-logo-triple.png?v=1'), 'Картой любого банка' ); } if (key === 'installment') { return createPaymentBlock( cpButtonIds.installment, cfgButtonImage('buttonImageInstallment', '/saas/images/t-credit.png'), 'В рассрочку' ); } if (key === 'dolyame') { return createPaymentBlock( cpButtonIds.dolyame, cfgButtonImage( 'buttonImageDolyame', 'https://fs26.getcourse.ru/fileservice/file/download/a/125071/sc/368/h/9ff417c8d370e94d9be4fff85cc18de5.svg' ), 'Долями' ); } return null; } var order = Array.isArray(CFG.buttonOrder) && CFG.buttonOrder.length ? CFG.buttonOrder.slice() : ['sbp', 'foreign', 'card', 'installment', 'dolyame']; var list = []; order.forEach(function (key) { if (!canShowCpButton(key)) return; var block = createCpButton(key); if (block) list.push(block); }); list.reverse().forEach(function (el) { container.insertBefore(el, container.firstChild); }); applyGcVisibility(container); } /* ========== Сбор данных (как gc-new-pay-page-body) ========== */ function getDealId() { var m = location.href.match(/\/sales\/shop\/dealPay\/id\/(\d+)/); if (m) return m[1]; m = location.href.match(/\/sales\/shop\/dealPaid\/id\/(\d+)/); if (m) return m[1]; m = location.href.match(/[?&]dealId=(\d+)/i); if (m) return m[1]; var scriptEl = $('.form-result-block script'); if (scriptEl) { m = scriptEl.textContent.match(/dealId\s*[=:]\s*['"]?(\d+)/); if (m) return m[1]; } return null; } function getHidden(key) { var input = $('input[name="params[DATA]"]'); if (!input) return ''; var m = (input.value || '').match(new RegExp(key + '=([^&]+)', 'i')); if (!m) return ''; try { return decodeURIComponent(m[1].replace(/\+/g, '%20')); } catch (e) { return m[1]; } } function getEmailFromUserData() { var items = document.querySelectorAll('.user-data__item'); for (var i = 0; i < items.length; i++) { var label = items[i].querySelector('.user-data__item-label'); var value = items[i].querySelector('.user-data__item-value'); if (!label || !value) continue; if (/e-?mail|эл\.?\s*почт/i.test(label.textContent || '')) { return (value.textContent || '').trim(); } } return ''; } function getEmail() { var selectors = [ 'input[name="formParams[email]"]', 'input[name="params[email]"]', 'input[type="email"]', '.gcpay-widget input[type="email"]', '.gcpay-widget-order-details input[name*="email" i]', ]; for (var i = 0; i < selectors.length; i++) { var el = document.querySelector(selectors[i]); if (el && el.value && String(el.value).trim()) return String(el.value).trim(); } var dataInput = $('input[name="params[DATA]"]'); if (dataInput && dataInput.value) { var m = dataInput.value.match(/Email=([^&]+)/i); if (m) { try { var decoded = decodeURIComponent(m[1].replace(/\+/g, '%20')).trim(); if (decoded) return decoded; } catch (e) { if (m[1] && m[1].trim()) return m[1].trim(); } } } return (getHidden('Email') || getEmailFromUserData() || '').trim(); } function applyEmailToPaymentContext(ctx, em) { if (!ctx || !em) return; ctx.email = em; ctx.accountId = em + '|deal:' + String(ctx.dealId); if (ctx.receiptGc) ctx.receiptGc.email = em; if (ctx.receiptCp) ctx.receiptCp.email = em; if (ctx.metadata) ctx.metadata.customerEmail = em; if (ctx.extra) ctx.extra.customerEmail = em; } function getPhone() { var raw = getHidden('Phone'); var digits = (raw || '').replace(/\D/g, ''); if (digits.length === 11 && (digits[0] === '7' || digits[0] === '8')) return '+7' + digits.slice(1); if (digits.length === 10) return '+7' + digits; return ''; } /** Номер заказа: «Информация о заказе #361840» */ function getGcOrderNumberFromHeader() { var span = document.querySelector('.gcpay-widget-modal-header span, .gcpay-widget-head h1 span'); if (span) { var n = String(span.textContent || '').replace(/\D/g, ''); if (/^\d+$/.test(n)) return n; } var hdr = document.querySelector('.gcpay-widget-modal-header, .gcpay-widget-head h1'); if (hdr) { var m = (hdr.textContent || '').match(/#(\d+)/); if (m) return m[1]; var nums = hdr.textContent.match(/\d+/g); if (nums) { return nums.reduce(function (a, b) { return b.length > a.length ? b : a; }, ''); } } return null; } /** Сумма из блока «Сумма к оплате» / таблицы «Итого к оплате» */ function getGcpayHowHappenValue(labelRegex) { var rows = document.querySelectorAll('.gcpay-widget-order-how-happen-table tr'); for (var i = 0; i < rows.length; i++) { var cells = rows[i].querySelectorAll('td'); if (cells.length < 2) continue; var label = (cells[0].textContent || '').replace(/\s+/g, ' ').trim(); if (!labelRegex.test(label)) continue; return rublNum(cells[1].textContent); } return 0; } function getTotalAmountRub() { var details = document.querySelector('.gcpay-widget-order-details-price-value'); if (details) { var v = rublNum(details.textContent); if (v > 0) return v; } var itogo = getGcpayHowHappenValue(/Итого к оплате/i); if (itogo > 0) return itogo; var inp = $('input[name="params[Amount]"]'); if (inp && /^\d+$/.test(String(inp.value || ''))) { return money(parseInt(inp.value, 10) / 100); } var el = $('.gcpay-widget-order-price p, .price-total__price, .deal-finish-price-title b'); return nodeAmountRub(el); } function normalizePositionTitle(raw) { return String(raw || '') .trim() .replace(/^\/+/, '') .replace(/\s+/g, ' ') .slice(0, 128); } /** Не путать с fallback «Заказ #123» */ function isValidPositionTitle(raw) { var s = normalizePositionTitle(raw); if (!s || s.length < 2) return false; if (/^\d+$/.test(s.replace(/[\s#]/g, ''))) return false; if (/^#?\d{4,}$/.test(s)) return false; if (/^заказ\s*#?\s*\d+$/i.test(s)) return false; if (/^информация\s+о\s+заказе/i.test(s)) return false; return true; } function hasValidOrderTitles() { return !!(orderCache.titles && orderCache.titles.length); } function pushUniqueTitle(titles, seen, raw) { var t = normalizePositionTitle(raw); if (!isValidPositionTitle(t) || seen[t]) return; seen[t] = true; titles.push(t); } function getGcpayWidgetRoot() { return ( document.querySelector('.gcpay-widget-modal-container .gcpay-widget') || document.querySelector('.gcpay-widget') || document.getElementById('app') || document.body ); } /** Как OTP: .gcpay-widget-order-position-title после «Информация о заказе» */ function collectPositionTitles(root) { var scopes = []; if (root && root.querySelectorAll) scopes.push(root); scopes.push(getGcpayWidgetRoot()); scopes.push(document); var titles = []; var seen = {}; var sel = '.gcpay-widget-order-positions-table .gcpay-widget-order-position-title,' + '.gcpay-widget-order-details .gcpay-widget-order-position-title,' + '.gcpay-widget-order-position-title,' + '[class*="order-position-title"]'; for (var s = 0; s < scopes.length; s++) { var scope = scopes[s]; if (!scope || !scope.querySelectorAll) continue; scope.querySelectorAll(sel).forEach(function (el) { pushUniqueTitle(titles, seen, el.textContent || el.innerText); }); if (titles.length) break; } document.querySelectorAll('.gcpay-widget-order-positions-table tr').forEach(function (tr) { var titleEl = tr.querySelector('.gcpay-widget-order-position-title'); var raw = titleEl ? titleEl.textContent || titleEl.innerText : ''; if (!raw) { var tds = tr.querySelectorAll('td'); if (tds.length) raw = tds[0].textContent || tds[0].innerText; } pushUniqueTitle(titles, seen, raw); }); return titles; } function getPrimaryPositionTitle() { var titles = collectPositionTitles(); return titles.length ? titles[0] : ''; } function findGcpayInfoButton() { return ( document.querySelector('.link-btn.gcpay-widget-btn--info') || document.querySelector('.gcpay-widget-btn--info.link-btn') || document.querySelector('.gcpay-widget-order-bottom .link-btn.gcpay-widget-btn--info') || document.querySelector('.gcpay-widget-order-bottom .gcpay-widget-btn--info') || document.querySelector('.gcpay-widget-btn--info') ); } function findGcpayInfoCloseButton() { return ( document.querySelector('.gcpay-widget-btn--close-btn.link-btn') || document.querySelector('.gcpay-widget-modal-container .gcpay-widget-btn--close-btn.link-btn') || document.querySelector('.gcpay-widget-modal-container .gcpay-widget-btn--close-btn') || document.querySelector('.gcpay-widget-btn--close-btn') ); } function resetOrderCache(full) { if (full) { orderCache.titles = []; orderCache.items = null; } orderCache.loaded = false; orderCache.loading = false; } function refreshOrderCacheFromDom() { orderCache.titles = collectPositionTitles(); var parsed = parseGcpayOrderPositionsTable(); if (parsed.length) { orderCache.items = parsed; if (!orderCache.titles.length) { orderCache.titles = parsed .map(function (it) { return normalizePositionTitle(it.label); }) .filter(isValidPositionTitle); } } } function getOfferTitleFromForm() { var el = document.querySelector('.offer-title, .form-position-title .offer-title, .form-position-title span'); if (!el) return ''; return (el.textContent || '').trim().replace(/\s+/g, ' ').slice(0, 128); } function getTitleFromParamsData() { var inp = $('input[name="params[DATA]"]'); if (!inp || !inp.value) return ''; var v = inp.value; var keys = ['OfferTitle', 'ProductTitle', 'ProductName', 'Title', 'offer_title', 'product']; for (var i = 0; i < keys.length; i++) { var m = v.match(new RegExp(keys[i] + '=([^&]+)', 'i')); if (!m) continue; try { var s = decodeURIComponent(m[1].replace(/\+/g, '%20')).trim(); if (s) return s.slice(0, 128); } catch (e) { if (m[1]) return m[1].slice(0, 128); } } return ''; } /** Открыть «Информация о заказе» (селекторы как у OTP), дождаться позиций, закрыть */ function ensureOrderInfoReady(force) { if (force) resetOrderCache(true); else resetOrderCache(false); refreshOrderCacheFromDom(); if (hasValidOrderTitles()) { orderCache.loaded = true; return Promise.resolve(orderCache); } if (orderCache.loading) { return new Promise(function (resolve) { var n = 0; var iv = setInterval(function () { n += 1; refreshOrderCacheFromDom(); if (!orderCache.loading || hasValidOrderTitles() || n > 80) { clearInterval(iv); resolve(orderCache); } }, 100); }); } var offer = getOfferTitleFromForm(); if (isValidPositionTitle(offer)) { orderCache.titles = [normalizePositionTitle(offer)]; orderCache.loaded = true; return Promise.resolve(orderCache); } var fromDataEarly = getTitleFromParamsData(); if (isValidPositionTitle(fromDataEarly)) { orderCache.titles = [normalizePositionTitle(fromDataEarly)]; orderCache.loaded = true; return Promise.resolve(orderCache); } var infoBtn = findGcpayInfoButton(); if (!infoBtn) { console.warn('[oi-cp] кнопка «Информация о заказе» не найдена'); return Promise.resolve(orderCache); } var maxTries = force ? 80 : 45; var pollMs = force ? 150 : 120; orderCache.loading = true; return new Promise(function (resolve) { try { infoBtn.click(); } catch (e) { orderCache.loading = false; console.warn('[oi-cp] info click failed', e); return resolve(orderCache); } var tries = 0; var poll = setInterval(function () { tries += 1; refreshOrderCacheFromDom(); if (hasValidOrderTitles() || tries >= maxTries) { clearInterval(poll); if (hasValidOrderTitles()) { var closeBtn = findGcpayInfoCloseButton(); if (closeBtn) { try { closeBtn.click(); } catch (e2) {} } orderCache.loaded = true; } orderCache.loading = false; console.log('[oi-cp] order info fetch', { force: !!force, tries: tries, titles: orderCache.titles, items: orderCache.items ? orderCache.items.length : 0 }); resolve(orderCache); } }, pollMs); }); } /** Description для CloudPayments = название позиции, не номер заказа */ function getTitleFromPage() { refreshOrderCacheFromDom(); if (orderCache.items && orderCache.items.length) { var fromItem = normalizePositionTitle(orderCache.items[0].label); if (isValidPositionTitle(fromItem)) return fromItem; } if (hasValidOrderTitles()) return orderCache.titles.join(', ').slice(0, 128); var offer = getOfferTitleFromForm(); if (isValidPositionTitle(offer)) return normalizePositionTitle(offer); var fromData = getTitleFromParamsData(); if (isValidPositionTitle(fromData)) return normalizePositionTitle(fromData); var gcNum = getGcOrderNumberFromHeader(); if (gcNum) { console.warn('[oi-cp] title fallback to order number — open order info or check DOM'); return 'Заказ #' + gcNum; } return 'Оплата обучения'; } function findParagraphValue(regex) { var v = getGcpayHowHappenValue(regex); if (v > 0) return v; var ps = document.querySelectorAll('.xdget-dealInfo p, .gcpay-widget p, p'); for (var i = 0; i < ps.length; i++) { var txt = (ps[i].textContent || '').replace(/\s+/g, ' ').trim(); if (!regex.test(txt)) continue; var b = ps[i].querySelector('b'); return (b ? rublNum(b.textContent) : rublNum(txt)) || 0; } return 0; } function getOriginalAmountFromDom() { var v = getGcpayHowHappenValue(/Сумма заказа/i); if (v > 0) return v; v = findParagraphValue(/Сумма заказа/i); if (v > 0) return v; return findParagraphValue(/Ваша цена/i) || 0; } function getBonusAvailableFromDom() { var v = findParagraphValue(/Доступно бонусных/i); if (v > 0) return v; var wrap = $('.price-bonuses__price'); return wrap ? rublNum(wrap.textContent) : 0; } function collectBonuses(finalAmount, dId, em) { var input = $('input[name="params[DATA]"]'); if (input && input.value) { var v = input.value; var getNum = function (k) { var m = v.match(new RegExp(k + '=([^&]+)', 'i')); if (!m) return null; var raw = ''; try { raw = decodeURIComponent(String(m[1]).replace(/\+/g, '%20')); } catch (e) { raw = String(m[1]); } var n = Number(String(raw).replace(/[^\d.,-]/g, '').replace(',', '.')); return Number.isFinite(n) ? n : null; }; var available = getNum('Bonuses'); var maxUse = getNum('MaxBonusToUse'); if (available != null || maxUse != null) { return { available: Number(available || 0), planned: Math.min(Number(maxUse || 0), Number(available || 0)), source: 'HiddenField.DATA', dealId: dId, email: em, originalAmount: getOriginalAmountFromDom(), finalAmount: finalAmount }; } } var original = getOriginalAmountFromDom(); var availableDom = getBonusAvailableFromDom(); var planned = Math.max(0, Math.min(availableDom, Math.max(original - (finalAmount || 0), 0))); return { available: availableDom, planned: planned, source: 'GetCourse.DOM', originalAmount: original, finalAmount: finalAmount, dealId: dId, email: em }; } function pushReceiptItem(items, label, price, qty) { qty = Math.max(1, parseInt(qty, 10) || 1); var unit = money(price); if (!(unit > 0)) return; var item = { label: String(label || 'Позиция').slice(0, 128), price: unit, quantity: qty, amount: money(unit * qty), object: 4, method: 4 }; if (CFG.defaultVat !== null && CFG.defaultVat !== undefined) item.vat = CFG.defaultVat; items.push(item); } /** Позиции из модалки/оверлея: .gcpay-widget-order-positions-table */ function parseGcpayOrderPositionsTable() { var items = []; var seen = {}; document.querySelectorAll('.gcpay-widget-order-positions-table tr').forEach(function (tr) { var titleEl = tr.querySelector('.gcpay-widget-order-position-title'); if (!titleEl) return; var label = normalizePositionTitle(titleEl.textContent || titleEl.innerText); if (!isValidPositionTitle(label)) return; var qty = 1; var countTd = tr.querySelector('.position-count, td[data-mobile-count]'); if (countTd) { var qSrc = countTd.getAttribute('data-mobile-count') || countTd.textContent || ''; var qm = qSrc.match(/(\d+)/); if (qm) qty = parseInt(qm[1], 10) || 1; } var tds = tr.querySelectorAll('td'); var priceTd = tds.length ? tds[tds.length - 1] : null; var unitPrice = rublNum(priceTd ? priceTd.textContent : ''); if (!(unitPrice > 0)) return; var key = label + '|' + unitPrice + '|' + qty; if (seen[key]) return; seen[key] = true; pushReceiptItem(items, label, unitPrice, qty); }); return items; } function parsePositionsFromDom() { if (orderCache.items && orderCache.items.length) return orderCache.items.slice(); var items = parseGcpayOrderPositionsTable(); if (items.length) return items; var seen = {}; function add(label, price, qty) { var key = label + '|' + price + '|' + (qty || 1); if (seen[key] || !(price > 0)) return; seen[key] = true; pushReceiptItem(items, label, price, qty); } document.querySelectorAll('.pay-orders-wrap .pay-order').forEach(function (row) { var label = ((row.querySelector('.pay-order__name') || {}).textContent || '').trim(); var priceEl = row.querySelector('.pay-order__price:not(.hidden)'); add(label, nodeAmountRub(priceEl), 1); }); document.querySelectorAll('.deal-positions > li, .pay-orders-wrap .order-item').forEach(function (li) { var titleEl = li.querySelector('.position-actual-title, .order-item__title'); var priceBox = li.querySelector('.deal-position-price, .order-item__price'); if (!priceBox) return; var clone = priceBox.cloneNode(true); clone.querySelectorAll('.source-price').forEach(function (n) { n.remove(); }); var label = (titleEl ? titleEl.textContent : '').trim() || 'Позиция заказа'; add(label, rublNum(clone.textContent), 1); }); return items; } function reconcileItemsTotalStrict(items, displayedTotal) { var sum = money(items.reduce(function (s, it) { return s + (it.amount || 0); }, 0)); var diff = money(displayedTotal - sum); if (!items.length || Math.abs(diff) <= 0.009) return items; var base = sum || 1; var remaining = Math.abs(diff); for (var i = 0; i < items.length; i++) { var it = items[i]; var share = money(Math.abs(diff) * ((it.amount || 0) / base)); var newAmt = diff > 0 ? money(it.amount + share) : money(it.amount - share); if (newAmt < 0.01) { remaining = money(remaining - (it.amount - 0.01)); it.amount = 0.01; } else { it.amount = newAmt; remaining = money(remaining - share); } } if (remaining > 0.001) { var last = items[items.length - 1]; last.amount = money(last.amount + (diff > 0 ? remaining : -remaining)); if (last.amount < 0.01) last.amount = 0.01; } items.forEach(function (it) { it.price = money((it.amount || 0) / (it.quantity || 1)); }); return items; } function collectOfferIds() { var ids = []; document.querySelectorAll('[data-offer-id], [data-offerid]').forEach(function (node) { var raw = node.getAttribute('data-offer-id') || node.getAttribute('data-offerid') || ''; if (/^\d+$/.test(String(raw).trim())) ids.push(String(raw).trim()); }); return ids.filter(function (v, i, a) { return a.indexOf(v) === i; }); } function shouldEnableSubscription(title) { if (!CFG.subscriptionEnabled) return false; var wl = (CFG.subscriptionOfferIds || []).map(String); if (wl.length) { return collectOfferIds().some(function (id) { return wl.indexOf(id) !== -1; }); } var lower = String(title || '').toLowerCase(); var kw = CFG.subscriptionKeywords || []; var ex = CFG.subscriptionExcludeKeywords || []; if (ex.some(function (k) { return lower.indexOf(String(k).toLowerCase()) >= 0; })) return false; return kw.some(function (k) { return lower.indexOf(String(k).toLowerCase()) >= 0; }); } function getSubscriptionPeriod(title) { var lower = String(title || '').toLowerCase(); var m = lower.match(/(?:на\s*)?(\d+)\s*(д(н|ней)|мес(яц|яцев)?|г(од|ода)?)/); if (!m) return null; var num = parseInt(m[1], 10); var unit = m[2] || ''; if (!Number.isFinite(num) || num <= 0) return null; if (unit.indexOf('д') === 0) return { interval: 'Day', period: num }; if (unit.indexOf('мес') === 0) return { interval: 'Month', period: num }; if (unit.indexOf('г') === 0) return { interval: 'Month', period: num * 12 }; return null; } function fetchItemsFromWebhook(dId) { if (!CFG.itemsWebhookUrl) return Promise.resolve(null); return fetch(CFG.itemsWebhookUrl + '?dealId=' + encodeURIComponent(dId), { method: 'GET' }) .then(function (r) { return r.ok ? r.json() : null; }) .catch(function () { return null; }); } /** Полный контекст платежа для extra / metadata / receipt */ function buildPaymentContext(selectedMethod) { var dId = getDealId(); var em = getEmail(); var ph = getPhone(); var total = getTotalAmountRub(); return ensureOrderInfoReady(true).then(function () { var title = getTitleFromPage(); return fetchItemsFromWebhook(dId).then(function (remoteItems) { var items = []; if (Array.isArray(remoteItems) && remoteItems.length) { remoteItems.forEach(function (it) { pushReceiptItem(items, it.label || it.name || title, it.price || it.amount || 0); }); } else { items = parsePositionsFromDom(); } if (!items.length && total > 0) pushReceiptItem(items, title, total); if (items.length === 1 && title) items[0].label = title; if (total > 0 && items.length) items = reconcileItemsTotalStrict(items, total); var finalTotal = money(items.reduce(function (s, it) { return s + (it.amount || 0); }, 0)) || total; var bonus = collectBonuses(finalTotal, dId, em); var appliedBonus = Number((bonus && bonus.planned) || 0); var originalAmount = Number((bonus && bonus.originalAmount) || getOriginalAmountFromDom() || 0); var gcOrderNumber = getGcOrderNumberFromHeader(); var invoiceId = gcOrderNumber || String(dId); var headerTitle = title; var accountId = (em ? em + '|' : '') + 'deal:' + String(dId); var methodTag = selectedMethod || 'card'; var receiptGc = { Items: items, calculationPlace: CFG.calculationPlace }; if (CFG.taxSystem !== null && CFG.taxSystem !== undefined) receiptGc.taxationSystem = CFG.taxSystem; if (em) receiptGc.email = em; if (ph) receiptGc.phone = ph; var receiptCp = { items: items, taxationSystem: CFG.taxSystem, calculationPlace: CFG.calculationPlace, amounts: { electronic: finalTotal } }; if (em) receiptCp.email = em; if (ph) receiptCp.phone = ph; var extra = { dealId: String(dId), selectedMethod: methodTag, publicId: CFG.publicId, appliedBonus: appliedBonus, originalAmount: originalAmount, finalAmount: finalTotal, bonusInfo: bonus, headerTitle: headerTitle, gcOrderNumber: gcOrderNumber, pageTitle: title, invoiceId: invoiceId, isSubscription: shouldEnableSubscription(title) ? '1' : '0', cloudPayments: { customerReceipt: receiptGc } }; var metadata = { dealId: String(dId), selectedMethod: methodTag, appliedBonus: appliedBonus, originalAmount: originalAmount, finalAmount: finalTotal, invoiceId: invoiceId, gcOrderNumber: gcOrderNumber || undefined, pageTitle: title, isSubscription: extra.isSubscription }; if (em) metadata.customerEmail = em; attachFiscalCpHints(extra, metadata, CFG, originalAmount, finalTotal); var successUrl = CFG.successUrl || location.href; if (successUrl.indexOf('__DEAL_ID__') >= 0) { successUrl = successUrl.replace('__DEAL_ID__', String(dId)); } return { dealId: dId, title: title, email: em, phone: ph, finalTotal: finalTotal, items: items, receiptGc: receiptGc, receiptCp: receiptCp, extra: extra, metadata: metadata, invoiceId: invoiceId, accountId: accountId, successUrl: successUrl, shouldSubscribe: shouldEnableSubscription(title), subPeriod: getSubscriptionPeriod(title) || { interval: CFG.defaultSubscriptionInterval, period: Number(CFG.defaultSubscriptionPeriod || 1) } }; }); }); } function loadCloudPayments(cb) { if (window.cp && window.cp.CloudPayments) return cb(); var s = document.createElement('script'); s.src = 'https://widget.cloudpayments.ru/bundles/cloudpayments.js'; s.onload = cb; document.head.appendChild(s); } function makePayment(buttonEl, methodSequence, restricted, selectedMethod) { if (selectedMethod === 'installment' && !canShowCpInstallment()) { alert('Рассрочка CloudPayments доступна от ' + CFG.minInstallmentRub + ' ₽.'); return; } if (selectedMethod === 'dolyame' && !canShowCpDolyame()) { alert('Оплата Долями недоступна для этого заказа.'); return; } if (selectedMethod === 'sbp' && !canShowCpSbp()) { alert('СБП недоступен — не настроен Public ID.'); return; } if (selectedMethod === 'foreign' && !canShowCpForeign()) { alert('Оплата иностранной картой недоступна — не настроен Public ID.'); return; } var terminalId = terminalIdForMethod(selectedMethod || 'card'); if (!terminalId) { alert('Не настроен Public ID для выбранного способа оплаты.'); return; } setButtonLoading(buttonEl, true); loadCloudPayments(function () { buildPaymentContext(selectedMethod) .then(function (ctx) { if (!ctx.dealId || !/^\d+$/.test(String(ctx.dealId))) { throw new Error('dealId'); } if (!ctx.finalTotal || ctx.finalTotal <= 0) { throw new Error('amount'); } applyEmailToPaymentContext(ctx, getEmail()); ctx.extra.selectedMethod = selectedMethod || 'card'; ctx.extra.publicId = terminalId; ctx.metadata.selectedMethod = ctx.extra.selectedMethod; var intent = { publicTerminalId: terminalId, description: ctx.title, culture: 'ru-RU', paymentSchema: 'Single', amount: ctx.finalTotal, currency: 'RUB', externalId: String(ctx.dealId), emailBehavior: 'Required', receipt: ctx.receiptCp, userInfo: { accountId: ctx.accountId, email: ctx.email || undefined, phone: ctx.phone || undefined }, extra: ctx.extra, metadata: ctx.metadata, paymentMethodSequence: methodSequence, restrictedPaymentMethods: restricted, successRedirectUrl: ctx.successUrl, failRedirectUrl: location.href }; if (ctx.email) intent.receiptEmail = ctx.email; if (selectedMethod === 'card') { var cardNf = cardNotfoldedForCpIntent(); if (cardNf > 0) intent.paymentMethodsNotfoldedCount = cardNf; } if (ctx.shouldSubscribe && ctx.subPeriod && ctx.subPeriod.period > 0) { intent.recurrent = { interval: ctx.subPeriod.interval, period: ctx.subPeriod.period, receipt: ctx.receiptCp }; } console.log('[oi-cp] intent payload', { dealId: ctx.dealId, amount: ctx.finalTotal, items: ctx.items, extra: ctx.extra }); return new cp.CloudPayments().start(intent); }) .then(function (result) { setButtonLoading(buttonEl, false); if (result && result.type === 'cancel') return; var closeBtn = $('.gcpay-widget-close-btn'); if (closeBtn) closeBtn.click(); }) .catch(function (err) { setButtonLoading(buttonEl, false); console.error('[oi-cp]', err); if (err && err.message === 'dealId') alert('Не найден номер заказа.'); else if (err && err.message === 'amount') alert('Не удалось определить сумму.'); else alert('Не удалось открыть оплату.'); }); }); } document.addEventListener('click', function (e) { var rf = e.target.closest('#pay-widget-cloudRF-button'); if (rf) { e.preventDefault(); e.stopPropagation(); makePayment(rf, cardMethodsForCpIntent(), CFG.cardPaymentRestricted, 'card'); return; } var inst = e.target.closest('#pay-widget-cloudINST-button'); if (inst) { e.preventDefault(); e.stopPropagation(); makePayment(inst, CFG.installmentPaymentMethods, CFG.installmentPaymentRestricted, 'installment'); return; } var doly = e.target.closest('#pay-widget-cloudDOLY-button'); if (doly) { e.preventDefault(); e.stopPropagation(); makePayment(doly, CFG.dolyamePaymentMethods, CFG.dolyamePaymentRestricted, 'dolyame'); return; } var sbp = e.target.closest('#pay-widget-cloudSBP-button'); if (sbp) { e.preventDefault(); e.stopPropagation(); makePayment(sbp, CFG.sbpPaymentMethods, CFG.sbpPaymentRestricted, 'sbp'); return; } var foreign = e.target.closest('#pay-widget-cloudFOREIGN-button'); if (foreign) { e.preventDefault(); e.stopPropagation(); makePayment(foreign, CFG.foreignPaymentMethods, CFG.foreignPaymentRestricted, 'foreign'); } }, true); function watchPositionTitlesInDom() { if (watchPositionTitlesInDom._obs) return; watchPositionTitlesInDom._obs = new MutationObserver(function () { var before = orderCache.titles.length; refreshOrderCacheFromDom(); if (!before && orderCache.titles.length) { description = getTitleFromPage(); console.log('[oi-cp] position title from DOM', description); } }); watchPositionTitlesInDom._obs.observe(document.body, { childList: true, subtree: true }); } function onPaymentOverlayAppeared(el) { resetOrderCache(true); dealId = getDealId(); currency = 'RUB'; email = getEmail(); amountNumber = getTotalAmountRub(); mountCustomPaymentButtons(el); watchGcpayPaymentMethodsDom(el); watchPositionTitlesInDom(); setTimeout(function () { reapplyGcPaymentUi(el); }, 300); setTimeout(function () { reapplyGcPaymentUi(el); }, 1200); ensureOrderInfoReady(true).then(function () { description = getTitleFromPage(); reapplyGcPaymentUi(el); console.log('[oi-cp] overlay ready', { dealId: dealId, sum: amountNumber, title: description, positions: orderCache.titles, showGcForeignCards: CFG.showGcForeignCards, foreignBtns: document.querySelectorAll('.gcpay-widget-payment-method-btn.made-world').length }); }); } function checkForPaymentMethods() { var app = document.getElementById('app'); if (app) { var x = app.querySelector('.gcpay-widget-payment-methods'); if (x) return x; } return document.querySelector('.gcpay-widget-payment-methods'); } var existing = checkForPaymentMethods(); var isVisible = !!existing; if (existing) onPaymentOverlayAppeared(existing); new MutationObserver(function () { var el = checkForPaymentMethods(); if (el && !isVisible) { isVisible = true; onPaymentOverlayAppeared(el); } else if (!el && isVisible) isVisible = false; }).observe(document.body, { childList: true, subtree: true }); })();