Налаштування прийняття платежів у TON
TON — не Ethereum з іншим RPC. Головна особливість, яка ламає інтуїцію: транзакції асинхронні та мають древовидну структуру. Коли користувач відправляє TON на вашу адресу, це одна транзакція. Коли відправляє Jetton (USDT на TON, наприклад) — це ланцюг із трьох сообщень: transfer → internal message → notification. Моніторинг повинен це враховувати.
Два сценарії: нативний TON та Jetton (USDT/USDC)
Приймання нативного TON
Найпростіший випадок. Генеруємо унікальну адресу або використовуємо одну адресу з comment (memo) для ідентифікації платежу — TON нативно підтримує текстові коментарі в транзакціях.
Моніторинг через 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 // останній відомий 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(`Отримано: ${value} nanoTON, коментар: ${comment}`);
}
}
}
Важливо: перевіряємо bounce флаг та bounced флаг. Bounced транзакція означає, що отримувач повернув кошти — не засчитуємо як оплату.
Приймання Jetton (USDT, USDC, NOT та ін.)
Jetton Transfer працює інакше: користувач відправляє сообщение своєму JettonWallet контракту, який шле внутрішнє сообщение до JettonWallet отримувача, а той відправляє transfer_notification на адресу отримувача.
Щоб отримувати сповіщення про Jetton перекази, в параметрах forward_ton_amount та forward_payload вкажіть суму TON для сповіщення та дані:
transfer_notification#7362d09c
query_id: uint64
amount: coins // кількість Jetton
sender: MsgAddress // адреса відправника
forward_payload: ^Cell // наш custom payload (payment ID)
Для прийняття Jetton на сервері моніторимо не основну адресу, а 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';
Унікальні адреси vs коментар-базована ідентифікація
Підхід 1: Comment/Memo ідентифікація
Одна адреса для всіх платежів, користувач вказує унікальний comment (payment ID). Просто в реалізації, але потребує обов'язкового UX — пояснити користувачу необхідність коментара. Помилка користувача = втрачений платіж (потрібен manual reconciliation).
Підхід 2: Унікальна адреса на кожний платіж
Генеруємо HD wallet адреси (TON підтримує стандартні мнемоніки BIP39 + нестандартну деривацію). Кожне замовлення — окремий адрес. Без коментарів, немає помилок користувача, простий моніторинг.
import { mnemonicToPrivateKey } from '@ton/crypto';
import { WalletContractV4 } from '@ton/ton';
async function derivePaymentAddress(
masterMnemonic: string[],
orderIndex: number
): Promise<string> {
// TON не стандартний BIP44, використовуємо різні subwalletId
const keyPair = await mnemonicToPrivateKey(masterMnemonic);
const wallet = WalletContractV4.create({
publicKey: keyPair.publicKey,
workchain: 0,
walletId: 698983191 + orderIndex, // унікальний subwalletId
});
return wallet.address.toString({ bounceable: false });
}
Мінус: потрібно періодично консолідувати кошти з дочірніх адрес на основну (consolidation sweep).
Polling vs Webhook
TON Center API підтримує webhook підписи на транзакції по адресі — це зручніше ніж polling. TonAPI (tonapi.io) — багатший API з WebSocket стрімінгом.
Для self-hosted варіанту: TON node (C++ або Go реалізація) зі своїм індексатором. Але це значно складніше — нода займає ~2TB та синхронізується кілька днів.
Для більшості проектів: TonAPI з WebSocket + polling як fallback з cursor по lt (logical time).
Підтвердження
У TON немає поняття "кількість підтвердженнь" як у Bitcoin. Транзакція або включена в блок і фінальна, або ні. TON використовує BFT-подібний консенсус — finality досягається за кілька секунд після включення в блок. Практично: достатньо дочекатися 1 блока (~5 секунд) та перевірити, що транзакція не bounce.
Тестування
TON Testnet — окрема мережа. Отримуємо Testnet TON через бот @testgiver_ton_bot. API endpoint: https://testnet.toncenter.com/api/v2/jsonRPC. Для розробки також корисна Sandbox від Blueprint — локальний TVM емулятор без звернень до мережі.







