/** * AKSRT CMS Download * * 功能: * - 监听 [data-action="aksrt-download"] 按钮点击 * - 调 POST /aksrt/v1/download/request * - 弹窗显示下载链接(含提取码) * * 依赖: * - window.aksrtData.{ ajaxUrl, nonce, siteUrl } * - 主题已加载 fetch (现代浏览器) */ (function () { 'use strict'; if (!window.aksrtData) return; const REST_BASE = window.aksrtData.siteUrl ? window.aksrtData.siteUrl.replace(/\/$/, '') + '/wp-json/aksrt/v1' : '/wp-json/aksrt/v1'; const PAY_CONTENT_PATH = '/pay/content/'; function tt(key, zh) { return zh; } function escapeHtml(value) { return String(value == null ? '' : value) .replace(/&/g, '&') .replace(//g, '>') .replace(/"/g, '"') .replace(/'/g, '''); } function escapeAttr(value) { return escapeHtml(value); } // ===== Focus trap state ===== let lastFocusedElement = null; function getFocusableElements(container) { if (!container) return []; return Array.prototype.slice.call(container.querySelectorAll( 'a[href], area[href], button:not([disabled]), input:not([disabled]):not([type="hidden"]),' + ' select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])' )).filter(function (el) { return !!(el.offsetWidth || el.offsetHeight || el.getClientRects().length); }); } function trapFocus(e, modal) { if (e.key !== 'Tab') return; const focusables = getFocusableElements(modal); if (!focusables.length) { e.preventDefault(); return; } const first = focusables[0]; const last = focusables[focusables.length - 1]; if (e.shiftKey && document.activeElement === first) { e.preventDefault(); last.focus(); } else if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); first.focus(); } } // ===== Modal helper ===== function buildModal() { let modal = document.getElementById('aksrt-dl-modal'); if (modal) return modal; modal = document.createElement('div'); modal.id = 'aksrt-dl-modal'; modal.className = 'aksrt-dl-modal'; modal.setAttribute('role', 'dialog'); modal.setAttribute('aria-modal', 'true'); modal.setAttribute('aria-hidden', 'true'); modal.setAttribute('aria-labelledby', 'aksrt-dl-modal-title'); modal.innerHTML = '
' + ''; document.body.appendChild(modal); modal.addEventListener('click', function (e) { if (e.target.closest('[data-close]')) close(); }); modal.addEventListener('keydown', function (e) { if (e.key === 'Escape' && modal.classList.contains('is-open')) { e.preventDefault(); close(); } else if (e.key === 'Tab' && modal.classList.contains('is-open')) { trapFocus(e, modal); } }); return modal; } function open(html, returnFocusEl, variant) { // 记录调用方按钮以便关闭后回焦 lastFocusedElement = returnFocusEl || document.activeElement; const modal = buildModal(); const body = modal.querySelector('[data-body]'); modal.classList.toggle('is-cashier', variant === 'cashier'); body.innerHTML = html; const title = body.querySelector('.aksrt-dl-modal__title, .aksrt-cashier__title'); if (title) title.id = 'aksrt-dl-modal-title'; modal.classList.add('is-open'); modal.setAttribute('aria-hidden', 'false'); document.body.classList.add('aksrt-dl-modal-open'); // 进入后将焦点放在第一个可聚焦元素,否则放 panel setTimeout(function () { const focusables = getFocusableElements(modal); if (focusables.length) { focusables[0].focus(); } else { modal.querySelector('.aksrt-dl-modal__panel').focus(); } }, 0); } function close() { const modal = document.getElementById('aksrt-dl-modal'); if (!modal) return; modal.classList.remove('is-open'); modal.setAttribute('aria-hidden', 'true'); document.body.classList.remove('aksrt-dl-modal-open'); // 焦点回到触发按钮 if (lastFocusedElement && typeof lastFocusedElement.focus === 'function') { try { lastFocusedElement.focus(); } catch (_) { /* ignore */ } } lastFocusedElement = null; } // ===== 错误提示渲染 ===== function renderError(msg, data, returnFocusEl, code) { let title = tt('failed', '下载失败'); let extra = ''; if (code === 'aksrt_need_login' && data && data.login_url) { title = tt('need_login', '请先登录'); msg = tt('login_hint', '登录后即可继续下载,访客不可使用此资源。'); extra = '' + tt('go_login', '前往登录') + ''; } else if (code === 'aksrt_need_purchase') { title = tt('need_purchase', '请先购买'); msg = msg || tt('purchase_hint', '请先完成购买后再下载该资源。'); extra = ''; } else if (code === 'aksrt_need_vip') { title = tt('need_vip', '会员专属'); msg = msg || tt('vip_hint', '该资源仅限会员购买/下载。'); if (data && data.vip_url) { extra = '' + tt('go_vip', '开通会员') + ''; } } else if (code === 'aksrt_points_insufficient' || code === 'aksrt_balance_insufficient') { title = tt('credit_low', '积分不够'); const balance = typeof (data && data.balance) === 'number' ? data.balance : 0; const cost = typeof (data && data.cost) === 'number' ? data.cost : 0; msg = tt('credit_hint', '当前余额 ' + balance + ' 积分,需要 ' + cost + ' 积分。可通过签到、阅读、邀请好友等方式获取积分。' ); if (data && data.credits_url) { extra = '' + tt('my_points', '我的积分') + ''; } } else if (code === 'aksrt_download_limit') { title = tt('quota_over', '今日免费额度已用完'); msg = msg || tt('quota_hint', '您今日下载免费资源个数已超限,请明日再下载。'); if (data && data.allow_limit_over && data.post_id) { extra = ''; } } else if (code === 'aksrt_captcha_failed') { title = tt('captcha_fail', '验证失败'); msg = msg || tt('captcha_retry', '人机验证失败,请重试。'); } else if (data && data.login_url) { extra = '' + tt('go_login', '前往登录') + ''; } open( '

' + title + '

' + '

' + (msg || '') + '

' + (extra ? '
' + extra + '
' : ''), returnFocusEl ); const openCashierBtn = document.querySelector('[data-aksrt-open-cashier]'); if (openCashierBtn) { openCashierBtn.addEventListener('click', function () { const pid = parseInt(openCashierBtn.getAttribute('data-aksrt-open-cashier'), 10) || 0; const pt = openCashierBtn.getAttribute('data-price-type') || ''; close(); if (pid) openCashier(pid, returnFocusEl, pt); }); } } // ===== 成功渲染 ===== function renderSuccess(payload, returnFocusEl) { const code = String(payload.extract_code || '').trim(); const has = !!payload.has_extract || code !== ''; const safeCode = escapeHtml(code); const safeCodeAttr = escapeAttr(code); const dlUrl = escapeAttr(payload.dl_url || '#'); const extractHtml = code !== '' ? '
' + tt('extract', '提取码') + '' + safeCode + '
' : (has ? '

' + tt('extract_missing', '下载源标记了提取码,但接口未返回具体内容,请检查资源下载源的 extract_code 字段。') + '

' : ''); open( '

' + tt('ready', '下载已就绪') + '

' + (extractHtml || '

' + tt('hint', '点击下方按钮开始下载,链接 10 分钟内有效。') + '

') + '
' + ' ' + tt('go', '立即下载') + '' + (typeof payload.balance === 'number' ? ' ' + tt('balance', '余额:') + payload.balance + '' : '') + '
', returnFocusEl ); // 复制按钮 document.querySelectorAll('[data-copy]').forEach(function (btn) { btn.addEventListener('click', function () { const text = btn.getAttribute('data-copy') || ''; const label = btn.querySelector('.aksrt-dl-modal__copy-text') || btn; if (navigator.clipboard) { navigator.clipboard.writeText(text).then(function () { label.textContent = tt('copied', '已复制'); setTimeout(function () { label.textContent = tt('copy', '复制'); }, 2000); }); } }); }); } // ===== 主流程 ===== function collectCaptchaPayload(sourceEl) { const box = sourceEl ? sourceEl.closest('[data-aksrt-download-box]') : null; const container = box ? box.querySelector('[data-aksrt-download-captcha]') : null; if (!container) return {}; const out = {}; const keys = [ 'captcha_verified', 'cf_turnstile_response', 'tencent_ticket', 'tencent_randstr', 'tencent_sid', 'geetest_lot_number', 'geetest_captcha_output', 'geetest_pass_token', 'geetest_gen_time', 'aliyun_captcha_verify_param', ]; keys.forEach(function (k) { const el = container.querySelector('input[name="' + k + '"]'); if (el && el.value !== '') out[k] = el.value; }); return out; } function isCaptchaActive(sourceEl) { const card = sourceEl ? sourceEl.closest('[data-aksrt-download-box]') : null; if (!card) return false; return card.getAttribute('data-captcha') === '1'; } function cashierHeader() { return ''; } function openCashierContent(content, returnFocusEl) { open( cashierHeader() + '', returnFocusEl, 'cashier' ); } function ensureCaptchaThen(sourceEl, callback) { if (!isCaptchaActive(sourceEl)) { callback(); return; } const state = window.AKSRTCaptchaState; const scene = state && state.scenes && state.scenes.download; if (!scene || typeof scene.verify !== 'function') { // captcha 组件未就绪:不直发,避免绕过服务端门槛 if (window.console && console.warn) { console.warn('[AKSRT] download captcha not ready'); } alert(tt('captcha_loading', '验证码组件加载中,请稍候再试')); return; } scene.verify(function () { callback(); }); } function performDownloadRequest(button) { const card = button.closest('[data-aksrt-download-box]'); if (!card) return; const postId = parseInt(card.getAttribute('data-post-id'), 10); const sourceIndex = parseInt(button.getAttribute('data-source-index'), 10); if (!postId || isNaN(sourceIndex)) return; button.disabled = true; const original = button.innerHTML; button.innerHTML = ' ' + tt('processing', '处理中…'); const restNonce = window.aksrtData.restNonce || ''; const captchaPayload = collectCaptchaPayload(button); fetch(REST_BASE + '/download/request', { method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/json', 'X-WP-Nonce': restNonce, }, body: JSON.stringify({ post_id: postId, source_index: sourceIndex, captcha: captchaPayload, }), }) .then(function (resp) { return resp.json().then(function (j) { return { ok: resp.ok, json: j }; }); }) .then(function (r) { button.disabled = false; button.innerHTML = original; if (!r.ok || (r.json && r.json.code)) { renderError(r.json.message || tt('default_err', '请稍后重试'), r.json.data || {}, button, r.json.code); // 验证码已被消费,重置以便用户重试 try { const scene = window.AKSRTCaptchaState && window.AKSRTCaptchaState.scenes && window.AKSRTCaptchaState.scenes.download; if (scene && typeof scene.reset === 'function') scene.reset(); } catch (_) { /* ignore */ } return; } renderSuccess(r.json, button); }) .catch(function () { button.disabled = false; button.innerHTML = original; renderError(tt('network_err', '网络错误,请稍后重试'), {}, button); }); } function requestDownload(button) { ensureCaptchaThen(button, function () { performDownloadRequest(button); }); } function pollOrderPaid(orderNumber, returnFocusEl) { if (!orderNumber) return; let tries = 0; const maxTries = 40; const timer = setInterval(function () { tries += 1; fetch(REST_BASE + '/pay/content/order/' + encodeURIComponent(orderNumber), { method: 'GET', credentials: 'same-origin', headers: restHeaders(), }) .then(function (resp) { return resp.json().then(function (j) { return { ok: resp.ok, json: j }; }); }) .then(function (r) { if (r.ok && r.json && r.json.paid) { clearInterval(timer); window.location.reload(); } }) .catch(function () { /* ignore */ }); if (tries >= maxTries) clearInterval(timer); }, 3000); } // ===== 积分不足按钮(前置拦截,不发请求) ===== function showLowCreditModal(button) { const balance = parseInt(button.getAttribute('data-balance'), 10) || 0; const cost = parseInt(button.getAttribute('data-cost'), 10) || 0; renderError('', { balance: balance, cost: cost }, button, 'aksrt_points_insufficient'); } // ===== 收银台(内容购买) ===== function restHeaders(idempotencyKey) { const headers = { 'Content-Type': 'application/json', 'X-WP-Nonce': window.aksrtData.restNonce || '', }; if (idempotencyKey) headers['Idempotency-Key'] = idempotencyKey; return headers; } function buildPreviewUrl(postId, priceType, couponCode) { var previewUrl = REST_BASE + PAY_CONTENT_PATH + postId; var qs = []; if (priceType) qs.push('price_type=' + encodeURIComponent(priceType)); if (couponCode) qs.push('coupon_code=' + encodeURIComponent(couponCode)); if (qs.length) previewUrl += '?' + qs.join('&'); return previewUrl; } function fetchPreview(postId, priceType, couponCode) { return fetch(buildPreviewUrl(postId, priceType, couponCode), { method: 'GET', credentials: 'same-origin', headers: restHeaders(), }).then(function (resp) { return resp.json().then(function (j) { return { ok: resp.ok, status: resp.status, json: j }; }); }); } function openCashier(postId, returnFocusEl, priceType, couponCode) { if (!postId) return; priceType = priceType || ''; couponCode = couponCode || ''; openCashierContent( '

' + tt('loading', '加载中…') + '

', returnFocusEl ); fetchPreview(postId, priceType, couponCode) .then(function (r) { if (!r.ok || (r.json && r.json.code)) { const code = r.json && r.json.code ? r.json.code : ''; const data = (r.json && r.json.data) || {}; if (code === 'aksrt_need_login' || r.status === 401) { renderError(r.json.message || '', data, returnFocusEl, 'aksrt_need_login'); return; } if (code === 'aksrt_need_vip') { renderError(r.json.message || '', data, returnFocusEl, 'aksrt_need_vip'); return; } if (code === 'aksrt_already_paid' || code === 'aksrt_already_followed') { window.location.reload(); return; } renderError((r.json && r.json.message) || tt('default_err', '请稍后重试'), data, returnFocusEl, code); return; } var prev = r.json || {}; if (priceType) prev._price_type = priceType; if (couponCode) prev._coupon_code = couponCode; renderCashier(prev, returnFocusEl); }) .catch(function () { renderError(tt('network_err', '网络错误,请稍后重试'), {}, returnFocusEl); }); } function formatAssetLabel(preview, value) { const currency = escapeHtml(preview.currency || (preview.pay_modo === 'points' ? '积分' : '¥')); const amount = escapeHtml(value == null ? '' : value); return preview.pay_modo === 'points' ? amount + currency : currency + amount; } function paymentMethodLogo(methodId) { const logoNames = { balance: 'pay-balance-logo.svg', alipay: 'pay-alipay-logo.svg', epay_alipay: 'pay-alipay-logo.svg', wechatpay: 'pay-wechat-logo.svg', epay_wxpay: 'pay-wechat-logo.svg', paypal: 'pay-paypal-logo.svg', stripe: 'pay-stripe-logo.svg', }; const logo = logoNames[methodId]; if (!logo) return ''; const themeUrl = String(window.aksrtData.themeUrl || '').replace(/\/$/, ''); return ''; } function cashierPriceRow(label, value, className) { return '
' + '' + label + '' + '
' + value + '
' + '
'; } function renderCashier(preview, returnFocusEl) { if (!preview) return; if (preview.need_login && !preview.allow_guest) { renderError('', { login_url: preview.login_url }, returnFocusEl, 'aksrt_need_login'); return; } if (preview.need_vip) { renderError('', { vip_url: preview.vip_url }, returnFocusEl, 'aksrt_need_vip'); return; } if (preview.has_paid_access) { window.location.reload(); return; } // Content free: reload. if (preview.is_free) { window.location.reload(); return; } const isPoints = preview.pay_modo === 'points'; const priceType = (preview._price_type || preview.price_type || ''); let amountLabel = escapeHtml(preview.amount_label || ''); let baseLabel = escapeHtml(preview.base_label || ''); const listLabel = escapeHtml(preview.list_amount_label || preview.amount_label || amountLabel); if (priceType === 'download_limit_over' && preview.download_limit_over_label && !preview.discounts) { amountLabel = escapeHtml(preview.download_limit_over_label); baseLabel = amountLabel; preview.amount = preview.download_limit_over_price; preview.base = preview.download_limit_over_price; } const title = escapeHtml(preview.title || ''); const typeLabel = escapeHtml(preview.pay_type_label || ''); const balance = isPoints && Number.isSafeInteger(preview.balance) ? preview.balance : 0; const methods = Array.isArray(preview.payment_methods) ? preview.payment_methods : []; const couponAllowed = !isPoints && !!preview.coupon_allowed; const couponCodeVal = escapeAttr(preview.coupon_code || preview._coupon_code || ''); let priceRows = ''; if (!preview.pay_limit) { let regularPrice = '' + baseLabel + ''; if (!isPoints && Number(preview.original) > Number(preview.base)) { regularPrice = '' + formatAssetLabel(preview, preview.original) + '' + regularPrice; } priceRows += cashierPriceRow(tt('price', '价格'), regularPrice, 'aksrt-cashier__regular-price'); } if (preview.list_amount !== preview.base) { priceRows += cashierPriceRow( tt('vip_price', '会员价') + 'VIP' + escapeHtml(preview.vip_level || '') + '', '' + listLabel + '', 'c-red' ); } else if (preview.amount !== preview.base && !preview.rebate_discount && !preview.coupon_discount) { priceRows += cashierPriceRow( tt('vip_price', '会员价') + 'VIP' + escapeHtml(preview.vip_level || '') + '', '' + amountLabel + '', 'c-red' ); } if (preview.rebate_discount > 0) { priceRows += cashierPriceRow( tt('rebate_discount', '推广折扣'), '' + escapeHtml(preview.rebate_discount_label || ('-' + preview.rebate_discount)) + '' ); } if (preview.coupon_discount > 0) { priceRows += cashierPriceRow( tt('coupon_discount', '优惠码'), '' + escapeHtml(preview.coupon_discount_label || ('-' + preview.coupon_discount)) + '' ); } if (preview.amount !== preview.base || preview.rebate_discount > 0 || preview.coupon_discount > 0) { priceRows += cashierPriceRow( tt('pay_amount', '支付金额'), '' + amountLabel + '', 'font-bold c-red' ); } let body = ''; body += '
' + '' + typeLabel + '' + title + '' + '
'; body += '
' + priceRows + '
'; if (isPoints) { body += '
' + '
' + '' + tt('my_points', '我的积分') + '
' + balance + '
'; if (!preview.balance_enough) { body += '
' + tt('points_short', '抱歉,您的积分不足,暂时无法支付') + '
'; body += ''; openCashierContent(body, returnFocusEl); return; } body += ''; } else { if (couponAllowed) { body += '
'; body += '
' + tt('use_coupon', '使用优惠码') + '
'; body += '
'; body += ''; body += ''; body += '
'; if (preview.coupon_desc) { body += '
' + escapeHtml(preview.coupon_desc) + '
'; } if (preview.coupon_error) { body += '
' + escapeHtml(preview.coupon_error) + '
'; } else if (preview.coupon_discount > 0) { body += '
' + tt('coupon_ok', '优惠码可用') + '
'; } body += '
'; } const walletBal = typeof preview.wallet_balance === 'string' ? preview.wallet_balance : (typeof preview.balance === 'string' ? preview.balance : '0.00'); const hasBalanceMethod = methods.some(function (m) { return (m && m.id) === 'balance'; }); let methodHtml = ''; methods.forEach(function (m, idx) { const rawId = String((m && m.id) || ''); const id = escapeAttr(rawId); const name = escapeHtml((m && m.name) || rawId); methodHtml += ''; }); if (methods.length > 1) { body += '
' + tt('pay_method', '请选择支付方式') + '
' + '
' + methodHtml + '
'; } if (methods.length) { body += ''; } if (hasBalanceMethod) { const balanceSelected = methods[0] && methods[0].id === 'balance'; body += '
' + '
' + '' + tt('my_wallet', '我的余额') + '
¥' + escapeHtml(walletBal) + '
'; if (preview.balance_enough === false) { if (preview.credits_url) { body += '' + tt('wallet_short', '抱歉!余额不足,请先充值') + ' '; } else { body += '
' + tt('wallet_short', '抱歉!余额不足,请先充值') + '
'; } } body += '
'; } if (methods.length) { body += ''; } else { body += '
' + tt('no_pay_method', '暂无可用支付方式,请联系站长配置收款接口') + '
'; } } openCashierContent(body, returnFocusEl); const modal = document.getElementById('aksrt-dl-modal'); const checkBtn = modal && modal.querySelector('[data-aksrt-check-coupon]'); if (checkBtn) { checkBtn.addEventListener('click', function () { const input = modal.querySelector('input[name="aksrt_coupon_code"]'); const code = input ? String(input.value || '').trim() : ''; if (!code) { if (input) input.focus(); return; } checkBtn.disabled = true; const old = checkBtn.innerHTML; checkBtn.innerHTML = tt('checking', '检查中…'); fetchPreview(preview.post_id, priceType, code) .then(function (r) { checkBtn.disabled = false; checkBtn.innerHTML = old; if (!r.ok || (r.json && r.json.code && !r.json.amount && r.json.amount !== 0)) { renderError((r.json && r.json.message) || tt('default_err', '请稍后重试'), (r.json && r.json.data) || {}, returnFocusEl, (r.json && r.json.code) || ''); return; } var prev = r.json || {}; if (priceType) prev._price_type = priceType; prev._coupon_code = code; renderCashier(prev, returnFocusEl); }) .catch(function () { checkBtn.disabled = false; checkBtn.innerHTML = old; renderError(tt('network_err', '网络错误,请稍后重试'), {}, returnFocusEl); }); }); } if (modal) { const methodCards = modal.querySelectorAll('[data-aksrt-payment-method]'); const methodInput = modal.querySelector('input[name="aksrt_pay_method"]'); const balanceBox = modal.querySelector('[data-aksrt-balance-box]'); const selectMethod = function (card) { const method = card.getAttribute('data-aksrt-payment-method') || ''; methodCards.forEach(function (item) { const active = item === card; item.classList.toggle('active', active); item.setAttribute('aria-checked', active ? 'true' : 'false'); }); if (methodInput) methodInput.value = method; if (balanceBox) balanceBox.hidden = method !== 'balance'; }; methodCards.forEach(function (card) { card.addEventListener('click', function () { selectMethod(card); }); card.addEventListener('keydown', function (event) { if (event.key === 'Enter' || event.key === ' ') { event.preventDefault(); selectMethod(card); } }); }); } const submitBtn = modal && modal.querySelector('[data-aksrt-pay-submit]'); if (submitBtn) { submitBtn.addEventListener('click', function () { const mode = submitBtn.getAttribute('data-mode') || (isPoints ? 'points' : 'cash'); let paymentMethod = 'balance'; const selected = modal.querySelector('input[name="aksrt_pay_method"]'); if (selected) paymentMethod = selected.value; const input = modal.querySelector('input[name="aksrt_coupon_code"]'); const code = input ? String(input.value || '').trim() : (preview.coupon_code || preview._coupon_code || ''); submitPurchase(preview.post_id, mode, paymentMethod, submitBtn, returnFocusEl, code); }); } } function submitPurchase(postId, mode, paymentMethod, button, returnFocusEl, couponCode) { if (!postId) return; const original = button.innerHTML; button.disabled = true; button.innerHTML = tt('processing', '处理中…'); const payload = { mode: mode }; if (mode === 'cash') payload.payment_method = paymentMethod || 'balance'; const pt = button.getAttribute('data-price-type') || ''; if (pt) payload.price_type = pt; if (mode === 'cash' && couponCode) payload.coupon_code = couponCode; if (!button.dataset.aksrtOperationKey) { button.dataset.aksrtOperationKey = window.AksrtRequest.createIdempotencyKey(); } fetch(REST_BASE + PAY_CONTENT_PATH + postId, { method: 'POST', credentials: 'same-origin', headers: restHeaders(button.dataset.aksrtOperationKey), body: JSON.stringify(payload), }) .then(function (resp) { return resp.json().then(function (j) { return { ok: resp.ok, status: resp.status, json: j }; }); }) .then(function (r) { button.disabled = false; button.innerHTML = original; if (!r.ok || (r.json && r.json.code && !r.json.success)) { const code = r.json && r.json.code ? r.json.code : ''; const data = (r.json && r.json.data) || {}; if (code === 'aksrt_already_paid' || code === 'aksrt_already_followed') { window.location.reload(); return; } renderError((r.json && r.json.message) || tt('default_err', '请稍后重试'), data, returnFocusEl, code); return; } const data = (r.json && r.json.data) ? r.json.data : r.json; delete button.dataset.aksrtOperationKey; if (data && data.redirect && (data.payment_url || data.qr_content)) { if (data.payment_url) { window.open(data.payment_url, '_blank'); } open( '

' + tt('scan_pay', '扫码支付') + '

' + '

' + escapeHtml(data.message || tt('scan_hint', '请使用手机扫码完成支付,支付后刷新页面。')) + '

' + (data.qr_content ? ('

' + escapeHtml(data.qr_content || '') + '

') : '') + '
', returnFocusEl ); const reloadBtn = document.querySelector('[data-aksrt-reload]'); if (reloadBtn) reloadBtn.addEventListener('click', function () { window.location.reload(); }); if (data.order_number) pollOrderPaid(data.order_number, returnFocusEl); return; } window.location.reload(); }) .catch(function () { button.disabled = false; button.innerHTML = original; renderError(tt('network_err', '网络错误,请稍后重试'), {}, returnFocusEl); }); } // ===== 事件绑定 ===== document.addEventListener('click', function (e) { const lowBtn = e.target.closest('[data-action="aksrt-need-credits"]'); if (lowBtn) { e.preventDefault(); showLowCreditModal(lowBtn); return; } const buyBtn = e.target.closest('[data-action="aksrt-need-purchase"], [data-aksrt-pay-content]'); if (buyBtn) { e.preventDefault(); const postId = parseInt( buyBtn.getAttribute('data-aksrt-pay-content') || buyBtn.getAttribute('data-post-id') || (buyBtn.closest('[data-post-id]') && buyBtn.closest('[data-post-id]').getAttribute('data-post-id')) || '0', 10 ); const priceType = buyBtn.getAttribute('data-price-type') || ''; if (postId) openCashier(postId, buyBtn, priceType); return; } const cashierLink = e.target.closest('.cashier-link'); if (cashierLink && cashierLink.hasAttribute('data-aksrt-pay-content')) { e.preventDefault(); const postId = parseInt(cashierLink.getAttribute('data-aksrt-pay-content') || '0', 10); if (postId) openCashier(postId, cashierLink); return; } const btn = e.target.closest('[data-action="aksrt-download"]'); if (!btn) return; if (btn.disabled || btn.getAttribute('aria-disabled') === 'true') return; e.preventDefault(); requestDownload(btn); }); // Public cashier bridge (content pay). window.aksrtOpenCashier = function (postId, returnFocusEl, priceType, couponCode) { openCashier(postId, returnFocusEl || null, priceType || '', couponCode || ''); }; })();