Закрывающие документы

Счета и акты по вашим B2B-платежам за тарифы.

Закрывающие документы еще не сформированы (API в разработке)

🔒 Оформление подписки
Безопасная оплата для юридических лиц, ИП и самозанятых
Тариф «Стандарт» Ежемесячно
Прямой контакт с заказчиками и автоотклики
1 990 ₽
СПОСОБ ОПЛАТЫ:
На этот адрес будет направлен электронный фискальный чек (54-ФЗ ↗) и закрывающие акты
МИР Mastercard Visa Точка Банк Эквайринг
Оплата любой банковской картой (дебетовой, кредитной, корпоративной любого российского банка). Подписка активируется мгновенно, чек высылается в соответствии с 54-ФЗ ↗.
Списание 1 990 ₽ раз в месяц. Следующее списание — 23.10.2026. Отмена в любой момент в личном кабинете.
Оформляя автопродление, вы соглашаетесь с условиями периодических списаний. Отмена в 1 клик в настройках.
Реквизиты успешно скопированы в буфер обмена
`); printWindow.document.close(); setTimeout(() => printWindow.print(), 500); } async function submitCheckout() { if (!validateForm()) return; const token = getAuthToken(); if (!token) { alert('Пожалуйста, авторизуйтесь в системе для оформления подписки.'); redirectToLogin(); return; } const phoneInput = document.getElementById('payerPhoneInput'); let phone = (phoneInput.value || '').trim(); if (!phone) { const authPhone = getAuthenticatedPhone(); if (authPhone) { phone = formatPhoneValue(authPhone); phoneInput.value = phone; } } const email = document.getElementById('payerEmailInput').value; const companyInput = document.getElementById('payerCompanyInput'); let company = (companyInput.value || '').trim(); if (!company) { company = getAuthenticatedCompanyName(activeLegalType); companyInput.value = company; } const innInput = document.getElementById('payerInnInput'); let inn = (innInput.value || '').trim(); if (!inn) { const authInn = getAuthenticatedInn(); if (authInn) { inn = authInn; innInput.value = inn; } } const kppInput = document.getElementById('payerKppInput'); let kpp = kppInput ? (kppInput.value || '').trim() : ''; if (!kpp && activeLegalType === 'ORG') { kpp = getAuthenticatedKpp(); if (kppInput) kppInput.value = kpp; } const submitBtn = document.getElementById('checkoutSubmitBtn'); const btnPayText = document.getElementById('btnPayText'); const originalText = btnPayText.textContent; submitBtn.disabled = true; btnPayText.textContent = 'Создание сессии оплаты...'; console.log(`[POST https://billings.rukki.pro/api/v1/subscriptions/checkout] plan=${selectedPlan}, method=${activePaymentMethod}, inn=${inn}`); try { // Точка Банк UAPI строго требует схему https:// для redirectUrl и failRedirectUrl let baseOrigin = window.location.origin; if (baseOrigin.startsWith('http://')) { if (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1' || window.location.hostname === '0.0.0.0') { baseOrigin = 'https://rukki.pro'; } else { baseOrigin = baseOrigin.replace(/^http:\/\//i, 'https://'); } } const redirectUrl = baseOrigin + '/subscriptions.html?status=success&plan=' + selectedPlan; const failRedirectUrl = baseOrigin + '/subscriptions.html?status=fail&plan=' + selectedPlan; let sendCompanyName = company; if (activeLegalType === 'IP' && !sendCompanyName.startsWith('ИП ') && !sendCompanyName.startsWith('Ип ')) { sendCompanyName = 'ИП ' + sendCompanyName; } const isRecurring = isRecurringEnabled(); const invoiceExpiryDaysInput = document.getElementById('invoiceExpiryDaysInput'); const checkoutPayload = { planCode: selectedPlan, interval: currentInterval, provider: 'TOCHKA', paymentMode: ['CARD', 'SBP', 'TINKOFF', 'DOLYAME', 'INVOICE'].includes(activePaymentMethod) ? activePaymentMethod : null, inn: inn, companyName: sendCompanyName, kpp: (activeLegalType === 'ORG' && kpp) ? kpp : null, payerPhone: phone, payerEmail: email || null, redirectUrl: redirectUrl, failRedirectUrl: failRedirectUrl, recurring: isRecurring, saveCard: isRecurring }; if (isRecurring) { checkoutPayload.options = { period: currentInterval === 'YEARLY' ? 'Year' : 'Month', trancheCount: currentInterval === 'YEARLY' ? 5 : 36 }; } if (activePaymentMethod === 'INVOICE') { if (invoiceExpiryDaysInput && invoiceExpiryDaysInput.value) checkoutPayload.invoiceExpiryDays = parseInt(invoiceExpiryDaysInput.value, 10) || 1; } const res = await fetch('https://billings.rukki.pro/api/v1/subscriptions/checkout', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify(checkoutPayload) }); const data = await res.json(); console.log('Ответ billing-service:', data); const errorBox = document.getElementById('checkoutErrorBox'); const errorText = document.getElementById('checkoutErrorText'); if (errorBox) errorBox.style.display = 'none'; if (res.ok && data.paymentLink) { if (activePaymentMethod === 'INVOICE') { showToast('Счёт сформирован! Открываем PDF-файл...'); window.open(data.paymentLink, '_blank'); closeCheckoutModal(); openPaymentResultModal('success', { plan: selectedPlan, operationId: data.operationId }); } else { btnPayText.textContent = 'Переход в банк...'; window.location.href = data.paymentLink; } } else { const errDetail = data.message || data.detail || `Ошибка ${res.status}`; if (errorBox && errorText) { if (String(errDetail).includes('Retailer not found')) { errorText.innerHTML = 'Интернет-эквайринг для ООО «ЕТЕХ» находится в процессе плановой активации банком. Вы можете оплатить подписку по безналичному расчёту через вкладку «🏢 B2B Счёт» или повторить попытку позже.'; } else { errorText.textContent = errDetail; } errorBox.style.display = 'block'; errorBox.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); } else { alert(`Не удалось создать сессию оплаты: ${errDetail}`); } submitBtn.disabled = false; btnPayText.textContent = originalText; } } catch (e) { console.error('Ошибка вызова billing-service:', e); const errorBox = document.getElementById('checkoutErrorBox'); const errorText = document.getElementById('checkoutErrorText'); if (errorBox && errorText) { errorText.textContent = `Ошибка связи с платёжным шлюзом (${e.message}). Проверьте интернет-соединение или повторите попытку через минуту.`; errorBox.style.display = 'block'; } else { alert(`Ошибка сети или сервиса оплаты: ${e.message}`); } submitBtn.disabled = false; btnPayText.textContent = originalText; } } function openPaymentResultModal(status, options) { const opts = options || {}; const overlay = document.getElementById('paymentResultModalOverlay'); const iconEl = document.getElementById('paymentResultIcon'); const titleEl = document.getElementById('paymentResultTitle'); const descEl = document.getElementById('paymentResultDesc'); const detailsBox = document.getElementById('paymentResultDetailsBox'); const planRow = document.getElementById('paymentResultDetailRowPlan'); const planVal = document.getElementById('paymentResultDetailPlanVal'); const opIdRow = document.getElementById('paymentResultDetailRowOpId'); const opIdVal = document.getElementById('paymentResultDetailOpIdVal'); const primaryBtn = document.getElementById('paymentResultPrimaryBtn'); if (!overlay) return; let hasDetails = false; if (status === 'success') { iconEl.style.background = '#dcfce7'; iconEl.style.color = '#16a34a'; iconEl.textContent = '✓'; titleEl.textContent = 'Оплата успешно завершена!'; if (opts.plan) { const planNames = { 'STANDARD': '«Стандарт»', 'PREMIUM': '«Премиум»', 'BASIC': '«Базовый»' }; const planTitle = planNames[opts.plan] || opts.plan; descEl.textContent = `Тариф ${planTitle} успешно оплачен и активирован на вашем аккаунте. Все возможности сервиса уже доступны!`; if (planRow && planVal) { planRow.style.display = 'flex'; planVal.textContent = planTitle; hasDetails = true; } } else { descEl.textContent = 'Платёжная сессия через интернет-эквайринг Точка Банка успешно подтверждена. Средства зачислены на счёт ООО «ЕТЕХ».'; } if (opts.operationId && opIdRow && opIdVal) { opIdRow.style.display = 'flex'; opIdVal.textContent = opts.operationId; hasDetails = true; } primaryBtn.textContent = 'Перейти в личный кабинет'; primaryBtn.onclick = function() { window.location.href = '/profile.html'; }; } else { iconEl.style.background = '#fee2e2'; iconEl.style.color = '#dc2626'; iconEl.textContent = '✕'; titleEl.textContent = 'Оплата не завершена'; descEl.textContent = 'Операция была отменена или отклонена банком. Средства с карты или счёта не были списаны.'; if (opts.operationId && opIdRow && opIdVal) { opIdRow.style.display = 'flex'; opIdVal.textContent = opts.operationId; hasDetails = true; } primaryBtn.textContent = opts.plan ? 'Попробовать снова' : 'Повторить платёж'; primaryBtn.onclick = function() { closePaymentResultModal(null); if (opts.plan) { openCheckoutModal(opts.plan, opts.interval || 'MONTHLY'); } }; } if (detailsBox) { detailsBox.style.display = hasDetails ? 'block' : 'none'; } overlay.style.display = 'flex'; setTimeout(() => overlay.classList.add('active'), 10); document.body.style.overflow = 'hidden'; // Мягко очищаем параметры статуса из строки браузера (без перезагрузки страницы) try { const cleanUrl = new URL(window.location.href); cleanUrl.searchParams.delete('status'); cleanUrl.searchParams.delete('operationId'); cleanUrl.searchParams.delete('operation_id'); cleanUrl.searchParams.delete('orderId'); cleanUrl.searchParams.delete('order_id'); cleanUrl.searchParams.delete('paymentLinkId'); const searchStr = cleanUrl.searchParams.toString(); window.history.replaceState({}, document.title, cleanUrl.pathname + (searchStr ? '?' + searchStr : '')); } catch (e) {} } function closePaymentResultModal(event) { if (event && event.target && event.target !== document.getElementById('paymentResultModalOverlay') && !event.target.classList.contains('checkout-close-btn')) { return; } const overlay = document.getElementById('paymentResultModalOverlay'); if (overlay) { overlay.classList.remove('active'); setTimeout(() => { overlay.style.display = 'none'; }, 200); } document.body.style.overflow = ''; } // Auto-detect URL parameters on load (e.g. /subscriptions.html?plan=STANDARD&checkout=1) document.addEventListener('DOMContentLoaded', () => { const urlParams = new URLSearchParams(window.location.search); const paramPlan = urlParams.get('plan'); const paramInterval = urlParams.get('interval'); const paramCheckout = urlParams.get('checkout'); const paramStatus = urlParams.get('status'); const paramOpId = urlParams.get('operationId') || urlParams.get('operation_id'); const paramOrderId = urlParams.get('orderId') || urlParams.get('order_id') || urlParams.get('paymentLinkId'); if (paramStatus === 'success' || paramStatus === 'fail' || paramStatus === 'cancel') { openPaymentResultModal(paramStatus === 'success' ? 'success' : 'fail', { plan: paramPlan, interval: paramInterval, operationId: paramOpId || paramOrderId }); if (paramStatus === 'success' && typeof loadUserProfile === 'function') { loadUserProfile(); } } if (paramPlan && (paramPlan === 'STANDARD' || paramPlan === 'PREMIUM' || paramPlan === 'BASIC')) { selectPlan(paramPlan); } if (paramInterval && (paramInterval === 'MONTHLY' || paramInterval === 'YEARLY')) { setIntervalMode(paramInterval); } if (paramCheckout === '1' || paramCheckout === 'true') { openCheckoutModal(selectedPlan, currentInterval); } // Check if there was a pending checkout before login const pendingPlan = sessionStorage.getItem('pending_checkout_plan'); if (pendingPlan && getAuthToken()) { sessionStorage.removeItem('pending_checkout_plan'); const pendingInterval = sessionStorage.getItem('pending_checkout_interval') || 'MONTHLY'; sessionStorage.removeItem('pending_checkout_interval'); selectPlan(pendingPlan); setIntervalMode(pendingInterval); openCheckoutModal(pendingPlan, pendingInterval); } });