Files
wordpress-theme-aksrt/assets/js/pay-vip.js
T
2026-08-04 23:24:37 +08:00

274 lines
9.3 KiB
JavaScript

/**
* AKSRT VIP pay modal — zibll .pay-vip parity.
* Loads modal HTML from GET /aksrt/v1/pay/vip/modal then purchases via POST /pay/vip.
*/
(function () {
"use strict";
function api(path, options) {
var opts = Object.assign({}, options || {});
var request = window.AksrtRequest && typeof window.AksrtRequest.request === "function"
? window.AksrtRequest.request(path, opts)
: fetch(path, Object.assign({ credentials: "same-origin" }, opts)).then(function (response) {
return response.json().then(function (json) {
return { ok: response.ok, status: response.status, json: json };
});
});
return request.then(function (response) {
return {
ok: response.ok,
status: response.status,
json: response.json || {},
};
}).catch(function (error) {
var response = error && error.response ? error.response : {};
return {
ok: false,
status: error && error.status ? error.status : (response.status || 0),
json: error && error.json ? error.json : (response.json || { message: error && error.message }),
};
});
}
var activeVipShell = null;
function openModal() {
if (!window.AksrtModalRuntime || typeof window.AksrtModalRuntime.open !== "function") return null;
activeVipShell = window.AksrtModalRuntime.open({
className: "aksrt-vip-modal-shell",
mobileBottom: false,
skeleton: '<div class="aksrt-vip-modal-dialog"><div class="aksrt-vip-modal-content" data-aksrt-vip-modal-body><div class="text-center padding-20 muted-2-color">加载中…</div></div></div>',
telemetry: false,
kind: "vip",
});
return activeVipShell;
}
function closeModal() {
if (window.AksrtModalRuntime && typeof window.AksrtModalRuntime.close === "function") {
window.AksrtModalRuntime.close("vip");
}
activeVipShell = null;
}
function bindModalUi(root) {
if (!root || root.dataset.aksrtVipBound === "1") return;
root.dataset.aksrtVipBound = "1";
root.addEventListener("click", function (e) {
var tab = e.target.closest("[data-vip-tab]");
if (tab) {
e.preventDefault();
var id = tab.getAttribute("data-vip-tab");
root.querySelectorAll(".aksrt-vip-tab").forEach(function (a) {
a.parentElement && a.parentElement.classList.remove("active");
});
tab.parentElement && tab.parentElement.classList.add("active");
root.querySelectorAll(".aksrt-vip-pane").forEach(function (p) {
var on = p.id === id;
p.hidden = !on;
p.classList.toggle("active", on);
});
return;
}
var submit = e.target.closest("[data-vip-submit]");
if (submit) {
e.preventDefault();
purchaseFromPane(submit.closest(".aksrt-vip-pane") || root, submit);
}
});
}
function selectedMethod(root) {
var fixed = root.querySelector("[data-aksrt-vip-method-fixed]");
if (fixed && fixed.value) return fixed.value;
var checked = root.querySelector('input[name="aksrt_vip_pay_method"]:checked');
if (checked) return checked.value;
var first = root.querySelector('input[name="aksrt_vip_pay_method"]');
return first ? first.value : "balance";
}
function selectedPackage(root) {
var checked = root.querySelector('input[name="vip_product_id"]:checked');
return checked ? parseInt(checked.value, 10) : 0;
}
function setTip(root, text, isError) {
var tip = root.querySelector("[data-aksrt-vip-tip]");
if (!tip) return;
tip.textContent = text || "";
tip.classList.toggle("is-error", !!isError);
}
function pollOrder(orderNumber) {
if (!orderNumber) return;
var tries = 0;
var timer = setInterval(function () {
tries += 1;
api("/pay/content/order/" + encodeURIComponent(orderNumber))
.then(function (r) {
if (r.ok && r.json && r.json.paid) {
clearInterval(timer);
window.location.reload();
}
})
.catch(function () {});
if (tries >= 40) clearInterval(timer);
}, 3000);
}
function purchaseFromPane(pane, btn) {
var packageId = selectedPackage(pane);
if (!packageId) {
setTip(pane, "请选择套餐", true);
return;
}
if (btn.disabled) return;
btn.disabled = true;
btn.classList.add("is-loading");
setTip(pane, "正在创建订单…");
api("/pay/vip", {
method: "POST",
body: JSON.stringify({
package_id: packageId,
payment_method: selectedMethod(pane),
}),
})
.then(function (r) {
btn.disabled = false;
btn.classList.remove("is-loading");
if (!r.ok || (r.json && r.json.code && !r.json.success && r.json.success !== true)) {
var msg = (r.json && r.json.message) || "开通失败";
if (r.status === 401) {
msg = "请先登录";
document.querySelector(".signin-loader") && document.querySelector(".signin-loader").click();
}
setTip(pane, msg, true);
return;
}
var data = r.json && r.json.data ? r.json.data : r.json;
if (data && data.redirect && (data.payment_url || data.qr_content)) {
if (window.AksrtModalRuntime && typeof window.AksrtModalRuntime.invalidate === "function") {
window.AksrtModalRuntime.invalidate(["vip", "orders"]);
}
if (data.payment_url) window.open(data.payment_url, "_blank");
setTip(pane, data.message || "请完成支付,支付成功后将自动刷新。");
if (data.order_number) pollOrder(data.order_number);
return;
}
if (window.AksrtModalRuntime && typeof window.AksrtModalRuntime.invalidate === "function") {
window.AksrtModalRuntime.invalidate(["vip", "orders"]);
}
setTip(pane, (data && data.message) || "开通成功,正在刷新…");
setTimeout(function () {
window.location.reload();
}, 400);
})
.catch(function () {
btn.disabled = false;
btn.classList.remove("is-loading");
setTip(pane, "网络错误,请重试", true);
});
}
var loading = false;
function vipPay(trigger) {
var level = 1;
if (trigger) {
level = parseInt(trigger.getAttribute("vip-level") || trigger.getAttribute("data-vip-level") || "1", 10) || 1;
}
if (loading) return;
loading = true;
var telemetry = window.AksrtModalTelemetry;
var telemetryId = telemetry && typeof telemetry.start === "function" ? telemetry.start("vip") : "";
function telemetryMark(event) {
if (telemetry && telemetryId && typeof telemetry.mark === "function") telemetry.mark(telemetryId, event);
}
var shell = openModal();
if (!shell) {
loading = false;
telemetryMark("error");
return;
}
var body = shell.querySelector("[data-aksrt-vip-modal-body]");
telemetryMark("shell");
api("/pay/vip/modal", { query: { vip_level: level } })
.then(function (r) {
loading = false;
if (r.status === 401) {
telemetryMark("unauthorized");
closeModal();
var login = document.querySelector(".signin-loader");
if (login) login.click();
return;
}
if (!r.ok || !r.json || !r.json.html) {
telemetryMark("error");
if (body) {
body.innerHTML =
'<div class="text-center padding-20 c-red">' +
((r.json && r.json.message) || "加载失败") +
"</div>";
}
return;
}
if (body) {
body.innerHTML = r.json.html;
telemetryMark("first-content");
if (window.AksrtFrontend && typeof window.AksrtFrontend.rehydrate === "function") {
window.AksrtFrontend.rehydrate(body, "modal");
}
bindModalUi(body);
telemetryMark("ready");
}
})
.catch(function () {
loading = false;
telemetryMark("error");
if (body) {
body.innerHTML = '<div class="text-center padding-20 c-red">网络错误</div>';
}
});
}
function onDocClick(e) {
// Header CTA, float rail, UC buttons, VIP hover cards.
var t = e.target.closest(".pay-vip, a.pay-vip, .float-btn.pay-vip, [data-aksrt-pay-vip]");
if (!t) return;
// Real external href without modal intent: leave alone.
var href = (t.getAttribute("href") || "").trim();
if (href && href !== "#" && href.indexOf("javascript:") !== 0 && !t.classList.contains("pay-vip") && !t.hasAttribute("data-aksrt-pay-vip") && !t.hasAttribute("vip-level") && !t.hasAttribute("data-vip-level")) {
return;
}
e.preventDefault();
e.stopPropagation();
vipPay(t);
}
function init() {
// Capture phase so float/header anchors with href never navigate first.
document.addEventListener("click", onDocClick, true);
}
// This handler is delegated from document, so registry rehydration is not
// needed. Bind exactly once and keep dynamically injected triggers working.
function bindOnce() {
if (window.__aksrtPayVipBound) return;
window.__aksrtPayVipBound = true;
init();
}
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", bindOnce, { once: true });
} else {
bindOnce();
}
window.aksrtVipPay = vipPay;
})();