(function () { var EXCHANGE_RATE = 80000; var WITHDRAW_MIN_AMOUNT = 50; var params = new URLSearchParams(window.location.search); var isMock = params.get('mock') === '1'; var currentMode = normalizeMode(params.get('mode')) || 'transfer'; var identity = String(params.get('identity') || '').trim(); var salaryAssetType = String( params.get('salaryType') || params.get('asset_type') || '' ).trim(); var state = { balance: 0, paymentInfo: null, selectedPayee: null, isSearching: false, isSaving: false, isSubmitting: false, historyItems: [], transferRateTiers: [], }; var lastSearchTriggerAt = 0; var autoSearchTimer = 0; var mockBalances = { HOST_SALARY_USD: 30650.75, AGENCY_SALARY_USD: 30650.75, BD_SALARY_USD: 48620.8, ADMIN_SALARY_USD: 168250.5, }; var mock = { balance: mockBalances[salaryAssetType] || 30650.75, paymentInfo: null, payees: { 163003: { id: 'payee_163003', name: 'Yumi Star Agency', account: '163003', avatar: '', }, 163000: { id: 'payee_163000', name: 'quick_1780052629537', account: '163000', avatar: '', }, }, history: [ { key: historyTitleKey(), title: historyTitle(), amount: 18400.25, balance: mockBalances[salaryAssetType] || 30650.75, type: 0, date: '2026-06-01 12:10', }, { key: 'withdraw_exchange.history_exchange_to_golds', title: 'Exchange to golds', amount: 800, balance: 12250.5, type: 1, date: '2026-05-30 20:42', }, { key: 'withdraw_exchange.history_gift_reward', title: 'Gift reward', amount: 8250.5, balance: 13050.5, type: 0, date: '2026-05-28 18:30', }, ], }; var inputs = { withdraw: $('withdrawAmount'), exchange: $('exchangeAmount'), transfer: $('transferAmount'), }; function $(id) { return document.getElementById(id); } function t(key, fallback) { return window.HyAppI18n && window.HyAppI18n.t ? window.HyAppI18n.t(key, fallback) : fallback || key; } function toast(message) { if (window.HyAppToast) { window.HyAppToast.show(message); return; } window.alert(message); } function normalizeMode(mode) { var value = String(mode || '').trim(); return ['withdraw', 'exchange', 'transfer'].indexOf(value) >= 0 ? value : ''; } function normalizeIdentity(value) { value = String(value || '').trim().toUpperCase().replace(/-/g, '_'); if (value === 'ADMIN') return 'BD_LEADER'; if (['HOST', 'AGENCY', 'BD', 'BD_LEADER'].indexOf(value) >= 0) return value; if (salaryAssetType === 'AGENCY_SALARY_USD') return 'AGENCY'; if (salaryAssetType === 'BD_SALARY_USD') return 'BD'; if (salaryAssetType === 'ADMIN_SALARY_USD') return 'BD_LEADER'; return 'HOST'; } function api() { if (window.HyAppAPI && window.HyAppAPI.salaryWallet) return window.HyAppAPI.salaryWallet; return window.HyApp && window.HyApp.api ? window.HyApp.api.salaryWallet : null; } function commandId(prefix) { return prefix + '-' + Date.now() + '-' + Math.random().toString(16).slice(2); } function money(value) { var number = Number(value || 0); return number.toLocaleString('en-US', { minimumFractionDigits: 2, maximumFractionDigits: 2, }); } function moneyInput(value) { var number = Number(value || 0); return Number.isFinite(number) ? number.toFixed(2) : '0.00'; } function amount() { var input = inputs[currentMode]; return Number(input && input.value ? input.value : 0) || 0; } function amountText(value) { return moneyInput(value); } function usdMinorToNumber(value) { return Number(value || 0) / 100; } function findTransferRate(value) { var minor = Math.round(Number(value || 0) * 100); return (state.transferRateTiers || []).find(function (tier) { return ( String(tier.status || 'active') === 'active' && minor >= Number(tier.min_usd_minor || 0) && minor <= Number(tier.max_usd_minor || 0) ); }); } function delay(value) { return new Promise(function (resolve) { window.setTimeout(function () { resolve(value); }, 260); }); } function mockRequest(action, payload) { if (action === 'balance') return delay(mock.balance); if (action === 'payment') return delay(mock.paymentInfo); if (action === 'history') return delay(mock.history.slice()); if (action === 'saveAddress') { mock.paymentInfo = { id: 'usdt_mock_1', cardNo: payload.cardNo, }; return delay(mock.paymentInfo); } if (action === 'searchPayee') { var account = String(payload.account || '').trim(); var payee = mock.payees[account]; if (!payee && account && account !== '000000') { payee = { id: 'payee_' + account, name: 'User ' + account, nameKey: 'withdraw_exchange.mock_user_name', account: account, avatar: '', }; } return delay(payee || null); } if (action === 'submit') { mock.balance = Math.max(0, mock.balance - payload.amount); mock.history.unshift({ title: titleForMode(payload.mode), amount: payload.amount, balance: mock.balance, type: 1, date: new Date().toISOString().replace('T', ' ').slice(0, 16), }); return delay({ ok: true }); } return delay(null); } function titleForMode(mode) { if (mode === 'withdraw') return t('withdraw_exchange.withdraw', 'Withdraw'); if (mode === 'exchange') return t('withdraw_exchange.exchange', 'Exchange'); return t('withdraw_exchange.transfer', 'Transfer'); } function historyTitleKey() { if (salaryAssetType === 'BD_SALARY_USD') return 'withdraw_exchange.history_bd_salary'; if (salaryAssetType === 'ADMIN_SALARY_USD') return 'withdraw_exchange.history_admin_salary'; if (salaryAssetType === 'AGENCY_SALARY_USD') return 'withdraw_exchange.history_agency_salary'; return 'withdraw_exchange.history_host_salary'; } function historyTitle() { if (salaryAssetType === 'BD_SALARY_USD') return 'BD salary'; if (salaryAssetType === 'ADMIN_SALARY_USD') return 'Admin salary'; if (salaryAssetType === 'AGENCY_SALARY_USD') return 'Agency salary'; return 'Host salary'; } function firstValue(source, keys) { if (!source) return ''; for (var i = 0; i < keys.length; i += 1) { var value = source[keys[i]]; if (value !== undefined && value !== null && value !== '') return value; } return ''; } function timeLabelFromMS(value) { var number = Number(value || 0); if (!Number.isFinite(number) || number <= 0) return ''; var date = new Date(number); if (Number.isNaN(date.getTime())) return ''; return ( date.getFullYear() + '-' + String(date.getMonth() + 1).padStart(2, '0') + '-' + String(date.getDate()).padStart(2, '0') + ' ' + String(date.getHours()).padStart(2, '0') + ':' + String(date.getMinutes()).padStart(2, '0') ); } function historyLabelForBizType(bizType, delta) { if (bizType === 'salary_exchange_to_coin') { return { key: 'withdraw_exchange.history_exchange_to_golds', title: 'Exchange to golds', }; } if (bizType === 'salary_transfer_to_coin_seller') { return { key: 'withdraw_exchange.transfer', title: 'Transfer', }; } if (bizType === 'host_salary_settlement' || delta >= 0) { return { key: historyTitleKey(), title: historyTitle(), }; } return { key: 'withdraw_exchange.withdraw', title: 'Withdraw', }; } function normalizeHistoryItem(item, index) { var delta = Number( firstValue(item, ['available_delta', 'availableDelta']) ); if (!Number.isFinite(delta)) delta = 0; var balance = Number( firstValue(item, ['available_after', 'availableAfter']) ); if (!Number.isFinite(balance)) balance = 0; var label = historyLabelForBizType( String(firstValue(item, ['biz_type', 'bizType']) || ''), delta ); return { id: firstValue(item, ['transaction_id', 'transactionId']) || String(firstValue(item, ['entry_id', 'entryId']) || index), key: label.key, title: label.title, amount: Math.abs(usdMinorToNumber(delta)), balance: usdMinorToNumber(balance), type: delta < 0 ? 1 : 0, date: timeLabelFromMS(firstValue(item, ['created_at_ms', 'createdAtMs'])), }; } function normalizeHistoryItems(payload) { var source = payload || {}; if (source.identity) identity = normalizeIdentity(source.identity); if (source.salary_asset_type) { salaryAssetType = source.salary_asset_type; } var items = Array.isArray(source.items) ? source.items : Array.isArray(source.transactions) ? source.transactions : Array.isArray(source) ? source : []; return items.map(function (item, index) { return normalizeHistoryItem(item || {}, index); }); } function isValidTrc20(value) { return /^T[1-9A-HJ-NP-Za-km-z]{33}$/.test(String(value || '').trim()); } function maskAddress(value) { var text = String(value || '').trim(); if (text.length <= 12) return text || '-'; return text.slice(0, 6) + '...' + text.slice(-6); } function activeSubmitLabel() { if (currentMode === 'withdraw') return t('withdraw_exchange.withdraw_now', 'Withdraw now'); if (currentMode === 'exchange') return t('withdraw_exchange.exchange_now', 'Exchange now'); return t('withdraw_exchange.transfer_now', 'Transfer now'); } function renderBalance() { $('balanceAmount').textContent = money(state.balance); Object.keys(inputs).forEach(function (key) { if (inputs[key]) inputs[key].max = moneyInput(state.balance); }); } function renderMode() { document.querySelectorAll('.mode-tab').forEach(function (tab) { var active = tab.getAttribute('data-mode') === currentMode; tab.classList.toggle('active', active); tab.setAttribute('aria-selected', String(active)); }); document.querySelectorAll('.mode-panel').forEach(function (panel) { panel.classList.toggle( 'active', panel.getAttribute('data-panel') === currentMode ); }); $('paymentSection').hidden = currentMode !== 'withdraw'; updateSubmit(); } function renderPaymentInfo() { var payment = state.paymentInfo; $('emptyAddressState').hidden = Boolean(payment); $('addressForm').hidden = Boolean(payment); $('boundAddressCard').hidden = !payment; if (payment) $('boundAddress').textContent = maskAddress(payment.cardNo); updateSubmit(); } function renderPayee() { var result = $('payeeResult'); result.textContent = ''; if (!state.selectedPayee) { result.hidden = true; updateSubmit(); return; } var payee = state.selectedPayee; var payeeName = payee.nameKey ? t(payee.nameKey, payee.name || 'User {account}').replace( '{account}', payee.account ) : payee.name; var avatar = document.createElement('div'); var copy = document.createElement('div'); var name = document.createElement('div'); var id = document.createElement('div'); avatar.className = 'payee-avatar'; copy.className = 'payee-copy'; name.className = 'payee-name'; id.className = 'payee-id'; if (payee.avatar) { var avatarImage = document.createElement('img'); avatarImage.src = payee.avatar; avatarImage.alt = ''; avatar.appendChild(avatarImage); } else { avatar.textContent = String(payeeName || 'U') .charAt(0) .toUpperCase(); } name.textContent = payeeName || '-'; id.textContent = t('withdraw_exchange.uid_prefix', 'UID:') + ' ' + payee.account; copy.appendChild(name); copy.appendChild(id); result.appendChild(avatar); result.appendChild(copy); result.hidden = false; updateSubmit(); } function renderHistory() { $('salaryHistoryTotal').textContent = '$' + money(state.balance); var list = $('salaryHistoryList'); list.textContent = ''; if (!state.historyItems.length) { var empty = document.createElement('div'); empty.className = 'salary-history-empty'; empty.textContent = t( 'withdraw_exchange.no_salary_history', 'No salary history' ); list.appendChild(empty); return; } state.historyItems.forEach(function (item) { var row = document.createElement('article'); var main = document.createElement('div'); var title = document.createElement('div'); var meta = document.createElement('div'); var value = document.createElement('div'); var isOut = Number(item.type) === 1; row.className = 'salary-history-item'; main.className = 'salary-history-item-main'; title.className = 'salary-history-item-title'; meta.className = 'salary-history-item-meta'; value.className = 'salary-history-amount' + (isOut ? ' is-out' : ''); title.textContent = item.key ? t(item.key, item.title) : item.title; meta.textContent = item.date + ' ' + t('withdraw_exchange.balance', 'Balance') + ': $' + money(item.balance); value.textContent = (isOut ? '-' : '+') + '$' + money(item.amount); main.appendChild(title); main.appendChild(meta); row.appendChild(main); row.appendChild(value); list.appendChild(row); }); } function updateSubmit() { var button = $('submitButton'); if (state.isSubmitting) { button.textContent = t( 'withdraw_exchange.submitting', 'Submitting...' ); button.disabled = true; return; } var value = amount(); var transferRateMissing = currentMode === 'transfer' && value > 0 && !findTransferRate(value); var disabled = value <= 0 || value > state.balance || (currentMode === 'withdraw' && (!state.paymentInfo || value < WITHDRAW_MIN_AMOUNT)) || (currentMode === 'transfer' && (!state.selectedPayee || transferRateMissing)); button.textContent = activeSubmitLabel(); button.disabled = disabled; } function normalizeAmount(event) { var input = event.target; var raw = String(input.value || '').replace(/[^\d.]/g, ''); var parts = raw.split('.'); var next = parts.length > 2 ? parts[0] + '.' + parts.slice(1).join('') : raw; var value = Number(next); if (Number.isFinite(value) && value > state.balance) next = moneyInput(state.balance); input.value = next; updateSubmit(); } function setMode(mode) { currentMode = normalizeMode(mode) || 'transfer'; renderMode(); } function openHistory() { $('salaryHistoryModal').hidden = false; document.body.classList.add('modal-open'); $('salaryHistoryList').textContent = t( 'withdraw_exchange.loading', 'Loading...' ); var historyRequest = isMock ? mockRequest('history') : api() && api().history ? api().history(identity, 30).then(normalizeHistoryItems) : Promise.resolve([]); historyRequest.then(function (items) { state.historyItems = items || []; renderHistory(); }).catch(function (err) { state.historyItems = []; renderHistory(); toast(err && err.message ? err.message : t('withdraw_exchange.load_failed', 'Load failed. Try again later.')); }); } function closeHistory() { $('salaryHistoryModal').hidden = true; document.body.classList.remove('modal-open'); } function saveAddress() { var address = String($('trc20Address').value || '').trim(); if (!isValidTrc20(address)) { toast( t( 'withdraw_exchange.invalid_address', 'Please enter a valid TRC20 USDT address' ) ); return; } state.isSaving = true; $('saveAddressButton').disabled = true; $('saveAddressButton').textContent = t( 'withdraw_exchange.saving', 'Saving...' ); mockRequest('saveAddress', { cardNo: address }).then( function (payment) { state.paymentInfo = payment; state.isSaving = false; $('saveAddressButton').disabled = false; $('saveAddressButton').textContent = t( 'withdraw_exchange.save_address', 'Save address' ); renderPaymentInfo(); toast(t('withdraw_exchange.address_saved', 'Address saved')); } ); } function searchPayee() { var now = Date.now(); if (now - lastSearchTriggerAt < 240) return; lastSearchTriggerAt = now; var account = String($('transferAccount').value || '').trim(); if (!account) { toast( t( 'withdraw_exchange.missing_payee', 'Please search and select a user' ) ); return; } state.selectedPayee = null; renderPayee(); if (!isMock && !api()) { toast(t('withdraw_exchange.search_failed', 'Search failed. Try again later.')); return; } state.isSearching = true; $('searchPayeeButton').disabled = true; $('searchPayeeButton').textContent = t( 'withdraw_exchange.searching', 'Searching...' ); var request = isMock ? mockRequest('searchPayee', { account: account }) : api().searchCoinSeller(account, identity).then(function (data) { var seller = data && data.seller; if (!seller) return null; return { id: seller.user_id || seller.userId, name: seller.username || seller.display_user_id || account, account: seller.display_user_id || account, avatar: seller.avatar || '', }; }); request .then(function (payee) { state.isSearching = false; $('searchPayeeButton').disabled = false; $('searchPayeeButton').textContent = t( 'withdraw_exchange.search', 'Search' ); if (!payee) { toast(t('withdraw_exchange.payee_not_found', 'User not found')); return; } state.selectedPayee = payee; renderPayee(); }) .catch(function (err) { state.isSearching = false; $('searchPayeeButton').disabled = false; $('searchPayeeButton').textContent = t( 'withdraw_exchange.search', 'Search' ); toast(err && err.message ? err.message : t('withdraw_exchange.payee_not_found', 'User not found')); }); } function validateSubmit() { var value = amount(); if (value <= 0) return t( 'withdraw_exchange.invalid_amount', 'Please enter a valid amount' ); if (value > state.balance) return t( 'withdraw_exchange.amount_exceeds_balance', 'Amount exceeds salary' ); if (currentMode === 'withdraw' && !state.paymentInfo) return t( 'withdraw_exchange.missing_payment_info', 'Please add a TRC20 USDT address' ); if (currentMode === 'withdraw' && value < WITHDRAW_MIN_AMOUNT) return t( 'withdraw_exchange.amount_too_low', 'Minimum withdrawal amount is 50 USD' ); if (currentMode === 'transfer' && !state.selectedPayee) return t( 'withdraw_exchange.missing_payee', 'Please search and select a user' ); if (currentMode === 'transfer' && !findTransferRate(value)) return t( 'withdraw_exchange.rate_not_configured', 'Exchange rate not configured.' ); return ''; } function submit() { var error = validateSubmit(); if (error) { toast(error); return; } if (!isMock && currentMode === 'withdraw') { toast( t( 'withdraw_exchange.withdraw_not_available', 'Withdraw is not available yet.' ) ); return; } state.isSubmitting = true; updateSubmit(); submitRequest() .then(function (result) { var input = inputs[currentMode]; if (input) input.value = ''; if (currentMode === 'transfer') { state.selectedPayee = null; $('transferAccount').value = ''; renderPayee(); } if (isMock) { state.balance = mock.balance; } else if (currentMode === 'exchange') { state.balance = usdMinorToNumber(result.salary_balance_after); } else if (currentMode === 'transfer') { state.balance = usdMinorToNumber( result.source_salary_balance_after ); } state.isSubmitting = false; renderBalance(); updateSubmit(); var key = currentMode === 'withdraw' ? 'withdraw_exchange.withdraw_success' : currentMode === 'exchange' ? 'withdraw_exchange.exchange_success' : 'withdraw_exchange.transfer_success'; toast(t(key, 'Submitted')); }) .catch(function (err) { state.isSubmitting = false; updateSubmit(); toast(err && err.message ? err.message : t('withdraw_exchange.submit_failed', 'Submit failed.')); }); } function submitRequest() { var value = amount(); if (isMock) { return mockRequest('submit', { mode: currentMode, amount: value, payee: state.selectedPayee, paymentInfo: state.paymentInfo, }); } if (!api()) { return Promise.reject( new Error(t('withdraw_exchange.submit_failed', 'Submit failed.')) ); } if (currentMode === 'exchange') { return api().exchange({ command_id: commandId('salary-exchange'), identity: identity, amount_usd: amountText(value), reason: 'salary exchange', }); } return api().transferToCoinSeller({ command_id: commandId('salary-transfer'), identity: identity, target_display_user_id: state.selectedPayee.account, amount_usd: amountText(value), reason: 'salary transfer', }); } function goBack() { if (window.history.length > 1) { window.history.back(); return; } var nextParams = new URLSearchParams(window.location.search); nextParams.delete('mode'); var query = nextParams.toString(); var centerPath = '../host-center/index.html'; if (identity === 'BD') centerPath = '../bd-center/index.html'; if (identity === 'BD_LEADER') centerPath = '../superadmin-center/index.html'; if (identity === 'AGENCY') centerPath = '../agency-center/index.html'; window.location.href = centerPath + (query ? '?' + query : ''); } function bindEvents() { function bindTap(id, handler) { var node = $(id); var last = 0; function run(event) { var now = Date.now(); if (now - last < 220) return; last = now; if (event && event.type === 'pointerdown') { event.preventDefault(); } handler(event); } node.addEventListener('pointerdown', run); node.addEventListener('click', run); } bindTap('backButton', goBack); document.querySelectorAll('.mode-tab').forEach(function (tab) { tab.addEventListener('click', function () { setMode(tab.getAttribute('data-mode')); }); }); Object.keys(inputs).forEach(function (key) { if (inputs[key]) inputs[key].addEventListener('input', normalizeAmount); }); bindTap('allButton', function () { inputs.withdraw.value = moneyInput(state.balance); updateSubmit(); }); $('transferAccount').addEventListener('input', function () { state.selectedPayee = null; renderPayee(); window.clearTimeout(autoSearchTimer); if (String($('transferAccount').value || '').trim().length >= 3) { autoSearchTimer = window.setTimeout(searchPayee, 260); } }); $('transferAccount').addEventListener('keydown', function (event) { if (event.key === 'Enter') searchPayee(); }); bindTap('searchPayeeButton', searchPayee); bindTap('saveAddressButton', saveAddress); bindTap('submitButton', submit); bindTap('salaryHistoryButton', openHistory); bindTap('salaryHistoryBackdrop', closeHistory); bindTap('salaryHistoryCloseButton', closeHistory); } function renderStaticDynamicText() { $('exchangeRateText').textContent = t( 'withdraw_exchange.exchange_rate_value', '1$ = {amount} golds' ).replace('{amount}', EXCHANGE_RATE.toLocaleString('en-US')); if (!$('salaryHistoryModal').hidden) renderHistory(); updateSubmit(); renderPayee(); } function init() { identity = normalizeIdentity(identity); bindEvents(); setMode(currentMode); renderStaticDynamicText(); loadOverview(); loadPaymentInfo(); if (window.HyAppBridge) { window.HyAppBridge.ready({ page: 'withdraw-exchange', mock: isMock, }); } } function loadOverview() { if (isMock) { mockRequest('balance').then(function (balance) { state.balance = balance; state.transferRateTiers = [ { min_usd_minor: 100, max_usd_minor: 1000, coin_per_usd: 90000, status: 'active', }, { min_usd_minor: 1100, max_usd_minor: 5000, coin_per_usd: 92000, status: 'active', }, ]; renderBalance(); updateSubmit(); }); return; } if (!api()) { toast(t('withdraw_exchange.load_failed', 'Load failed. Try again later.')); return; } api() .overview(identity) .then(function (data) { identity = normalizeIdentity(data.identity || identity); salaryAssetType = data.salary_asset_type || salaryAssetType; EXCHANGE_RATE = Number(data.exchange_rate || EXCHANGE_RATE); state.balance = usdMinorToNumber( data.salary && data.salary.available_amount ); state.transferRateTiers = data.transfer_rate_tiers || []; renderStaticDynamicText(); renderBalance(); updateSubmit(); }) .catch(function (err) { toast(err && err.message ? err.message : t('withdraw_exchange.load_failed', 'Load failed. Try again later.')); }); } function loadPaymentInfo() { if (isMock) { mockRequest('payment').then(function (payment) { state.paymentInfo = payment; renderPaymentInfo(); }); return; } state.paymentInfo = null; renderPaymentInfo(); } document.addEventListener('DOMContentLoaded', init); window.addEventListener('hyapp:i18n-ready', renderStaticDynamicText); })();