TON — не Ethereum с другим RPC. Асинхронная модель транзакций и древовидная структура сообщений ломают интуицию разработчика. Когда пользователь отправляет нативный TON на ваш адрес — это одна транзакция. Когда Jetton (USDT на TON) — цепочка из трёх: transfer → internal message → notification. Ошибка в мониторинге ведёт к потерянным платежам и головной боли с возвратами.
Недавно к нам обратился проект с 5000 ежедневных Jetton-платежей. После аудита их системы мы выявили, что они не учитывали bounced-транзакции, и 2% платежей зачислялись ошибочно. Мы перестроили архитектуру на уникальные адреса и Gasless-релей, сократив потери до нуля. Мы настраиваем приём платежей под ключ: пишите, оценим проект и подберём оптимальную архитектуру за один день.
Как принимать нативный TON и Jetton
Нативный TON
Генерируем уникальный адрес или используем один адрес с comment (memo) для идентификации. Мониторинг через TON Center API или TonAPI:
import { TonClient } from '@ton/ton'; import { Address } from '@ton/core'; const client = new TonClient({ endpoint: 'https://toncenter.com/api/v2/jsonRPC', apiKey: process.env.TONCENTER_API_KEY, }); async function checkIncomingTransactions( address: string, lastLt: string // last known logical time ) { const addr = Address.parse(address); const transactions = await client.getTransactions(addr, { limit: 20, lt: lastLt, archival: false, }); for (const tx of transactions) { // Только входящие, не bounce if (tx.inMessage && tx.inMessage.info.type === 'internal') { const info = tx.inMessage.info; const value = info.value.coins; // в nanoTON const comment = tx.inMessage.body; // текстовый комментарий // Матчим комментарий с нашим payment ID console.log(`Received: ${value} nanoTON, comment: ${comment}`); } } } Важно: проверяем bounce флаг и bounced флаг. Bounced транзакция означает возврат — не засчитываем.
Jetton (USDT, USDC, NOT)
Jetton Transfer сложнее: пользователь отправляет сообщение своему JettonWallet, который шлёт внутреннее сообщение к контракту получателя, а тот — transfer_notification на адрес получателя. В forward_ton_amount закладываем сумму для уведомления, в forward_payload — payment ID:
transfer_notification#7362d09c query_id: uint64 amount: coins // количество Jetton sender: MsgAddress // адрес отправителя forward_payload: ^Cell // наш custom payload (payment ID) Мониторим не основной адрес, а JettonWallet нашего адреса:
// Получаем адрес нашего JettonWallet для USDT async function getJettonWalletAddress( ownerAddress: string, jettonMasterAddress: string ): Promise<string> { const master = client.open( JettonMaster.create(Address.parse(jettonMasterAddress)) ); const walletAddr = await master.getWalletAddress( Address.parse(ownerAddress) ); return walletAddr.toString(); } // USDT на TON mainnet const USDT_MASTER = 'EQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs'; Что выбрать: уникальные адреса или comment?
| Характеристика | Комментарий (memo) | Уникальный адрес |
|---|---|---|
| Сложность реализации | Низкая | Средняя (HD wallet) |
| Ошибки пользователя | 1-3% забывают комментарий | 0% |
| Сводка средств | Не требуется | Требуется sweep |
| Мониторинг | Один адрес | Много адресов |
| Рекомендация | До 100 платежей/день | От 1000 платежей/день |
Comment/Memo идентификация
Один адрес, пользователь указывает comment (payment ID). Просто, но требует UX — объяснить необходимость комментария. Ошибка = потерянный платёж (нужен manual reconciliation).
Уникальный адрес на каждый платёж
Генерируем HD wallet (BIP39 + нестандартная деривация). Каждый заказ — отдельный адрес. Никаких комментариев, нет ошибок, простой мониторинг:
import { mnemonicToPrivateKey } from '@ton/crypto'; import { WalletContractV4 } from '@ton/ton'; async function derivePaymentAddress( masterMnemonic: string[], orderIndex: number ): Promise<string> { const keyPair = await mnemonicToPrivateKey(masterMnemonic); const wallet = WalletContractV4.create({ publicKey: keyPair.publicKey, workchain: 0, walletId: 698983191 + orderIndex, // уникальный subwalletId }); return wallet.address.toString({ bounceable: false }); } Минус: нужно сводить средства на основной адрес (sweep).
Polling или Webhook?
| Метод | Задержка | Нагрузка | Сложность |
|---|---|---|---|
| Polling (TON Center) | ~5-30 сек | Средняя | Низкая |
| Webhook (TON Center) | ~1-2 сек | Низкая | Средняя |
| WebSocket (TonAPI) | ~0.5 сек | Низкая | Высокая |
| Собственная нода | ~0 сек | Очень высокая | Очень высокая |
Для продакшена — TonAPI + WebSocket, с fallback через polling. Self-hosted нода оправдана при миллионах транзакций в день.
Gasless и bounce: частые проблемы
Gasless
Gasless позволяет пользователю платить без баланса TON для комиссии. Это критично для Jetton-платежей: чтобы отправить USDT, нужен TON на газ. Сервис покрывает комиссию через релей. Настраиваем релей через TON Connect или контракт-релей. Gasless повышает конверсию на 15-30% в мобильных приложениях.
Bounce
Если не фильтровать bounced транзакции, вы можете зачислить платёж, который на самом деле не дошёл. В TON bounce — нормальное явление: контракт получателя может отвергнуть сообщение. Проверяйте bounced флаг в теле сообщения. Для Jetton дополнительно отслеживайте transfer_notification — его отсутствие тоже признак неудачи.
Этапы настройки приёма платежей TON
- Анализ — оценка нагрузки, типов активов (TON, Jetton), выбор архитектуры.
- Выбор метода идентификации — comment или уникальные адреса.
- Разработка мониторинга — интеграция с TON Center / TonAPI, обработка webhook/WebSocket.
- Интеграция с бэкендом — маппинг транзакций на заказы, обработка ошибок.
- Тестирование на testnet — использование бота для раздачи тестовых TON и Sandbox от Blueprint.
- Деплой — настройка продакшен-среды, мониторинг и алерты.
Сроки и стоимость
Сроки настройки базовой схемы — от 2 до 4 недель. Стоимость рассчитывается индивидуально в зависимости от сложности: количества активов, нагрузки и необходимости Gasless-релея. Получите консультацию по архитектуре — свяжитесь для оценки проекта.
Типичные ошибки при приёме TON
- Игнорирование bounced-транзакций — зачисление несостоявшихся платежей.
- Мониторинг основного адреса вместо JettonWallet — пропуск Jetton-платежей.
- Использование только polling без fallback — потеря транзакций при высокой нагрузке.
- Отсутствие тестирования на testnet — ошибки в продакшене.
- Неучёт асинхронности — попытка синхронно ждать ответа от контракта.
Тестирование и деплой
Testnet: используем бота для раздачи тестовых TON. API endpoint https://testnet.toncenter.com/api/v2/jsonRPC. Для локальной разработки — Sandbox от Blueprint: эмулятор TVM без сети. Модель асинхронных сообщений требует особого подхода к тестированию. Закажите настройку приёма платежей TON, чтобы исключить ошибки в мониторинге и автоматизировать учёт.







