Налаштування прийому платежів у TON

Проєктуємо та розробляємо блокчейн-рішення повного циклу: від архітектури смарт-контрактів до запуску DeFi-протоколів, NFT-маркетплейсів та криптобірж. Аудит безпеки, токеноміка, інтеграція з наявною інфраструктурою.
Показано 1 з 1Усі 1306 послуг
Налаштування прийому платежів у TON
Простий
~2-3 дні
Часті запитання

Напрямки блокчейн-розробки

Етапи блокчейн-розробки

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1307
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1219
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    920
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1148
  • image_logo-advance_0.webp
    Розробка логотипу компанії B2B Advance
    611
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    885

Налаштування прийняття платежів у 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 емулятор без звернень до мережі.