1045 lines
36 KiB
JavaScript
1045 lines
36 KiB
JavaScript
(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,
|
|
isSavingContact: false,
|
|
isSubmitting: false,
|
|
contactInfo: '',
|
|
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 userApi() {
|
|
if (window.HyAppAPI && window.HyAppAPI.user)
|
|
return window.HyAppAPI.user;
|
|
return window.HyApp && window.HyApp.api ? window.HyApp.api.user : 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 contactFrom(value) {
|
|
if (!value || typeof value !== 'object') return '';
|
|
var source = value.profile || value.user || value;
|
|
return String(
|
|
firstValue(source, [
|
|
'contact_info',
|
|
'contactInfo',
|
|
'whatsapp_phone',
|
|
'whatsappPhone',
|
|
'contact',
|
|
]) || ''
|
|
).trim();
|
|
}
|
|
|
|
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 needsContactForSubmit() {
|
|
return currentMode === 'exchange' || currentMode === 'transfer';
|
|
}
|
|
|
|
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 setContactStatus(message) {
|
|
var node = $('contactStatus');
|
|
node.textContent = message || '';
|
|
node.hidden = !message;
|
|
}
|
|
|
|
function setContactSaving(saving) {
|
|
state.isSavingContact = saving;
|
|
$('saveContactButton').disabled = saving;
|
|
$('saveContactButton').textContent = saving
|
|
? t('contact_card.saving', 'Saving...')
|
|
: t('contact_card.save', 'Save');
|
|
}
|
|
|
|
function openContactModal() {
|
|
$('contactInput').value = state.contactInfo || '';
|
|
setContactStatus('');
|
|
$('contactModal').hidden = false;
|
|
$('contactModal').setAttribute('aria-hidden', 'false');
|
|
document.body.classList.add('modal-open');
|
|
window.setTimeout(function () {
|
|
$('contactInput').focus();
|
|
}, 0);
|
|
}
|
|
|
|
function closeContactModal() {
|
|
$('contactModal').hidden = true;
|
|
$('contactModal').setAttribute('aria-hidden', 'true');
|
|
document.body.classList.remove('modal-open');
|
|
}
|
|
|
|
function saveContact(event) {
|
|
if (event) event.preventDefault();
|
|
var contact = String($('contactInput').value || '').trim();
|
|
if (!contact) {
|
|
setContactStatus(
|
|
t('contact_card.required', 'Enter WhatsApp contact.')
|
|
);
|
|
return;
|
|
}
|
|
var apiClient = userApi();
|
|
if (!apiClient || !apiClient.updateContact) {
|
|
setContactStatus(
|
|
t('contact_card.api_unavailable', 'Contact API unavailable.')
|
|
);
|
|
return;
|
|
}
|
|
|
|
// Exchange/Transfer 需要运营联系方式用于后台跟进;联系方式是用户主资料,保存成功后才允许继续提交工资兑换或转账请求。
|
|
setContactSaving(true);
|
|
setContactStatus('');
|
|
apiClient
|
|
.updateContact({ contact_info: contact })
|
|
.then(function (response) {
|
|
state.contactInfo = contactFrom(response) || contact;
|
|
closeContactModal();
|
|
updateSubmit();
|
|
toast(t('contact_card.saved', 'Saved.'));
|
|
})
|
|
.catch(function () {
|
|
setContactStatus(
|
|
t('contact_card.save_failed', 'Save failed.')
|
|
);
|
|
})
|
|
.finally(function () {
|
|
setContactSaving(false);
|
|
});
|
|
}
|
|
|
|
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 (needsContactForSubmit() && !state.contactInfo) {
|
|
openContactModal();
|
|
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);
|
|
bindTap('contactBackdrop', closeContactModal);
|
|
bindTap('contactCloseButton', closeContactModal);
|
|
$('contactForm').addEventListener('submit', saveContact);
|
|
}
|
|
|
|
function renderStaticDynamicText() {
|
|
$('exchangeRateText').textContent = t(
|
|
'withdraw_exchange.exchange_rate_value',
|
|
'1$ = {amount} golds'
|
|
).replace('{amount}', EXCHANGE_RATE.toLocaleString('en-US'));
|
|
if (!$('salaryHistoryModal').hidden) renderHistory();
|
|
if (!$('contactModal').hidden && !state.isSavingContact) {
|
|
$('saveContactButton').textContent = t('contact_card.save', 'Save');
|
|
}
|
|
updateSubmit();
|
|
renderPayee();
|
|
}
|
|
|
|
function init() {
|
|
identity = normalizeIdentity(identity);
|
|
bindEvents();
|
|
setMode(currentMode);
|
|
renderStaticDynamicText();
|
|
loadOverview();
|
|
loadPaymentInfo();
|
|
loadContactInfo();
|
|
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();
|
|
}
|
|
|
|
function loadContactInfo() {
|
|
if (isMock) {
|
|
state.contactInfo = String(params.get('contact') || '').trim();
|
|
updateSubmit();
|
|
return;
|
|
}
|
|
var apiClient = userApi();
|
|
if (!apiClient || !apiClient.me) {
|
|
state.contactInfo = '';
|
|
updateSubmit();
|
|
return;
|
|
}
|
|
apiClient
|
|
.me()
|
|
.then(function (profile) {
|
|
state.contactInfo = contactFrom(profile);
|
|
updateSubmit();
|
|
})
|
|
.catch(function () {
|
|
state.contactInfo = '';
|
|
updateSubmit();
|
|
});
|
|
}
|
|
|
|
document.addEventListener('DOMContentLoaded', init);
|
|
window.addEventListener('hyapp:i18n-ready', renderStaticDynamicText);
|
|
})();
|