1/** 2 * account_billing.js â Ñаздел «ÐплаÑа» кабинеÑа: ÑÑÑÑ ÐºÐ°ÑÑ, покÑпка, движениÑ. 3 * 4 * Раздел ÐÐÐÐÐЫÐÐÐТ СÐÐЯ СÐÐ, и ÑÑо не ÑкÑаÑение, а пÑавило. Ðока пÑодажа 5 * закÑÑÑа, `/api/billing/catalog` оÑвеÑÐ°ÐµÑ Ñовно как неÑÑÑеÑÑвÑÑÑий пÑÑÑ â 6 * знаÑÐ¸Ñ Ð²ÐºÐ»Ð°Ð´ÐºÐ¸ бÑÑÑ Ð½Ðµ должно вовÑе: пÑÑÑÐ°Ñ Ð²Ð¸ÑÑина без ÑоваÑа обеÑала Ð±Ñ 7 * покÑпкÑ, коÑоÑой неÑ. ÐоÑÑÐ¾Ð¼Ñ Ð²ÐºÐ»Ð°Ð´ÐºÐ° в ÑазмеÑке Ð»ÐµÐ¶Ð¸Ñ Ñ `hidden`, и ÑÐ½Ð¸Ð¼Ð°ÐµÑ 8 * его ÑолÑко ÑÑпеÑнÑй оÑÐ²ÐµÑ Ð²Ð¸ÑÑинÑ. 9 * 10 * ÐÑавило ÑеÑÑноÑÑи пÑо оÑÑаÑок: `balance: null` â ÑÑо «ÑÑÑÑÑик не оÑвеÑил», а 11 * ÐР«каÑÑ Ð½ÐµÑ». ÐÐ¾Ð»Ñ Ð¸ пÑоÑеÑк â ÑазнÑе оÑвеÑÑ, и пÑÑаÑÑ Ð¸Ñ Ð½ÐµÐ»ÑзÑ: по ÑÑÐ¾Ð¼Ñ 12 * ÑиÑÐ»Ñ Ñеловек ÑÑÐ´Ð¸Ñ Ð¾ ÑÐ²Ð¾Ð¸Ñ Ð´ÐµÐ½ÑÐ³Ð°Ñ . 13 * 14 * ÐодклÑÑаÑÑ ÐÐСÐÐ account.js â беÑÑÑ ÐµÐ³Ð¾ VMSAccount.request (ÑÐ¾Ñ Ð¾Ð´Ð¸Ð½ Ñаз 15 * пÑÐ¾Ð´Ð»ÐµÐ²Ð°ÐµÑ ÑеÑÑÐ¸Ñ Ð½Ð° 401 и повÑоÑÑÐµÑ Ð·Ð°Ð¿ÑоÑ). 16 */ 17 18(function () { 19 'use strict'; 20 21 var T = window.VMSAccountText; 22 23 //: СколÑко Ñаз пеÑепÑовеÑиÑÑ ÑÑÑÑ Ð¿Ð¾Ñле возвÑаÑа Ñ Ð¾Ð¿Ð»Ð°ÑÑ. ÐаÑÑÑ ÐºÐ»Ð°Ð´ÑÑ 24 //: Ð²ÐµÐ±Ñ Ñк банка, и он Ð¼Ð¾Ð¶ÐµÑ Ð¿ÑийÑи на ÑекÑндÑ-дÑÑгÑÑ Ð¿Ð¾Ð·Ð¶Ðµ покÑпаÑелÑ. 25 var TOPUP_POLL_TRIES = 6; 26 var TOPUP_POLL_MS = 2000; 27 28 function el(id) { return document.getElementById(id); } 29 30 /** 31 * Ð ÑÑÑкое Ñклонение Ñлова «каÑÑа» по ÑиÑлÑ: пÑÑÑ ÑÑÑок вмеÑÑо библиоÑеки, заÑо 32 * «1 каÑÑа», «2 каÑÑÑ», «5 каÑÑ». ÐнглийÑÐºÐ¾Ð¼Ñ Ñ Ð²Ð°ÑÐ°ÐµÑ Â«map/maps». 33 * 34 * @param {number} n ÐолиÑеÑÑво. 35 * @returns {string} ЧиÑло Ñо Ñловом. 36 */ 37 function cards(n) { 38 var v = Math.abs(Number(n) || 0); 39 var lang = 'ru'; 40 try { 41 if (window.I18N && typeof I18N.getLang === 'function') lang = I18N.getLang(); 42 } catch (e) { /* ÑловаÑÑ Ð½Ðµ поднÑлÑÑ â ÑÑиÑаем ÑÑÑÑким, как и оÑÑалÑной Ð·Ð°Ð¿Ð°Ñ */ } 43 if (lang !== 'ru') return v + (v === 1 ? ' map' : ' maps'); 44 45 var tail100 = v % 100; 46 var tail10 = v % 10; 47 var word = 'каÑÑ'; 48 if (tail100 < 11 || tail100 > 14) { 49 if (tail10 === 1) word = 'каÑÑа'; 50 else if (tail10 >= 2 && tail10 <= 4) word = 'каÑÑÑ'; 51 } 52 return v + ' ' + word; 53 } 54 55 /** СÑмма Ñ ÑазÑÑдами и знаком ÑÑÐ±Ð»Ñ â Ñак же, как ÑкÑан гейÑа и ÑаÑиÑÑ. */ 56 function price(rub) { 57 var n = Number(rub) || 0; 58 var grouped = String(n).replace(/\B(?=(\d{3})+(?!\d))/g, ' '); 59 return T.tf('acc_bill_price', { price: grouped }); 60 } 61 62 function say(id, key, bad) { 63 var node = el(id); 64 if (!node) return; 65 node.hidden = !key; 66 node.className = 'acc-msg' + (bad ? ' acc-msg-error' : ''); 67 node.textContent = key ? T.t(key) : ''; 68 } 69 70 // --------------------------------------------------------------------------- 71 // СÑÑÑ 72 // --------------------------------------------------------------------------- 73 74 /** 75 * ÐоказаÑÑ Ð¾ÑÑаÑок каÑÑ. 76 * 77 * @param {Object|null} body Тело `/api/auth/me/credits` либо null. 78 */ 79 function renderBalance(body) { 80 var node = el('accBillBalance'); 81 if (!node) return; 82 // ÐÑоÑеÑк â ÑÑо оÑÐ²ÐµÑ Â«Ð½Ðµ знаем». ÐÐ¾Ð»Ñ Ð¿Ð¸Ñем ÑиÑÑой: Ñ Ñеловека 83 // дейÑÑвиÑелÑно Ð¼Ð¾Ð¶ÐµÑ Ð±ÑÑÑ Ð¿ÑÑÑой ÑÑÑÑ, и молÑаÑÑ Ð¾Ð± ÑÑом неÑеÑÑно. 84 var known = body && typeof body.balance === 'number'; 85 node.textContent = known ? cards(body.balance) : 'â'; 86 } 87 88 function loadBalance() { 89 var A = window.VMSAccount; 90 if (!A) return Promise.resolve(null); 91 return A.request('/api/auth/me/credits').then(function (res) { 92 var body = (res.ok && res.body) ? res.body : null; 93 renderBalance(body); 94 return body; 95 }); 96 } 97 98 // --------------------------------------------------------------------------- 99 // ÐиÑÑина 100 // --------------------------------------------------------------------------- 101 102 /** 103 * Ðдна позиÑÐ¸Ñ Ð²Ð¸ÑÑинÑ.
104 * 105 * @param {Object} item {sku, cards, price_rub, base_price_rub?} 106 * @returns {HTMLElement} 107 */ 108 function shopItem(item) { 109 var li = document.createElement('li'); 110 li.className = 'acc-bill-item'; 111 112 var head = document.createElement('div'); 113 head.className = 'acc-bill-item-head'; 114 115 var qty = document.createElement('b'); 116 qty.textContent = cards(item.cards); 117 head.appendChild(qty); 118 119 var sum = document.createElement('span'); 120 sum.className = 'acc-bill-item-price'; 121 sum.textContent = price(item.price_rub); 122 head.appendChild(sum); 123 124 // ÐаÑÑÑкнÑÑÐ°Ñ Ñена поÑвлÑеÑÑÑ Ð¢ÐÐЬÐРкогда ÑеÑÐ²ÐµÑ Ð¿ÑиÑлал базовÑÑ: ÑÐºÐ¸Ð´ÐºÑ 125 // на клиенÑе не вÑдÑмÑваем â ÐµÑ Ð»Ð¸Ð±Ð¾ назвал пÑайÑ, либо ÐµÑ Ð½ÐµÑ. 126 if (item.base_price_rub) { 127 var was = document.createElement('s'); 128 was.className = 'acc-bill-item-was'; 129 was.textContent = price(item.base_price_rub); 130 head.appendChild(was); 131 } 132 li.appendChild(head); 133 134 var btn = document.createElement('button'); 135 btn.type = 'button'; 136 btn.className = 'acc-btn acc-btn-primary acc-bill-buy'; 137 btn.textContent = T.t('acc_bill_buy'); 138 btn.addEventListener('click', function () { buy(item.sku, btn); }); 139 li.appendChild(btn); 140 141 // ÐÑоÑой ÑпоÑоб оплаÑÑ Ñой же позиÑии â денÑгами коÑелÑка (#1266). ÐÐ½Ð¾Ð¿ÐºÑ 142 // Ð´ÐµÐ»Ð°ÐµÑ Ð¡ÐРкоÑелÑк: он Ð·Ð½Ð°ÐµÑ Ð¾ÑÑаÑок и пеÑеÑиÑÑÐµÑ ÑÑÑÑ. ÐÐµÑ ÐµÐ³Ð¾ â Ð½ÐµÑ ÐµÑ. 143 if (window.VMSAccountWallet && VMSAccountWallet.packButton) { 144 li.appendChild(VMSAccountWallet.packButton(item)); 145 } 146 147 return li; 148 } 149
150 function renderShop(payload) { 151 var list = el('accBillShop'); 152 var card = el('accBillShopCard'); 153 if (!list) return; 154 list.textContent = ''; 155 var items = (payload && payload.items) || []; 156 items.forEach(function (item) { list.appendChild(shopItem(item)); }); 157 // ÐиÑÑина без единого ÑоваÑа не показÑваеÑÑÑ: каÑÑоÑка «кÑпиÑе» Ñ Ð¿ÑÑÑÑм 158 // ÑпиÑком вÑглÑÐ´Ð¸Ñ Ð¿Ð¾Ð»Ð¾Ð¼ÐºÐ¾Ð¹, а не оÑÑÑÑÑÑвием пÑедложениÑ. 159 if (card) card.hidden = items.length === 0; 160 161 // ÐоÑÑдок ÑпиÑÐ°Ð½Ð¸Ñ (accBillBeta) пиÑÐµÑ ÐÐÐРмодÑÐ»Ñ â account_cards.js: 162 // ÑÑаза вÑбиÑаеÑÑÑ Ð¿Ð¾ ÑоÑÑоÑÐ½Ð¸Ñ Ð³ÐµÐ¹Ñа взиманиÑ, и вÑоÑой пиÑаÑÐµÐ»Ñ 163 // Ð¾Ð´Ð½Ð°Ð¶Ð´Ñ Ð¿Ð¾ÐºÐ°Ð·Ð°Ð» Ð±Ñ Ð²Ð¾ вкладке не Ñо, ÑÑо на «Ðлавной» (T-260924-36b8). 164 } 165 166 /** 167 * ÐÑпиÑÑ Ð¿Ð°ÐºÐµÑ: заказ на ÑеÑвеÑе, заÑем ÑÑ Ð¾Ð´ на плаÑÑжнÑÑ ÑÑÑаниÑÑ Ð±Ð°Ð½ÐºÐ°. 168 * 169 * @param {string} sku ÐденÑиÑикаÑÐ¾Ñ ÑоваÑа. 170 * @param {HTMLElement} btn ÐажаÑÐ°Ñ ÐºÐ½Ð¾Ð¿ÐºÐ° â ÐµÑ Ð³Ð°Ñим на вÑÐµÐ¼Ñ Ð·Ð°Ð¿ÑоÑа. 171 */ 172 function buy(sku, btn) { 173 var A = window.VMSAccount; 174 if (!A) return; 175 if (btn) btn.disabled = true; 176 say('accBillStatus', 'acc_bill_wait'); 177 178 A.request('/api/billing/topup', { 179 method: 'POST', 180 body: JSON.stringify({ sku: sku }) 181 }).then(function (res) { 182 if (res.status === 401) return; // request Ñже ÑвÑл на Ð²Ñ Ð¾Ð´ 183 if (res.ok && res.body && res.body.payment_url) { 184 // Ð£Ñ Ð¾Ð´Ð¸Ð¼ на ÑÑÑаниÑÑ Ð±Ð°Ð½ÐºÐ°. ÐÐ½Ð¾Ð¿ÐºÑ ÐРвклÑÑаем обÑаÑно: ÑÑÑаниÑа 185 // ÑейÑÐ°Ñ ÑмениÑÑÑ, а Ð¶Ð¸Ð²Ð°Ñ ÐºÐ½Ð¾Ð¿ÐºÐ° ÑÑпела Ð±Ñ Ð·Ð°Ð²ÐµÑÑи вÑоÑой заказ. 186 window.location.href = res.body.payment_url; 187 return; 188 } 189 if (btn) btn.disabled = false; 190 say('accBillStatus', 'acc_bill_fail', true); 191 }); 192 } 193 194 // --------------------------------------------------------------------------- 195 // ÐÐ²Ð¸Ð¶ÐµÐ½Ð¸Ñ 196 // --------------------------------------------------------------------------- 197 198 /** 199 * ÐеÑеÑиÑаÑÑ Ð´Ð²Ð¸Ð¶ÐµÐ½Ð¸Ñ ÑÑÑÑа. РиÑÑÐµÑ Ð¸Ñ account_statement.js: жÑÑнал каÑÑ Ð¸ 200 * ÐЫÐÐСÐРкоÑелÑка (#1266) â одна Ñема и один ÑпиÑок на ÑкÑане, а деÑжаÑÑ 201 * двÑÑ ÑиÑоваÑелей одного Ñзла знаÑÐ¸Ñ Ñано или поздно даÑÑ Ð¸Ð¼ заÑеÑеÑÑ 202 * дÑÑг дÑÑга. 203 * 204 * ÐоÑелÑк жив â показÑваем вÑпиÑкÑ: она ÑиÑе, денÑги и каÑÑÑ Ð²Ð¼ÐµÑÑе. 205 */ 206 function refreshHistory() { 207 var S = window.VMSAccountStatement; 208 if (!S) return; 209 var W = window.VMSAccountWallet; 210 if (W && W.alive()) S.load(); else S.loadHistory(); 211 } 212 213 // --------------------------------------------------------------------------- 214 // ÐозвÑÐ°Ñ Ñ Ð¾Ð¿Ð»Ð°ÑÑ 215 // --------------------------------------------------------------------------- 216 217 /** ÐаÑамеÑÑ Ð°Ð´ÑеÑной ÑÑÑоки без ÑазбоÑа вÑего URL. */ 218 function param(name) { 219 try { 220 return new URLSearchParams(window.location.search).get(name); 221 } catch (e) { 222 return null; 223 } 224 } 225 226 /** 227 * ÐождаÑÑÑÑ, пока каÑÑÑ Ð´Ð¾ÐµÐ´ÑÑ Ð´Ð¾ ÑÑÑÑа. 228 * 229 * ÐаÑÑÑ ÐºÐ»Ð°Ð´ÑÑ Ñведомление банка, а покÑпаÑÐµÐ»Ñ Ð²Ð¾Ð·Ð²ÑаÑаеÑÑÑ Ñвоим Ñ Ð¾Ð´Ð¾Ð¼ и 230 * Ð¼Ð¾Ð¶ÐµÑ Ð¾Ð±Ð¾Ð³Ð½Ð°ÑÑ ÐµÐ³Ð¾ на ÑекÑндÑ-дÑÑгÑÑ. ÐоÑÑÐ¾Ð¼Ñ ÑÑÑÑ Ð¿ÐµÑеÑиÑÑваеÑÑÑ 231 * неÑколÑко Ñаз, и ÑолÑко иÑÑеÑпав попÑÑки, ÑÑÑаниÑа ÑеÑÑно говоÑиÑ, ÑÑо 232 * оплаÑа пÑинÑÑа, а каÑÑÑ Ð²Ð¾Ñ-Ð²Ð¾Ñ Ð¿Ð¾ÑвÑÑÑÑ. 233 * 234 * @param {number} before ÐÑÑаÑок до оплаÑÑ (null â бÑл неизвеÑÑен). 235 * @param {number} tries СколÑко попÑÑок оÑÑалоÑÑ. 236 */ 237 function awaitTopup(before, tries) { 238 loadBalance().then(function (body) { 239 var now = (body && typeof body.balance === 'number') ? body.balance : null; 240 if (now !== null && (before === null || now > before)) { 241 say('accBillStatus', 'acc_bill_paid'); 242 refreshHistory(); 243 return; 244 } 245 if (tries <= 0) { 246 say('accBillStatus', 'acc_bill_slow'); 247 return; 248 } 249 window.setTimeout(function () { awaitTopup(before, tries - 1); }, TOPUP_POLL_MS); 250 }); 251 } 252 253 //: Слово коÑелÑка о возвÑаÑе и оÑложеннÑй ÑазбоÑ: кÑо оÑвеÑÐ¸Ñ Ð¿ÐµÑвÑм â 254 //: виÑÑина Ñо Ñвоим ÑÑÑÑом или коÑелÑк Ñо Ñвоим â заÑанее не извеÑÑно (#1266). 255 var walletWord = null;
256 var pendingReturn = null; 257 258 /** ÐÑидеÑжаÑÑ ÑÐ°Ð·Ð±Ð¾Ñ Ð²Ð¾Ð·Ð²ÑаÑа до Ñлова коÑелÑка. */ 259 function deferReturn(before) { 260 if (walletWord === null) pendingReturn = function () { handleReturn(before); }; 261 else if (!walletWord) handleReturn(before); 262 } 263 264 /** 265 * Слово коÑелÑка: возвÑÐ°Ñ ÐµÐ³Ð¾ â виÑÑина молÑиÑ. 266 * @param {boolean} taken ÐоÑелÑк жив и ÑазбиÑÐ°ÐµÑ Ð²Ð¾Ð·Ð²ÑÐ°Ñ Ñам. 267 */ 268 function yieldReturn(taken) { 269 walletWord = !!taken; 270 var deferred = pendingReturn; 271 pendingReturn = null; 272 if (deferred && !walletWord) deferred(); 273 } 274 275 /** РазобÑаÑÑ Ð²Ð¾Ð·Ð²ÑÐ°Ñ Ð¸Ð· банка: ÑÑÐ¿ÐµÑ Ð¶Ð´Ñм, оÑказ назÑваем ÑÑазÑ. */ 276 function handleReturn(before) { 277 if (param('topup_failed')) { 278 say('accBillStatus', 'acc_bill_fail', true); 279 return; 280 } 281 if (!param('topup')) return; 282 say('accBillStatus', 'acc_bill_wait'); 283 awaitTopup(before, TOPUP_POLL_TRIES); 284 } 285 286 // --------------------------------------------------------------------------- 287 // ÐапÑÑк 288 // --------------------------------------------------------------------------- 289 290 /** ÐÑкÑÑÑÑ Ñаздел и ÑбÑаÑÑ `hidden` Ñо вкладки. */ 291 function reveal() { 292 var tab = el('accTabBilling'); 293 if (tab) tab.hidden = false; 294 // ÐозвÑÐ°Ñ Ð¸Ð· банка ведÑÑ Ð½Ð° /account?topup=â¦, без ÑкоÑÑ Ñаздела â оÑкÑÑваем 295 // Ñаздел Ñами, инаÑе Ñеловек веÑнÑлÑÑ Ð±Ñ Ð½Ð° главнÑй ÑкÑан кабинеÑа. 296 if ((param('topup') || param('topup_failed')) && window.VMSAccountShell) { 297 window.VMSAccountShell.show('billing', true); 298 } 299 } 300 301 function init() { 302 var A = window.VMSAccount; 303 if (!A) return; 304 A.request('/api/billing/catalog').then(function (res) { 305 // ТаÑиÑÑ Â«Ðлавной» коÑÐ¼Ð¸Ñ Ð¢ÐТ ÐРоÑвеÑ: вÑоÑого запÑоÑа за Ñеми же 306 // Ñенами неÑ, а закÑÑÑÐ°Ñ Ð²Ð¸ÑÑина Ð´Ð¾ÐµÐ·Ð¶Ð°ÐµÑ ÑÑда null'ом (T-260921-b791). 307 var C = window.VMSAccountCards; 308 if (C) C.catalog((res.ok && res.body) ? res.body : null); 309 // 404 = пÑодажа закÑÑÑа. ÐолÑа оÑÑаÑмÑÑ Ð½ÐµÐ²Ð¸Ð´Ð¸Ð¼Ñми: ÑÑо не оÑибка 310 // ÑÑÑаниÑÑ, а ÑегоднÑÑнее ÑоÑÑоÑние пÑодÑкÑа. 311 if (!res.ok || !res.body) return; 312 reveal(); 313 renderShop(res.body); 314 loadBalance().then(function (body) { 315 var before = (body && typeof body.balance === 'number') ? body.balance : null; 316 // ÐозвÑÐ°Ñ Ð¼Ð¾Ð¶ÐµÑ Ð¾ÐºÐ°Ð·Ð°ÑÑÑÑ Ð¿Ð¾Ð¿Ð¾Ð»Ð½ÐµÐ½Ð¸ÐµÐ¼ ÐÐÐÐÐ: ÑÑÑÑ ÐºÐ°ÑÑ Ð¿Ð¾ Ð½ÐµÐ¼Ñ Ð½Ðµ 317 // ÑдвинеÑÑÑ Ð½Ð¸ÐºÐ¾Ð³Ð´Ð°, и опÑÐ¾Ñ Ð´Ð¾ÑÑиÑал Ð±Ñ Ð´Ð¾ «оплаÑа пÑинÑÑа, каÑÑÑ 318 // воÑ-Ð²Ð¾Ñ Ð¿Ð¾ÑвÑÑÑÑ» â вÑанÑÑ. ÐÑи живом коÑелÑке возвÑÐ°Ñ ÑазбиÑÐ°ÐµÑ 319 // ÐÐ (#1266), обоими ÑÑеÑами ÑÑазÑ. 320 if (window.VMSAccountWalletReturn 321 && (param('topup') || param('topup_failed'))) { 322 deferReturn(before); 323 return; 324 } 325 handleReturn(before); 326 }); 327 // ÐÐ²Ð¸Ð¶ÐµÐ½Ð¸Ñ Ð³ÑÑÐ·Ð¸Ñ ÐÐШÐÐÐÐ, когда он на ÑÑÑаниÑе еÑÑÑ: он один знаеÑ, 328 // оÑвеÑил ли ÐµÐ¼Ñ `/api/billing/wallet`, а знаÑÐ¸Ñ â вÑпиÑÐºÑ Ð¿Ð¾ÐºÐ°Ð·ÑваÑÑ 329 // или пÑежний жÑÑнал каÑÑ. ÐÐµÑ ÐºÐ¾ÑелÑка â ÑеÑаем Ñами. 330 if (!window.VMSAccountWallet) refreshHistory(); 331 }); 332 } 333 334 // ÐаÑÑÐ¶Ñ â Ñо, ÑÑо нÑжно ÑоÑедÑм: пеÑеÑиÑаÑÑ ÑÑÑÑ ÐºÐ°ÑÑ, ÑклониÑÑ Â«ÐºÐ°ÑÑ», 335 // пÑоÑиÑаÑÑ Ð¿Ð°ÑамеÑÑ Ð²Ð¾Ð·Ð²ÑаÑа (вÑоÑÑÑ ÐºÐ¾Ð¿Ð¸Ð¹ не заводим) и пÑинÑÑÑ Ñлово 336 // коÑелÑка о Ñом, Ñей ÑÑо возвÑаÑ. 337 window.VMSAccountBilling = { 338 reload: loadBalance, cards: cards, price: price, param: param, yieldReturn: yieldReturn 339 }; 340 341 if (document.readyState === 'loading') { 342 document.addEventListener('DOMContentLoaded', init); 343 } else { 344 init(); 345 } 346})();
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.