feat: add StealthEX deposit integration for wallet top-up

- New depositHandler.js: wallet selection, amount picker, instruction page with StealthEX link
- Updated topUpHandler.js: shows deposit buttons per wallet + deposit via StealthEX
- Routes: deposit_select_wallet, deposit_wallet_, deposit_amount_, deposit_copy_
- Config: STEALTHEX_REF env var for optional referral
- Fixed archived wallets filter in deposit wallet query
This commit is contained in:
NW
2026-06-25 18:55:19 +01:00
parent fcd7f063c2
commit d44a15064f
6 changed files with 283 additions and 25 deletions

View File

@@ -29,6 +29,10 @@ COMMISSION_WALLET_USDT=
COMMISSION_WALLET_USDC=
COMMISSION_WALLET_ETH=
# --- StealthEX Deposit Integration ---
# Optional: your StealthEX referral ID (leave empty if none)
STEALTHEX_REF=
# --- WireGuard ---
WG_ENABLED=false
WG_PRIVATE_KEY=

View File

@@ -42,5 +42,7 @@ export default {
USDT: process.env.COMMISSION_WALLET_USDT,
USDC: process.env.COMMISSION_WALLET_USDC,
ETH: process.env.COMMISSION_WALLET_ETH
}
},
STEALTHEX_REF: process.env.STEALTHEX_REF || ''
};

View File

@@ -0,0 +1,212 @@
import db from '../../../config/database.js';
import config from '../../../config/config.js';
import UserService from '../../../services/userService.js';
import bot from '../../../context/bot.js';
import logger from '../../../utils/logger.js';
import { editOrSendCallback } from '../../../utils/messageUtils.js';
const DEPOSIT_AMOUNTS = [25, 50, 100, 250, 500];
const STEALTHEX_CRYPTO_MAP = {
BTC: 'btc',
ETH: 'eth',
LTC: 'ltc',
USDT: 'usdterc20',
USDC: 'usdcerc20'
};
const CRYPTO_SYMBOLS = {
BTC: '₿',
ETH: 'Ξ',
LTC: 'Ł',
USDT: '💲',
USDC: '💲'
};
export default class DepositHandler {
static async handleDepositSelectWallet(callbackQuery) {
const chatId = callbackQuery.message.chat.id;
const telegramId = callbackQuery.from.id;
try {
const user = await UserService.getUserByTelegramId(telegramId);
if (!user) {
await editOrSendCallback(callbackQuery, 'Profile not found. Please use /start to create one.');
return;
}
const cryptoWallets = await db.allAsync(
"SELECT wallet_type, address FROM crypto_wallets WHERE user_id = ? AND wallet_type NOT LIKE '%#_%' ESCAPE '#' ORDER BY wallet_type",
[user.id]
);
if (cryptoWallets.length === 0) {
await bot.editMessageText(
'❌ You don\'t have any wallets yet. Create one first.',
{
chat_id: chatId,
message_id: callbackQuery.message.message_id,
reply_markup: {
inline_keyboard: [
[{ text: ' Add Wallet', callback_data: 'add_wallet' }],
[{ text: '« Back', callback_data: 'back_to_balance' }]
]
}
}
);
return;
}
const walletButtons = cryptoWallets.map(w => {
const symbol = CRYPTO_SYMBOLS[w.wallet_type] || '';
return [{
text: `${symbol} ${w.wallet_type}`,
callback_data: `deposit_wallet_${w.wallet_type}`
}];
});
walletButtons.push([{ text: '« Back', callback_data: 'back_to_balance' }]);
await bot.editMessageText(
'💳 *Deposit via StealthEX*\n\nSelect the wallet you want to top up:',
{
chat_id: chatId,
message_id: callbackQuery.message.message_id,
parse_mode: 'Markdown',
reply_markup: { inline_keyboard: walletButtons }
}
);
} catch (error) {
logger.error({ err: error }, 'Error in handleDepositSelectWallet');
await editOrSendCallback(callbackQuery, 'Error loading wallets. Please try again.');
}
}
static async handleDepositSelectAmount(callbackQuery) {
const chatId = callbackQuery.message.chat.id;
const walletType = callbackQuery.data.replace('deposit_wallet_', '');
const amountButtons = DEPOSIT_AMOUNTS.map(amount => ([{
text: `$${amount}`,
callback_data: `deposit_amount_${walletType}_${amount}`
}]));
amountButtons.push([{ text: '« Back', callback_data: 'top_up_wallet' }]);
await bot.editMessageText(
`💳 *Deposit ${walletType}*\n\nSelect the amount (USD) you want to deposit:`,
{
chat_id: chatId,
message_id: callbackQuery.message.message_id,
parse_mode: 'Markdown',
reply_markup: { inline_keyboard: amountButtons }
}
);
}
static async handleDepositInstruction(callbackQuery) {
const chatId = callbackQuery.message.chat.id;
const telegramId = callbackQuery.from.id;
const parts = callbackQuery.data.replace('deposit_amount_', '').split('_');
const walletType = parts[0];
const amount = parts[1];
try {
const user = await UserService.getUserByTelegramId(telegramId);
if (!user) {
await editOrSendCallback(callbackQuery, 'Profile not found. Please use /start to create one.');
return;
}
const wallet = await db.getAsync(
'SELECT address FROM crypto_wallets WHERE user_id = ? AND wallet_type = ?',
[user.id, walletType]
);
if (!wallet) {
await editOrSendCallback(callbackQuery, 'Wallet not found. Please try again.');
return;
}
const stealthexFrom = 'eur';
const stealthexTo = STEALTHEX_CRYPTO_MAP[walletType] || walletType.toLowerCase();
const refId = config.STEALTHEX_REF;
const stealthexUrl = `https://stealthex.io/exchange/new/?amount=${amount}&from=${stealthexFrom}&to=${stealthexTo}${refId ? `&ref_id=${refId}` : ''}`;
let message = `💳 *Deposit ${walletType}$${amount}*\n\n`;
message += `📋 *Instructions:*\n`;
message += `1\\. Copy your wallet address below\n`;
message += `2\\. Click "Open StealthEX" button\n`;
message += `3\\. The exchange is pre\\-configured: EUR → ${walletType}, $${amount}\n`;
message += `4\\. Complete the payment on StealthEX\n`;
message += `5\\. Paste your wallet address as the receiving address in StealthEX\n\n`;
message += `🔐 *Your ${walletType} Wallet Address:*\n`;
message += `\`${wallet.address}\`\n\n`;
message += `⚠️ *Important:* Always double\\-check the wallet address before confirming\\.`;
const keyboard = {
inline_keyboard: [
[{ text: `🌐 Open StealthEX — EUR → ${walletType} ($${amount})`, url: stealthexUrl }],
[
{ text: '📋 Copy Address', callback_data: `deposit_copy_${walletType}` },
{ text: '🔄 Change Amount', callback_data: `deposit_wallet_${walletType}` }
],
[
{ text: '💸 Choose Different Wallet', callback_data: 'top_up_wallet' },
{ text: '« Back to Balance', callback_data: 'back_to_balance' }
]
]
};
await bot.editMessageText(message, {
chat_id: chatId,
message_id: callbackQuery.message.message_id,
parse_mode: 'MarkdownV2',
reply_markup: keyboard
});
} catch (error) {
logger.error({ err: error }, 'Error in handleDepositInstruction');
await editOrSendCallback(callbackQuery, 'Error creating deposit instructions. Please try again.');
}
}
static async handleDepositCopyAddress(callbackQuery) {
const chatId = callbackQuery.message.chat.id;
const telegramId = callbackQuery.from.id;
const walletType = callbackQuery.data.replace('deposit_copy_', '');
try {
const user = await UserService.getUserByTelegramId(telegramId);
if (!user) {
await bot.answerCallbackQuery(callbackQuery.id, { text: 'Profile not found.' });
return;
}
const wallet = await db.getAsync(
'SELECT address FROM crypto_wallets WHERE user_id = ? AND wallet_type = ?',
[user.id, walletType]
);
if (!wallet) {
await bot.answerCallbackQuery(callbackQuery.id, { text: 'Wallet not found.' });
return;
}
await bot.sendMessage(chatId, `${walletType} wallet address:\n\n\`${wallet.address}\``, {
parse_mode: 'Markdown',
reply_markup: {
inline_keyboard: [
[{ text: '« Back to Deposit', callback_data: `deposit_wallet_${walletType}` }]
]
}
});
await bot.answerCallbackQuery(callbackQuery.id, {
text: `📋 ${walletType} address sent! Copy it from the message below.`
});
} catch (error) {
logger.error({ err: error }, 'Error in handleDepositCopyAddress');
await bot.answerCallbackQuery(callbackQuery.id, { text: 'Error copying address.' });
}
}
}

View File

@@ -4,6 +4,7 @@ import RefreshHandler from './refreshHandler.js';
import CreateHandler from './createHandler.js';
import TopUpHandler from './topUpHandler.js';
import ArchiveHandler from './archiveHandler.js';
import DepositHandler from './depositHandler.js';
import WalletHelpers from './helpers.js';
export default {
@@ -16,6 +17,10 @@ export default {
handleTopUpWallet: TopUpHandler.handleTopUpWallet,
handleViewArchivedWallets: ArchiveHandler.handleViewArchivedWallets,
handleBackToBalance: BalanceHandler.handleBackToBalance,
handleDepositSelectWallet: DepositHandler.handleDepositSelectWallet,
handleDepositSelectAmount: DepositHandler.handleDepositSelectAmount,
handleDepositInstruction: DepositHandler.handleDepositInstruction,
handleDepositCopyAddress: DepositHandler.handleDepositCopyAddress,
getNetworkName: WalletHelpers.getNetworkName,
getWalletAddress: WalletHelpers.getWalletAddress,
};

View File

@@ -12,48 +12,66 @@ export default class TopUpHandler {
try {
const user = await UserService.getUserByTelegramId(telegramId);
const cryptoWallets = await db.allAsync(`
SELECT wallet_type, address FROM crypto_wallets
WHERE user_id = ? ORDER BY wallet_type
`, [user.id]);
if (cryptoWallets.length === 0) {
await bot.editMessageText('You don\'t have any wallets yet.', {
chat_id: chatId, message_id: callbackQuery.message.message_id,
reply_markup: { inline_keyboard: [[{ text: ' Add Wallet', callback_data: 'add_wallet' }]] }
});
if (!user) {
await editOrSendCallback(callbackQuery, 'Profile not found. Please use /start to create one.');
return;
}
let message = '💰 *Available wallets for replenishment.* Click on a wallet to copy the address:\n\n';
const cryptoWallets = await db.allAsync(`
SELECT wallet_type, address, balance FROM crypto_wallets
WHERE user_id = ? AND wallet_type NOT LIKE '%#_%' ESCAPE '#'
ORDER BY wallet_type
`, [user.id]);
if (cryptoWallets.length === 0) {
await bot.editMessageText('You don\'t have any wallets yet. Create one first.', {
chat_id: chatId, message_id: callbackQuery.message.message_id,
reply_markup: { inline_keyboard: [[{ text: ' Add Wallet', callback_data: 'add_wallet' }], [{ text: '« Back', callback_data: 'back_to_balance' }]] }
});
return;
}
const walletUtilsInstance = new WalletUtils(
cryptoWallets.find(w => w.wallet_type === 'BTC')?.address,
cryptoWallets.find(w => w.wallet_type === 'LTC')?.address,
cryptoWallets.find(w => w.wallet_type === 'ETH')?.address,
cryptoWallets.find(w => w.wallet_type === 'USDT')?.address,
cryptoWallets.find(w => w.wallet_type === 'USDC')?.address,
user.id,
Date.now() - 30 * 24 * 60 * 60 * 1000
);
const balances = await walletUtilsInstance.getAllBalances();
const balances = await walletUtilsInstance.getAllBalancesFromDB();
for (const [type, balance] of Object.entries(balances)) {
const wallet = cryptoWallets.find(w =>
w.wallet_type === type.split(' ')[0] ||
(type.includes('ERC-20') && w.wallet_type === 'ETH')
);
if (wallet) {
message += `🔐 *${type}*\n`;
message += `├ Balance: ${balance.amount.toFixed(8)} ${type.split(' ')[0]}\n`;
message += `├ Value: $${balance.usdValue.toFixed(2)}\n`;
message += `└ Address: \`${wallet.address}\`\n\n`;
}
let message = '💰 *Your Wallets*\n\n';
for (const wallet of cryptoWallets) {
const balanceData = balances[wallet.wallet_type];
const amount = balanceData ? balanceData.amount.toFixed(8) : '0.00000000';
const usdValue = balanceData ? balanceData.usdValue.toFixed(2) : '0.00';
message += `🔐 *${wallet.wallet_type}*\n`;
message += `├ Balance: ${amount} ${wallet.wallet_type}\n`;
message += `├ Value: $${usdValue}\n`;
message += `└ Address: \`${wallet.address}\`\n\n`;
}
const walletButtons = cryptoWallets.map(w => ([{
text: `💳 Deposit ${w.wallet_type}`,
callback_data: `deposit_wallet_${w.wallet_type}`
}]));
const keyboard = {
inline_keyboard: [
[{ text: '💳 Deposit via StealthEX', callback_data: 'deposit_select_wallet' }],
...walletButtons,
[{ text: '« Back', callback_data: 'back_to_balance' }]
]
};
await bot.editMessageText(message, {
chat_id: chatId, message_id: callbackQuery.message.message_id,
parse_mode: 'Markdown',
reply_markup: { inline_keyboard: [[{ text: '« Back', callback_data: 'back_to_balance' }]] }
reply_markup: keyboard
});
} catch (error) {
logger.error({ err: error }, 'Error in handleTopUpWallet');

View File

@@ -8,6 +8,7 @@ import userPurchaseHandler from '../handlers/userHandlers/userPurchaseHandler.js
import userLocationHandler from '../handlers/userHandlers/userLocationHandler.js';
import userProductHandler from '../handlers/userHandlers/userProductHandler.js';
import userWalletsHandler from '../handlers/userHandlers/wallet/index.js';
import DepositHandler from '../handlers/userHandlers/wallet/depositHandler.js';
import userDeletionHandler from '../handlers/userHandlers/userDeletionHandler.js';
import adminHandler from '../handlers/adminHandlers/adminHandler.js';
import adminUserLocationHandler from '../handlers/adminHandlers/adminUserLocationHandler.js';
@@ -103,6 +104,22 @@ export function registerRoutes() {
logDebug(cq.data, 'handleTopUpWallet');
await userWalletsHandler.handleTopUpWallet(cq);
});
callbackRouter.registerExact('deposit_select_wallet', async (cq) => {
logDebug(cq.data, 'handleDepositSelectWallet');
await DepositHandler.handleDepositSelectWallet(cq);
});
callbackRouter.registerPrefix('deposit_wallet_', async (cq) => {
logDebug(cq.data, 'handleDepositSelectAmount');
await DepositHandler.handleDepositSelectAmount(cq);
});
callbackRouter.registerPrefix('deposit_amount_', async (cq) => {
logDebug(cq.data, 'handleDepositInstruction');
await DepositHandler.handleDepositInstruction(cq);
});
callbackRouter.registerPrefix('deposit_copy_', async (cq) => {
logDebug(cq.data, 'handleDepositCopyAddress');
await DepositHandler.handleDepositCopyAddress(cq);
});
callbackRouter.registerExact('wallet_history', async (cq) => {
logDebug(cq.data, 'handleWalletHistory');
await userWalletsHandler.handleWalletHistory(cq);