Настройка приёма платежей в Ethereum
Приём ETH звучит просто: пользователь отправляет транзакцию, деньги пришли. На практике вопрос "как я узнаю, что платёж пришёл и от кого" решается несколькими способами, и большинство быстрых реализаций имеют race conditions или проблемы с безопасностью.
Архитектура: не делайте один адрес для всех
Наиболее надёжная схема — уникальный адрес на каждый заказ/пользователя. Это устраняет проблему атрибуции: не нужно сопоставлять суммы с заказами, не бывает коллизий когда два пользователя платят одинаковую сумму.
Реализация через HD wallet (BIP-44): один master private key, адреса деривируются детерминированно по индексу. Пользователю выдаётся адрес m/44'/60'/0'/0/{order_id}, средства с него пересылаются на горячий кошелёк после подтверждения.
import { HDNodeWallet } from 'ethers'
const masterWallet = HDNodeWallet.fromPhrase(process.env.MNEMONIC)
function getDepositAddress(orderId: number): string {
return masterWallet.deriveChild(orderId).address
}
Master mnemonic — в HSM или как минимум в зашифрованном виде в переменных окружения, никогда в коде.
Мониторинг входящих транзакций
Плохой способ — поллить eth_getBalance каждые N секунд. Медленно, дорого по RPC-запросам, пропускает транзакции при перезапуске.
Правильный способ — подписка на события через WebSocket RPC:
provider.on({ address: depositAddress }, (log) => {
// Обработка входящего перевода
})
Или через eth_subscribe newPendingTransactions — получаем уведомление до подтверждения, но статус pending нельзя считать финальным.
Для надёжного production решения — Alchemy Notify или QuickNode Streams: вебхуки при активности на адресах, работают даже если ваш backend перезапустился.
Подтверждения и finality
На Ethereum после Merge finality наступает через ~2 эпохи (~12.8 минут). Для платежей:
| Сумма | Рекомендуемые подтверждения |
|---|---|
| < $100 | 1–3 блока (~15–45 сек) |
| $100–$10k | 6–12 блоков (~1.5–2.5 мин) |
| > $10k | 32–64 блока (до finality) |
Никогда не зачисляйте средства по pending транзакции — транзакция может быть заменена через EIP-1559 replacement или вытолкнута из mempool.
Работа с ERC-20 токенами
Если нужно принимать USDC/USDT поверх ETH — логика усложняется: Transfer событие вместо нативной транзакции. Необходимо слушать логи по ABI токена:
const filter = {
address: USDC_CONTRACT,
topics: [
ethers.id('Transfer(address,address,uint256)'),
null,
ethers.zeroPadValue(depositAddress, 32)
]
}
provider.on(filter, handleUsdcDeposit)
Отдельно нужно учесть: USDT на Ethereum имеет нестандартный approve (возвращает void, а не bool) — это ломает стандартный ERC-20 интерфейс. Используйте SafeERC20 от OpenZeppelin или обрабатывайте оба случая.
Что настраивается
За 2–3 дня реализуем: генерацию уникальных deposit адресов через HD wallet, вебхук-мониторинг через Alchemy/QuickNode или self-hosted listener, логику подтверждений с настраиваемым порогом, автоматическую пересылку средств на основной кошелёк (sweeping), базовый API для интеграции с вашим бэкендом. Инфраструктура: любой EVM-совместимый бэкенд (Node.js, Python, Go).







