1// Payment System for Voice of Caspian 2// Т-Ðанк (Tinkoff) Payment Integration 3 4// Payment configuration 5const PAYMENT_CONFIG = { 6 // ÐÐ»Ñ production замениÑе на ÑеалÑнÑе даннÑе 7 merchantId: "your_merchant_id", 8 terminalKey: "your_terminal_key", 9 apiUrl: "https://securepay.tinkoff.ru/v2/", 10 successUrl: window.location.origin + "/payment-success.html", 11 failUrl: window.location.origin + "/payment-fail.html", 12}; 13 14// Organization details 15const ORGANIZATION = { 16 name: 'ÐÐÐ "ÐУÐЬТÐФУÐÐЦÐÐÐÐÐЬÐЫРÐÐÐÐТÐÐ ÐÐÐÐÐЫРЦÐÐТР"', 17 inn: "9704251448", 18 kpp: "770401001", 19 account: "40702810710001714283", 20 bank: "ÐР«ТÐанк»", 21 bik: "044525974", 22 correspondentAccount: "30101810145250000974", 23 address: 24 "119121, РоÑÑиÑ, г.ÐоÑква, вн.ÑеÑ.г. мÑниÑипалÑнÑй окÑÑг Хамовники, б-Ñ Ð¡Ð¼Ð¾Ð»ÐµÐ½Ñкий, д. 7, помеÑ. 3/1", 25}; 26 27// Modal management 28function openPaymentModal() { 29 const modal = document.getElementById("paymentModal"); 30 modal.classList.add("show"); 31 document.body.style.overflow = "hidden"; 32 33 // Initialize amount buttons 34 initializeAmountButtons(); 35} 36 37function closePaymentModal() { 38 const modal = document.getElementById("paymentModal"); 39 modal.classList.remove("show"); 40 document.body.style.overflow = "auto"; 41} 42 43// Tab switching 44function initializePaymentTabs() { 45 const tabs = document.querySelectorAll(".payment-tab"); 46 const contents = document.querySelectorAll(".payment-tab-content"); 47
48 tabs.forEach((tab) => { 49 tab.addEventListener("click", () => { 50 const targetTab = tab.getAttribute("data-tab"); 51 52 // Remove active class from all tabs and contents 53 tabs.forEach((t) => t.classList.remove("active")); 54 contents.forEach((c) => c.classList.remove("active")); 55 56 // Add active class to clicked tab and corresponding content 57 tab.classList.add("active"); 58 document.getElementById(targetTab + "Tab").classList.add("active"); 59 }); 60 }); 61} 62 63// Amount buttons functionality 64function initializeAmountButtons() { 65 const amountButtons = document.querySelectorAll(".amount-btn"); 66 const paymentAmountInput = document.getElementById("paymentAmount"); 67 68 amountButtons.forEach((button) => { 69 button.addEventListener("click", () => { 70 // Remove active class from all buttons 71 amountButtons.forEach((btn) => btn.classList.remove("active")); 72 73 // Add active class to clicked button 74 button.classList.add("active"); 75 76 // Set amount in payment form 77 const amount = button.getAttribute("data-amount"); 78 if (paymentAmountInput) { 79 paymentAmountInput.value = amount; 80 } 81 }); 82 }); 83} 84 85// Copy to clipboard functionality 86function copyToClipboard(text) { 87 navigator.clipboard 88 .writeText(text) 89 .then(() => { 90 showNotification("СкопиÑовано в бÑÑÐµÑ Ð¾Ð±Ð¼ÐµÐ½Ð°!", "success"); 91 }) 92 .catch((err) => { 93 console.error("Failed to copy: ", err); 94 // Fallback for older browsers 95 const textArea = document.createElement("textarea"); 96 textArea.value = text; 97 document.body.appendChild(textArea); 98 textArea.select(); 99 document.execCommand("copy"); 100 document.body.removeChild(textArea); 101 showNotification("СкопиÑовано в бÑÑÐµÑ Ð¾Ð±Ð¼ÐµÐ½Ð°!", "success"); 102 }); 103} 104 105// Notification system 106function showNotification(message, type = "info") { 107 const notification = document.createElement("div"); 108 notification.className = `notification notification-${type}`; 109 notification.innerHTML = ` 110 <i class="fas fa-${ 111 type === "success" ? "check-circle" : "info-circle" 112 }"></i> 113 <span>${message}</span> 114 `; 115 116 notification.style.cssText = ` 117 position: fixed; 118 top: 20px; 119 right: 20px; 120 background: ${type === "success" ? "#10b981" : "#3b82f6"}; 121 color: white; 122 padding: 15px 20px; 123 border-radius: 8px; 124 display: flex;
125 align-items: center; 126 gap: 10px; 127 z-index: 10000; 128 animation: slideInRight 0.3s ease-out; 129 box-shadow: 0 10px 25px rgba(0,0,0,0.2); 130 `; 131 132 document.body.appendChild(notification); 133 134 setTimeout(() => { 135 notification.style.animation = "slideOutRight 0.3s ease-out"; 136 setTimeout(() => { 137 document.body.removeChild(notification); 138 }, 300); 139 }, 3000); 140} 141 142// Удалено: Payment form handler Ð´Ð»Ñ ÐºÐ°ÑÑ Ð±Ð¾Ð»ÑÑе не нÑжен 143 144// Удалено: processPayment болÑÑе не нÑжна Ð´Ð»Ñ Ð½Ð¾Ð²Ð¾Ð¹ ÑÑÑÑкÑÑÑÑ Ð±ÐµÐ· каÑÑ 145 146// Удалено: Mock ÑÑнкÑии Ð´Ð»Ñ ÐºÐ°ÑÑ Ð±Ð¾Ð»ÑÑе не нÑÐ¶Ð½Ñ 147 148// Generate unique order ID 149function generateOrderId() { 150 const timestamp = Date.now(); 151 const random = Math.floor(Math.random() * 1000); 152 return `VOC_${timestamp}_${random}`; 153} 154 155// QR Code generation 156function generatePaymentQR(amount) { 157 const qrContainer = document.getElementById("paymentQR"); 158 const customAmountInput = document.getElementById("customAmountInput"); 159 160 if (amount === "custom") { 161 customAmountInput.style.display = "block"; 162 qrContainer.innerHTML = ` 163 <div class="qr-placeholder"> 164 <i class="fas fa-qrcode"></i> 165 <p>ÐведиÑе ÑÑÐ¼Ð¼Ñ Ð¸ нажмиÑе "СоздаÑÑ QR"</p> 166 </div> 167 `; 168 return; 169 } else { 170 customAmountInput.style.display = "none"; 171 } 172 173 if (amount) { 174 // Generate QR code with payment data 175 const qrData = generateQRPaymentData(amount); 176 qrContainer.innerHTML = ` 177 <img src="https://api.qrserver.com/v1/create-qr-code/?size=180x180&data=${encodeURIComponent( 178 qrData 179 )}" 180 alt="QR код Ð´Ð»Ñ Ð¾Ð¿Ð»Ð°ÑÑ ${amount} â½" 181 style="width: 180px; height: 180px;" /> 182 `; 183 } 184} 185 186function generateCustomQR() { 187 const customAmount = document.getElementById("customAmount").value; 188 if (customAmount && customAmount >= 100) { 189 generatePaymentQR(customAmount); 190 } else { 191 showNotification("ÐинималÑÐ½Ð°Ñ ÑÑмма: 100 â½", "error"); 192 } 193} 194 195// Generate QR payment data in SBP format 196function generateQRPaymentData(amount) { 197 // Simplified QR format for demo 198 // In production use proper SBP QR format 199 return `ST00012|Name=${ORGANIZATION.name}|PersonalAcc=${ 200 ORGANIZATION.account 201 }|BankName=${ORGANIZATION.bank}|BIC=${ORGANIZATION.bik}|CorrespAcc=${ 202 ORGANIZATION.correspondentAccount 203 }|PayeeINN=${ 204 ORGANIZATION.inn 205 }|Purpose=ÐÑганизаÑионнÑй Ð²Ð·Ð½Ð¾Ñ Ð·Ð° ÑÑаÑÑие в конкÑÑÑе|Sum=${amount * 100}`; 206} 207 208// Initialize all payment functionality 209document.addEventListener("DOMContentLoaded", function () { 210 initializePaymentTabs(); 211 212 // Add CSS animations 213 const style = document.createElement("style"); 214 style.textContent = ` 215 @keyframes slideInRight { 216 from { transform: translateX(100%); opacity: 0; } 217 to { transform: translateX(0); opacity: 1; } 218 } 219 220 @keyframes slideOutRight { 221 from { transform: translateX(0); opacity: 1; } 222 to { transform: translateX(100%); opacity: 0; } 223 } 224 225 .payment-confirmation-content { 226 background: white; 227 padding: 40px; 228 border-radius: 16px; 229 text-align: center; 230 max-width: 500px; 231 margin: 20px; 232 } 233 234 .payment-confirmation-content h3 { 235 margin-bottom: 20px; 236 color: #0f172a; 237 } 238 239 .payment-confirmation-content p { 240 margin-bottom: 15px; 241 color: #475569; 242 } 243 244 .confirmation-actions { 245 display: flex; 246 gap: 15px; 247 justify-content: center; 248 margin-top: 30px; 249 } 250 251 .notification { 252 font-weight: 500; 253 } 254 `; 255 document.head.appendChild(style); 256}); 257 258// Close modal when clicking outside 259document.addEventListener("click", function (e) { 260 const modal = document.getElementById("paymentModal"); 261 if (e.target === modal) { 262 closePaymentModal(); 263 } 264}); 265 266// Close modal with Escape key 267document.addEventListener("keydown", function (e) { 268 if (e.key === "Escape") { 269 const modal = document.getElementById("paymentModal"); 270 if (modal && modal.classList.contains("show")) { 271 closePaymentModal(); 272 } 273 } 274});
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.