2026-04-24 12:42:12 +08:00

408 lines
10 KiB
JavaScript

const query = new URLSearchParams(window.location.search);
const storedAppId = window.localStorage.getItem("mifa-pay-application-id") || "";
const LOGO_SRC = "./mifapay-logo.png";
function getPathApplicationId() {
const segments = window.location.pathname.split("/").filter(Boolean);
const pageIndex = segments.lastIndexOf("mifa-pay");
if (pageIndex === -1) {
return "";
}
return segments[pageIndex + 1] || "";
}
const defaults = {
applicationId:
getPathApplicationId() ||
query.get("applicationId") ||
query.get("appId") ||
storedAppId ||
"2048200000000000701",
productType: query.get("type") || "GOLD",
};
const deps = {
getApi: null,
postApi: null,
fetchCurrentUser: null,
getCachedUserProfile: null,
connectApp: null,
};
const state = {
applicationId: defaults.applicationId,
productType: defaults.productType,
userProfile: null,
countries: [],
selectedCountryId: "",
commodityCard: null,
selectedGoodsId: "",
selectedGoods: null,
pendingChannelCode: "",
dependencyPromise: null,
};
const els = {
applicationId: document.getElementById("applicationId"),
productList: document.getElementById("productList"),
channelList: document.getElementById("channelList"),
pageStatus: document.getElementById("pageStatus"),
};
function setStatus(text = "") {
if (!text) {
els.pageStatus.textContent = "";
els.pageStatus.className = "status";
return;
}
els.pageStatus.textContent = text;
els.pageStatus.className = "status show";
}
function persistSettings() {
window.localStorage.setItem("mifa-pay-application-id", state.applicationId);
}
function extractErrorMessage(error) {
return (
error?.response?.errorMsg ||
error?.response?.body?.errorMsg ||
error?.errorMsg ||
error?.message ||
"Unknown error"
);
}
function readStoredUserProfile() {
const raw = window.localStorage.getItem("userProfile");
if (!raw) {
return null;
}
try {
const parsed = JSON.parse(raw);
return parsed && typeof parsed === "object" ? parsed : null;
} catch (error) {
return null;
}
}
function resolveUserProfile(response = null) {
if (response?.body && typeof response.body === "object") {
return response.body;
}
if (typeof deps.getCachedUserProfile === "function") {
const cached = deps.getCachedUserProfile();
if (cached && typeof cached === "object") {
return cached;
}
}
return readStoredUserProfile();
}
function currentUserId() {
return String(state.userProfile?.id || "").trim();
}
function currentCountryId() {
return state.selectedCountryId;
}
function getCountryLabel(item) {
return (
item?.label ||
item?.countryName ||
item?.country?.aliasName ||
item?.country?.countryName ||
String(item?.id || "")
);
}
function getGoodsList() {
return state.commodityCard?.commodity || [];
}
function getChannelList() {
return state.commodityCard?.channels || [];
}
function getChannelCode(item) {
return item?.details?.channelCode || item?.channel?.channelCode || "";
}
function getAmountText(item) {
const content = item?.content || item?.obtainGoldsQuantity || "-";
return `${content} ${item?.type || ""}`.trim();
}
function getPriceText(item) {
const currency = item?.currency || state.commodityCard?.payCountry?.currency || "USD";
return `${item?.amount || item?.amountUsd || "-"} ${currency}`;
}
function ensureSelectedGoods() {
const goods = getGoodsList();
if (!goods.length) {
state.selectedGoodsId = "";
state.selectedGoods = null;
return;
}
const matched =
goods.find((item) => String(item.id) === state.selectedGoodsId) ||
goods.find((item) => item.type === state.productType) ||
goods[0];
state.selectedGoods = matched;
state.selectedGoodsId = String(matched.id);
}
function renderProducts() {
const goods = getGoodsList();
ensureSelectedGoods();
if (!goods.length) {
els.productList.innerHTML = '<div class="empty">No amount available.</div>';
return;
}
els.productList.innerHTML = goods
.map((item) => {
const selected = String(item.id) === state.selectedGoodsId;
return `
<button
type="button"
class="amount-item${selected ? " selected" : ""}"
data-goods-id="${item.id}"
>
<span class="check"></span>
<span class="amount-copy">
<span class="amount-value">${item.content || item.obtainGoldsQuantity || "-"}</span>
<span class="amount-meta">${item.type || "Recharge"}</span>
</span>
<span class="price-copy">
<strong>${item.amount || item.amountUsd || "-"}</strong>
<span>USD ${item.amountUsd || "-"}</span>
</span>
</button>
`;
})
.join("");
els.productList.querySelectorAll("[data-goods-id]").forEach((button) => {
button.addEventListener("click", () => {
const goodsId = button.getAttribute("data-goods-id");
state.selectedGoodsId = goodsId;
state.selectedGoods =
getGoodsList().find((item) => String(item.id) === goodsId) || null;
renderProducts();
});
});
}
function renderChannels() {
const channels = getChannelList();
if (!channels.length) {
els.channelList.innerHTML = '<div class="empty">No payment method available.</div>';
return;
}
els.channelList.innerHTML = channels
.map((item) => {
const code = getChannelCode(item);
const pending = state.pendingChannelCode === code;
return `
<button
type="button"
class="channel-button"
data-channel-code="${code}"
${pending ? "disabled" : ""}
>
<span class="channel-logo">
<img src="${LOGO_SRC}" alt="MiFaPay" />
</span>
<span class="channel-name">${pending ? "Processing..." : "Pay with MiFaPay"}</span>
</button>
`;
})
.join("");
els.channelList.querySelectorAll("[data-channel-code]").forEach((button) => {
button.addEventListener("click", async () => {
const channelCode = button.getAttribute("data-channel-code");
try {
await createOrder(channelCode);
} catch (error) {
state.pendingChannelCode = "";
renderChannels();
setStatus(extractErrorMessage(error));
}
});
});
}
function selectCountryByProfile() {
const userCountryCode = state.userProfile?.countryCode;
if (!userCountryCode) {
return false;
}
const matched = state.countries.find((item) =>
getCountryLabel(item).toUpperCase().includes(userCountryCode.toUpperCase())
);
if (!matched) {
return false;
}
state.selectedCountryId = String(matched.id);
return true;
}
async function ensureDependencies() {
if (!state.dependencyPromise) {
state.dependencyPromise = Promise.all([
import("../js/index-CIAVq8iD-1776148658686.js"),
import("../js/appConnector-B5Pbaops-1776148658686.js"),
]).then(([indexModule, connectorModule]) => {
deps.getApi = indexModule.L;
deps.postApi = indexModule.W;
deps.fetchCurrentUser = indexModule.au;
deps.getCachedUserProfile = indexModule.at;
deps.connectApp = connectorModule.c;
return deps;
});
}
return state.dependencyPromise;
}
async function connectSession() {
await ensureDependencies();
const connection = await deps.connectApp();
if (!connection?.success) {
throw new Error(connection?.error || "Failed to initialize app session");
}
const cachedUserProfile = resolveUserProfile();
if (cachedUserProfile?.id) {
state.userProfile = cachedUserProfile;
return;
}
const response = await deps.fetchCurrentUser();
const userProfile = resolveUserProfile(response);
if (!userProfile?.id) {
throw new Error("Please open this page inside the app");
}
state.userProfile = userProfile;
}
async function refreshMeta() {
if (!state.applicationId) {
throw new Error("Missing applicationId");
}
persistSettings();
els.applicationId.value = state.applicationId;
const countriesResp = await deps.getApi("/order/web/pay/country");
state.countries = Array.isArray(countriesResp?.body) ? countriesResp.body : [];
if (!state.countries.length) {
state.selectedCountryId = "";
state.commodityCard = null;
renderProducts();
renderChannels();
throw new Error("No supported country");
}
if (
!state.selectedCountryId ||
!state.countries.some((item) => String(item.id) === String(state.selectedCountryId))
) {
if (!selectCountryByProfile()) {
state.selectedCountryId = String(state.countries[0].id);
}
}
const commodityResp = await deps.getApi(
`/order/web/pay/commodity?applicationId=${encodeURIComponent(
state.applicationId
)}&payCountryId=${encodeURIComponent(currentCountryId())}&type=${encodeURIComponent(
state.productType
)}`
);
state.commodityCard = commodityResp?.body || {};
renderProducts();
renderChannels();
setStatus("");
}
function safeDecodeUrl(url) {
if (!url) {
return "";
}
try {
return decodeURIComponent(url);
} catch (error) {
return url;
}
}
async function createOrder(channelCode) {
if (!state.selectedGoods) {
throw new Error("Select an amount first");
}
if (!currentUserId()) {
throw new Error("Account not available");
}
state.pendingChannelCode = channelCode;
setStatus("");
renderChannels();
const payload = {
applicationId: Number(state.applicationId),
goodsId: Number(state.selectedGoodsId),
payCountryId: Number(currentCountryId()),
userId: Number(currentUserId()),
channelCode,
newVersion: true,
};
const response = await deps.postApi("/order/web/pay/recharge", payload);
const requestUrl = safeDecodeUrl(response?.body?.requestUrl || "");
if (!requestUrl) {
throw new Error("Missing payment url");
}
window.location.href = requestUrl;
}
async function reloadPageData() {
await ensureDependencies();
await connectSession();
await refreshMeta();
}
async function init() {
renderProducts();
renderChannels();
try {
await reloadPageData();
} catch (error) {
setStatus(extractErrorMessage(error));
}
}
init();