Перейти до основного вмісту

Обробка платежів

Ця сторінка пояснює, як обробляти** (відправляти і приймати) "цифрові активи" в блокчейні TON. В основному тут описано, як працювати з монетами TON, але теоретична частина важлива, навіть якщо ви хочете обробляти тільки джеттони.

Смарт-контракт гаманця

Смарт-контракти гаманців - це контракти в мережі TON, які слугують для того, щоб дозволити суб'єктам за межами блокчейну взаємодіяти з об'єктами блокчейну. Загалом, вони вирішують три завдання:

  • аутентифікує власника: Відмовляється обробляти та сплачувати збори за запити невласників.
  • захист від повторів: Забороняє повторне виконання одного запиту, наприклад, надсилання активів іншому смарт-контракту.
  • ініціює довільну взаємодію з іншими смарт-контрактами.

Стандартним рішенням для першої проблеми є криптографія з відкритим ключем: "гаманець" зберігає відкритий ключ і перевіряє, що вхідне повідомлення із запитом підписане відповідним закритим ключем, який відомий лише власнику.

Вирішення третьої проблеми також є поширеним: зазвичай запит містить повністю сформоване внутрішнє повідомлення, яке "гаманець" надсилає в мережу. Однак, для захисту від повторного відтворення існує декілька різних підходів.

Гаманці на основі Seqno

Гаманці на основі Seqno використовують найпростіший підхід до впорядкування повідомлень. Кожне повідомлення має спеціальне ціле число seqno, яке повинно збігатися з лічильником, що зберігається в смарт-контракті гаманця. Гаманець оновлює свій лічильник при кожному запиті, таким чином гарантуючи, що один запит не буде оброблений двічі. Існує кілька версій "гаманця", які відрізняються загальнодоступними методами: можливість обмежувати запити за часом дії та можливість мати кілька гаманців з одним і тим же публічним ключем. Однак, невід'ємною вимогою такого підходу є надсилання запитів по одному, оскільки будь-яка прогалина в послідовності seqno призведе до неможливості обробки всіх наступних запитів.

Гаманці з високим навантаженням

Цей тип "гаманця" використовує підхід, заснований на зберіганні ідентифікатора оброблених запитів, термін дії яких не закінчився, у сховищі смарт-контрактів. У цьому підході будь-який запит перевіряється на наявність дублікату вже обробленого запиту і, якщо виявлено повторення, відхиляється. Через закінчення терміну дії контракт не може зберігати всі запити вічно, але він буде видаляти ті, які не можуть бути оброблені через обмеження терміну дії. Запити до цього "гаманця" можуть надсилатися паралельно, не заважаючи один одному, але такий підхід вимагає більш складного моніторингу обробки запитів.

Розгортання гаманця

Для розгортання гаманця через TonLib потрібно зробити наступне:

  1. Згенеруйте пару приватний/відкритий ключ за допомогою createNewKey або його функцій-обгорток (приклад у tonlib-go). Зверніть увагу, що приватний ключ генерується локально і не залишає хост-машину.
  2. Сформуйте структуру InitialAccountWallet, що відповідає одному з увімкнених гаманців. Наразі доступні wallet.v3, wallet.v4, wallet.highload.v1, wallet.highload.v2.
  3. Обчисліть адресу нового смарт-контракту гаманця за допомогою методу getAccountAddress. Ми рекомендуємо використовувати ревізію за замовчуванням 0, а також розгортати гаманці в базовому ланцюжку workchain=0 для зниження витрат на обробку та зберігання.
  4. Надішліть трохи Toncoin на розраховану адресу. Зверніть увагу, що ви повинні відправити їх в режимі non-bounce, оскільки ця адреса ще не має коду і не може обробляти вхідні повідомлення. Прапорець non-bounce вказує на те, що навіть у разі невдалої обробки, гроші не повинні повертатися з повідомленням про відмову. Ми не рекомендуємо використовувати прапорець non-bounce для інших транзакцій, особливо при переказі великих сум, оскільки механізм відмов забезпечує певний захист від помилок.
  5. Сформуйте потрібну action, наприклад, actionNoop тільки для розгортання. Потім використовуйте createQuery і sendQuery, щоб ініціювати взаємодію з блокчейном.
  6. Перевірте контракт за кілька секунд за допомогою методу getAccountState.
порада

Дізнайтеся більше в [Навчальному посібнику з гаманця] (/develop/smart-contracts/tutorials/wallet#-deploying-a-wallet)

Перевірте дійсність адреси гаманця

Більшість SDK вимагають верифікації адреси (більшість перевіряє її під час створення гаманця або підготовки транзакції), тому, як правило, це не вимагає від вас ніяких додаткових складних кроків.

  const TonWeb = require("tonweb")
TonWeb.utils.Address.isValid('...')
порада

Повний опис адрес на сторінці Адреси смарт-контрактів.

Робота з переказами

Перевірте транзакції за контрактом

Транзакції контракту можна отримати за допомогою getTransactions. Цей метод дозволяє отримати 10 транзакцій, починаючи з деякого last_transaction_id і раніше. Щоб обробити всі отримані транзакції, слід виконати наступні кроки:

  1. Останній last_transaction_id можна отримати за допомогою getAddressInformation
  2. Список з 10 транзакцій повинен бути завантажений за допомогою методу getTransactions.
  3. Обробляти транзакції з не порожнім джерелом у вхідному повідомленні та призначенням, що дорівнює адресі акаунта.
  4. Завантажте наступні 10 транзакцій і повторіть кроки 2,3,4,5, поки не обробите всі вхідні транзакції.

Отримуйте вхідні/вихідні транзакції

Під час обробки транзакцій можна відстежувати потік повідомлень. Оскільки потік повідомлень є DAG, достатньо отримати поточну транзакцію методом getTransactions і знайти вхідну транзакцію за out_msg за допомогою tryLocateResultTx або вихідну за in_msg за допомогою tryLocateSourceTx.

import { TonClient, Transaction } from '@ton/ton';
import { getHttpEndpoint } from '@orbs-network/ton-access';
import { CommonMessageInfoInternal } from '@ton/core';

async function findIncomingTransaction(client: TonClient, transaction: Transaction): Promise<Transaction | null> {
const inMessage = transaction.inMessage?.info;
if (inMessage?.type !== 'internal') return null;
return client.tryLocateSourceTx(inMessage.src, inMessage.dest, inMessage.createdLt.toString());
}

async function findOutgoingTransactions(client: TonClient, transaction: Transaction): Promise<Transaction[]> {
const outMessagesInfos = transaction.outMessages.values()
.map(message => message.info)
.filter((info): info is CommonMessageInfoInternal => info.type === 'internal');

return Promise.all(
outMessagesInfos.map((info) => client.tryLocateResultTx(info.src, info.dest, info.createdLt.toString())),
);
}

async function traverseIncomingTransactions(client: TonClient, transaction: Transaction): Promise<void> {
const inTx = await findIncomingTransaction(client, transaction);
// now you can traverse this transaction graph backwards
if (!inTx) return;
await traverseIncomingTransactions(client, inTx);
}

async function traverseOutgoingTransactions(client: TonClient, transaction: Transaction): Promise<void> {
const outTxs = await findOutgoingTransactions(client, transaction);
// do smth with out txs
for (const out of outTxs) {
await traverseOutgoingTransactions(client, out);
}
}

async function main() {
const endpoint = await getHttpEndpoint({ network: 'testnet' });
const client = new TonClient({
endpoint,
apiKey: '[API-KEY]',
});

const transaction: Transaction = ...; // Obtain first transaction to start traversing
await traverseIncomingTransactions(client, transaction);
await traverseOutgoingTransactions(client, transaction);
}

main();

Надсилайте платежі

  1. Сервіс повинен розгорнути "гаманець" і підтримувати його фінансування, щоб запобігти розірванню контракту через плату за зберігання. Зауважте, що плата за зберігання зазвичай становить менше 1 тонкоїна на рік.
  2. Сервіс повинен отримувати від користувача адреса_призначення та необов'язковий коментар. Зауважте, що поки що ми рекомендуємо або заборонити незавершені вихідні платежі з однаковим набором (адреса_призначення, вартість, коментар), або правильно розпланувати ці платежі - так, щоб наступний платіж ініціювався лише після підтвердження попереднього.
  3. Сформуйте msg.dataText з comment як текстом.
  4. Сформуйте msg.message, що містить адресу_одержувача, порожній публічний_ключ, суму та msg.dataText.
  5. Форма Дія, яка містить набір вихідних повідомлень.
  6. Для відправки вихідних платежів використовуйте запити createQuery та sendQuery.
  7. Сервіс повинен регулярно опитувати метод getTransactions для контракту гаманця. Зіставлення підтверджених транзакцій з вихідними платежами за (адреса_призначення, вартість, коментар) дозволяє позначати платежі як завершені; визначати та показувати користувачеві відповідний хеш транзакції та lt (логічний час).
  8. Запити до v3 гаманців з високим навантаженням за замовчуванням мають термін дії, що дорівнює 60 секундам. Після закінчення цього часу необроблені запити можуть бути безпечно відправлені в мережу (див. кроки 3-6).

Отримати ідентифікатор транзакції

Може бути незрозуміло, що для отримання додаткової інформації про транзакцію користувач повинен просканувати блокчейн за допомогою функції getTransactions. Неможливо отримати ідентифікатор транзакції одразу після відправлення повідомлення, оскільки транзакція повинна бути підтверджена мережею блокчейн. Щоб зрозуміти необхідний конвеєр, уважно прочитайте Send payments, особливо 7-й пункт.

Підхід на основі рахунків-фактур

Щоб приймати платежі на основі прикріплених коментарів, сервіс повинен

  1. Розгорніть контракт "гаманця".
  2. Згенеруйте унікальний invoice для кожного користувача. Рядкового представлення uuid32 буде достатньо.
  3. Користувачам слід проінструктувати, щоб вони надсилали Toncoin на контракт "гаманця" сервісу з прикріпленим "рахунком-фактурою" в якості коментаря.
  4. Сервіс повинен регулярно опитувати метод getTransactions для контракту гаманця.
  5. Для нових транзакцій вхідне повідомлення має бути витягнуто, коментар перевірено за базою даних, а значення вхідного повідомлення занесено до облікового запису користувача.

Щоб обчислити значення вхідного повідомлення, яке повідомлення приносить контракту, потрібно розібрати транзакцію. Це відбувається, коли повідомлення потрапляє в контракт. Транзакцію можна отримати за допомогою getTransactions. Для вхідної транзакції гаманця правильні дані складаються з одного вхідного повідомлення і нуля вихідних повідомлень. В іншому випадку, або на гаманець надсилається зовнішнє повідомлення, і тоді власник витрачає Toncoin, або гаманець не розгортається і вхідна транзакція повертається назад.

Так чи інакше, в загальному випадку суму, яку приносить повідомлення в контракт, можна розрахувати як вартість вхідного повідомлення мінус сума вартостей вихідних повідомлень мінус комісія: value_{in_msg} - SUM(value_{out_msg}) - fee. Технічно представлення транзакції містить три різних поля з fee в назві: fee, storage_fee та other_fee, тобто загальну комісію, частину комісії, пов'язану з витратами на зберігання, та частину комісії, пов'язану з обробкою транзакції. Використовувати слід тільки першу з них.

Інвойси з TON Connect

Найкраще підходить для dApps, яким потрібно підписувати кілька платежів/транзакцій протягом сесії або підтримувати з'єднання з гаманцем протягом певного часу.

  • ✅ Є постійний канал зв'язку з гаманцем, інформація про адресу користувача

  • ✅ Користувачам потрібно відсканувати QR-код лише один раз

  • ✅ Можна дізнатися, чи підтвердив користувач транзакцію в гаманці, відстежити транзакцію за поверненою BOC

  • ✅ Готові SDK та набори інтерфейсів доступні для різних платформ

  • ❌ Якщо потрібно відправити лише один платіж, користувачеві потрібно виконати дві дії: підключити гаманець і підтвердити транзакцію

  • ❌ Інтеграція складніша, ніж посилання ton://

Дізнатися більше

Інвойси з посиланням ton://

небезпека

Посилання Ton застаріле, не використовуйте його

Якщо вам потрібна легка інтеграція для простого потоку користувачів, підійде посилання ton://. Найкраще підходить для одноразових платежів та інвойсів.

ton://transfer/<destination-address>?
[nft=<nft-address>&]
[fee-amount=<nanocoins>&]
[forward-amount=<nanocoins>]
  • ✅ Легка інтеграція

  • ✅ Не потрібно підключати гаманець

  • ❌ Користувачі повинні сканувати новий QR-код для кожного платежу

  • ❌ Неможливо відстежити, чи підписав користувач транзакцію чи ні

  • ❌ Немає інформації про адресу користувача

  • ❌ Обхідні шляхи потрібні на платформах, де такі посилання не можна натискати (наприклад, повідомлення від ботів для десктопних клієнтів Telegram)

[Дізнайтеся більше про тонни посилань тут] (https://github.com/tonkeeper/wallet-api#payment-urls)

Дослідники

Дослідник блокчейну - https://tonscan.org.

Щоб згенерувати посилання на транзакцію в провіднику, сервіс має отримати lt (логічний час), хеш транзакції та адресу акаунта (адресу акаунта, для якого lt і txhash було отримано за допомогою методу getTransactions). https://tonscan.org і https://explorer.toncoin.org/ можуть показати сторінку для цього tx у наступному форматі:

https://tonviewer.com/transaction/{txhash as base64url}

https://tonscan.org/tx/{lt as int}:{txhash as base64url}:{account address}

https://explorer.toncoin.org/transaction?account={account address}&lt={lt as int}&hash={txhash as base64url}.

Найкращі практики

Створення гаманця

Депозити в тонкоїнах (отримати тонкоїни)

Виведення токенів (надсилання токенів)

Отримуйте транзакції за контрактом

SDK

Список SDK для різних мов (JS, Python, Golang, C#, Rust і т.д.) можна знайти тут.