Skip to content
Last updated

Приём депозитов для обменников

Пошаговое руководство по настройке приёма USDT от пользователей вашей платформы.

Сценарий

Вы — обменник или биржа. Ваши пользователи хотят пополнить баланс в USDT:

  1. Пользователь запрашивает адрес для пополнения
  2. Ваша система создаёт уникальный кошелёк
  3. Пользователь отправляет USDT
  4. Lumo проводит AML-проверку
  5. Вы получаете webhook о зачислении

Преимущества

  • Без блокчейн-кода — не нужно работать с TRON напрямую
  • AML из коробки — автоматическая проверка входящих транзакций
  • Автопересылка — средства собираются на вашем treasury
  • Webhooks — real-time уведомления

Шаг 1: Настройка webhook

Сначала настройте endpoint для получения уведомлений:

const crypto = require('crypto');

app.post('/webhooks/lumo', (req, res) => {
  // Верификация подписи
  const signature = req.headers['x-lumo-signature'];
  const expected = crypto
    .createHmac('sha256', process.env.WEBHOOK_SECRET)
    .update(JSON.stringify(req.body))
    .digest('hex');
    
  if (signature !== expected) {
    return res.status(401).send('Invalid signature');
  }
  
  // Обработка события
  handleWebhook(req.body);
  res.status(200).send('OK');
});

function handleWebhook(payload) {
  const { event, externalId, amount, status } = payload;
  
  switch (event) {
    case 'deposit.completed':
      creditUserBalance(externalId, amount);
      break;
    case 'deposit.aml_failed':
      notifySupport(payload);
      break;
  }
}

Обновите профиль компании:

curl -X PATCH 'https://b2b.lumowallet.io/auth/company/profile' \
  -H 'X-API-Key: YOUR_API_KEY' \
  -H 'Content-Type: application/json' \
  -d '{
    "webhookUrl": "https://your-server.com/webhooks/lumo"
  }'

Шаг 2: Создание кошелька для пользователя

Когда пользователь запрашивает адрес для пополнения:

async function getDepositAddress(userId) {
  const response = await fetch('https://b2b.lumowallet.io/user-wallets', {
    method: 'POST',
    headers: {
      'X-API-Key': process.env.LUMO_API_KEY,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      externalId: userId  // Ваш внутренний ID пользователя
    })
  });
  
  const wallet = await response.json();
  return wallet.address;
}

Ответ:

{
  "id": "550e8400-e29b-41d4-a716-446655440000",
  "companyId": "660e8400-e29b-41d4-a716-446655440001",
  "externalId": "user-12345",
  "address": "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE",
  "isBlocked": false,
  "lastUsdtBalance": 0,
  "createdAt": "2026-03-10T12:00:00Z"
}

💡 Идемпотентность: Повторный запрос с тем же externalId вернёт существующий кошелёк.

Шаг 3: Показ адреса пользователю

function DepositPage({ userId }) {
  const [address, setAddress] = useState(null);
  
  useEffect(() => {
    getDepositAddress(userId).then(setAddress);
  }, [userId]);
  
  return (
    <div>
      <h2>Пополнение баланса</h2>
      <p>Отправьте USDT (TRC-20) на адрес:</p>
      <AddressBox>{address}</AddressBox>
      <QRCode value={address} />
      <Warning>
        ⚠️ Отправляйте только USDT в сети TRON (TRC-20)
      </Warning>
    </div>
  );
}

Шаг 4: Обработка депозита

Когда пользователь отправляет USDT, вы получаете webhooks:

4.1 Депозит обнаружен

{
  "event": "deposit.created",
  "depositId": "770e8400-e29b-41d4-a716-446655440002",
  "externalId": "user-12345",
  "txHash": "abc123...",
  "walletAddress": "TQn9Y2khEsLJW1ChVWFMSMeRDow5KcbLSE",
  "amount": 100.00,
  "status": "new"
}

Действие: Показать пользователю "Депозит обнаружен, ожидает подтверждения"

4.2 AML-проверка

{
  "event": "deposit.aml_pending",
  "depositId": "770e8400-e29b-41d4-a716-446655440002",
  "externalId": "user-12345",
  "status": "aml_pending"
}

Действие: Показать "Проверка безопасности..."

4.3 Депозит завершён

{
  "event": "deposit.completed",
  "depositId": "770e8400-e29b-41d4-a716-446655440002",
  "externalId": "user-12345",
  "amount": 100.00,
  "status": "completed"
}

Действие: Зачислить средства на баланс пользователя

async function creditUserBalance(externalId, amount) {
  await db.users.update({
    where: { id: externalId },
    data: { 
      balance: { increment: amount },
      lastDepositAt: new Date()
    }
  });
  
  // Уведомить пользователя
  await notifyUser(externalId, {
    type: 'deposit_success',
    amount: amount
  });
}

Шаг 5: Обработка AML-блокировок

При aml_failed:

function handleAmlFailed(payload) {
  const { externalId, amount, depositId } = payload;
  
  // Уведомить пользователя
  notifyUser(externalId, {
    type: 'deposit_blocked',
    message: 'Депозит заблокирован для проверки'
  });
  
  // Создать тикет для поддержки
  createSupportTicket({
    type: 'aml_review',
    userId: externalId,
    depositId: depositId,
    amount: amount
  });
}

Шаг 6: Просмотр истории депозитов

async function getUserDeposits(userId) {
  const response = await fetch(
    `https://b2b.lumowallet.io/user-wallets/deposits?externalId=${userId}`,
    { headers: { 'X-API-Key': process.env.LUMO_API_KEY } }
  );
  
  return response.json();
}

Блокировка кошелька

При подозрительной активности заблокируйте кошелёк:

async function blockUserWallet(walletId, reason) {
  await fetch(`https://b2b.lumowallet.io/user-wallets/${walletId}/block`, {
    method: 'POST',
    headers: {
      'X-API-Key': process.env.LUMO_API_KEY,
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ reason })
  });
}

Замена кошелька

Если пользователь просит новый адрес:

async function replaceUserWallet(walletId) {
  const response = await fetch(
    `https://b2b.lumowallet.io/user-wallets/${walletId}/replace`,
    {
      method: 'POST',
      headers: {
        'X-API-Key': process.env.LUMO_API_KEY,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ reason: 'User requested new address' })
    }
  );
  
  const newWallet = await response.json();
  return newWallet.address;  // Новый адрес
}

Полный пример

class DepositService {
  constructor(apiKey, webhookSecret) {
    this.apiKey = apiKey;
    this.webhookSecret = webhookSecret;
    this.baseUrl = 'https://b2b.lumowallet.io';
  }

  async getOrCreateWallet(userId) {
    const response = await fetch(`${this.baseUrl}/user-wallets`, {
      method: 'POST',
      headers: {
        'X-API-Key': this.apiKey,
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ externalId: userId })
    });
    
    return response.json();
  }

  verifyWebhook(body, signature) {
    const expected = crypto
      .createHmac('sha256', this.webhookSecret)
      .update(JSON.stringify(body))
      .digest('hex');
    return signature === expected;
  }

  async handleWebhook(payload) {
    switch (payload.event) {
      case 'deposit.completed':
        await this.onDepositCompleted(payload);
        break;
      case 'deposit.aml_failed':
        await this.onAmlFailed(payload);
        break;
    }
  }

  async onDepositCompleted({ externalId, amount }) {
    // Зачисляем баланс
    await db.users.increment(externalId, 'balance', amount);
    
    // Уведомляем пользователя
    await notifications.send(externalId, {
      title: 'Депозит зачислен',
      body: `+${amount} USDT`
    });
  }

  async onAmlFailed({ externalId, depositId }) {
    await notifications.send(externalId, {
      title: 'Депозит на проверке',
      body: 'Свяжитесь с поддержкой'
    });
  }
}

Чек-лист интеграции

  • Настроен webhook endpoint
  • Webhook URL обновлён в профиле
  • Верификация подписи webhook
  • Создание кошельков для пользователей
  • UI для показа адреса + QR-код
  • Обработка deposit.completed
  • Обработка deposit.aml_failed
  • Зачисление баланса пользователю
  • Уведомления пользователю
  • Логирование всех событий