1121 lines
40 KiB
JavaScript
1121 lines
40 KiB
JavaScript
(function () {
|
|
const API_BASE_URL = "https://jvapi.haiyihy.com";
|
|
const RECHARGE_APPLICATION_ID = "1963531459019739137";
|
|
const supportedLanguages = ["en", "ar", "tr", "id"];
|
|
const languageOptions = [
|
|
{ type: "en", label: "EN" },
|
|
{ type: "ar", label: "AR" },
|
|
{ type: "tr", label: "TR" },
|
|
{ type: "id", label: "ID" }
|
|
];
|
|
const languageLabels = languageOptions.reduce((result, item) => {
|
|
result[item.type] = item.label;
|
|
return result;
|
|
}, {});
|
|
|
|
const params = readURLParams();
|
|
const isMock = params.get("mock") === "1";
|
|
const hasTokenParam = Boolean(readRawParam("token") || readRawParam("accessToken") || readRawParam("authorization") || readRawParam("Authorization"));
|
|
let activeLang = normalizeLanguage(params.get("lang") || "en");
|
|
|
|
let headers = {
|
|
Authorization: "",
|
|
"req-app-intel": "version=1.0.0;build=1;model=H5;sysVersion=Web;channel=Web",
|
|
"req-client": "H5",
|
|
"req-lang": "en",
|
|
"req-sys-origin": "origin=ATYOU;originChild=ATYOU",
|
|
"req-version": "V2",
|
|
"req-zone": Intl.DateTimeFormat().resolvedOptions().timeZone || "Asia/Shanghai"
|
|
};
|
|
let appConnectionPromise = null;
|
|
|
|
const fallbackMessages = {
|
|
en: {
|
|
documentTitle: "Recharge Center",
|
|
back: "Back",
|
|
title: "Recharge Center",
|
|
languageButtonAria: "Change language",
|
|
authMissingTitle: "Login token not found",
|
|
authMissingDesc: "Open this page from the app or include token in the URL.",
|
|
searchTitle: "Find account",
|
|
searchGuide: "Confirm the account before payment. Completed recharges cannot be refunded.",
|
|
loadingAccount: "Loading account...",
|
|
accountPlaceholder: "Enter User ID",
|
|
search: "Search",
|
|
searching: "Searching...",
|
|
userId: "ID:",
|
|
country: "Country:",
|
|
normalUser: "Normal user",
|
|
dealer: "Dealer",
|
|
superDealer: "Super dealer",
|
|
lowFee: "Low fee",
|
|
change: "Change",
|
|
packageTitle: "Recharge package",
|
|
methodTitle: "Payment methods",
|
|
selectPackageFirst: "Select a package first.",
|
|
selectPaymentMethod: "Select a payment method first.",
|
|
createOrder: "Create order",
|
|
creatingOrder: "Creating...",
|
|
usdtOrderTitle: "USDT TRC20 order",
|
|
trc20Address: "TRC20 address",
|
|
copy: "Copy",
|
|
copied: "Copied",
|
|
amount: "Amount",
|
|
orderInputLabel: "Enter order no. (transfer address)",
|
|
orderInputPlaceholder: "Enter order no.",
|
|
queryPaymentStatus: "Query payment status",
|
|
queryingStatus: "Querying...",
|
|
paymentPending: "Payment is not confirmed yet.",
|
|
paymentStatusFailed: "Payment status query failed.",
|
|
loading: "Loading...",
|
|
noOptions: "No available recharge methods.",
|
|
searchFirst: "Search an account first.",
|
|
orderNo: "Order no.",
|
|
done: "Done",
|
|
paymentSuccessTitle: "Payment successful",
|
|
paymentSuccessMessage: "The order result has been returned.",
|
|
paymentFailTitle: "Payment failed",
|
|
paymentFailMessage: "Please retry or change payment method.",
|
|
userNotFound: "User info not found",
|
|
noCountry: "No supported country",
|
|
noPackage: "No recharge packages",
|
|
paymentUnavailable: "Payment not available",
|
|
orderCreated: "Order created",
|
|
orderCreatedMessage: "Please finish payment according to the payment channel instructions.",
|
|
redirecting: "Redirecting to payment..."
|
|
},
|
|
ar: {},
|
|
tr: {},
|
|
id: {}
|
|
};
|
|
supportedLanguages.forEach((lang) => {
|
|
fallbackMessages[lang] = { ...fallbackMessages.en, ...(fallbackMessages[lang] || {}) };
|
|
});
|
|
|
|
function readURLParams() {
|
|
const result = new URLSearchParams(window.location.search);
|
|
const hashQuery = window.location.hash.includes("?") ? window.location.hash.split("?")[1] : "";
|
|
const hashParams = new URLSearchParams(hashQuery);
|
|
hashParams.forEach((value, key) => {
|
|
if (!result.has(key)) result.set(key, value);
|
|
});
|
|
return result;
|
|
}
|
|
|
|
function readRawParam(name) {
|
|
const queries = [window.location.search.replace(/^\?/, "")];
|
|
if (window.location.hash.includes("?")) queries.push(window.location.hash.split("?")[1]);
|
|
for (const query of queries) {
|
|
for (const part of query.split("&")) {
|
|
if (!part) continue;
|
|
const equalIndex = part.indexOf("=");
|
|
const rawKey = equalIndex >= 0 ? part.slice(0, equalIndex) : part;
|
|
const rawValue = equalIndex >= 0 ? part.slice(equalIndex + 1) : "";
|
|
let decodedKey = "";
|
|
try {
|
|
decodedKey = decodeURIComponent(rawKey);
|
|
} catch (error) {
|
|
continue;
|
|
}
|
|
if (decodedKey !== name) continue;
|
|
try {
|
|
return decodeURIComponent(rawValue.replace(/\+/g, "%2B"));
|
|
} catch (error) {
|
|
return rawValue;
|
|
}
|
|
}
|
|
}
|
|
return "";
|
|
}
|
|
|
|
function normalizeLanguage(lang) {
|
|
const value = String(lang || "en").toLowerCase();
|
|
if (value.startsWith("ar")) return "ar";
|
|
if (value.startsWith("tr")) return "tr";
|
|
if (value.startsWith("id") || value.startsWith("in")) return "id";
|
|
return supportedLanguages.includes(value) ? value : "en";
|
|
}
|
|
|
|
async function loadMessages(lang) {
|
|
const normalizedLang = normalizeLanguage(lang);
|
|
if (window.location.protocol === "file:") return fallbackMessages[normalizedLang] || fallbackMessages.en;
|
|
|
|
try {
|
|
const response = await fetch(`./locales/${normalizedLang}.json?v=20260601-0600`, { cache: "no-store" });
|
|
if (response.ok) {
|
|
const data = await response.json();
|
|
return { ...fallbackMessages.en, ...data };
|
|
}
|
|
} catch (error) {}
|
|
|
|
return fallbackMessages[normalizedLang] || fallbackMessages.en;
|
|
}
|
|
|
|
function splitHeaderPairs(value) {
|
|
const result = {};
|
|
if (!value) return result;
|
|
String(value).split(";").forEach((item) => {
|
|
const index = item.indexOf("=");
|
|
if (index > -1) result[item.slice(0, index)] = item.slice(index + 1);
|
|
});
|
|
return result;
|
|
}
|
|
|
|
function mapHeaderValue(target, key, value) {
|
|
if (value === null || value === undefined || value === "" || target[key]) return;
|
|
target[key] = String(value);
|
|
}
|
|
|
|
function normalizeHeaderCandidate(value, depth = 0) {
|
|
if (depth > 4 || value === null || value === undefined) return {};
|
|
if (typeof value === "string") {
|
|
const trimmed = value.trim();
|
|
if (!trimmed) return {};
|
|
if (trimmed[0] === "{" || trimmed[0] === "[") {
|
|
try {
|
|
return normalizeHeaderCandidate(JSON.parse(trimmed), depth + 1);
|
|
} catch (error) {
|
|
return {};
|
|
}
|
|
}
|
|
return /^bearer\s+/i.test(trimmed) ? { authorization: trimmed } : {};
|
|
}
|
|
if (Array.isArray(value)) {
|
|
return value.reduce((result, item) => ({ ...result, ...normalizeHeaderCandidate(item, depth + 1) }), {});
|
|
}
|
|
if (typeof value !== "object") return {};
|
|
|
|
const appIntel = splitHeaderPairs(value["Req-App-Intel"] || value["req-app-intel"]);
|
|
const sysOrigin = splitHeaderPairs(value["Req-Sys-Origin"] || value["req-sys-origin"]);
|
|
const normalized = {};
|
|
mapHeaderValue(normalized, "authorization", value.Authorization || value.authorization || value.token || value.accessToken);
|
|
mapHeaderValue(normalized, "reqLang", value["Req-Lang"] || value["req-lang"] || value.reqLang || value.lang || value.language);
|
|
mapHeaderValue(normalized, "appVersion", appIntel.version || value["App-Version"] || value.appVersion);
|
|
mapHeaderValue(normalized, "buildVersion", appIntel.build || value["Build-Version"] || value.buildVersion);
|
|
mapHeaderValue(normalized, "channel", appIntel.channel || value["App-Channel"] || value.appChannel || value.channel);
|
|
mapHeaderValue(normalized, "reqImei", value["Req-Imei"] || value["req-imei"] || value.reqImei || appIntel.reqImei);
|
|
mapHeaderValue(normalized, "origin", sysOrigin.origin || value["Sys-Origin"] || value.sysOrigin || value.origin);
|
|
mapHeaderValue(normalized, "child", sysOrigin.originChild || sysOrigin.child || value["Sys-Origin-Child"] || value.sysOriginChild || value.child);
|
|
["headerInfo", "headers", "auth", "data", "state"].forEach((key) => {
|
|
Object.assign(normalized, normalizeHeaderCandidate(value[key], depth + 1));
|
|
});
|
|
return normalized;
|
|
}
|
|
|
|
function normalizeAuthorization(value) {
|
|
const token = String(value || "").trim();
|
|
if (!token) return "";
|
|
return /^bearer\s+/i.test(token) ? token : `Bearer ${token}`;
|
|
}
|
|
|
|
function setHeadersFromApp(rawOrObject) {
|
|
const data = normalizeHeaderCandidate(rawOrObject || {});
|
|
const next = {};
|
|
if (data.authorization) next.Authorization = normalizeAuthorization(data.authorization);
|
|
if (data.reqLang) next["req-lang"] = normalizeLanguage(data.reqLang);
|
|
if (data.appVersion || data.buildVersion || data.channel) {
|
|
next["req-app-intel"] = [
|
|
data.appVersion ? `version=${data.appVersion}` : "",
|
|
data.buildVersion ? `build=${data.buildVersion}` : "",
|
|
data.channel ? `channel=${data.channel}` : ""
|
|
].filter(Boolean).join(";");
|
|
}
|
|
if (data.reqImei) next["req-imei"] = data.reqImei;
|
|
if (data.origin) {
|
|
next["req-sys-origin"] = [`origin=${data.origin}`, data.child ? `originChild=${data.child}` : ""].filter(Boolean).join(";");
|
|
}
|
|
headers = { ...headers, ...next };
|
|
}
|
|
|
|
function collectHeaderCandidate(collected, value) {
|
|
const normalized = normalizeHeaderCandidate(value);
|
|
if (Object.keys(normalized).length) collected.push(normalized);
|
|
}
|
|
|
|
function collectHeadersFromStorage(collected, storage) {
|
|
if (!storage) return;
|
|
["authorization", "Authorization", "token", "accessToken", "headerInfo", "headersFromApp", "appHeaders", "user", "userInfo"].forEach((key) => {
|
|
try {
|
|
collectHeaderCandidate(collected, storage.getItem(key));
|
|
} catch (error) {}
|
|
});
|
|
}
|
|
|
|
function queryHeaderCandidate() {
|
|
return {
|
|
authorization: readRawParam("authorization") || readRawParam("Authorization") || readRawParam("token") || readRawParam("accessToken"),
|
|
reqLang: params.get("lang"),
|
|
appVersion: params.get("appVersion"),
|
|
buildVersion: params.get("buildVersion"),
|
|
channel: params.get("appChannel") || params.get("channel"),
|
|
reqImei: params.get("reqImei"),
|
|
origin: params.get("sysOrigin"),
|
|
child: params.get("sysOriginChild")
|
|
};
|
|
}
|
|
|
|
function discoverAppHeaders() {
|
|
const collected = [{ reqLang: activeLang }, queryHeaderCandidate()];
|
|
collectHeaderCandidate(collected, window.__APP_HEADERS__);
|
|
collectHeaderCandidate(collected, window.__HEADER_INFO__);
|
|
collectHeaderCandidate(collected, window.headerInfo);
|
|
collectHeadersFromStorage(collected, window.localStorage);
|
|
collectHeadersFromStorage(collected, window.sessionStorage);
|
|
return collected.reduce((result, item) => ({ ...result, ...item }), {});
|
|
}
|
|
|
|
function applyDiscoveredHeaders() {
|
|
const discovered = discoverAppHeaders();
|
|
if (Object.keys(discovered).length) setHeadersFromApp(discovered);
|
|
return discovered;
|
|
}
|
|
|
|
function isAppEnvironment() {
|
|
return Boolean(window.app || window.webkit || window.FlutterPageControl || window.FlutterApp);
|
|
}
|
|
|
|
function requestAccessOrigin() {
|
|
return new Promise((resolve) => {
|
|
let settled = false;
|
|
let timeoutId = null;
|
|
const finish = (raw) => {
|
|
if (settled) {
|
|
if (raw) setHeadersFromApp(raw);
|
|
return;
|
|
}
|
|
settled = true;
|
|
if (timeoutId) window.clearTimeout(timeoutId);
|
|
resolve(raw || null);
|
|
};
|
|
|
|
window.renderData = finish;
|
|
window.getIosAccessOriginParam = finish;
|
|
|
|
if (!isAppEnvironment()) {
|
|
setHeadersFromApp({
|
|
reqLang: activeLang,
|
|
appVersion: "1.0.0",
|
|
buildVersion: "1",
|
|
channel: "Web",
|
|
reqImei: "H5-STATIC",
|
|
origin: "ATYOU",
|
|
child: "ATYOU"
|
|
});
|
|
finish(null);
|
|
return;
|
|
}
|
|
|
|
const requestFromFlutter = () => {
|
|
if (window.FlutterApp && typeof window.FlutterApp.postMessage === "function") {
|
|
window.FlutterApp.postMessage("requestAccessOrigin");
|
|
}
|
|
};
|
|
|
|
if (window.app && typeof window.app.getAccessOrigin === "function") {
|
|
finish(window.app.getAccessOrigin());
|
|
return;
|
|
}
|
|
|
|
requestFromFlutter();
|
|
timeoutId = window.setTimeout(() => finish(null), 12000);
|
|
|
|
let attempt = 0;
|
|
const poll = () => {
|
|
attempt += 1;
|
|
if (window.app && typeof window.app.getAccessOrigin === "function") {
|
|
finish(window.app.getAccessOrigin());
|
|
return;
|
|
}
|
|
if (attempt < 50) {
|
|
window.setTimeout(poll, 100);
|
|
return;
|
|
}
|
|
requestFromFlutter();
|
|
};
|
|
poll();
|
|
});
|
|
}
|
|
|
|
async function connectToApp() {
|
|
if (!appConnectionPromise) {
|
|
applyDiscoveredHeaders();
|
|
appConnectionPromise = requestAccessOrigin()
|
|
.then((raw) => {
|
|
if (raw) setHeadersFromApp(raw);
|
|
else applyDiscoveredHeaders();
|
|
return {
|
|
success: true,
|
|
environment: isAppEnvironment() ? "app" : "browser",
|
|
authenticated: Boolean(headers.Authorization)
|
|
};
|
|
})
|
|
.finally(() => {
|
|
appConnectionPromise = null;
|
|
});
|
|
}
|
|
return appConnectionPromise;
|
|
}
|
|
|
|
function apiBaseURL() {
|
|
const override = window.HYAPP_API_BASE_URL || params.get("apiBase");
|
|
return String(override || API_BASE_URL).replace(/\/+$/, "");
|
|
}
|
|
|
|
function buildRequestURL(path) {
|
|
const base = `${apiBaseURL()}/`;
|
|
const target = String(path || "").replace(/^\/+/, "");
|
|
return new URL(target, base).toString();
|
|
}
|
|
|
|
async function requestJSON(path, options = {}) {
|
|
if (isMock) return mockRequest(path, options);
|
|
applyDiscoveredHeaders();
|
|
const requestHeaders = { ...headers };
|
|
if (!requestHeaders.Authorization && readRawParam("token")) {
|
|
requestHeaders.Authorization = normalizeAuthorization(readRawParam("token"));
|
|
}
|
|
const request = {
|
|
method: options.method || "GET",
|
|
cache: "no-store",
|
|
headers: requestHeaders
|
|
};
|
|
if (options.body !== undefined) {
|
|
requestHeaders["Content-Type"] = "application/json";
|
|
request.body = JSON.stringify(options.body);
|
|
}
|
|
|
|
const response = await fetch(buildRequestURL(path), request);
|
|
const data = await response.json().catch(() => ({}));
|
|
if (!response.ok || data.status === false) {
|
|
const error = new Error(data.errorMsg || data.message || `Request failed: ${response.status}`);
|
|
error.response = data;
|
|
throw error;
|
|
}
|
|
return data.body ?? data.data ?? null;
|
|
}
|
|
|
|
function closePage() {
|
|
try {
|
|
const from = params.get("from");
|
|
if (from) {
|
|
const target = new URL(from, window.location.origin);
|
|
const currentLang = params.get("lang");
|
|
if (currentLang && !target.searchParams.has("lang")) target.searchParams.set("lang", currentLang);
|
|
window.location.href = `${target.pathname}${target.search}${target.hash}`;
|
|
return;
|
|
}
|
|
if (window.app && typeof window.app.closePage === "function") {
|
|
window.app.closePage();
|
|
return;
|
|
}
|
|
if (window.FlutterPageControl && typeof window.FlutterPageControl.postMessage === "function") {
|
|
window.FlutterPageControl.postMessage("close_page");
|
|
return;
|
|
}
|
|
if (window.history.length > 1) window.history.back();
|
|
} catch (error) {
|
|
if (window.history.length > 1) window.history.back();
|
|
}
|
|
}
|
|
|
|
function endpointOpenSearch(account) {
|
|
return `/user/user-profile/open-search?account=${encodeURIComponent(account)}`;
|
|
}
|
|
|
|
function endpointSelfProfile() {
|
|
return "/team/member/profile";
|
|
}
|
|
|
|
function endpointApplication() {
|
|
return `/order/web/pay/application/${encodeURIComponent(RECHARGE_APPLICATION_ID)}`;
|
|
}
|
|
|
|
function endpointPayProfile(appCode, account, type) {
|
|
const query = new URLSearchParams({ sysOrigin: appCode, account, type });
|
|
return `/order/web/pay/user-profile?${query.toString()}`;
|
|
}
|
|
|
|
function endpointCommodity(countryId, regionId, type) {
|
|
const query = new URLSearchParams({
|
|
applicationId: RECHARGE_APPLICATION_ID,
|
|
payCountryId: countryId,
|
|
regionId: regionId || "",
|
|
type
|
|
});
|
|
return `/order/web/pay/commodity?${query.toString()}`;
|
|
}
|
|
|
|
function endpointOrderStatus(orderId) {
|
|
const query = new URLSearchParams({ orderId });
|
|
return `/order/web/pay/order-status?${query.toString()}`;
|
|
}
|
|
|
|
function profileId(profile) {
|
|
return profile?.id || profile?.userId || profile?.uid || "";
|
|
}
|
|
|
|
function profileAccount(profile) {
|
|
return profile?.account || profile?.userAccount || profile?.ownSpecialId?.account || profile?.goodId || profile?.goodID || "-";
|
|
}
|
|
|
|
function profileName(profile) {
|
|
return profile?.userNickname || profile?.nickName || profile?.nickname || profile?.name || "-";
|
|
}
|
|
|
|
function avatarUrl(profile) {
|
|
return profile?.userAvatar || profile?.avatar || profile?.avatarUrl || "";
|
|
}
|
|
|
|
function isDealerProfile(profile) {
|
|
const role = String(profile?.role || profile?.userRole || profile?.identity || profile?.type || "").toUpperCase();
|
|
return Boolean(profile?.isSuperFreightAgent || profile?.isFreightAgent || role.includes("DEALER") || role.includes("SELLER") || role.includes("FREIGHT"));
|
|
}
|
|
|
|
function commodityType(profile) {
|
|
if (profile?.isSuperFreightAgent) return "FREIGHT_GOLD_SUPER";
|
|
if (isDealerProfile(profile)) return "FREIGHT_GOLD";
|
|
return "GOLD";
|
|
}
|
|
|
|
function countryName(item) {
|
|
return item?.countryName || item?.country?.aliasName || item?.country?.countryName || item?.name || "-";
|
|
}
|
|
|
|
function countryId(item) {
|
|
return item?.id || item?.countryId || item?.country?.id || item?.country?.countryId || "";
|
|
}
|
|
|
|
function channelCode(item) {
|
|
return item?.channel?.channelCode || item?.details?.channelCode || item?.channelCode || "";
|
|
}
|
|
|
|
function channelName(item) {
|
|
return item?.channel?.channelName || item?.channelName || channelCode(item) || "-";
|
|
}
|
|
|
|
function channelIcon(item) {
|
|
return item?.channel?.channelIcon || item?.channelIcon || "";
|
|
}
|
|
|
|
function usdtAddress(item) {
|
|
return (
|
|
item?.details?.trc20Address ||
|
|
item?.details?.usdtTrc20Address ||
|
|
item?.details?.usdtAddress ||
|
|
item?.details?.walletAddress ||
|
|
item?.channel?.trc20Address ||
|
|
item?.channel?.usdtAddress ||
|
|
item?.channel?.address ||
|
|
item?.trc20Address ||
|
|
item?.usdtAddress ||
|
|
item?.walletAddress ||
|
|
item?.address ||
|
|
params.get("trc20Address") ||
|
|
window.USDT_TRC20_ADDRESS ||
|
|
"TMockTrc20RechargeAddress000000000001"
|
|
);
|
|
}
|
|
|
|
function isUsdtChannel(item) {
|
|
const value = `${channelCode(item)} ${channelName(item)} ${item?.details?.factoryChannel || ""}`.toUpperCase();
|
|
return value.includes("USDT") || value.includes("UDST") || value.includes("TRC20") || value.includes("BEP20");
|
|
}
|
|
|
|
function isUsdtTrc20Channel(item) {
|
|
const value = `${channelCode(item)} ${channelName(item)} ${item?.details?.factoryChannel || ""}`.toUpperCase();
|
|
return value.includes("TRC20") && (value.includes("USDT") || value.includes("UDST"));
|
|
}
|
|
|
|
function isBankTransferChannel(item) {
|
|
const code = String(channelCode(item) || "").trim().toUpperCase();
|
|
const value = `${code} ${channelName(item)} ${item?.details?.factoryChannel || ""}`.toUpperCase();
|
|
return code === "BANK" || value.includes("BANK TRANSFER") || value.includes("BANKTRANSFER") || value.includes("BANK_TRANSFER");
|
|
}
|
|
|
|
function isSuccessfulPaymentStatus(data) {
|
|
if (data?.success === true || data?.paid === true || data?.paySuccess === true || data?.orderPaid === true) return true;
|
|
const status = String(data?.status || data?.orderStatus || data?.payStatus || data?.tradeStatus || "").trim().toUpperCase();
|
|
return ["SUCCESS", "SUCCEEDED", "PAID", "DONE", "COMPLETED"].includes(status);
|
|
}
|
|
|
|
async function copyText(value) {
|
|
const text = String(value || "");
|
|
if (!text) return;
|
|
if (navigator.clipboard && window.isSecureContext) {
|
|
await navigator.clipboard.writeText(text);
|
|
return;
|
|
}
|
|
const input = document.createElement("textarea");
|
|
input.value = text;
|
|
input.setAttribute("readonly", "readonly");
|
|
input.style.position = "fixed";
|
|
input.style.top = "-1000px";
|
|
document.body.appendChild(input);
|
|
input.select();
|
|
document.execCommand("copy");
|
|
document.body.removeChild(input);
|
|
}
|
|
|
|
function pickDefaultCountry(list, regionId) {
|
|
const countries = Array.isArray(list) ? list : [];
|
|
if (!countries.length) return null;
|
|
const region = String(regionId || "").toUpperCase();
|
|
return countries.find((item) => {
|
|
const values = [
|
|
item?.id,
|
|
item?.countryId,
|
|
item?.regionId,
|
|
item?.country?.id,
|
|
item?.country?.countryId,
|
|
item?.country?.regionId,
|
|
item?.countryCode,
|
|
item?.country?.countryCode
|
|
].map((value) => String(value || "").toUpperCase());
|
|
return region && values.includes(region);
|
|
}) || countries[0];
|
|
}
|
|
|
|
function parseExpand(value) {
|
|
if (!value) return {};
|
|
if (typeof value === "object") return value;
|
|
try {
|
|
return JSON.parse(value);
|
|
} catch (error) {
|
|
return {};
|
|
}
|
|
}
|
|
|
|
function payEnvironment() {
|
|
const value = String(window.AIRWALLEX_ENV || params.get("payEnv") || window.ENV || "").toLowerCase();
|
|
if (value === "production" || value === "prod") return "prod";
|
|
if (value === "staging") return "staging";
|
|
if (value === "dev") return "dev";
|
|
if (value === "local") return "local";
|
|
return "demo";
|
|
}
|
|
|
|
function airwallexHost(env) {
|
|
const hosts = {
|
|
prod: "checkout.airwallex.com",
|
|
demo: "checkout-demo.airwallex.com",
|
|
staging: "checkout-staging.airwallex.com",
|
|
dev: "checkout-dev.airwallex.com",
|
|
local: "localhost:3000"
|
|
};
|
|
return `https://${hosts[env] || hosts.prod}`;
|
|
}
|
|
|
|
function loadAirwallex(env) {
|
|
if (window.Airwallex) {
|
|
window.Airwallex.init({ env });
|
|
return Promise.resolve(window.Airwallex);
|
|
}
|
|
const src = `${airwallexHost(env)}/assets/elements.bundle.min.js?version=1.160.0`;
|
|
const existing = document.querySelector(`[data-airwallex-script="${env}"]`);
|
|
const script = existing || document.createElement("script");
|
|
if (!existing) {
|
|
script.src = src;
|
|
script.crossOrigin = "anonymous";
|
|
script.dataset.airwallexScript = env;
|
|
document.head.appendChild(script);
|
|
}
|
|
return new Promise((resolve, reject) => {
|
|
script.addEventListener("load", () => {
|
|
if (!window.Airwallex) {
|
|
reject(new Error("Failed to load Airwallex"));
|
|
return;
|
|
}
|
|
window.Airwallex.init({ env });
|
|
resolve(window.Airwallex);
|
|
}, { once: true });
|
|
script.addEventListener("error", () => reject(new Error("Failed to load Airwallex")), { once: true });
|
|
});
|
|
}
|
|
|
|
function mockRequest(path, options) {
|
|
return new Promise((resolve) => {
|
|
window.setTimeout(() => {
|
|
const account = params.get("account") || "80008888";
|
|
if (path.includes("/application/")) {
|
|
resolve({ appCode: "ATYOU" });
|
|
return;
|
|
}
|
|
if (path.includes("/team/member/profile")) {
|
|
const dealer = String(account).startsWith("8");
|
|
resolve({
|
|
id: dealer ? "9001" : "1001",
|
|
account,
|
|
userNickname: dealer ? "India Dealer" : "India User",
|
|
userAvatar: "",
|
|
isFreightAgent: dealer
|
|
});
|
|
return;
|
|
}
|
|
if (path.includes("/open-search")) {
|
|
const requested = decodeURIComponent(path.split("account=")[1] || account);
|
|
const dealer = String(requested).startsWith("8");
|
|
resolve({
|
|
id: dealer ? "9001" : "1001",
|
|
account: requested,
|
|
userNickname: dealer ? "India Dealer" : "India User",
|
|
userAvatar: "",
|
|
isFreightAgent: dealer
|
|
});
|
|
return;
|
|
}
|
|
if (path.includes("/user-profile")) {
|
|
resolve({
|
|
regionId: "IN",
|
|
countryList: [
|
|
{ id: "101", countryName: "India", country: { countryName: "India" } },
|
|
{ id: "102", countryName: "Saudi Arabia", country: { countryName: "Saudi Arabia" } }
|
|
]
|
|
});
|
|
return;
|
|
}
|
|
if (path.includes("/commodity")) {
|
|
resolve({
|
|
commodity: [
|
|
{ id: "g1", content: "10,000 coins", amountUsd: 1 },
|
|
{ id: "g2", content: "50,000 coins", amountUsd: 5, awardContent: "+5,000 bonus" },
|
|
{ id: "g3", content: "100,000 coins", amountUsd: 10, awardContent: "+12,000 bonus" }
|
|
],
|
|
channels: [
|
|
{ channel: { channelCode: "UPI_BHIM", channelName: "BHIM UPI", channelIcon: "../mifa-pay/logos/bhim.png" } },
|
|
{ channel: { channelCode: "UPI_PHONEPE", channelName: "PhonePe", channelIcon: "../mifa-pay/logos/phonepe.png" } },
|
|
{ channel: { channelCode: "UPI_GOOGLEPAY", channelName: "Google Pay", channelIcon: "../mifa-pay/logos/googlepay.png" } },
|
|
{ channel: { channelCode: "UPI_PAYTM", channelName: "Paytm", channelIcon: "../mifa-pay/logos/paytm.png" } },
|
|
{ channel: { channelCode: "UPI_MOBIKWIK", channelName: "Mobikwik", channelIcon: "../mifa-pay/logos/mobikwik.png" } },
|
|
{ channel: { channelCode: "UPI", channelName: "UPI", channelIcon: "../mifa-pay/logos/upi.png" } },
|
|
{
|
|
channel: { channelCode: "USDT_TRC20", channelName: "USDT TRC20" },
|
|
details: { trc20Address: "TMockTrc20RechargeAddress000000000001" }
|
|
}
|
|
]
|
|
});
|
|
return;
|
|
}
|
|
if (path.includes("/pay/recharge")) {
|
|
resolve({ orderId: "RCMOCK202606010001", factoryCode: "MANUAL" });
|
|
return;
|
|
}
|
|
if (path.includes("/order-status")) {
|
|
const orderId = new URLSearchParams(path.split("?")[1] || "").get("orderId") || "";
|
|
const failed = !orderId || /fail|pending/i.test(orderId);
|
|
resolve({
|
|
orderId,
|
|
status: failed ? "PENDING" : "SUCCESS",
|
|
success: !failed,
|
|
finished: !failed,
|
|
reason: failed ? "Payment is not confirmed yet." : ""
|
|
});
|
|
return;
|
|
}
|
|
resolve({});
|
|
}, 260);
|
|
});
|
|
}
|
|
|
|
const app = Vue.createApp({
|
|
data() {
|
|
return {
|
|
lang: activeLang,
|
|
languages: languageOptions,
|
|
languageMenuOpen: false,
|
|
messages: fallbackMessages[activeLang] || fallbackMessages.en,
|
|
isMock,
|
|
selfMode: hasTokenParam,
|
|
authenticated: false,
|
|
account: params.get("account") || params.get("targetAccount") || "",
|
|
searching: false,
|
|
loadingOptions: false,
|
|
ordering: false,
|
|
statusText: "",
|
|
statusType: "",
|
|
toastText: "",
|
|
targetProfile: null,
|
|
application: null,
|
|
appCode: "",
|
|
rechargeType: "GOLD",
|
|
payProfile: null,
|
|
countryList: [],
|
|
selectedCountryId: "",
|
|
regionId: "",
|
|
commodity: [],
|
|
selectedGoodsId: "",
|
|
selectedChannelKey: "",
|
|
channels: [],
|
|
orderResult: null,
|
|
usdtModalOpen: false,
|
|
usdtOrderInput: "",
|
|
usdtOrderError: "",
|
|
queryingPayment: false,
|
|
paymentReturn: null
|
|
};
|
|
},
|
|
computed: {
|
|
text() {
|
|
return this.messages || fallbackMessages.en;
|
|
},
|
|
languageLabel() {
|
|
return languageLabels[this.lang] || "EN";
|
|
},
|
|
isRtl() {
|
|
return this.lang === "ar";
|
|
},
|
|
selectedCountry() {
|
|
return this.countryList.find((item) => String(countryId(item)) === String(this.selectedCountryId)) || this.countryList[0] || null;
|
|
},
|
|
selectedGoods() {
|
|
return this.commodity.find((item) => String(item?.id) === String(this.selectedGoodsId)) || null;
|
|
},
|
|
selectedChannel() {
|
|
return this.visibleChannels.find((item) => this.channelKey(item) === this.selectedChannelKey) || null;
|
|
},
|
|
usdtAddressText() {
|
|
return usdtAddress(this.selectedChannel);
|
|
},
|
|
isDealer() {
|
|
return isDealerProfile(this.targetProfile);
|
|
},
|
|
roleLabel() {
|
|
if (this.targetProfile?.isSuperFreightAgent) return this.text.superDealer;
|
|
if (this.isDealer) return this.text.dealer;
|
|
return this.text.normalUser;
|
|
},
|
|
visibleChannels() {
|
|
const usdtTrc20 = this.channels.filter(isUsdtTrc20Channel);
|
|
const thirdParty = this.channels.filter((channel) => !isUsdtChannel(channel) && !isBankTransferChannel(channel));
|
|
return [...thirdParty, ...usdtTrc20];
|
|
}
|
|
},
|
|
async mounted() {
|
|
document.documentElement.lang = this.lang;
|
|
document.documentElement.dir = this.isRtl ? "rtl" : "ltr";
|
|
await this.applyLanguage(this.lang);
|
|
this.readPaymentReturn();
|
|
connectToApp().then((result) => {
|
|
this.authenticated = Boolean(result.authenticated || headers.Authorization || isMock);
|
|
});
|
|
if (this.selfMode) {
|
|
this.loadSelfAccount();
|
|
} else if (this.account && params.get("autoSearch") === "1") {
|
|
this.searchAccount();
|
|
}
|
|
},
|
|
methods: {
|
|
closePage,
|
|
profileName,
|
|
profileAccount,
|
|
avatarUrl,
|
|
countryName,
|
|
countryId,
|
|
channelCode,
|
|
channelName,
|
|
channelIcon,
|
|
usdtAddress,
|
|
isUsdtChannel,
|
|
isUsdtTrc20Channel,
|
|
fallbackInitial(profile) {
|
|
return String(profileName(profile) || profileAccount(profile) || "U").trim().slice(0, 1).toUpperCase();
|
|
},
|
|
hideBrokenImage(event) {
|
|
event.currentTarget.style.display = "none";
|
|
},
|
|
toggleLanguageMenu() {
|
|
this.languageMenuOpen = !this.languageMenuOpen;
|
|
},
|
|
closeLanguageMenu() {
|
|
this.languageMenuOpen = false;
|
|
},
|
|
async applyLanguage(lang) {
|
|
this.lang = normalizeLanguage(lang);
|
|
activeLang = this.lang;
|
|
this.messages = await loadMessages(this.lang);
|
|
headers["req-lang"] = this.lang;
|
|
document.documentElement.lang = this.lang;
|
|
document.documentElement.dir = this.isRtl ? "rtl" : "ltr";
|
|
document.title = this.text.documentTitle || this.text.title || "Recharge Center";
|
|
this.languageMenuOpen = false;
|
|
if (this.paymentReturn) this.readPaymentReturn();
|
|
},
|
|
showToast(text) {
|
|
this.toastText = text;
|
|
window.clearTimeout(this.toastTimer);
|
|
this.toastTimer = window.setTimeout(() => {
|
|
this.toastText = "";
|
|
}, 2400);
|
|
},
|
|
setStatus(text, type = "") {
|
|
this.statusText = text;
|
|
this.statusType = type;
|
|
},
|
|
selectGoods(goods) {
|
|
this.selectedGoodsId = String(goods?.id || "");
|
|
},
|
|
selectChannel(channel) {
|
|
this.selectedChannelKey = this.channelKey(channel);
|
|
this.usdtOrderError = "";
|
|
},
|
|
readPaymentReturn() {
|
|
const status = params.get("paymentStatus") || params.get("appStatus");
|
|
if (status === "0" || status === "success") {
|
|
this.paymentReturn = {
|
|
type: "success",
|
|
title: this.text.paymentSuccessTitle,
|
|
message: this.text.paymentSuccessMessage
|
|
};
|
|
} else if (status === "1" || status === "fail") {
|
|
this.paymentReturn = {
|
|
type: "error",
|
|
title: this.text.paymentFailTitle,
|
|
message: this.text.paymentFailMessage
|
|
};
|
|
}
|
|
},
|
|
resetTarget() {
|
|
this.targetProfile = null;
|
|
this.payProfile = null;
|
|
this.countryList = [];
|
|
this.selectedCountryId = "";
|
|
this.regionId = "";
|
|
this.commodity = [];
|
|
this.selectedGoodsId = "";
|
|
this.selectedChannelKey = "";
|
|
this.channels = [];
|
|
this.usdtModalOpen = false;
|
|
this.setStatus("");
|
|
},
|
|
async ensureApplication() {
|
|
if (this.appCode) return;
|
|
this.application = await requestJSON(endpointApplication());
|
|
this.appCode = this.application?.appCode || this.application?.sysOrigin || "ATYOU";
|
|
},
|
|
async loadSelfAccount() {
|
|
if (this.searching) return;
|
|
this.searching = true;
|
|
this.setStatus(this.text.loadingAccount);
|
|
this.targetProfile = null;
|
|
this.countryList = [];
|
|
this.commodity = [];
|
|
this.selectedGoodsId = "";
|
|
this.selectedChannelKey = "";
|
|
this.channels = [];
|
|
try {
|
|
await connectToApp();
|
|
const profile = await requestJSON(endpointSelfProfile());
|
|
if (!profile || !profileId(profile)) throw new Error(this.text.userNotFound);
|
|
this.targetProfile = profile;
|
|
this.account = profileAccount(profile);
|
|
this.rechargeType = commodityType(profile);
|
|
await this.loadRechargeOptions();
|
|
this.setStatus("");
|
|
} catch (error) {
|
|
const text = error.response?.errorMsg || error.message || this.text.userNotFound;
|
|
this.setStatus(text, "error");
|
|
this.showToast(text);
|
|
} finally {
|
|
this.searching = false;
|
|
}
|
|
},
|
|
async searchAccount() {
|
|
if (!this.account || this.searching) return;
|
|
this.searching = true;
|
|
this.setStatus(this.text.searching);
|
|
this.targetProfile = null;
|
|
this.countryList = [];
|
|
this.commodity = [];
|
|
this.selectedGoodsId = "";
|
|
this.selectedChannelKey = "";
|
|
this.channels = [];
|
|
try {
|
|
await connectToApp();
|
|
const profile = await requestJSON(endpointOpenSearch(this.account));
|
|
if (!profile || !profileId(profile)) throw new Error(this.text.userNotFound);
|
|
this.targetProfile = profile;
|
|
this.rechargeType = commodityType(profile);
|
|
await this.loadRechargeOptions();
|
|
this.setStatus("");
|
|
} catch (error) {
|
|
const text = error.response?.errorMsg || error.message || this.text.userNotFound;
|
|
this.setStatus(text, "error");
|
|
this.showToast(text);
|
|
} finally {
|
|
this.searching = false;
|
|
}
|
|
},
|
|
async loadRechargeOptions() {
|
|
this.loadingOptions = true;
|
|
try {
|
|
await this.ensureApplication();
|
|
const data = await requestJSON(endpointPayProfile(this.appCode, profileAccount(this.targetProfile), this.rechargeType));
|
|
this.payProfile = data || {};
|
|
if (data?.userProfile) this.targetProfile = { ...this.targetProfile, ...data.userProfile };
|
|
this.countryList = Array.isArray(data?.countryList) ? data.countryList : [];
|
|
this.regionId = data?.regionId || "";
|
|
if (!this.countryList.length) throw new Error(this.text.noCountry);
|
|
const country = pickDefaultCountry(this.countryList, this.regionId);
|
|
this.selectedCountryId = String(countryId(country));
|
|
await this.loadCommodity();
|
|
} finally {
|
|
this.loadingOptions = false;
|
|
}
|
|
},
|
|
async loadCommodity() {
|
|
const country = this.selectedCountry;
|
|
const id = countryId(country);
|
|
if (!id) {
|
|
this.commodity = [];
|
|
this.channels = [];
|
|
this.selectedChannelKey = "";
|
|
return;
|
|
}
|
|
const data = await requestJSON(endpointCommodity(String(id), this.regionId, this.rechargeType));
|
|
this.commodity = Array.isArray(data?.commodity) ? data.commodity : [];
|
|
this.channels = Array.isArray(data?.channels) ? data.channels : [];
|
|
this.selectedGoodsId = "";
|
|
this.selectedChannelKey = "";
|
|
if (!this.commodity.length) this.setStatus(this.text.noPackage, "error");
|
|
},
|
|
channelKey(channel) {
|
|
return `${channelCode(channel)}:${channelName(channel)}`;
|
|
},
|
|
formatPrice(goods) {
|
|
const currency = goods.currency || "USD";
|
|
const amount = goods.amountUsd ?? goods.amount ?? goods.price ?? "";
|
|
if (amount === "") return "";
|
|
return currency === "USD" ? `$${amount}` : `${currency} ${amount}`;
|
|
},
|
|
paymentResultURL(status) {
|
|
const url = new URL(window.location.origin + window.location.pathname);
|
|
url.searchParams.set("paymentStatus", status);
|
|
url.searchParams.set("lang", this.lang);
|
|
if (this.account) url.searchParams.set("account", this.account);
|
|
return url.toString();
|
|
},
|
|
async redirectAirwallex(result) {
|
|
const env = payEnvironment();
|
|
const expand = parseExpand(result.expand);
|
|
if (!expand.id || !expand.client_secret) throw new Error(this.text.paymentUnavailable);
|
|
await loadAirwallex(env);
|
|
window.Airwallex.redirectToCheckout({
|
|
env,
|
|
mode: "payment",
|
|
currency: result.currency,
|
|
intent_id: expand.id,
|
|
client_secret: expand.client_secret,
|
|
methods: result.factoryChannelCode ? [result.factoryChannelCode] : undefined,
|
|
logoUrl: window.sysOriginLog || "",
|
|
successUrl: this.paymentResultURL("0"),
|
|
failUrl: this.paymentResultURL("1")
|
|
});
|
|
},
|
|
async handlePaymentResponse(result) {
|
|
if (!result) throw new Error(this.text.paymentUnavailable);
|
|
if (result.factoryCode === "AIRWALLEX") {
|
|
this.showToast(this.text.redirecting);
|
|
await this.redirectAirwallex(result);
|
|
return;
|
|
}
|
|
if (result.requestUrl) {
|
|
this.showToast(this.text.redirecting);
|
|
window.location.href = decodeURIComponent(result.requestUrl);
|
|
return;
|
|
}
|
|
this.orderResult = {
|
|
type: "info",
|
|
title: this.text.orderCreated,
|
|
message: this.text.orderCreatedMessage,
|
|
orderNo: result.orderNo || result.orderId || result.tradeNo || ""
|
|
};
|
|
},
|
|
async createOrder() {
|
|
if (!this.selectedGoods) {
|
|
this.showToast(this.text.selectPackageFirst);
|
|
return;
|
|
}
|
|
if (!this.selectedChannel) {
|
|
this.showToast(this.text.selectPaymentMethod);
|
|
return;
|
|
}
|
|
if (isUsdtTrc20Channel(this.selectedChannel)) {
|
|
this.openUsdtModal();
|
|
return;
|
|
}
|
|
await this.placeOrder(this.selectedChannel);
|
|
},
|
|
openUsdtModal() {
|
|
this.usdtOrderInput = "";
|
|
this.usdtOrderError = "";
|
|
this.usdtModalOpen = true;
|
|
},
|
|
closeUsdtModal() {
|
|
this.usdtModalOpen = false;
|
|
this.usdtOrderInput = "";
|
|
this.usdtOrderError = "";
|
|
this.queryingPayment = false;
|
|
},
|
|
async copyUsdtAddress() {
|
|
try {
|
|
await copyText(this.usdtAddressText);
|
|
this.showToast(this.text.copied);
|
|
} catch (error) {
|
|
this.showToast(this.usdtAddressText);
|
|
}
|
|
},
|
|
async queryUsdtPaymentStatus() {
|
|
if (!this.usdtOrderInput || this.queryingPayment) return;
|
|
this.queryingPayment = true;
|
|
this.usdtOrderError = "";
|
|
try {
|
|
const result = await requestJSON(endpointOrderStatus(this.usdtOrderInput));
|
|
if (isSuccessfulPaymentStatus(result)) {
|
|
this.closeUsdtModal();
|
|
this.showToast(this.text.paymentSuccessTitle);
|
|
return;
|
|
}
|
|
this.usdtOrderError = result?.reason || result?.message || result?.status || this.text.paymentPending;
|
|
} catch (error) {
|
|
this.usdtOrderError = error.response?.errorMsg || error.message || this.text.paymentStatusFailed;
|
|
} finally {
|
|
this.queryingPayment = false;
|
|
}
|
|
},
|
|
async placeOrder(channel) {
|
|
const country = this.selectedCountry;
|
|
const goods = this.selectedGoods;
|
|
const id = countryId(country);
|
|
const code = channelCode(channel);
|
|
const userId = profileId(this.targetProfile);
|
|
if (!id || !goods?.id || !userId || !code) {
|
|
this.showToast(!goods?.id ? this.text.selectPackageFirst : this.text.paymentUnavailable);
|
|
return;
|
|
}
|
|
this.ordering = true;
|
|
try {
|
|
const result = await requestJSON("/order/web/pay/recharge", {
|
|
method: "POST",
|
|
body: {
|
|
applicationId: RECHARGE_APPLICATION_ID,
|
|
payCountryId: String(id),
|
|
goodsId: String(goods.id),
|
|
userId: String(userId),
|
|
channelCode: code,
|
|
newVersion: true
|
|
}
|
|
});
|
|
await this.handlePaymentResponse(result);
|
|
} catch (error) {
|
|
const text = error.response?.errorMsg || error.message || this.text.paymentUnavailable;
|
|
this.orderResult = { type: "error", title: this.text.paymentUnavailable, message: text };
|
|
this.showToast(text);
|
|
} finally {
|
|
this.ordering = false;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
app.mount("#app");
|
|
})();
|