2026-07-09 13:25:54 +08:00

1936 lines
68 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

var test_api = 'https://api-test.global-interaction.com/';
var local_api = 'http://127.0.0.1:13000/';
var default_api = 'https://api.global-interaction.com/';
(function () {
var API_ENV_KEY = 'hyapp_h5_env';
var ACCESS_TOKEN_KEY = 'hyapp_access_token';
var APP_CODE_KEY = 'hyapp_app_code';
var RENEWED_ACCESS_TOKEN_HEADER = 'X-Hyapp-Access-Token';
var LANGUAGE_QUERY_KEYS = [
'language',
'lang',
'locale',
'app_lang',
'appLanguage',
];
var memoryEnv = '';
var memoryAccessToken = '';
var memoryAppCode = '';
var activeAppConfig = null;
function currentQueryParams() {
var params = new URLSearchParams(window.location.search);
var hash = window.location.hash || '';
if (hash.indexOf('?') >= 0) {
new URLSearchParams(hash.split('?').slice(1).join('?')).forEach(
function (value, key) {
if (!params.has(key)) params.set(key, value);
}
);
}
return params;
}
function readQuery(name) {
return currentQueryParams().get(name);
}
function readLanguageQuery() {
var params = currentQueryParams();
for (var i = 0; i < LANGUAGE_QUERY_KEYS.length; i += 1) {
var value = params.get(LANGUAGE_QUERY_KEYS[i]);
if (value) return value;
}
return '';
}
function normalizeToken(value) {
var token = String(value || '').trim();
if (!token) return '';
if (/^Bearer\s+/i.test(token)) token = token.replace(/^Bearer\s+/i, '');
var match = token.match(
/[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/
);
return match ? match[0] : token;
}
function decodeBase64URLText(value) {
try {
var normalized = String(value || '')
.replace(/-/g, '+')
.replace(/_/g, '/');
while (normalized.length % 4) normalized += '=';
return window.atob(normalized);
} catch (_) {
return '';
}
}
function decodeBase64URLJSON(value) {
try {
var text = decodeBase64URLText(value);
return text ? JSON.parse(text) : null;
} catch (_) {
return null;
}
}
function numericUserID(value) {
var text = String(value || '').trim();
return /^\d{6,}$/.test(text) ? text : '';
}
function firstLongNumericID(value) {
var match = String(value || '').match(/\d{12,}/);
return match ? match[0] : '';
}
function tokenPayloadUserID(payload) {
if (!payload || typeof payload !== 'object') return '';
var keys = ['userId', 'user_id', 'uid', 'id', 'sub'];
for (var i = 0; i < keys.length; i += 1) {
var userID = numericUserID(payload[keys[i]]);
if (userID) return userID;
}
return '';
}
function tokenUserID() {
var queryUserID =
numericUserID(readQuery('userId')) ||
numericUserID(readQuery('user_id')) ||
numericUserID(readQuery('uid'));
if (queryUserID) return queryUserID;
var token = getAccessToken();
if (!token) return '';
var parts = String(token).split('.');
for (var i = 0; i < parts.length; i += 1) {
var payloadUserID = tokenPayloadUserID(
decodeBase64URLJSON(parts[i])
);
if (payloadUserID) return payloadUserID;
var textUserID = firstLongNumericID(decodeBase64URLText(parts[i]));
if (textUserID) return textUserID;
}
return '';
}
function isExpiredToken(token) {
var parts = String(token || '').split('.');
if (parts.length < 2) return false;
var payload = decodeBase64URLJSON(parts[1]);
if (!payload || !payload.exp) return false;
// JWT exp 是秒级时间戳;本地 H5 不能只凭 query 有 token 就进入 token 模式,过期 token 会被 gateway 当成未登录。
// 这里预留 5 秒偏移,避免页面发请求时刚好跨过过期点,导致 H5 隐藏账号输入但服务端收到空 display_user_id。
return Number(payload.exp) * 1000 <= Date.now() + 5000;
}
function normalizeBaseURL(value) {
return String(value || '').replace(/\/+$/, '') + '/';
}
function isLocalDevOrigin() {
var host = window.location.hostname;
return host === '127.0.0.1' || host === 'localhost' || host === '::1';
}
function shouldPersist() {
return !isLocalDevOrigin();
}
function clearLocalDevCache() {
if (shouldPersist()) return;
window.localStorage.removeItem(API_ENV_KEY);
window.localStorage.removeItem(ACCESS_TOKEN_KEY);
window.localStorage.removeItem(APP_CODE_KEY);
}
function devProxyBase(path) {
return normalizeBaseURL(window.location.origin + path);
}
function localDevProxyBase(env) {
if (!isLocalDevOrigin()) return '';
if (env === 'test') return devProxyBase('/__api_test__/');
if (env === 'local') return devProxyBase('/__api_local__/');
// 本地 H5 调试默认仍访问生产数据,但浏览器只能请求同源代理;由 scripts/h5_dev_server.py 代发到生产 API。
return devProxyBase('/__api_prod__/');
}
function resolveEnv() {
var env = readQuery('env');
if (env === 'test' || env === 'local') {
memoryEnv = env;
if (shouldPersist()) window.localStorage.setItem(API_ENV_KEY, env);
return env;
}
if (memoryEnv) return memoryEnv;
if (!shouldPersist()) return '';
return window.localStorage.getItem(API_ENV_KEY) || '';
}
function resolveBaseURL() {
var env = resolveEnv();
var configuredAPI = activeAppAPIBaseURL(env);
if (configuredAPI) return configuredAPI;
var devProxy = localDevProxyBase(env);
if (devProxy) return devProxy;
if (env === 'test') return normalizeBaseURL(test_api);
if (env === 'local') return normalizeBaseURL(local_api);
return normalizeBaseURL(default_api);
}
function activeAppAPIBaseURL(env) {
var api = activeAppConfig && activeAppConfig.api;
if (!api) return '';
if (env === 'local' && api.local) return normalizeBaseURL(api.local);
if (env === 'test' && api.test) return normalizeBaseURL(api.test);
return api.prod || api.default
? normalizeBaseURL(api.prod || api.default)
: '';
}
function buildURL(path, query) {
var url = new URL(path.replace(/^\/+/, ''), resolveBaseURL());
if (query) {
Object.keys(query).forEach(function (key) {
var value = query[key];
if (value === undefined || value === null || value === '')
return;
url.searchParams.set(key, value);
});
}
return url.toString();
}
function isSameOriginURL(url) {
try {
return (
new URL(url, window.location.href).origin ===
window.location.origin
);
} catch (_) {
return false;
}
}
function getAccessToken() {
if (memoryAccessToken && !isExpiredToken(memoryAccessToken))
return memoryAccessToken;
if (memoryAccessToken && isExpiredToken(memoryAccessToken)) {
memoryAccessToken = '';
if (shouldPersist())
window.localStorage.removeItem(ACCESS_TOKEN_KEY);
}
var queryToken =
normalizeToken(readQuery('token')) ||
normalizeToken(readQuery('access_token')) ||
normalizeToken(readQuery('accessToken'));
if (queryToken) {
if (isExpiredToken(queryToken)) {
if (shouldPersist())
window.localStorage.removeItem(ACCESS_TOKEN_KEY);
return '';
}
memoryAccessToken = queryToken;
if (shouldPersist())
window.localStorage.setItem(ACCESS_TOKEN_KEY, queryToken);
return queryToken;
}
if (!shouldPersist()) return '';
var storedToken = normalizeToken(
window.localStorage.getItem(ACCESS_TOKEN_KEY) || ''
);
if (storedToken && isExpiredToken(storedToken)) {
window.localStorage.removeItem(ACCESS_TOKEN_KEY);
return '';
}
return storedToken;
}
function themeAppCode(appCode) {
appCode = String(appCode || 'lalu')
.trim()
.toLowerCase();
return appCode === 'yumi' || appCode === 'aslan' ? appCode : 'lalu';
}
function requestAppCode() {
var queryAppCode = String(readQuery('app_code') || '')
.trim()
.toLowerCase();
// X-App-Code 只表达本次 URL 显式指定的后端租户,不能复用 getAppCode 的内存或 localStorage 结果。
// 这样没有 app_code 参数的入口会稳定落到 lalu避免历史主题缓存把后续普通页面继续打到 yumi/aslan。
return queryAppCode || 'lalu';
}
function applyAppTheme(appCode) {
if (!window.document || !window.document.documentElement) return;
window.document.documentElement.setAttribute(
'data-hy-app-code',
themeAppCode(appCode)
);
}
function peekAppCode() {
var queryAppCode =
readQuery('app') || readQuery('app_code') || readQuery('appCode');
if (queryAppCode) return queryAppCode;
if (memoryAppCode) return memoryAppCode;
if (!shouldPersist()) return 'lalu';
return window.localStorage.getItem(APP_CODE_KEY) || 'lalu';
}
function getAppCode() {
var queryAppCode =
readQuery('app') || readQuery('app_code') || readQuery('appCode');
if (queryAppCode) {
memoryAppCode = queryAppCode;
if (shouldPersist())
window.localStorage.setItem(APP_CODE_KEY, queryAppCode);
applyAppTheme(queryAppCode);
return queryAppCode;
}
if (memoryAppCode) {
applyAppTheme(memoryAppCode);
return memoryAppCode;
}
if (!shouldPersist()) {
applyAppTheme('lalu');
return 'lalu';
}
var storedAppCode = window.localStorage.getItem(APP_CODE_KEY) || 'lalu';
applyAppTheme(storedAppCode);
return storedAppCode;
}
function setAccessToken(token) {
token = normalizeToken(token);
if (token && isExpiredToken(token)) token = '';
memoryAccessToken = token || '';
if (token) {
if (shouldPersist())
window.localStorage.setItem(ACCESS_TOKEN_KEY, token);
return;
}
if (shouldPersist()) window.localStorage.removeItem(ACCESS_TOKEN_KEY);
}
function setAppCode(appCode) {
memoryAppCode = appCode || '';
applyAppTheme(appCode || 'lalu');
if (appCode) {
if (shouldPersist())
window.localStorage.setItem(APP_CODE_KEY, appCode);
return;
}
if (shouldPersist()) window.localStorage.removeItem(APP_CODE_KEY);
}
applyAppTheme(peekAppCode());
function setAppConfig(config) {
activeAppConfig = config || null;
if (activeAppConfig && activeAppConfig.app) {
setAppCode(activeAppConfig.app);
}
return activeAppConfig;
}
function getAppConfig() {
return activeAppConfig;
}
function loadAppConfig(scope) {
var appCode = String(getAppCode() || 'lalu')
.trim()
.toLowerCase();
if (!appCode || appCode === 'lalu') {
activeAppConfig = null;
return Promise.resolve(null);
}
var scopePath = String(scope || '').replace(/^\/+|\/+$/g, '');
var configPath =
(scopePath ? '/' + scopePath : '.') +
'/config/' +
appCode +
'.json';
var configURL = new URL(
configPath,
window.location.origin + window.location.pathname
);
return window
.fetch(configURL.toString(), {
cache: shouldPersist() ? 'default' : 'no-store',
})
.then(function (response) {
if (!response.ok) return null;
return response.json();
})
.then(function (config) {
return setAppConfig(config);
})
.catch(function () {
activeAppConfig = null;
return null;
});
}
function errorMessageForResponse(response, payload) {
if (response.status === 409) return 'Duplicate request.';
return payload.message || response.statusText || 'request_failed';
}
function notifyHTTPError(response, message) {
if (response.status !== 409 || !message) return;
if (window.HyAppToast && window.HyAppToast.show) {
window.HyAppToast.show(message);
return;
}
window.dispatchEvent(
new CustomEvent('hyapp:toast', {
detail: { message: message },
})
);
}
function parseResponse(response) {
return response.text().then(function (text) {
var payload = text ? JSON.parse(text) : {};
if (!response.ok) {
var message = errorMessageForResponse(response, payload);
notifyHTTPError(response, message);
var httpError = new Error(message);
httpError.status = response.status;
httpError.code = payload.code;
httpError.request_id = payload.request_id;
httpError.payload = payload;
throw httpError;
}
var code = payload && payload.code;
var errorCode = payload && payload.errorCode;
var isErrorCode =
(typeof code === 'number' && code !== 0) ||
(typeof code === 'string' && code !== '' && code !== 'OK') ||
(typeof errorCode === 'number' && errorCode !== 0) ||
(typeof errorCode === 'string' &&
errorCode !== '' &&
errorCode !== '0' &&
errorCode !== 'OK');
if (isErrorCode) {
var apiError = new Error(
payload.message ||
payload.errorMsg ||
payload.error ||
'api_request_failed'
);
apiError.code = payload.code || payload.errorCode;
apiError.request_id = payload.request_id;
apiError.payload = payload;
throw apiError;
}
return payload &&
Object.prototype.hasOwnProperty.call(payload, 'data')
? payload.data
: payload &&
Object.prototype.hasOwnProperty.call(payload, 'body')
? payload.body
: payload;
});
}
function persistRenewedAccessToken(response) {
var renewedToken =
response && response.headers
? response.headers.get(RENEWED_ACCESS_TOKEN_HEADER)
: '';
if (renewedToken) setAccessToken(renewedToken);
}
function request(path, options) {
var opts = options || {};
var headers = Object.assign(
{},
(activeAppConfig && activeAppConfig.headers) || {},
opts.headers || {}
);
var token = getAccessToken();
var url = buildURL(path, opts.query);
if (token && !opts.skipAuth) headers.Authorization = 'Bearer ' + token;
headers['X-App-Code'] = requestAppCode();
if (!shouldPersist() && isSameOriginURL(url))
headers['Cache-Control'] = 'no-cache';
var body = opts.body;
if (body && typeof body === 'object' && !(body instanceof FormData)) {
headers['Content-Type'] =
headers['Content-Type'] || 'application/json';
body = JSON.stringify(body);
}
return window
.fetch(url, {
method: opts.method || 'GET',
headers: headers,
body: body,
cache: shouldPersist() ? 'default' : 'no-store',
credentials: opts.credentials || 'omit',
})
.then(function (response) {
persistRenewedAccessToken(response);
return parseResponse(response);
});
}
var vipAPI = {
me: function () {
return request('/api/v1/vip/me', { method: 'GET' });
},
packages: function () {
return request('/api/v1/vip/packages', { method: 'GET' });
},
purchase: function (payload) {
return request('/api/v1/vip/purchase', {
method: 'POST',
body: payload || {},
});
},
};
var userAPI = {
me: function () {
return request('/api/v1/users/me', { method: 'GET' });
},
overview: function () {
return request('/api/v1/users/me/overview', { method: 'GET' });
},
profile: function (userID, options) {
var id = String(userID || '').trim();
var opts = options || {};
if (!id) return Promise.reject(new Error('user_id is required'));
return request(
'/api/v1/users/by-id/' + encodeURIComponent(id) + '/profile',
{
method: 'GET',
skipAuth: !!opts.skipAuth,
}
);
},
appearance: function (userID, options) {
var id = String(userID || '').trim();
var opts = options || {};
return request(
id
? '/api/v1/users/by-id/' +
encodeURIComponent(id) +
'/appearance'
: '/api/v1/users/me/appearance',
{ method: 'GET', skipAuth: !!opts.skipAuth }
);
},
hostIdentity: function () {
return request('/api/v1/users/me/host-identity', {
method: 'GET',
});
},
roleSummary: function () {
return request('/api/v1/users/me/role-summary', {
method: 'GET',
});
},
updateContact: function (payload) {
return request('/api/v1/users/me/contact-info', {
method: 'POST',
body: payload || {},
});
},
completeOnboarding: function (payload) {
return request('/api/v1/users/me/onboarding/complete', {
method: 'POST',
body: payload || {},
});
},
resolveDisplayUserID: function (displayUserID) {
return request(
'/api/v1/users/by-display-user-id/' +
encodeURIComponent(displayUserID || ''),
{
method: 'GET',
}
);
},
getInviteReferrer: function () {
return request('/api/v1/users/me/invite-referrer', {
method: 'GET',
});
},
searchInviteReferrer: function (inviteCode) {
return request('/api/v1/users/me/invite-referrer/search', {
method: 'GET',
query: { invite_code: inviteCode || '' },
});
},
bindInviteReferrer: function (inviteCode) {
return request('/api/v1/users/me/invite-referrer/bind', {
method: 'POST',
body: { invite_code: String(inviteCode || '') },
});
},
};
var walletAPI = {
balances: function (assetTypes) {
var value = Array.isArray(assetTypes)
? assetTypes.join(',')
: assetTypes;
return request('/api/v1/wallet/me/balances', {
method: 'GET',
query: { asset_type: value },
});
},
transactions: function (params) {
return request('/api/v1/wallet/transactions', {
method: 'GET',
query: params || {},
});
},
transferCoinFromSeller: function (payload) {
return request('/api/v1/wallet/coin-seller/transfer', {
method: 'POST',
body: payload || {},
});
},
getCoinSellerSubSellerPermission: function () {
return request(
'/api/v1/wallet/coin-seller/sub-sellers/permission',
{
method: 'GET',
}
);
},
inviteCoinSellerSubSeller: function (payload) {
return request('/api/v1/wallet/coin-seller/sub-sellers', {
method: 'POST',
body: payload || {},
});
},
listCoinSellerSubSellers: function (params) {
return request('/api/v1/wallet/coin-seller/sub-sellers', {
method: 'GET',
query: params || {},
});
},
transferCoinSellerToSubSeller: function (payload) {
return request('/api/v1/wallet/coin-seller/sub-sellers/transfer', {
method: 'POST',
body: payload || {},
});
},
};
// H5 充值 API 只做路径和参数名封装token、app_code、env、错误 envelope 仍由 request 统一处理。
// 金额、金币数、普通/币商钱包、MiFaPay 参数和 USDT 链上校验都以后端订单快照为准。
function isAslanWebPay() {
return activeAppConfig && activeAppConfig.adapter === 'aslan-web-pay';
}
function isYumiWebPay() {
return activeAppConfig && activeAppConfig.adapter === 'yumi-web-pay';
}
function isConfiguredWebPay() {
return isAslanWebPay() || isYumiWebPay();
}
function appEndpoint(name, fallback) {
return (
(activeAppConfig &&
activeAppConfig.endpoints &&
activeAppConfig.endpoints[name]) ||
fallback
);
}
function normalizeCommodityType(value) {
var type = String(value || '')
.trim()
.toUpperCase();
return type === 'FREIGHT_GOLD' || type === 'FREIGHT_GOLD_SUPER'
? type
: 'GOLD';
}
function configuredCommodityType(extra) {
return normalizeCommodityType(
(extra &&
(extra.commodity_type || extra.commodityType || extra.type)) ||
(activeAppConfig && activeAppConfig.type) ||
'GOLD'
);
}
function aslanUserQuery(displayUserID, extra) {
var value = String(displayUserID || '').trim();
var query = {
applicationId: activeAppConfig.application_id,
type: configuredCommodityType(extra),
sysOrigin: activeAppConfig.sys_origin || 'ATYOU',
};
if (value) {
query.account = value;
}
if (/^\d+$/.test(value)) {
query.userId = value;
}
return query;
}
function configuredWebPayUserQuery(displayUserID, extra) {
var query = Object.assign(aslanUserQuery(displayUserID, extra), {
sysOrigin: activeAppConfig.sys_origin || 'LIKEI',
});
if (isYumiWebPay() && !query.userId && !query.account) {
// Yumi 的 user-profile 当前只从 query 里的 userId 或 account+sysOrigin 查用户,不会从 Authorization token 反解用户。
// H5 token 入口会隐藏账号输入并传空 displayUserID因此这里仅在没有显式身份参数时把 token 里的 uid 补成 userId。
query.userId = tokenUserID();
}
return query;
}
function aslanPaymentMethods(options) {
var providers = (activeAppConfig && activeAppConfig.providers) || {};
var mifapay = providers.mifapay || {};
var countries = mifapay.countries || [];
var countryCode = String(
options.country_code || options.countryCode || ''
).toUpperCase();
var matchedCountries = countries.filter(function (country) {
return countryMatchesCode(country, countryCode);
});
var globalCountries = countries.filter(function (country) {
return countryMatchesCode(country, 'GLOBAL');
});
var matchedMethods = collectMifaPayMethods(
matchedCountries,
mifapay,
options,
false
);
var methods = matchedMethods.concat(
collectMifaPayMethods(globalCountries, mifapay, options, false)
);
if (!matchedMethods.length && countryCode) {
// 用户国家在 app JSON 里没有具体支付方式时,商品仍按用户国家查询,支付方式降级展示 JSON 中全部方式。
// 兜底方式只影响前端展示和用户选择,下单仍带当前用户国家的 payCountryId 供后端校验金额和币种。
methods = collectMifaPayMethods(countries, mifapay, options, true);
}
return methods;
}
function collectMifaPayMethods(countries, mifapay, options, fallbackAll) {
var methods = [];
(countries || []).forEach(function (country) {
var countryCode = primaryCountryCode(country);
(country.methods || []).forEach(function (method, index) {
methods.push(
Object.assign({}, method, {
method_id: [
mifapay.provider_code || 'mifapay',
countryCode,
method.pay_way,
method.pay_type,
index,
].join(':'),
provider_code: mifapay.provider_code || 'mifapay',
provider_name: mifapay.provider_name || 'MiFaPay',
country_code: countryCode,
country_name: country.country_name,
currency_code:
fallbackAll || countryMatchesCode(country, 'GLOBAL')
? options.currency_code || country.currency_code
: country.currency_code,
usd_to_currency_rate:
options.usd_to_currency_rate ||
options.usdToCurrencyRate ||
0,
pay_country_id:
options.pay_country_id ||
options.payCountryId ||
'',
status: mifapay.status || 'active',
fallback_all_countries: !!fallbackAll,
})
);
});
});
return methods;
}
function primaryCountryCode(country) {
country = country || {};
var codes = countryCodeAliases(country);
return codes[0] || '';
}
function countryMatchesCode(country, code) {
code = String(code || '')
.trim()
.toUpperCase();
if (!code) return false;
return countryCodeAliases(country).indexOf(code) >= 0;
}
function countryCodeAliases(country) {
country = country || {};
var rawCodes = []
.concat(country.country_code || [])
.concat(country.countryCode || [])
.concat(country.country_codes || [])
.concat(country.countryCodes || []);
var codes = [];
rawCodes.forEach(function (value) {
if (Array.isArray(value)) {
value.forEach(function (item) {
pushCountryCodeAlias(codes, item);
});
return;
}
pushCountryCodeAlias(codes, value);
});
return codes;
}
function pushCountryCodeAlias(codes, value) {
value = String(value || '')
.trim()
.toUpperCase();
if (value && codes.indexOf(value) < 0) {
codes.push(value);
}
}
function firstValue(object, keys) {
object = object || {};
for (var i = 0; i < keys.length; i += 1) {
var value = object[keys[i]];
if (value !== undefined && value !== null && value !== '')
return value;
}
return '';
}
function normalizeCountryCode(value) {
return String(value || '')
.trim()
.toUpperCase();
}
function countryCodeFromCountry(country) {
country = country || {};
return normalizeCountryCode(
firstValue(country, [
'alphaTwo',
'alpha_two',
'countryCode',
'country_code',
'code',
])
);
}
function countryNameFromCountry(country) {
return firstValue(country, [
'aliasName',
'enName',
'countryName',
'country_name',
'name',
]);
}
function normalizeAmountUsd(value) {
var amount = Number(value || 0);
return Number.isFinite(amount) && amount > 0 ? amount : 0;
}
function decimalToMinor(value) {
return Math.round(normalizeAmountUsd(value) * 100);
}
function booleanValue(value) {
if (value === true || value === 1) return true;
var text = String(value || '')
.trim()
.toLowerCase();
return text === 'true' || text === '1' || text === 'yes';
}
function mapYumiContext(context) {
context = context || {};
var profile = context.userProfile || context.user_profile || {};
var countryList = context.countryList || context.country_list || [];
var selectedCountry = countryList[0] || {};
var country = selectedCountry.country || {};
var countryCode =
normalizeCountryCode(profile.countryCode || profile.country_code) ||
countryCodeFromCountry(country);
var freightUser = booleanValue(
profile.freightUser ||
profile.freight_user ||
context.freightUser ||
context.freight_user
);
var commodityType = normalizeCommodityType(
profile.commodityType ||
profile.commodity_type ||
context.commodityType ||
context.commodity_type ||
(freightUser ? 'FREIGHT_GOLD' : 'GOLD')
);
freightUser = freightUser || commodityType === 'FREIGHT_GOLD';
return {
auth_mode: 'display_user_id',
account: {
id: profile.id || '',
user_id: profile.id || '',
userId: profile.id || '',
display_user_id: profile.account || profile.id || '',
username: profile.userNickname || profile.user_nickname || '',
avatar: profile.userAvatar || profile.user_avatar || '',
country_code: countryCode,
countryCode: countryCode,
country_name:
profile.countryName ||
profile.country_name ||
countryNameFromCountry(country),
countryName:
profile.countryName ||
profile.country_name ||
countryNameFromCountry(country),
audience_type: freightUser ? 'coin_seller' : 'ordinary_user',
freight_user: freightUser,
freightUser: freightUser,
commodity_type: commodityType,
commodityType: commodityType,
},
country_list: countryList.map(function (item) {
var itemCountry = item.country || {};
return {
id: item.id || '',
pay_country_id:
item.payCountryId ||
item.pay_country_id ||
item.id ||
'',
payCountryId:
item.payCountryId ||
item.pay_country_id ||
item.id ||
'',
country_id: item.countryId || item.country_id || '',
currency_code: item.currency || item.currency_code || '',
currencyCode: item.currency || item.currency_code || '',
country_code: countryCodeFromCountry(itemCountry),
countryCode: countryCodeFromCountry(itemCountry),
country_name: countryNameFromCountry(itemCountry),
countryName: countryNameFromCountry(itemCountry),
country: itemCountry,
};
}),
commodity_type: commodityType,
commodityType: commodityType,
freight_user: freightUser,
freightUser: freightUser,
region_id: context.regionId || context.region_id || '',
regionId: context.regionId || context.region_id || '',
raw: context,
};
}
function mapYumiOptions(options) {
options = options || {};
var products = (options.commodity || options.products || []).map(
function (item) {
var content = Number(item.content || 0);
var awardContent = Number(
item.awardContent || item.award_content || 0
);
var amountUsd = normalizeAmountUsd(
item.amountUsd || item.amount_usd
);
var localAmount = normalizeAmountUsd(
item.amount || item.amountLocal || item.amount_local
);
var currencyCode = item.currency || item.currency_code || '';
return {
product_id: item.id,
productId: item.id,
pay_country_id:
item.payCountryId ||
item.pay_country_id ||
options.payCountryId ||
options.pay_country_id ||
'',
coin_amount:
(Number.isFinite(content) ? content : 0) +
(Number.isFinite(awardContent) ? awardContent : 0),
base_coin_amount: Number.isFinite(content) ? content : 0,
award_coin_amount: Number.isFinite(awardContent)
? awardContent
: 0,
commodity_type:
item.type ||
item.commodityType ||
item.commodity_type ||
options.type ||
'',
commodityType:
item.type ||
item.commodityType ||
item.commodity_type ||
options.type ||
'',
amount_usdt: amountUsd,
amount_usdt_micro: Math.round(amountUsd * 1000000),
amount_minor: decimalToMinor(amountUsd),
// Yumi commodity returns both USD amount and local-currency amount; keep both so H5 display does not treat USD as EGP.
local_amount: localAmount,
localAmount: localAmount,
currency_code: currencyCode,
currencyCode: currencyCode,
raw: item,
};
}
);
var firstProduct = products[0] || {};
var countryCode = normalizeCountryCode(
options.country_code || options.countryCode || ''
);
var currencyCode =
options.currency_code ||
options.currencyCode ||
firstProduct.currency_code ||
'';
return {
products: products,
payment_methods: yumiPaymentMethods(
Object.assign({}, options, {
pay_country_id:
options.payCountryId ||
options.pay_country_id ||
firstProduct.pay_country_id ||
'',
country_code: countryCode,
currency_code: currencyCode,
usd_to_currency_rate:
options.usd_to_currency_rate ||
options.usdToCurrencyRate ||
0,
})
),
pay_country_id:
options.payCountryId ||
options.pay_country_id ||
firstProduct.pay_country_id ||
'',
country_code: countryCode,
currency_code: currencyCode,
usd_to_currency_rate:
options.usd_to_currency_rate || options.usdToCurrencyRate || 0,
usdt_trc20_enabled: false,
raw: options,
};
}
function yumiPaymentMethods(options) {
var channels =
(options.raw && options.raw.channels) || options.channels || [];
var supported = {};
channels.forEach(function (item) {
var channel = item.channel || {};
var details = item.details || {};
[
channel.channelCode,
channel.channel_code,
details.channelCode,
details.channel_code,
].forEach(function (code) {
if (code) supported[String(code)] = item;
});
});
return aslanPaymentMethods(options).filter(function (method) {
var channelCode =
method.channel_code ||
[method.pay_way, method.pay_type].filter(Boolean).join('_');
if (!Object.keys(supported).length) return true;
return !!supported[channelCode];
});
}
function mapYumiOrder(order) {
order = order || {};
var factoryCode = String(
order.factoryCode || order.factory_code || ''
).toUpperCase();
return {
order_id: order.orderId || order.order_id || '',
trade_no: order.tradeNo || order.trade_no || '',
provider_code: factoryCode === 'MIFA_PAY' ? 'mifapay' : factoryCode,
factory_code: factoryCode,
pay_url:
order.requestUrl || order.request_url || order.pay_url || '',
currency_code: order.currency || order.currency_code || '',
country_code: order.countryCode || order.country_code || '',
provider_amount_minor: decimalToMinor(order.amount),
status: 'pending',
raw: order,
};
}
function mapYumiOrderStatus(order) {
order = order || {};
var status = String(order.status || '').toUpperCase();
var normalized =
order.success === true || status === 'SUCCESS'
? 'credited'
: order.finished === true || status === 'FAIL'
? 'failed'
: 'pending';
var factoryCode = String(
order.factoryCode || order.factory_code || ''
).toUpperCase();
return {
order_id: order.orderId || order.order_id || '',
trade_no: order.tradeNo || order.trade_no || '',
provider_code: factoryCode === 'MIFA_PAY' ? 'mifapay' : factoryCode,
factory_code: factoryCode,
currency_code: order.currency || order.currency_code || '',
provider_amount_minor: decimalToMinor(order.amount),
status: normalized,
failure_reason: order.reason || '',
raw: order,
};
}
var rechargeAPI = {
context: function (displayUserID) {
if (isAslanWebPay()) {
return request(
appEndpoint('context', '/order/web/pay/h5/context'),
{
method: 'GET',
query: aslanUserQuery(displayUserID),
}
);
}
if (isYumiWebPay()) {
return request(
appEndpoint('context', '/order/web/pay/user-profile'),
{
method: 'GET',
query: configuredWebPayUserQuery(displayUserID),
}
).then(mapYumiContext);
}
return request('/api/v1/recharge/h5/context', {
method: 'GET',
query: { display_user_id: displayUserID || '' },
});
},
options: function (displayUserID, extra) {
if (isConfiguredWebPay()) {
var query = Object.assign(
aslanUserQuery(displayUserID, extra),
{
payCountryId:
extra &&
(extra.pay_country_id || extra.payCountryId),
countryCode:
extra && (extra.country_code || extra.countryCode),
regionId: extra && (extra.region_id || extra.regionId),
userId: extra && (extra.user_id || extra.userId),
}
);
return request(
appEndpoint('options', '/order/web/pay/h5/options'),
{
method: 'GET',
query: query,
}
).then(function (options) {
options = options || {};
if (isYumiWebPay()) {
return mapYumiOptions(
Object.assign({}, options, {
pay_country_id:
query.payCountryId ||
options.pay_country_id ||
options.payCountryId ||
'',
country_code:
query.countryCode || options.country_code,
})
);
}
options.payment_methods = aslanPaymentMethods(options);
options.usdt_trc20_enabled = false;
return options;
});
}
return request('/api/v1/recharge/h5/options', {
method: 'GET',
query: { display_user_id: displayUserID || '' },
});
},
createOrder: function (payload) {
if (isAslanWebPay()) {
return request(
appEndpoint('orders', '/order/web/pay/h5/orders'),
{
method: 'POST',
body: {
applicationId: activeAppConfig.application_id,
goodsId: payload && payload.product_id,
payCountryId: payload && payload.pay_country_id,
userId: payload && payload.user_id,
providerCode: payload && payload.provider_code,
payWay: payload && payload.pay_way,
payType: payload && payload.pay_type,
channelCode: payload && payload.channel_code,
returnUrl: payload && payload.return_url,
commandId: payload && payload.command_id,
type: configuredCommodityType(payload),
language: readLanguageQuery() || 'en',
},
}
);
}
if (isYumiWebPay()) {
return request(
appEndpoint('orders', '/order/web/pay/recharge'),
{
method: 'POST',
body: {
applicationId: activeAppConfig.application_id,
goodsId: payload && payload.product_id,
payCountryId: payload && payload.pay_country_id,
userId: payload && payload.user_id,
channelCode: payload && payload.channel_code,
newVersion: true,
appVersion: 'h5',
},
}
).then(mapYumiOrder);
}
return request('/api/v1/recharge/h5/orders', {
method: 'POST',
query: { language: readLanguageQuery() },
body: payload || {},
});
},
submitTx: function (orderID, payload) {
return request(
'/api/v1/recharge/h5/orders/' +
encodeURIComponent(orderID || '') +
'/tx',
{
method: 'POST',
body: payload || {},
}
);
},
getOrder: function (orderID, displayUserID) {
if (isAslanWebPay()) {
return request(
appEndpoint(
'order',
'/order/web/pay/h5/orders/{orderId}'
).replace('{orderId}', encodeURIComponent(orderID || '')),
{ method: 'GET' }
);
}
if (isYumiWebPay()) {
return request(
appEndpoint('order', '/order/web/pay/order-status'),
{
method: 'GET',
query: {
orderId: orderID || '',
userId: displayUserID || '',
},
}
).then(mapYumiOrderStatus);
}
return request(
'/api/v1/recharge/h5/orders/' +
encodeURIComponent(orderID || ''),
{
method: 'GET',
query: { display_user_id: displayUserID || '' },
}
);
},
};
var salaryWalletAPI = {
overview: function (identity) {
return request('/api/v1/salary-wallet/overview', {
method: 'GET',
query: { identity: identity },
});
},
history: function (identity, limit) {
return request('/api/v1/salary-wallet/history', {
method: 'GET',
query: {
identity: identity,
page_size: limit || 30,
},
});
},
searchCoinSeller: function (displayUserID, identity) {
return request('/api/v1/salary-wallet/coin-sellers/search', {
method: 'GET',
query: {
display_user_id: displayUserID,
identity: identity,
},
});
},
exchange: function (payload) {
return request('/api/v1/salary-wallet/exchange', {
method: 'POST',
body: payload || {},
});
},
transferToCoinSeller: function (payload) {
return request('/api/v1/salary-wallet/transfer-to-coin-seller', {
method: 'POST',
body: payload || {},
});
},
getWithdrawAddress: function () {
return request('/api/v1/salary-wallet/withdraw-address', {
method: 'GET',
});
},
saveWithdrawAddress: function (payload) {
return request('/api/v1/salary-wallet/withdraw-address', {
method: 'POST',
body: payload || {},
});
},
withdraw: function (payload) {
return request('/api/v1/salary-wallet/withdraw', {
method: 'POST',
body: payload || {},
});
},
};
var hostAPI = {
searchAgencies: function (shortId, pageSize) {
return request('/api/v1/host/agencies/search', {
method: 'GET',
query: { short_id: shortId, page_size: pageSize || 20 },
});
},
applyToAgency: function (payload) {
return request('/api/v1/host/agency-applications', {
method: 'POST',
body: payload || {},
});
},
};
var agencyCenterAPI = {
overview: function () {
return request('/api/v1/agency-center/overview', {
method: 'GET',
});
},
platformPolicy: function () {
return request('/api/v1/agency-center/platform-policy', {
method: 'GET',
});
},
hosts: function () {
return request('/api/v1/agency-center/hosts', {
method: 'GET',
});
},
applications: function (status) {
return request('/api/v1/agency-center/applications', {
method: 'GET',
query: { status: status || 'pending' },
});
},
reviewApplication: function (applicationId, payload) {
return request(
'/api/v1/agency-center/applications/' +
encodeURIComponent(applicationId || '') +
'/review',
{
method: 'POST',
body: payload || {},
}
);
},
removeHost: function (hostUserId, payload) {
return request(
'/api/v1/agency-center/hosts/' +
encodeURIComponent(hostUserId || '') +
'/remove',
{
method: 'POST',
body: payload || {},
}
);
},
inviteHost: function (payload) {
return request('/api/v1/agency-center/invitations/host', {
method: 'POST',
body: payload || {},
});
},
};
var hostCenterAPI = {
agency: function () {
return request('/api/v1/host-center/agency', {
method: 'GET',
});
},
platformPolicy: function () {
return request('/api/v1/host-center/platform-policy', {
method: 'GET',
});
},
};
var bdLeaderCenterAPI = {
overview: function () {
return request('/api/v1/bd-leader-center/overview', {
method: 'GET',
});
},
bds: function (pageSize) {
return request('/api/v1/bd-leader-center/bds', {
method: 'GET',
query: { page_size: pageSize || 20 },
});
},
agencies: function (pageSize) {
return request('/api/v1/bd-leader-center/agencies', {
method: 'GET',
query: { page_size: pageSize || 20 },
});
},
inviteBD: function (payload) {
return request('/api/v1/bd-leader/invitations/bd', {
method: 'POST',
body: payload || {},
});
},
inviteAgency: function (payload) {
return request('/api/v1/bd-leader/invitations/agency', {
method: 'POST',
body: payload || {},
});
},
};
var bdCenterAPI = {
overview: function () {
return request('/api/v1/bd-center/overview', {
method: 'GET',
});
},
agencies: function (pageSize) {
return request('/api/v1/bd-center/agencies', {
method: 'GET',
query: { page_size: pageSize || 20 },
});
},
inviteAgency: function (payload) {
return request('/api/v1/bd/invitations/agency', {
method: 'POST',
body: payload || {},
});
},
};
var roleInvitationAPI = {
list: function (status, pageSize) {
return request('/api/v1/role-invitations', {
method: 'GET',
query: {
status: status || 'pending',
page_size: pageSize || 50,
},
});
},
process: function (invitationID, payload) {
return request(
'/api/v1/role-invitations/' +
encodeURIComponent(invitationID || '') +
'/process',
{
method: 'POST',
body: payload || {},
}
);
},
};
var managerCenterAPI = {
overview: function () {
return request('/api/v1/manager-center/overview', {
method: 'GET',
});
},
searchUsers: function (keyword, pageSize, scope, action) {
return request('/api/v1/manager-center/users/search', {
method: 'GET',
query: {
keyword: keyword,
page_size: pageSize || 20,
scope: scope,
action: action,
},
});
},
listCountries: function () {
return request('/api/v1/countries', {
method: 'GET',
});
},
listResources: function (resourceType, pageSize) {
return request('/api/v1/manager-center/resource-grants/resources', {
method: 'GET',
query: {
resource_type: resourceType,
page_size: pageSize || 20,
},
});
},
grantResource: function (payload) {
return request('/api/v1/manager-center/resource-grants', {
method: 'POST',
body: payload || {},
});
},
grantVip: function (payload) {
return request('/api/v1/manager-center/vip-grants', {
method: 'POST',
body: payload || {},
});
},
listBlocks: function (status, pageSize) {
return request('/api/v1/manager-center/blocks', {
method: 'GET',
query: {
status: status || 'active',
page_size: pageSize || 50,
},
});
},
blockUser: function (payload) {
return request('/api/v1/manager-center/blocks', {
method: 'POST',
body: payload || {},
});
},
unblockUser: function (blockId, payload) {
return request(
'/api/v1/manager-center/blocks/' +
encodeURIComponent(blockId || '') +
'/unblock',
{
method: 'POST',
body: payload || {},
}
);
},
updateLevel: function (payload) {
return request('/api/v1/manager-center/levels', {
method: 'POST',
body: payload || {},
});
},
createBDLeader: function (payload) {
return request('/api/v1/manager-center/bd-leaders', {
method: 'POST',
body: payload || {},
});
},
transferUserCountry: function (payload) {
return request('/api/v1/manager-center/users/country', {
method: 'POST',
body: payload || {},
});
},
};
var superadminCenterAPI = bdLeaderCenterAPI;
var levelAPI = {
overview: function () {
return request('/api/v1/levels/me/overview', { method: 'GET' });
},
track: function (track) {
return request(
'/api/v1/levels/tracks/' + encodeURIComponent(track || ''),
{
method: 'GET',
}
);
},
rewards: function (query) {
return request('/api/v1/levels/rewards', {
method: 'GET',
query: query || {},
});
},
};
// 成就页只读当前用户的 App 成就和徽章展示资料认证、app_code、env、错误 envelope 仍由 request 统一处理。
// collection_type 由页面按 tab 传入,避免 H5 自己拼接不同业务集合导致后端筛选语义漂移。
var achievementAPI = {
list: function (query, options) {
var opts = options || {};
return request('/api/v1/achievements', {
method: 'GET',
query: query || {},
skipAuth: !!opts.skipAuth,
});
},
badges: function () {
return request('/api/v1/badges/me', { method: 'GET' });
},
setDisplay: function (payload) {
return request('/api/v1/badges/display', {
method: 'POST',
body: payload || {},
});
},
};
// 任务页 API 只封装 H5 需要的两个 App 合约登录态、app_code、env 和错误 envelope 仍由 request 统一处理。
// 领取奖励的 command_id 必须来自点击动作本身,页面脚本负责生成并在同一次重试中复用,避免重复点击造成多次发奖。
var taskAPI = {
tabs: function () {
return request('/api/v1/tasks/tabs', { method: 'GET' });
},
claim: function (payload) {
return request('/api/v1/tasks/claim', {
method: 'POST',
body: payload || {},
});
},
};
var weeklyStarAPI = {
current: function () {
return request('/api/v1/activities/weekly-star/current', {
method: 'GET',
});
},
leaderboard: function (params) {
return request('/api/v1/activities/weekly-star/leaderboard', {
method: 'GET',
query: params || {},
});
},
history: function (limit) {
return request('/api/v1/activities/weekly-star/history', {
method: 'GET',
query: { limit: limit || 10 },
});
},
};
var cpWeeklyRankAPI = {
status: function (params) {
return request('/api/v1/activities/cp-weekly-rank', {
method: 'GET',
query: params || {},
});
},
};
var cpSpaceAPI = {
relationships: function (params) {
return request('/api/v1/cp/relationships', {
method: 'GET',
query: params || {},
});
},
intimacyLeaderboard: function (params) {
return request('/api/v1/cp/intimacy-leaderboard', {
method: 'GET',
query: params || {},
});
},
breakRelationship: function (relationshipID, payload) {
return request(
'/api/v1/cp/relationships/' +
encodeURIComponent(relationshipID || '') +
'/break',
{
method: 'POST',
body: payload || {},
}
);
},
};
// 转盘 H5 只通过 gateway 活动接口读配置、抽奖、查记录和跑马灯提示;页面不再内置 mock 奖品,避免后台资源组未配置时前端仍展示假奖池。
// 抽奖 command_id 由页面点击时生成并随请求提交,服务端用它做幂等,网络重试不会重复扣费或重复发奖。
var activityWheelAPI = {
config: function (wheelID) {
return request(
'/api/v1/activity/wheel/' +
encodeURIComponent(wheelID || 'classic') +
'/config',
{ method: 'GET' }
);
},
draw: function (wheelID, payload) {
return request(
'/api/v1/activity/wheel/' +
encodeURIComponent(wheelID || 'classic') +
'/draw',
{
method: 'POST',
body: payload || {},
}
);
},
history: function (wheelID, params) {
return request(
'/api/v1/activity/wheel/' +
encodeURIComponent(wheelID || 'classic') +
'/history',
{
method: 'POST',
query: params || {},
body: {},
}
);
},
hints: function (wheelID) {
return request(
'/api/v1/activity/wheel/' +
encodeURIComponent(wheelID || 'classic') +
'/hints',
{
method: 'POST',
body: {},
}
);
},
};
// 邀请活动 H5 只关心当前用户本月状态和领取动作;有效人数、累计充值和最高档差额都以后端聚合事实为准。
// 领取时页面必须传入本次点击生成的 command_id服务端按用户、周期、档位和 command_id 做幂等发奖。
var inviteActivityRewardAPI = {
status: function () {
return request('/api/v1/activities/invite-reward', {
method: 'GET',
});
},
leaderboard: function (params) {
return request('/api/v1/activities/invite-reward/leaderboard', {
method: 'GET',
query: params || {},
});
},
claim: function (payload) {
return request('/api/v1/activities/invite-reward/claim', {
method: 'POST',
body: payload || {},
});
},
};
var diceGameAPI = {
config: function (gameID) {
return request('/api/v1/games/dice/config', {
method: 'GET',
query: { game_id: gameID || 'dice' },
});
},
match: function (payload) {
return request('/api/v1/games/dice/match', {
method: 'POST',
body: payload || {},
});
},
getMatch: function (matchID) {
return request(
'/api/v1/games/dice/matches/' +
encodeURIComponent(matchID || ''),
{ method: 'GET' }
);
},
roll: function (matchID) {
return request(
'/api/v1/games/dice/matches/' +
encodeURIComponent(matchID || '') +
'/roll',
{ method: 'POST', body: {} }
);
},
cancel: function (matchID) {
return request(
'/api/v1/games/dice/matches/' +
encodeURIComponent(matchID || '') +
'/cancel',
{ method: 'POST', body: {} }
);
},
};
var rpsGameAPI = {
config: function (gameID) {
return request('/api/v1/games/rps/config', {
method: 'GET',
query: { game_id: gameID || 'rock' },
});
},
match: function (payload) {
return request('/api/v1/games/rps/match', {
method: 'POST',
body: payload || {},
});
},
create: function (payload) {
return request('/api/v1/games/rps/matches', {
method: 'POST',
body: payload || {},
});
},
join: function (matchID, payload) {
return request(
'/api/v1/games/rps/matches/' +
encodeURIComponent(matchID || '') +
'/join',
{ method: 'POST', body: payload || {} }
);
},
getMatch: function (matchID) {
return request(
'/api/v1/games/rps/matches/' +
encodeURIComponent(matchID || ''),
{ method: 'GET' }
);
},
roll: function (matchID, payload) {
return request(
'/api/v1/games/rps/matches/' +
encodeURIComponent(matchID || '') +
'/roll',
{ method: 'POST', body: payload || {} }
);
},
cancel: function (matchID) {
return request(
'/api/v1/games/rps/matches/' +
encodeURIComponent(matchID || '') +
'/cancel',
{ method: 'POST', body: {} }
);
},
};
function analyticsEventID(prefix) {
var random = '';
if (window.crypto && window.crypto.getRandomValues) {
var bytes = new Uint32Array(2);
window.crypto.getRandomValues(bytes);
random = bytes[0].toString(36) + bytes[1].toString(36);
} else {
random = Math.random().toString(36).slice(2);
}
return (
String(prefix || 'self_game') +
'_' +
Date.now().toString(36) +
'_' +
random
);
}
function reportSelfGameEvent(payload) {
var body = Object.assign({}, payload || {});
body.event_id = body.event_id || analyticsEventID(body.event_name);
body.occurred_at_ms = body.occurred_at_ms || Date.now();
return request('/api/v1/games/self/report-events', {
method: 'POST',
body: body,
});
}
var gameAPI = {
dice: diceGameAPI,
rps: rpsGameAPI,
reportEvent: reportSelfGameEvent,
};
window.HyAppAPI = {
baseURL: resolveBaseURL,
buildURL: buildURL,
getAccessToken: getAccessToken,
getAppCode: getAppCode,
setAccessToken: setAccessToken,
setAppCode: setAppCode,
setAppConfig: setAppConfig,
getAppConfig: getAppConfig,
loadAppConfig: loadAppConfig,
request: request,
get: function (path, query) {
return request(path, { method: 'GET', query: query });
},
post: function (path, body, query) {
return request(path, { method: 'POST', body: body, query: query });
},
put: function (path, body, query) {
return request(path, { method: 'PUT', body: body, query: query });
},
patch: function (path, body, query) {
return request(path, { method: 'PATCH', body: body, query: query });
},
delete: function (path, query) {
return request(path, { method: 'DELETE', query: query });
},
vip: vipAPI,
user: userAPI,
wallet: walletAPI,
recharge: rechargeAPI,
salaryWallet: salaryWalletAPI,
host: hostAPI,
hostCenter: hostCenterAPI,
agencyCenter: agencyCenterAPI,
bdCenter: bdCenterAPI,
bdLeaderCenter: bdLeaderCenterAPI,
roleInvitations: roleInvitationAPI,
managerCenter: managerCenterAPI,
superadminCenter: superadminCenterAPI,
activityWheel: activityWheelAPI,
level: levelAPI,
achievement: achievementAPI,
task: taskAPI,
weeklyStar: weeklyStarAPI,
cpWeeklyRank: cpWeeklyRankAPI,
cpSpace: cpSpaceAPI,
inviteActivityReward: inviteActivityRewardAPI,
game: gameAPI,
};
clearLocalDevCache();
})();