From d33cbbcdac6fd3d37453dd5f56a7c1ec26be2977 Mon Sep 17 00:00:00 2001 From: zhx Date: Mon, 1 Jun 2026 17:36:04 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=BB=9A=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- h5/hyapp/recharge-center/index.html | 2 +- h5/hyapp/recharge-center/style.css | 4 + h5/recharge/index.html | 224 ++++++ h5/recharge/locales/ar.json | 54 ++ h5/recharge/locales/en.json | 54 ++ h5/recharge/locales/id.json | 54 ++ h5/recharge/locales/tr.json | 54 ++ h5/recharge/script.js | 1111 +++++++++++++++++++++++++++ h5/recharge/style.css | 771 +++++++++++++++++++ www/sitemap.xml | 1 + 10 files changed, 2328 insertions(+), 1 deletion(-) create mode 100644 h5/recharge/index.html create mode 100644 h5/recharge/locales/ar.json create mode 100644 h5/recharge/locales/en.json create mode 100644 h5/recharge/locales/id.json create mode 100644 h5/recharge/locales/tr.json create mode 100644 h5/recharge/script.js create mode 100644 h5/recharge/style.css 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 @@ Recharge Center - +
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 + + + + +
+
+
+ +

{{ text.title }}

+
+ +
+ +
+
+
+ +
+
+ {{ paymentReturn.title }} + {{ paymentReturn.message }} +
+ +
+ {{ text.authMissingTitle }} + {{ text.authMissingDesc }} +
+ +
+
{{ searching ? text.loadingAccount : (statusText || text.loadingAccount) }}
+
+ +
+
+

{{ text.searchTitle }}

+
+
+ + +
+

{{ text.searchGuide }}

+

{{ statusText }}

+
+ + + +
+
+

{{ text.packageTitle }}

+
+ +
{{ text.loading }}
+ + +
+
+ +
+
+ +
+

{{ orderResult.title }}

+

{{ orderResult.message }}

+
+
{{ text.orderNo }}
+
{{ orderResult.orderNo }}
+
+ +
+
+ +
+
+ +

{{ text.usdtOrderTitle }}

+ +
+ +
+ + +
+
+ +
+ +
{{ formatPrice(selectedGoods) }}
+
+ +
+
+ + +
+

{{ usdtOrderError }}

+ +
+
+
+ +
{{ 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/