Перейти к основному содержимому

Интеграция со смарт-контрактами в TRON

В этом руководстве описано, как принимать некастодиальные криптоплатежи в TRON через платёжные смарт-контракты Volet.com и TronWeb.

Клиенты подключают свои кошельки, подтверждают транзакции и платят через смарт-контракт напрямую на ваш кошелёк для приёма средств. Средства не зачисляются на аккаунт Volet.com.

Прежде чем продолжить, ознакомьтесь с обзором платёжного сценария в статье Интеграция со смарт-контрактами.

Интеграция в общих чертах​

  1. Подключите кошелёк клиента и убедитесь, что он работает в TRON Mainnet.
  2. Настройте платёжные контракты и свой кошелёк для приёма средств.
  3. Создайте заказ на бэкенде до начала оплаты.
  4. Вызовите previewPayment(), чтобы рассчитать полную сумму платежа.
  5. Вызовите transfer(), чтобы инициировать платёж. Для USDT проверьте разрешение (allowance) на списание токенов и при необходимости запросите одобрение.
  6. Сохраните хеш платёжной транзакции (txid) в заказе.
  7. Проверьте транзакцию на бэкенде по solidified-данным блокчейна, прежде чем помечать заказ как оплаченный.

Перед началом​

Вам понадобятся:

  • Кошелёк клиента: TronLink для платежей в браузере.
  • Библиотека: TronWeb для работы с кошельками и смарт-контрактами TRON.
  • Кошелёк мерчанта: адрес кошелька TRON под вашим контролем для приёма платежей.
  • Бэкенд и база данных: чтобы создавать заказы, хранить данные транзакций и проверять платежи.
  • Доступ к блокчейну: нода TRON или облачный сервис, например TronGrid, чтобы получать и проверять транзакции.

Для подключения мобильных кошельков также понадобятся WalletConnect Project ID и совместимая интеграция с кошельками TRON.

Поддерживаемые сети и активы​

СетьПоддерживаемые активы
TRON MainnetTRX, USDT (TRC-20)

Для TRX и USDT используются разные контракты-процессоры.

Суммы в TRX указываются в SUN — минимальной единице TRX. 1 TRX равен 1 000 000 SUN.

У USDT TRC-20 6 decimals. 1 USDT равен 1 000 000 базовых единиц токена.

В примерах кода ниже указаны адреса процессоров и токенов из конфигурации интеграции.

Адреса контрактов, decimals токенов и параметры функций приведены в справочнике по смарт-контрактам.

Прежде чем использовать примеры в рабочей среде, сверьте адреса развёрнутых процессоров и интерфейс контракта с Volet.com.

Шаг 1. Подключите кошелёк клиента​

Подключите браузерный кошелёк клиента через TronLink.

<button id="connect-tron">Connect TronLink</button>

<script>
let tronWeb;

document.getElementById('connect-tron').addEventListener('click', async () => {
if (!window.tronLink || !window.tronWeb) {
alert('Install and unlock TronLink.');
return;
}

const result = await window.tronLink.request({
method: 'tron_requestAccounts'
});

if (result?.code !== 200) {
throw new Error('Wallet connection rejected');
}

tronWeb = window.tronWeb;

document.getElementById('connect-tron').textContent =
tronWeb.defaultAddress.base58;
});
</script>

Клиент должен подключиться к TRON Mainnet.

Проверяйте активную сеть перед отправкой платежа. Не определяйте сеть только по URL RPC-эндпоинта: кошельки могут использовать собственные ноды.

Приложение также должно отслеживать смену аккаунта и сети в кошельке и заново проверять подключение перед отправкой транзакции.

Шаг 2. Настройте платёжные контракты​

Добавьте в приложение ABI платёжного процессора и функции токена.

const PROCESSOR_ABI = [
{
name: 'previewPayment',
type: 'function',
stateMutability: 'view',
inputs: [
{ name: 'amount', type: 'uint256' },
{ name: 'buyerPaysCommission', type: 'bool' },
],
outputs: [
{ name: 'commission', type: 'uint256' },
{ name: 'totalPay', type: 'uint256' },
],
},
{
name: 'transfer',
type: 'function',
stateMutability: 'payable',
inputs: [
{ name: 'seller', type: 'address' },
{ name: 'amount', type: 'uint256' },
{ name: 'buyerPaysCommission', type: 'bool' },
{ name: 'optionalCommissionWallet', type: 'address' },
],
outputs: [],
},
];

const TOKEN_ABI = [
{
name: 'balanceOf',
type: 'function',
stateMutability: 'view',
inputs: [{ name: 'account', type: 'address' }],
outputs: [{ type: 'uint256' }],
},
{
name: 'allowance',
type: 'function',
stateMutability: 'view',
inputs: [
{ name: 'owner', type: 'address' },
{ name: 'spender', type: 'address' },
],
outputs: [{ type: 'uint256' }],
},
{
name: 'approve',
type: 'function',
stateMutability: 'nonpayable',
inputs: [
{ name: 'spender', type: 'address' },
{ name: 'amount', type: 'uint256' },
],
outputs: [{ type: 'bool' }],
},
];

const TRON_PROCESSOR_TRX = 'TSK9sgBTjvZa6x2yW1bXNWqvQ57WsVUQ3W';
const TRON_PROCESSOR_USDT = 'TDuE6VueULx5oivQd4MsYr9DEhNMgXNfTm';
const TRON_USDT = 'TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t';
const TRON_ZERO = 'T9yD14Nj9j7xAB4dbGeiX9h8unkKHxuWwb';

const TRON_FEE_LIMIT = 500_000_000;

У платёжного процессора две основные функции:

  • previewPayment(amount, buyerPaysCommission) рассчитывает комиссию за обработку и полную сумму, которую должен заплатить клиент.
  • transfer(seller, amount, buyerPaysCommission, optionalCommissionWallet) выполняет платёж и распределяет средства по настройкам комиссии контракта.

Параметр amount указывается в минимальных единицах актива: в SUN для TRX или в базовых единицах токена для USDT.

Параметр buyerPaysCommission определяет, кто платит комиссию за обработку:

  • true: комиссия добавляется к платежу клиента.
  • false: комиссия вычитается из суммы, которую получает мерчант.

Если партнёрского кошелька для комиссии нет, передайте TRON_ZERO в optionalCommissionWallet.

Всегда берите полную сумму платежа из previewPayment(). Не рассчитывайте комиссию за обработку вручную.

TRON_FEE_LIMIT в примере указан в SUN и задаёт максимальный лимит комиссии в 500 TRX, а не ожидаемую комиссию за транзакцию. Перед запуском в рабочей среде оцените нужный объём Energy и подберите подходящий лимит комиссии.

Полное описание параметров — в справочнике по смарт-контрактам.

Шаг 3. Приём TRX​

Для платежа в нативных TRX нужна одна транзакция в кошельке.

С помощью previewPayment() рассчитайте точное количество SUN, которое нужно отправить с транзакцией.

async function payTrx(tronWeb, seller, humanAmount, buyerPaysCommission = true) {
if (!tronWeb.isAddress(seller)) {
throw new Error('Invalid TRON seller address');
}

const processor = tronWeb.contract(
PROCESSOR_ABI,
TRON_PROCESSOR_TRX
);

const amount = tronWeb.toSun(humanAmount).toString();

const preview = await processor.previewPayment(
amount,
buyerPaysCommission
).call();

return processor.transfer(
seller,
amount,
buyerPaysCommission,
TRON_ZERO
).send({
callValue: preview.totalPay.toString(),
feeLimit: TRON_FEE_LIMIT
});
}

callValue транзакции должен быть равен totalPay, который вернул previewPayment().

У клиента также должно быть достаточно TRX для оплаты ресурсов блокчейна и комиссий за транзакцию.

Функция возвращает хеш платёжной транзакции (txid). Сохраните его в существующем заказе и проверьте платёж, прежде чем выполнять заказ.

Шаг 4. Приём USDT​

Платежи в USDT TRC-20 проходят через контракт токена и могут потребовать дополнительной транзакции одобрения.

Перед платежом приложение должно:

  1. Проверить баланс USDT клиента с помощью balanceOf().
  2. Проверить разрешение процессора на списание с помощью allowance().
  3. Если разрешения недостаточно, запросить одобрение через approve().
  4. Дождаться подтверждения одобрения.
  5. Вызвать transfer(), чтобы выполнить платёж.
async function payTronUsdt(tronWeb, seller, amountBaseUnits, buyerPaysCommission = true) {
if (!tronWeb.isAddress(seller)) {
throw new Error('Invalid TRON seller address');
}

if (!/^\d+$/.test(amountBaseUnits) || BigInt(amountBaseUnits) <= 0n) {
throw new Error('USDT amount must be a positive integer in base units');
}

const processor = tronWeb.contract(
PROCESSOR_ABI,
TRON_PROCESSOR_USDT
);

const token = tronWeb.contract(
TOKEN_ABI,
TRON_USDT
);

const owner = tronWeb.defaultAddress.base58;

const preview = await processor.previewPayment(
amountBaseUnits,
buyerPaysCommission
).call();

const total = BigInt(preview.totalPay.toString());

const balance = BigInt(
(await token.balanceOf(owner).call()).toString()
);

if (balance < total) {
throw new Error('Insufficient USDT');
}

const allowance = BigInt(
(await token.allowance(owner, TRON_PROCESSOR_USDT).call()).toString()
);

if (allowance < total) {
const approvalTxid = await token.approve(
TRON_PROCESSOR_USDT,
total.toString()
).send({ feeLimit: TRON_FEE_LIMIT });

return {
state: 'approval_pending',
approvalTxid
};
}

const paymentTxid = await processor.transfer(
seller,
amountBaseUnits,
buyerPaysCommission,
TRON_ZERO
).send({ feeLimit: TRON_FEE_LIMIT });

return {
state: 'payment_pending',
paymentTxid
};
}

Требования к одобрению USDT​

В зависимости от текущего разрешения для платежа в USDT может понадобиться:

Текущее разрешениеНужные транзакции
ДостаточноеТолько платёж
НедостаточноеОдобрение + платёж

Если нужно одобрение, функция возвращает approval_pending с хешем транзакции одобрения.

Дождитесь подтверждения одобрения, прежде чем снова вызывать функцию оплаты. Когда разрешения станет достаточно, функция отправит платёж и вернёт payment_pending с хешем платёжной транзакции.

Важно:

  • Транзакция approve() — это не платёж.
  • Клиенту нужно достаточно TRX для оплаты Energy и Bandwidth, даже если он платит в USDT.
  • Суммы в USDT указываются целыми числами в базовых единицах токена. Избегайте вычислений с плавающей точкой вроде Math.round(parseFloat(amount) * 1e6).
  • Отслеживайте транзакции одобрения отдельно от итоговой платёжной транзакции.

Шаг 5. Сохраните платёжную транзакцию​

Ваш бэкенд должен создать заказ до того, как клиент отправит платёж.

В каждом заказе должны быть ожидаемые:

  • Идентификатор заказа
  • Блокчейн-сеть
  • Актив и сумма
  • Кошелёк мерчанта для приёма средств (seller)
  • Утверждённый контракт-процессор
  • Кошелёк покупателя, если этого требует ваша страница оплаты
  • Настройка комиссии за обработку (buyerPaysCommission)

После того как клиент отправил платёж:

  1. Получите хеш платёжной транзакции (txid), который вернул кошелёк.
  2. Отправьте хеш на бэкенд.
  3. Сохраните его вместе с идентификатором заказа и блокчейн-сетью.
  4. Запустите проверку платежа на сервере.

Каждая платёжная транзакция может быть связана не более чем с одним заказом. Обеспечьте это ограничением уникальности (network, txid) в базе данных.

Функция transfer() смарт-контракта не принимает идентификатор заказа. Связь между заказом и его платёжной транзакцией должно поддерживать ваше приложение.

Если клиент повторяет платёж, сохраняйте попытки оплаты отдельно и не перезаписывайте записи о транзакциях, которые ещё могут быть в ожидании.

Шаг 6. Проверьте платёж​

Проверка платежа обязательна перед завершением заказа.

Полученный хеш транзакции означает лишь, что транзакция отправлена. Он не доказывает, что мерчант получил правильный платёж.

Ваш бэкенд должен самостоятельно:

  1. Получить транзакцию и квитанцию о выполнении из блокчейна.
  2. Подтвердить успешное выполнение по solidified-квитанции TRON.
  3. Проверить, что транзакция прошла через ожидаемый платёжный процессор.
  4. Сверить актив, кошелёк мерчанта, сумму и вызов контракта с заказом.
  5. Подтвердить, что мерчант получил ожидаемые средства.
  6. Убедиться, что транзакция ещё не использовалась для другого заказа.
  7. Пометить заказ как оплаченный и выполнить его только один раз.

Для solidified-данных о транзакции используйте эндпоинт SolidityNode TRON:

POST https://api.trongrid.io/walletsolidity/gettransactioninfobyid

Проверьте, что receipt.result === 'SUCCESS', а затем сверьте тело транзакции и фактические данные платежа с заказом.

Одной успешной квитанции недостаточно. Транзакция может успешно выполниться, но заплатить не тому мерчанту или не ту сумму.

Если блокчейн-сервис временно недоступен или транзакция ещё не solidified, оставьте заказ в ожидании и повторите проверку.

Полная реализация на бэкенде, требования к подтверждениям в TRON и чек-лист для запуска описаны в статье Проверка платежей и отслеживание заказов.

См. также документацию TRON confirmation semantics.

Дополнительно: поддержка мобильных кошельков​

Чтобы поддержать мобильные кошельки через WalletConnect, используйте совместимую интеграцию с кошельками TRON, например @tronweb3/walletconnect-tron, вместе с TronWeb.

Для этого нужны WalletConnect Project ID и интеграция, которая умеет создавать транзакции TRON, запрашивать подписи у мобильных кошельков и отправлять подписанные транзакции в сеть.

Описанный ниже пример для браузера поддерживает TronLink. Сценарий с мобильными кошельками через WalletConnect в нём не реализован.

Пример интеграции​

Пример example-tron.html показывает:

  • Подключение TronLink.
  • Расчёт суммы платежа через previewPayment().
  • Отправку платежей в TRX и USDT TRC-20.
  • Вывод хешей транзакций и отслеживание статуса транзакций.

Пример можно открыть в браузере или скачать HTML-файл, чтобы изучить и доработать код:

  • Открыть живой пример — открывает интерактивное демо. Оно работает в TRON Mainnet, поэтому подтверждённый платёж переводит реальные средства.
  • Скачать example-tron.html — сохраняет HTML-файл на ваш компьютер.

Браузерные расширения кошельков, например TronLink, могут некорректно работать со страницами, открытыми прямо с диска (file://). Вместо этого откройте скачанный файл через локальный веб-сервер. Например, запустите npx serve в папке с файлом и откройте адрес localhost, который он выведет.

Попробуйте пример​

  1. Откройте живой пример или скачанный файл через локальный сервер в браузере с установленным TronLink.
  2. Выберите Mainnet в TronLink, затем нажмите Connect TronLink. Демо предупредит, если кошелёк подключён к другой сети.
  3. Выберите способ оплаты: TRX или USDT TRC-20.
  4. Введите кошелёк TRON для приёма средств в поле Seller address.
  5. Введите сумму платежа и выберите, кто платит комиссию за обработку.
  6. Нажмите Preview, чтобы увидеть комиссию за обработку и полную сумму платежа. Затем нажмите Pay и подтвердите транзакцию в TronLink. Для платежа в USDT сначала может понадобиться отдельное одобрение токена.
  7. Заказ и хеш транзакции появятся в таблице Orders. Демо проверяет статус транзакции до 5 минут.

Перед оплатой:

  • Используйте два разных кошелька. Адрес продавца должен отличаться от кошелька, с которого вы платите. Иначе контракт отклонит платёж с ошибкой SellerIsBuyer.
  • Транзакции идут с реальными средствами. В примере заранее указан 1 TRX. Перед подтверждением проверьте сумму платежа, комиссию за обработку и расход ресурсов сети TRON (Energy и Bandwidth).

Ограничения демо: пример хранит историю транзакций в localStorage браузера и не является рабочей страницей оплаты. Он проверяет последнюю квитанцию транзакции через getTransactionInfo(), а это не гарантирует, что транзакция solidified в TRON. Окно отслеживания в 5 минут — ограничение демо, а не срок подтверждения платежа. Прежде чем принимать заказы клиентов, реализуйте полный процесс проверки платежей.