diff --git a/h5/hyapp/recharge-center/index.html b/h5/hyapp/recharge-center/index.html
index 2bbbab6..68f5209 100644
--- a/h5/hyapp/recharge-center/index.html
+++ b/h5/hyapp/recharge-center/index.html
@@ -4,7 +4,7 @@
diff --git a/h5/hyapp/recharge-center/style.css b/h5/hyapp/recharge-center/style.css
index f5b49c2..c8a2398 100644
--- a/h5/hyapp/recharge-center/style.css
+++ b/h5/hyapp/recharge-center/style.css
@@ -946,6 +946,7 @@ button {
.history-dialog {
width: min(100%, 390px);
+ grid-template-rows: auto minmax(0, 1fr);
}
.whatsapp-dialog {
@@ -1006,8 +1007,11 @@ button {
.history-body {
display: grid;
gap: 12px;
+ min-height: 0;
overflow-y: auto;
+ overscroll-behavior: contain;
padding: 0 16px 18px;
+ touch-action: pan-y;
-webkit-overflow-scrolling: touch;
}
diff --git a/h5/recharge/index.html b/h5/recharge/index.html
new file mode 100644
index 0000000..19bfca9
--- /dev/null
+++ b/h5/recharge/index.html
@@ -0,0 +1,224 @@
+
+
+
+
+
+
+
Recharge Center
+
+
+
+
+
+
+
+
+
+
+ {{ paymentReturn.title }}
+ {{ paymentReturn.message }}
+
+
+
+ {{ text.authMissingTitle }}
+ {{ text.authMissingDesc }}
+
+
+
+ {{ searching ? text.loadingAccount : (statusText || text.loadingAccount) }}
+
+
+
+
+
{{ text.searchTitle }}
+
+
+ {{ text.searchGuide }}
+ {{ statusText }}
+
+
+
+
+
![]()
+
{{ fallbackInitial(targetProfile) }}
+
+
+
+ {{ roleLabel }}
+ {{ text.lowFee }}
+
+
{{ profileName(targetProfile) }}
+
{{ text.userId }} {{ profileAccount(targetProfile) }}
+
{{ text.country }} {{ countryName(selectedCountry) }}
+
+
+
+
+
+
+
{{ text.packageTitle }}
+
+
+ {{ text.loading }}
+
+
+
+
+
+ {{ text.noPackage }}
+
+
+
{{ text.methodTitle }}
+
+
+
+
+
+ {{ text.noOptions }}
+
+
+
+
+
+
+
+
+
+
+ {{ orderResult.title }}
+ {{ orderResult.message }}
+
+ - {{ text.orderNo }}
+ - {{ orderResult.orderNo }}
+
+
+
+
+
+
+
+
+ {{ text.usdtOrderTitle }}
+
+
+
+
+
+
+
+
+
+
+
+
{{ formatPrice(selectedGoods) }}
+
+
+
+
+
+
+
{{ toastText }}
+
+
+
+
+
+
diff --git a/h5/recharge/locales/ar.json b/h5/recharge/locales/ar.json
new file mode 100644
index 0000000..142e42d
--- /dev/null
+++ b/h5/recharge/locales/ar.json
@@ -0,0 +1,54 @@
+{
+ "documentTitle": "مركز الشحن",
+ "back": "رجوع",
+ "title": "مركز الشحن",
+ "languageButtonAria": "تغيير اللغة",
+ "authMissingTitle": "لم يتم العثور على رمز الدخول",
+ "authMissingDesc": "افتح هذه الصفحة من التطبيق أو أضف الرمز في الرابط.",
+ "searchTitle": "البحث عن الحساب",
+ "searchGuide": "تأكد من الحساب قبل الدفع. لا يمكن رد عمليات الشحن المكتملة.",
+ "loadingAccount": "جار تحميل الحساب...",
+ "accountPlaceholder": "أدخل معرف المستخدم",
+ "search": "بحث",
+ "searching": "جار البحث...",
+ "userId": "المعرف:",
+ "country": "الدولة:",
+ "normalUser": "مستخدم عادي",
+ "dealer": "تاجر",
+ "superDealer": "تاجر مميز",
+ "lowFee": "رسوم منخفضة",
+ "change": "تغيير",
+ "packageTitle": "باقة الشحن",
+ "methodTitle": "طرق الدفع",
+ "selectPackageFirst": "اختر باقة أولا.",
+ "selectPaymentMethod": "اختر طريقة دفع أولا.",
+ "createOrder": "إنشاء طلب",
+ "creatingOrder": "جار الإنشاء...",
+ "usdtOrderTitle": "طلب USDT TRC20",
+ "trc20Address": "عنوان TRC20",
+ "copy": "نسخ",
+ "copied": "تم النسخ",
+ "amount": "المبلغ",
+ "orderInputLabel": "أدخل رقم الطلب (عنوان التحويل)",
+ "orderInputPlaceholder": "أدخل رقم الطلب",
+ "queryPaymentStatus": "الاستعلام عن حالة الدفع",
+ "queryingStatus": "جار الاستعلام...",
+ "paymentPending": "لم يتم تأكيد الدفع بعد.",
+ "paymentStatusFailed": "فشل الاستعلام عن حالة الدفع.",
+ "loading": "جار التحميل...",
+ "noOptions": "لا توجد طرق شحن متاحة.",
+ "searchFirst": "ابحث عن الحساب أولا.",
+ "orderNo": "رقم الطلب",
+ "done": "تم",
+ "paymentSuccessTitle": "تم الدفع بنجاح",
+ "paymentSuccessMessage": "تم إرجاع نتيجة الطلب.",
+ "paymentFailTitle": "فشل الدفع",
+ "paymentFailMessage": "يرجى إعادة المحاولة أو تغيير طريقة الدفع.",
+ "userNotFound": "لم يتم العثور على معلومات المستخدم",
+ "noCountry": "لا توجد دولة مدعومة",
+ "noPackage": "لا توجد باقات شحن",
+ "paymentUnavailable": "الدفع غير متاح",
+ "orderCreated": "تم إنشاء الطلب",
+ "orderCreatedMessage": "يرجى إكمال الدفع حسب تعليمات قناة الدفع.",
+ "redirecting": "جار التحويل إلى الدفع..."
+}
diff --git a/h5/recharge/locales/en.json b/h5/recharge/locales/en.json
new file mode 100644
index 0000000..db5dc2d
--- /dev/null
+++ b/h5/recharge/locales/en.json
@@ -0,0 +1,54 @@
+{
+ "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..."
+}
diff --git a/h5/recharge/locales/id.json b/h5/recharge/locales/id.json
new file mode 100644
index 0000000..e5975f3
--- /dev/null
+++ b/h5/recharge/locales/id.json
@@ -0,0 +1,54 @@
+{
+ "documentTitle": "Pusat Isi Ulang",
+ "back": "Kembali",
+ "title": "Pusat Isi Ulang",
+ "languageButtonAria": "Ubah bahasa",
+ "authMissingTitle": "Token login tidak ditemukan",
+ "authMissingDesc": "Buka halaman ini dari aplikasi atau sertakan token di URL.",
+ "searchTitle": "Cari akun",
+ "searchGuide": "Konfirmasi akun sebelum membayar. Isi ulang yang selesai tidak dapat dikembalikan.",
+ "loadingAccount": "Memuat akun...",
+ "accountPlaceholder": "Masukkan User ID",
+ "search": "Cari",
+ "searching": "Mencari...",
+ "userId": "ID:",
+ "country": "Negara:",
+ "normalUser": "Pengguna biasa",
+ "dealer": "Dealer",
+ "superDealer": "Dealer super",
+ "lowFee": "Biaya rendah",
+ "change": "Ubah",
+ "packageTitle": "Paket isi ulang",
+ "methodTitle": "Metode pembayaran",
+ "selectPackageFirst": "Pilih paket terlebih dahulu.",
+ "selectPaymentMethod": "Pilih metode pembayaran terlebih dahulu.",
+ "createOrder": "Buat pesanan",
+ "creatingOrder": "Membuat...",
+ "usdtOrderTitle": "Pesanan USDT TRC20",
+ "trc20Address": "Alamat TRC20",
+ "copy": "Salin",
+ "copied": "Disalin",
+ "amount": "Jumlah",
+ "orderInputLabel": "Masukkan no. pesanan (alamat transfer)",
+ "orderInputPlaceholder": "Masukkan no. pesanan",
+ "queryPaymentStatus": "Cek status pembayaran",
+ "queryingStatus": "Mengecek...",
+ "paymentPending": "Pembayaran belum dikonfirmasi.",
+ "paymentStatusFailed": "Gagal mengecek status pembayaran.",
+ "loading": "Memuat...",
+ "noOptions": "Tidak ada metode isi ulang.",
+ "searchFirst": "Cari akun terlebih dahulu.",
+ "orderNo": "No. pesanan",
+ "done": "Selesai",
+ "paymentSuccessTitle": "Pembayaran berhasil",
+ "paymentSuccessMessage": "Hasil pesanan telah kembali.",
+ "paymentFailTitle": "Pembayaran gagal",
+ "paymentFailMessage": "Coba lagi atau ganti metode pembayaran.",
+ "userNotFound": "Info pengguna tidak ditemukan",
+ "noCountry": "Tidak ada negara yang didukung",
+ "noPackage": "Tidak ada paket isi ulang",
+ "paymentUnavailable": "Pembayaran tidak tersedia",
+ "orderCreated": "Pesanan dibuat",
+ "orderCreatedMessage": "Selesaikan pembayaran sesuai instruksi kanal pembayaran.",
+ "redirecting": "Mengalihkan ke pembayaran..."
+}
diff --git a/h5/recharge/locales/tr.json b/h5/recharge/locales/tr.json
new file mode 100644
index 0000000..c4d7186
--- /dev/null
+++ b/h5/recharge/locales/tr.json
@@ -0,0 +1,54 @@
+{
+ "documentTitle": "Yukleme Merkezi",
+ "back": "Geri",
+ "title": "Yukleme Merkezi",
+ "languageButtonAria": "Dili degistir",
+ "authMissingTitle": "Giris tokeni bulunamadi",
+ "authMissingDesc": "Bu sayfayi uygulamadan acin veya URL'ye token ekleyin.",
+ "searchTitle": "Hesap bul",
+ "searchGuide": "Odeme oncesi hesabi kontrol edin. Tamamlanan yuklemeler iade edilemez.",
+ "loadingAccount": "Hesap yukleniyor...",
+ "accountPlaceholder": "Kullanici ID gir",
+ "search": "Ara",
+ "searching": "Araniyor...",
+ "userId": "ID:",
+ "country": "Ulke:",
+ "normalUser": "Normal kullanici",
+ "dealer": "Bayi",
+ "superDealer": "Super bayi",
+ "lowFee": "Dusuk ucret",
+ "change": "Degistir",
+ "packageTitle": "Yukleme paketi",
+ "methodTitle": "Odeme yontemleri",
+ "selectPackageFirst": "Once bir paket secin.",
+ "selectPaymentMethod": "Once bir odeme yontemi secin.",
+ "createOrder": "Siparis olustur",
+ "creatingOrder": "Olusturuluyor...",
+ "usdtOrderTitle": "USDT TRC20 siparisi",
+ "trc20Address": "TRC20 adresi",
+ "copy": "Kopyala",
+ "copied": "Kopyalandi",
+ "amount": "Tutar",
+ "orderInputLabel": "Siparis no. girin (transfer adresi)",
+ "orderInputPlaceholder": "Siparis no. girin",
+ "queryPaymentStatus": "Odeme durumunu sorgula",
+ "queryingStatus": "Sorgulaniyor...",
+ "paymentPending": "Odeme henuz onaylanmadi.",
+ "paymentStatusFailed": "Odeme durumu sorgulanamadi.",
+ "loading": "Yukleniyor...",
+ "noOptions": "Kullanilabilir yukleme yontemi yok.",
+ "searchFirst": "Once bir hesap arayin.",
+ "orderNo": "Siparis no.",
+ "done": "Tamam",
+ "paymentSuccessTitle": "Odeme basarili",
+ "paymentSuccessMessage": "Siparis sonucu geri dondu.",
+ "paymentFailTitle": "Odeme basarisiz",
+ "paymentFailMessage": "Lutfen tekrar deneyin veya odeme yontemini degistirin.",
+ "userNotFound": "Kullanici bilgisi bulunamadi",
+ "noCountry": "Desteklenen ulke yok",
+ "noPackage": "Yukleme paketi yok",
+ "paymentUnavailable": "Odeme kullanilamiyor",
+ "orderCreated": "Siparis olusturuldu",
+ "orderCreatedMessage": "Lutfen odeme kanali talimatlarina gore odemeyi tamamlayin.",
+ "redirecting": "Odemeye yonlendiriliyor..."
+}
diff --git a/h5/recharge/script.js b/h5/recharge/script.js
new file mode 100644
index 0000000..4fcbc30
--- /dev/null
+++ b/h5/recharge/script.js
@@ -0,0 +1,1111 @@
+(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-0400`, { 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 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", channelName: "UPI" } },
+ { channel: { channelCode: "BANK", channelName: "Bank Transfer" } },
+ {
+ 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);
+ if (this.isDealer) return usdtTrc20;
+ const thirdParty = this.channels.filter((channel) => !isUsdtChannel(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");
+})();
diff --git a/h5/recharge/style.css b/h5/recharge/style.css
new file mode 100644
index 0000000..f1df8c4
--- /dev/null
+++ b/h5/recharge/style.css
@@ -0,0 +1,771 @@
+:root {
+ --bg: #f5f7fb;
+ --surface: #ffffff;
+ --surface-soft: #f9fbff;
+ --text: #20242c;
+ --muted: #737b8c;
+ --line: #e7ebf2;
+ --brand: #18c7ae;
+ --brand-dark: #089986;
+ --gold: #f5bd41;
+ --danger: #e45757;
+ --warning: #b7791f;
+ --shadow: 0 10px 28px rgba(24, 33, 54, 0.08);
+}
+
+* {
+ box-sizing: border-box;
+}
+
+html,
+body {
+ margin: 0;
+ min-height: 100%;
+ -webkit-font-smoothing: antialiased;
+ -webkit-text-size-adjust: 100%;
+}
+
+body {
+ display: flex;
+ justify-content: center;
+ background: #10131a;
+ color: var(--text);
+ font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
+}
+
+button,
+input {
+ font: inherit;
+}
+
+button {
+ border: 0;
+ cursor: pointer;
+}
+
+button:disabled {
+ cursor: not-allowed;
+ opacity: 0.58;
+}
+
+[v-cloak] {
+ display: none;
+}
+
+.page {
+ position: relative;
+ width: 100%;
+ max-width: 440px;
+ min-height: 100vh;
+ overflow-x: hidden;
+ background: var(--bg);
+}
+
+.topbar {
+ position: sticky;
+ top: 0;
+ z-index: 20;
+ display: grid;
+ grid-template-columns: 44px minmax(0, 1fr) 58px;
+ align-items: center;
+ min-height: 56px;
+ padding: 8px 14px;
+ background: rgba(255, 255, 255, 0.96);
+ box-shadow: 0 4px 14px rgba(24, 33, 54, 0.06);
+ direction: ltr;
+}
+
+.topbar h1 {
+ margin: 0;
+ overflow: hidden;
+ font-size: 18px;
+ font-weight: 850;
+ line-height: 1.2;
+ text-align: center;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.icon-button,
+.language-button {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ color: var(--text);
+ background: rgba(255, 255, 255, 0.72);
+ box-shadow: 0 4px 12px rgba(24, 33, 54, 0.07);
+}
+
+.icon-button {
+ width: 38px;
+ height: 38px;
+ border-radius: 50%;
+}
+
+.icon-button svg {
+ width: 26px;
+ height: 26px;
+ fill: none;
+ stroke: currentColor;
+ stroke-linecap: round;
+ stroke-linejoin: round;
+ stroke-width: 2.2;
+}
+
+.language-switcher {
+ position: relative;
+ justify-self: end;
+}
+
+.language-button {
+ width: 42px;
+ height: 30px;
+ border: 1px solid rgba(35, 40, 46, 0.12);
+ border-radius: 15px;
+ background: rgba(255, 255, 255, 0.72);
+ color: #25282e;
+ font-size: 13px;
+ font-weight: 900;
+}
+
+.language-menu {
+ position: absolute;
+ z-index: 30;
+ top: calc(100% + 7px);
+ right: 0;
+ display: grid;
+ gap: 2px;
+ min-width: 58px;
+ padding: 5px;
+ border: 1px solid rgba(35, 40, 46, 0.1);
+ border-radius: 8px;
+ background: rgba(255, 255, 255, 0.98);
+ box-shadow: 0 10px 26px rgba(36, 46, 60, 0.14);
+}
+
+.language-menu button {
+ min-height: 28px;
+ border-radius: 6px;
+ color: #25282e;
+ font-size: 12px;
+ font-weight: 900;
+ background: transparent;
+}
+
+.language-menu button.is-active {
+ background: #e6fbf8;
+ color: #0e8f82;
+}
+
+[dir="rtl"] .language-menu {
+ right: auto;
+ left: 0;
+}
+
+.content {
+ display: grid;
+ gap: 12px;
+ padding: 14px 14px 32px;
+}
+
+.panel,
+.notice {
+ border: 1px solid rgba(231, 235, 242, 0.86);
+ border-radius: 8px;
+ background: var(--surface);
+ box-shadow: var(--shadow);
+}
+
+.panel {
+ display: grid;
+ gap: 14px;
+ padding: 16px;
+}
+
+.notice {
+ display: grid;
+ gap: 4px;
+ padding: 12px 14px;
+ font-size: 13px;
+ line-height: 1.35;
+}
+
+.notice strong {
+ font-size: 14px;
+}
+
+.notice.warning {
+ border-color: #f7d48b;
+ background: #fff8e9;
+ color: #74510c;
+}
+
+.notice.success {
+ border-color: #9be6d9;
+ background: #ecfffb;
+ color: #087565;
+}
+
+.notice.error {
+ border-color: #f4aaaa;
+ background: #fff0f0;
+ color: #a33f3f;
+}
+
+.section-head {
+ display: flex;
+ justify-content: space-between;
+ gap: 12px;
+}
+
+.section-head h3 {
+ margin: 0;
+ font-size: 16px;
+ font-weight: 900;
+ line-height: 1.2;
+}
+
+.guide-text {
+ margin: -4px 0 0;
+ color: var(--muted);
+ font-size: 12px;
+ font-weight: 700;
+ line-height: 1.35;
+}
+
+.search-form {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) auto;
+ gap: 8px;
+}
+
+.search-form input {
+ min-width: 0;
+ height: 44px;
+ border: 1px solid var(--line);
+ border-radius: 8px;
+ padding: 0 12px;
+ outline: none;
+ background: var(--surface-soft);
+ color: var(--text);
+ font-size: 15px;
+}
+
+.search-form input:focus {
+ border-color: var(--brand);
+ box-shadow: 0 0 0 3px rgba(24, 199, 174, 0.13);
+}
+
+.search-form button,
+.primary-wide {
+ min-height: 44px;
+ border-radius: 8px;
+ padding: 0 16px;
+ background: var(--brand);
+ color: #fff;
+ font-weight: 850;
+ box-shadow: 0 8px 18px rgba(24, 199, 174, 0.22);
+}
+
+.status {
+ margin: 0;
+ color: var(--muted);
+ font-size: 13px;
+ line-height: 1.35;
+}
+
+.status.error {
+ color: var(--danger);
+}
+
+.status.success {
+ color: var(--brand-dark);
+}
+
+.account-panel {
+ grid-template-columns: 54px minmax(0, 1fr) auto;
+ align-items: center;
+}
+
+.avatar-wrap {
+ width: 54px;
+ height: 54px;
+ overflow: hidden;
+ border-radius: 50%;
+ background: linear-gradient(135deg, #e5fbf7, #fff6db);
+}
+
+.avatar-wrap img,
+.avatar-wrap span {
+ width: 100%;
+ height: 100%;
+}
+
+.avatar-wrap img {
+ display: block;
+ object-fit: cover;
+}
+
+.avatar-wrap span {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: var(--brand-dark);
+ font-size: 20px;
+ font-weight: 900;
+}
+
+.account-copy {
+ min-width: 0;
+}
+
+.role-row {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 6px;
+ margin-bottom: 5px;
+}
+
+.role-pill {
+ display: inline-flex;
+ align-items: center;
+ min-height: 22px;
+ border-radius: 999px;
+ padding: 0 8px;
+ background: #edf2f7;
+ color: #4a5568;
+ font-size: 11px;
+ font-weight: 850;
+}
+
+.role-pill.dealer {
+ background: #fff2cf;
+ color: #946000;
+}
+
+.role-pill.fee {
+ background: #e8fbf7;
+ color: var(--brand-dark);
+}
+
+.account-copy h3 {
+ margin: 0;
+ overflow: hidden;
+ font-size: 16px;
+ font-weight: 900;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.account-copy p {
+ margin: 3px 0 0;
+ overflow: hidden;
+ color: var(--muted);
+ font-size: 12px;
+ font-weight: 700;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.mini-button {
+ min-height: 34px;
+ border-radius: 8px;
+ padding: 0 10px;
+ background: #eef6f4;
+ color: var(--brand-dark);
+ font-size: 12px;
+ font-weight: 850;
+}
+
+.method-icon {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ width: 42px;
+ height: 42px;
+ overflow: hidden;
+ border-radius: 8px;
+ background: #eaf7f5;
+ color: var(--brand-dark);
+ font-weight: 900;
+}
+
+.method-icon img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+}
+
+.method-head h4 {
+ margin: 0;
+ overflow: hidden;
+ font-size: 15px;
+ font-weight: 900;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.method-badge {
+ min-height: 22px;
+ border-radius: 999px;
+ padding: 3px 8px;
+ background: #fff0c2;
+ color: #8a5c00;
+ font-size: 11px;
+ font-weight: 900;
+}
+
+.payment-head {
+ margin-top: 4px;
+}
+
+.package-grid {
+ display: grid;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ gap: 8px;
+}
+
+.package-button {
+ display: grid;
+ justify-items: center;
+ gap: 4px;
+ min-height: 106px;
+ border: 1px solid var(--line);
+ border-radius: 8px;
+ padding: 10px 8px;
+ background: #fff;
+ color: var(--text);
+}
+
+.package-button.active {
+ border-color: var(--brand);
+ background: #eafffb;
+ box-shadow: 0 0 0 3px rgba(24, 199, 174, 0.12);
+}
+
+.coin-mark {
+ width: 26px;
+ height: 26px;
+ border-radius: 50%;
+ background:
+ radial-gradient(circle at 35% 26%, #fff8d8 0 18%, transparent 20%),
+ linear-gradient(145deg, #ffd963, #f0a516);
+ box-shadow: inset 0 -2px 0 rgba(133, 85, 0, 0.16);
+}
+
+.package-button strong {
+ max-width: 100%;
+ overflow: hidden;
+ font-size: 14px;
+ line-height: 1.2;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.package-button span {
+ color: var(--brand-dark);
+ font-size: 13px;
+ font-weight: 900;
+}
+
+.package-button small {
+ max-width: 100%;
+ overflow: hidden;
+ color: #b7791f;
+ font-size: 11px;
+ font-weight: 800;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.channel-list {
+ display: grid;
+ gap: 8px;
+}
+
+.channel-button {
+ display: grid;
+ grid-template-columns: 42px minmax(0, 1fr) auto;
+ align-items: center;
+ gap: 10px;
+ width: 100%;
+ min-height: 62px;
+ border: 1px solid var(--line);
+ border-radius: 8px;
+ padding: 10px;
+ background: var(--surface-soft);
+ color: var(--text);
+ text-align: left;
+}
+
+.channel-button.usdt {
+ border-color: rgba(245, 189, 65, 0.7);
+ background: linear-gradient(180deg, #fffaf0, #fff);
+}
+
+.channel-button.active {
+ border-color: var(--brand);
+ background: #eafffb;
+ box-shadow: 0 0 0 3px rgba(24, 199, 174, 0.12);
+}
+
+.channel-button.usdt.active {
+ border-color: var(--brand);
+ background: linear-gradient(180deg, #effffc, #fffaf0);
+}
+
+.channel-copy {
+ min-width: 0;
+}
+
+.channel-copy h4 {
+ margin: 0;
+ overflow: hidden;
+ font-size: 15px;
+ font-weight: 900;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.channel-copy p {
+ margin: 3px 0 0;
+ color: var(--muted);
+ font-size: 12px;
+ line-height: 1.25;
+}
+
+.create-order-button {
+ width: 100%;
+ min-height: 50px;
+ border-radius: 8px;
+ margin-top: 2px;
+ background: var(--brand);
+ color: #fff;
+ font-size: 15px;
+ font-weight: 900;
+}
+
+.create-order-button:not(:disabled) {
+ box-shadow: 0 8px 18px rgba(24, 199, 174, 0.22);
+}
+
+[dir="rtl"] .channel-button {
+ text-align: right;
+}
+
+.empty-state {
+ min-height: 92px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border: 1px dashed #d9dee8;
+ border-radius: 8px;
+ color: var(--muted);
+ font-size: 13px;
+ font-weight: 750;
+ text-align: center;
+}
+
+.sheet-mask {
+ position: fixed;
+ inset: 0;
+ z-index: 50;
+ display: flex;
+ align-items: flex-end;
+ justify-content: center;
+ background: rgba(15, 20, 28, 0.46);
+}
+
+.usdt-mask {
+ align-items: center;
+ padding: 18px 14px;
+}
+
+.result-sheet {
+ position: relative;
+ width: min(100%, 440px);
+ display: grid;
+ justify-items: center;
+ gap: 12px;
+ padding: 24px 18px 24px;
+ border-radius: 16px 16px 0 0;
+ background: #fff;
+}
+
+.usdt-dialog {
+ position: relative;
+ width: min(100%, 400px);
+ max-height: calc(100vh - 36px);
+ overflow: auto;
+ display: grid;
+ gap: 14px;
+ border-radius: 12px;
+ padding: 20px 16px 18px;
+ background: #fff;
+ box-shadow: 0 18px 40px rgba(18, 28, 43, 0.2);
+}
+
+.sheet-close {
+ position: absolute;
+ top: 10px;
+ right: 14px;
+ color: var(--muted);
+ background: transparent;
+ font-size: 24px;
+ line-height: 1;
+}
+
+.usdt-dialog h3 {
+ margin: 0;
+ padding-inline-end: 28px;
+ font-size: 18px;
+ font-weight: 900;
+ line-height: 1.25;
+}
+
+.field-block,
+.usdt-form {
+ display: grid;
+ gap: 8px;
+}
+
+.field-block label {
+ color: var(--muted);
+ font-size: 12px;
+ font-weight: 850;
+ line-height: 1.25;
+}
+
+.copy-row {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) auto;
+ gap: 8px;
+}
+
+.address-value,
+.readonly-amount,
+.usdt-input {
+ min-width: 0;
+ min-height: 44px;
+ border: 1px solid var(--line);
+ border-radius: 8px;
+ padding: 10px 12px;
+ background: var(--surface-soft);
+ color: var(--text);
+ font-size: 13px;
+ font-weight: 800;
+}
+
+.address-value {
+ overflow-wrap: anywhere;
+ text-align: left;
+ direction: ltr;
+}
+
+.readonly-amount {
+ display: flex;
+ align-items: center;
+ color: var(--brand-dark);
+ background: #eefaf8;
+ font-size: 15px;
+ font-weight: 900;
+}
+
+.copy-button {
+ min-height: 44px;
+ border-radius: 8px;
+ padding: 0 12px;
+ background: #fff0c2;
+ color: #8a5c00;
+ font-size: 12px;
+ font-weight: 900;
+}
+
+.usdt-input {
+ width: 100%;
+ outline: none;
+ font-weight: 750;
+}
+
+.usdt-input:focus {
+ border-color: var(--brand);
+ box-shadow: 0 0 0 3px rgba(24, 199, 174, 0.13);
+}
+
+.result-icon {
+ width: 54px;
+ height: 54px;
+ border-radius: 50%;
+ background: var(--brand);
+}
+
+.result-icon.error {
+ background: var(--danger);
+}
+
+.result-icon.info {
+ background: var(--gold);
+}
+
+.result-sheet h3 {
+ margin: 0;
+ font-size: 18px;
+ font-weight: 900;
+}
+
+.result-sheet p {
+ margin: 0;
+ color: var(--muted);
+ font-size: 13px;
+ line-height: 1.45;
+ text-align: center;
+}
+
+.result-sheet dl {
+ width: 100%;
+ display: grid;
+ grid-template-columns: auto minmax(0, 1fr);
+ gap: 8px 12px;
+ margin: 0;
+ padding: 12px;
+ border-radius: 8px;
+ background: var(--surface-soft);
+ font-size: 13px;
+}
+
+.result-sheet dt {
+ color: var(--muted);
+}
+
+.result-sheet dd {
+ min-width: 0;
+ margin: 0;
+ overflow-wrap: anywhere;
+ font-weight: 850;
+}
+
+.primary-wide {
+ width: 100%;
+}
+
+.toast {
+ position: fixed;
+ left: 50%;
+ bottom: 28px;
+ z-index: 80;
+ max-width: min(360px, calc(100vw - 44px));
+ transform: translateX(-50%);
+ border-radius: 999px;
+ padding: 10px 14px;
+ background: rgba(23, 29, 38, 0.92);
+ color: #fff;
+ font-size: 13px;
+ font-weight: 750;
+ text-align: center;
+}
+
+@media (max-width: 360px) {
+ .package-grid {
+ grid-template-columns: 1fr;
+ }
+}
diff --git a/www/sitemap.xml b/www/sitemap.xml
index 3d2facf..4d83c4c 100644
--- a/www/sitemap.xml
+++ b/www/sitemap.xml
@@ -12,6 +12,7 @@
https://h5.haiyihy.com/bd-center/
https://h5.haiyihy.com/bd-leader-center/
https://h5.haiyihy.com/coin-seller/
+
https://h5.haiyihy.com/recharge/
https://h5.haiyihy.com/games-king/
https://h5.haiyihy.com/host-center/
https://h5.haiyihy.com/invite-new-user/