Архитектура приёма криптовалютных платежей USDT TRC20
CARA Pay
Архитектура приёма криптовалютных платежей USDT TRC20
Версия: 1.0
Статус: MVP Architecture Specification
Компонент: Crypto Payment Processing
Поддерживаемая сеть: TRON Mainnet
Поддерживаемый актив: USDT TRC20
Срок действия котировки и платёжной ссылки: 15 минут
Модель адресов MVP: уникальный депозитный адрес для каждой криптоплатёжной попытки
Фиксированный сбор CARA Pay: 2.00 USDT
Точность клиентских сумм: 2 знака после запятой
1. Назначение системы
CARA Pay предоставляет мерчанту возможность создать платёжную ссылку в валюте заказа, например IDR, после чего клиент выбирает способ оплаты:
-
банковский перевод в RUB;
-
криптовалютный перевод USDT в сети TRON.
Если клиент выбирает оплату USDT TRC20, система:
-
рассчитывает сумму заказа в USDT;
-
округляет сумму до двух знаков;
-
добавляет фиксированный сбор 2.00 USDT;
-
выделяет уникальный депозитный TRON-адрес;
-
активирует адрес в сети TRON;
-
показывает клиенту адрес, QR-код и точную итоговую сумму;
-
отслеживает входящий перевод USDT;
-
подтверждает платёж после окончательного подтверждения транзакции;
-
переводит полученные USDT с депозитного адреса на основной Treasury Wallet;
-
сохраняет полную историю котировки, оплаты, сетевых расходов и sweep-транзакции.
Основной принцип MVP:
Один Payment Order
→ максимум один Crypto Payment Attempt
→ один уникальный TRON Deposit Address
→ одна ожидаемая итоговая сумма USDT
→ один принятый клиентский платёж
→ один основной sweep в Treasury Wallet
Уникальный адрес, однажды показанный клиенту, больше никогда не используется для другой платёжной операции.
2. Основные решения MVP
2.1. Поддерживается только одна сеть
В MVP поддерживается исключительно:
Network: TRON Mainnet
Asset: USDT
Token standard: TRC20
Не поддерживаются:
-
Ethereum;
-
BNB Smart Chain;
-
Solana;
-
TON;
-
TRX как платёжный актив;
-
другие TRC20-токены;
-
bridged-версии USDT;
-
токены с таким же символом
USDT, но другим контрактом.
2.2. Разрешён только официальный контракт USDT
Разрешённый контракт:
TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t
Этот адрес опубликован Tether как текущий контракт USD₮ в сети TRON. Проверка платежа должна выполняться по адресу контракта, а не только по символу или названию токена.
Правило:
network = TRON_MAINNET
AND token_contract = TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t
Любой другой токен не считается оплатой, даже если:
-
он называется USDT;
-
имеет символ USDT;
-
отображает похожий логотип;
-
имеет такую же сумму;
-
поступил на правильный депозитный адрес.
2.3. Уникальный адрес для каждой криптопопытки
Каждая Crypto Payment Attempt получает отдельный адрес:
Payment Order A
└── Crypto Attempt A
└── Deposit Address A
Payment Order B
└── Crypto Attempt B
└── Deposit Address B
Даже если обе операции имеют одинаковую сумму:
Order A → 52.00 USDT → Address A
Order B → 52.00 USDT → Address B
Система однозначно определяет платёж по адресу назначения.
2.4. Фиксированный сбор 2.00 USDT
К рассчитанной стоимости заказа добавляется:
crypto_processing_fee = 2.00 USDT
Этот сбор предназначен для покрытия:
-
активации нового TRON-адреса;
-
Energy и Bandwidth;
-
аренды или делегирования ресурсов;
-
sweep с депозитного адреса;
-
инфраструктуры мониторинга;
-
возможной разницы между расчётным и фактическим сетевым расходом.
Внутри системы рекомендуется использовать название:
crypto_processing_fee
На странице оплаты более корректное клиентское название:
Комиссия сети и обработки
Важно: эти 2.00 USDT являются фиксированным сбором CARA Pay, а не точным фактическим размером комиссии TRON. Реальная стоимость активации и sweep может быть меньше или больше.
2.5. Комиссия кошелька клиента оплачивается отдельно
Если кошелёк или биржа клиента взимает комиссию за вывод, эта комиссия не должна вычитаться из суммы, поступающей CARA Pay.
Например:
Итоговая сумма CARA Pay: 52.00 USDT
Комиссия биржи клиента: 1.00 USDT
Со счёта клиента списано: 53.00 USDT
На CARA Pay поступило: 52.00 USDT
Если биржа отправила только:
51.00 USDT
платёж считается недоплаченным.
3. Термины
Payment Order
Основная операция, созданная мерчантом.
Содержит:
-
мерчанта;
-
внешний номер заказа;
-
сумму и валюту заказа;
-
срок действия;
-
статус оплаты;
-
статус исполнения.
Пример:
Order amount: 5 000 000 IDR
External Order ID: ORDER-8711
Payment Option
Конкретный доступный способ оплаты Payment Order.
Примеры:
RUB_BANK
USDT_TRC20
Каждый Payment Option содержит собственную котировку и сумму.
Crypto Payment Attempt
Конкретная попытка оплатить Payment Order через USDT TRC20.
В MVP действует ограничение:
Один Payment Order
→ не более одной Crypto Payment Attempt
Если ссылка истекла, создаётся новый Payment Order и новый Crypto Payment Attempt.
Deposit Address
Уникальный TRON-адрес, выделенный одной Crypto Payment Attempt.
На него клиент отправляет USDT.
Treasury Wallet
Основной адрес CARA Pay, на который собираются USDT с депозитных адресов.
Sweep
Внутренний перевод:
Deposit Address
→ Treasury Wallet
Activation Wallet
Служебный TRON-кошелёк, который активирует новые депозитные адреса.
Resource Wallet
Служебный кошелёк, который:
-
хранит TRX;
-
получает Energy и Bandwidth;
-
делегирует ресурсы депозитным адресам;
-
при необходимости оплачивает сетевые расходы.
Crypto Processing Fee
Фиксированный сбор CARA Pay:
2.00 USDT
Blockchain Transfer
Зафиксированный в сети TRON перевод токена между адресами.
4. Финансовая модель
4.1. Исходная сумма заказа
Пример:
order_amount = 5 000 000 IDR
order_currency = IDR
Эта сумма принадлежит исходному Payment Order и после создания не меняется.
4.2. Курс IDR/USDT
Система получает рыночный курс:
client_rate = 16 600.00 IDR за 1 USDT
Курс фиксируется в Rate Snapshot и действует до expires_at.
4.3. Расчёт основной суммы USDT
Формула:
crypto_principal_amount =
round(
order_amount_idr / client_rate_idr_per_usdt,
2,
ROUND_HALF_UP
)
Пример:
5 000 000 / 16 600
= 301.204819...
crypto_principal_amount
= 301.20 USDT
4.4. Добавление сбора
crypto_processing_fee = 2.00 USDT
Итог:
crypto_total_amount =
crypto_principal_amount
+ crypto_processing_fee
Пример:
Основная сумма: 301.20 USDT
Комиссия сети и обработки: 2.00 USDT
Итого к оплате: 303.20 USDT
4.5. Точность
Все клиентские суммы USDT имеют только два знака:
301.20 USDT
52.00 USDT
147.35 USDT
Запрещено отображать клиенту:
301.204819 USDT
52.001278 USDT
Типы данных:
crypto_principal_amount DECIMAL(24,2)
crypto_processing_fee DECIMAL(24,2)
crypto_total_amount DECIMAL(24,2)
Для сравнения с блокчейном дополнительно хранится raw-сумма.
USDT TRC20 использует шесть десятичных знаков, поэтому:
303.20 USDT
=
303200000 raw units
Формула:
expected_amount_raw =
crypto_total_amount × 1 000 000
Пример:
303.20 × 1 000 000
= 303 200 000
Последние четыре знака raw-суммы для клиентских платежей CARA Pay всегда равны нулю.
4.6. Разделение основной суммы и сбора
После подтверждения платежа бухгалтерский учёт должен разделять:
303.20 USDT получено
├── 301.20 USDT — основная сумма операции
└── 2.00 USDT — crypto processing fee
Фиксированный сбор не увеличивает сумму заказа мерчанта.
4.7. Риск превышения фактических расходов
Фиксированный сбор 2.00 USDT не гарантирует, что реальная стоимость активации и sweep всегда составит не более 2.00 USDT.
Правило MVP:
Если фактические сетевые расходы > 2.00 USDT,
разницу временно несёт CARA Pay.
По каждой операции обязательно рассчитывается:
fee_collected_usdt
actual_activation_cost_usdt
actual_sweep_cost_usdt
actual_total_cost_usdt
fee_margin_usdt
Формула:
fee_margin_usdt =
fee_collected_usdt
- actual_total_cost_usdt
5. Связь с существующим Payment Order
Исходный Payment Order остаётся общей сущностью для RUB и USDT.
Payment Order
├── RUB Payment Option
└── USDT TRC20 Payment Option
Рекомендуемая структура:
Payment Order
├── merchant_id
├── external_order_id
├── order_amount
├── order_currency
├── created_at
├── expires_at
├── accepted_payment_method
└── accepted_payment_attempt_id
Конкретные суммы оплаты выносятся из payment_orders в дочерние сущности:
RUB option:
├── payment_amount_rub
├── RUB rate snapshot
└── bank QR
Crypto option:
├── crypto_principal_amount
├── crypto_processing_fee
├── crypto_total_amount
├── USDT rate snapshot
└── deposit address
6. Момент создания криптопопытки
Уникальный адрес не нужно создавать и активировать сразу при создании каждого Payment Order.
Рекомендуемый процесс:
1. Мерчант создаёт Payment Order.
2. Клиент открывает ссылку.
3. Клиент выбирает «Оплатить USDT TRC20».
4. Только после выбора создаётся Crypto Payment Attempt.
5. Система выделяет и активирует адрес.
6. После успешной активации адрес показывается клиенту.
Это позволяет не расходовать TRX на адреса для клиентов, которые выбрали RUB или вообще не стали платить.
6.1. Идемпотентность выбора
Повторное нажатие кнопки USDT не должно создавать новый адрес.
Ограничение:
UNIQUE(payment_order_id)
для активной Crypto Payment Attempt в MVP.
Алгоритм:
if crypto_attempt already exists:
return existing crypto_attempt
else:
create crypto_attempt
6.2. Запрет создания после expiry
Если:
now >= payment_order.expires_at
новую Crypto Payment Attempt создавать нельзя.
Ответ:
{
"error": "PAYMENT_ORDER_EXPIRED",
"message": "Срок действия платёжной ссылки истёк"
}
7. Неизменяемость криптопопытки
После публикации адреса запрещается изменять:
-
Payment Order;
-
мерчанта;
-
депозитный адрес;
-
сеть;
-
контракт USDT;
-
основную сумму USDT;
-
фиксированный сбор;
-
итоговую сумму;
-
курс;
-
Rate Snapshot;
-
дату создания;
-
срок действия;
-
QR payload;
-
QR image;
-
wallet key version;
-
derivation index.
Разрешается изменять:
-
статус обнаружения;
-
статус подтверждения;
-
статус оплаты;
-
статус ручной проверки;
-
статус sweep;
-
transaction hash;
-
block number;
-
фактические сетевые расходы;
-
данные возврата;
-
историю событий.
8. Архитектура TRON-адресов
8.1. Что является дочерним адресом
CARA Pay использует отдельную master seed, из которой детерминированно создаются TRON-адреса.
CARA Pay Deposit Master Key
├── index 1 → Deposit Address 1
├── index 2 → Deposit Address 2
├── index 3 → Deposit Address 3
└── ...
На уровне сети TRON каждый такой адрес является самостоятельным аккаунтом:
-
имеет собственный приватный ключ;
-
имеет отдельный баланс;
-
имеет собственную историю;
-
самостоятельно подписывает исходящие транзакции.
TRON использует ключи на кривой secp256k1, а адрес может храниться в Hex- и Base58Check-формате; пользовательские Base58Check-адреса начинаются с T.
8.2. Derivation path
Точный derivation path должен быть зафиксирован до production-запуска и никогда не меняться для существующей версии ключа.
Пример:
m/44'/195'/0'/0/{index}
В базе сохраняются:
wallet_key_version
derivation_index
derivation_path
address_base58
address_hex
Пример:
wallet_key_version = tron-deposit-v1
derivation_index = 1842
derivation_path = m/44'/195'/0'/0/1842
address_base58 = TY...
address_hex = 41...
8.3. Версии ключей
Нельзя привязывать всю систему навсегда к одной seed.
Используется понятие:
wallet_key_version
Примеры:
tron-deposit-v1
tron-deposit-v2
При ротации:
-
новые адреса создаются из
v2; -
старые адреса
v1продолжают мониториться; -
signing service сохраняет возможность забрать средства со старых адресов;
-
старый ключ нельзя удалять, пока существует вероятность позднего поступления.
8.4. Запрет переиспользования
После того как адрес был показан клиенту:
address_reuse_allowed = false
Даже если:
-
клиент не оплатил;
-
ссылка истекла;
-
заказ отменён;
-
адрес остался пустым;
-
прошёл месяц;
-
сумма нового заказа совпадает.
Причина: клиент может сохранить адрес и выполнить поздний перевод.
9. Key Management
9.1. Приватные ключи не хранятся в основной базе
Запрещено хранить:
private_key
master_seed
mnemonic
в:
-
таблице депозитных адресов;
-
.envосновного приложения; -
логах;
-
Sentry;
-
analytics;
-
резервной копии обычной базы;
-
frontend;
-
Payload CMS;
-
админке.
9.2. Компоненты
Main Application
├── знает адрес;
├── знает derivation index;
├── знает Payment Attempt;
└── не может подписывать транзакции.
Address Allocator
├── получает новый адрес;
├── резервирует index;
└── не выдаёт приватный ключ приложению.
Signing Service
├── имеет доступ к master key;
├── производит дочерний ключ;
├── подписывает разрешённый sweep;
└── не отдаёт приватный ключ наружу.
9.3. Политика Signing Service
Signing Service должен подписывать только транзакции, соответствующие всем условиям:
network = TRON_MAINNET
contract =
TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t
method = transfer(address,uint256)
destination =
approved_treasury_address
Запрещается принимать произвольный адрес получателя от frontend или пользователя.
9.4. Treasury Wallet
Treasury Wallet должен использовать другой ключевой контур.
Deposit Master Wallet
→ создаёт клиентские депозитные адреса
Treasury Wallet
→ принимает собранные USDT
Не рекомендуется использовать seed текущего личного Trust Wallet как production master seed.
9.5. Резервные копии
Для master seed обязательны:
-
минимум две независимые офлайн-копии;
-
раздельное физическое хранение;
-
документированная процедура восстановления;
-
периодический recovery test;
-
журнал доступа;
-
отсутствие seed в облачных заметках и мессенджерах.
10. Генерация и резервирование адреса
10.1. Address Pool
Система может заранее генерировать пул неактивированных адресов:
available
available
available
Активация выполняется только после назначения Crypto Payment Attempt.
10.2. Алгоритм Allocation
1. Заблокировать счётчик derivation index.
2. Получить следующий индекс.
3. Сформировать derivation path.
4. Получить публичный адрес через Key Service.
5. Проверить валидность Base58Check.
6. Сохранить Base58 и Hex.
7. Убедиться в уникальности.
8. Связать адрес с Crypto Payment Attempt.
9. Установить address_status = assigned.
10. Записать событие address.assigned.
Ограничения:
UNIQUE(network, address_base58)
UNIQUE(wallet_key_version, derivation_index)
UNIQUE(crypto_payment_attempt_id)
10.3. Конкурентность
Индекс получается атомарно:
SELECT ...
FOR UPDATE
или через database sequence.
Пропуски индексов допустимы.
Повторное использование индекса запрещено.
11. Активация адреса
Новый локально созданный TRON-адрес ещё не существует как активный аккаунт в сети. TRON предусматривает активацию через перевод TRX/TRC10 или через createaccount. В актуальной документации также указана комиссия активации 1 TRX и возможный дополнительный расход Bandwidth; эти параметры должны считаться внешними и изменяемыми, а не навсегда захардкоженными.
11.1. Правило MVP
Адрес активируется до показа клиенту.
generate
→ assign
→ activate
→ verify activation
→ publish
Запрещено показывать адрес, пока:
activation_status != confirmed
11.2. Activation Wallet
Отдельный кошелёк:
TRON Activation Wallet
Он:
-
имеет ограниченный баланс TRX;
-
не хранит клиентские USDT;
-
активирует новые адреса;
-
имеет дневные лимиты;
-
используется только Activation Service.
11.3. Статусы активации
not_started
queued
broadcast
confirming
confirmed
failed
11.4. Алгоритм
1. Получить назначенный депозитный адрес.
2. Проверить его существование в сети.
3. Если адрес уже активен:
activation_status = confirmed.
4. Если не активен:
сформировать activation transaction.
5. Подписать её Activation Wallet.
6. Отправить в TRON.
7. Сохранить activation_tx_hash.
8. Дождаться confirmed state.
9. Повторно проверить существование аккаунта.
10. Установить activation_status = confirmed.
11. Разрешить публикацию адреса.
11.5. Ошибка активации
Если активация не завершилась до expiry:
crypto_creation_status = failed
Адрес клиенту не показывается.
API возвращает:
{
"error": "TRON_ADDRESS_ACTIVATION_FAILED",
"message": "Криптовалютный способ оплаты временно недоступен"
}
12. Создание Crypto Payment Attempt
12.1. Входные данные
Из Payment Order берутся:
payment_order_id
merchant_id
order_amount
order_currency
created_at
expires_at
Клиент не передаёт сумму самостоятельно.
12.2. Алгоритм
1. Найти Payment Order по public token.
2. Проверить link_status = active.
3. Проверить now < expires_at.
4. Проверить payment_status = pending.
5. Проверить отсутствие принятого платежа.
6. Проверить, существует ли Crypto Payment Attempt.
7. Если существует — вернуть её.
8. Получить актуальный IDR/USDT Rate Snapshot.
9. Проверить свежесть курса.
10. Рассчитать principal USDT.
11. Округлить principal до 2 знаков.
12. Добавить fixed fee 2.00 USDT.
13. Рассчитать total USDT.
14. Рассчитать expected_amount_raw.
15. Создать Crypto Payment Attempt.
16. Выделить уникальный Deposit Address.
17. Активировать Deposit Address.
18. Проверить активацию.
19. Сформировать QR payload.
20. Сохранить QR image и SHA-256.
21. Установить creation_status = ready.
22. Записать payment_address.ready.
23. Вернуть данные клиенту.
12.3. Атомарность
Клиенту разрешено увидеть адрес только после успешного создания:
-
Crypto Payment Attempt;
-
Rate Snapshot;
-
Fee Snapshot;
-
Deposit Address;
-
активации адреса;
-
QR payload;
-
QR image.
При частичном сбое операция остаётся внутренней и не публикуется.
13. Ответ клиенту
Пример:
{
"payment_order_id": "0198...",
"crypto_payment_attempt_id": "0198...",
"network": "TRON",
"asset": "USDT",
"token_standard": "TRC20",
"token_contract": "TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
"principal_amount": "301.20",
"processing_fee": "2.00",
"total_amount": "303.20",
"currency": "USDT",
"deposit_address": "TY...",
"created_at": "2026-07-12T12:00:00Z",
"expires_at": "2026-07-12T12:15:00Z",
"creation_status": "ready",
"payment_status": "pending"
}
14. Публичная страница оплаты
Страница должна показывать:
Сеть:
TRON (TRC20)
Актив:
USDT
Стоимость заказа:
301.20 USDT
Комиссия сети и обработки:
2.00 USDT
Итого к оплате:
303.20 USDT
Адрес:
TY...
Ссылка действует до:
20:15:00
14.1. Обязательные предупреждения
Отправляйте только USDT в сети TRON (TRC20).
Отправьте ровно 303.20 USDT.
Комиссия вашего кошелька или биржи должна
оплачиваться отдельно и не должна вычитаться
из суммы 303.20 USDT.
Не отправляйте TRX или другие токены.
Платёж в другой сети может быть потерян.
14.2. Кнопки
Скопировать адрес
Скопировать сумму
Показать QR
Я оплатил
Вернуться к выбору способа
Кнопка «Я оплатил» не подтверждает платёж. Она может только:
-
обновить статус;
-
ускорить пользовательский polling;
-
позволить указать tx hash для поддержки.
Источником истины остаётся блокчейн.
14.3. QR-код
Для MVP рекомендуется кодировать в QR только адрес:
TY...
Сумма отображается и копируется отдельно.
Причина: разные кошельки неодинаково обрабатывают URI с сетью, токеном и суммой.
Сохраняются:
qr_payload
qr_image_storage_key
qr_image_sha256
qr_created_at
14.4. После обнаружения транзакции
Страница меняется на:
Платёж обнаружен.
Ожидаем подтверждение сети TRON.
Не отправляйте платёж повторно
и не оплачивайте другим способом.
После обнаружения скрываются активные кнопки оплаты через RUB.
15. Истечение ссылки
15.1. Срок действия
expires_at = payment_order.created_at + 15 минут
Crypto Payment Attempt наследует expires_at Payment Order.
Она не получает собственные дополнительные 15 минут после выбора USDT.
15.2. Что истекает
Истекают:
-
котировка;
-
право автоматического исполнения;
-
публичное предложение оплатить;
-
возможность создать новую попытку в рамках старого Order.
Не истекают:
-
приватный ключ;
-
сам адрес;
-
мониторинг адреса;
-
возможность обнаружить поздний перевод;
-
история операции.
Нельзя технически отключить TRON-адрес после expiry.
15.3. Поведение страницы
После expiry:
Срок действия платёжной ссылки истёк.
Не переводите средства на ранее показанный адрес.
Запросите новую ссылку у продавца.
Запрещены:
-
продление;
-
обновление курса;
-
замена суммы;
-
повторная активация той же попытки;
-
привязка старого адреса к новой ссылке.
16. Blockchain Monitor
16.1. Назначение
Blockchain Monitor отслеживает:
-
входящие USDT;
-
подтверждение транзакций;
-
поздние платежи;
-
повторные платежи;
-
неправильные токены;
-
sweep-транзакции;
-
активационные транзакции.
TRON рекомендует интеграциям кошельков и бирж использовать TronGrid либо собственный FullNode; входящие операции можно получать через историю аккаунта или путём обработки блоков. В документации также указаны лимиты публичного TronGrid, поэтому высокая нагрузка должна учитываться отдельно.
16.2. Архитектура MVP
Primary source:
TRON API / TronGrid
Secondary control:
periodic reconciliation scan
Blockchain Monitor должен иметь:
block cursor
last processed block
last confirmed block
retry cursor
reconciliation cursor
16.3. Не полагаться только на webhook
Даже если используется event subscription, система обязана периодически повторно сканировать блоки.
Причины:
-
временная недоступность API;
-
пропущенное событие;
-
перезапуск сервиса;
-
ошибка очереди;
-
задержка индексации;
-
рассинхронизация cursor.
16.4. Backfill
После перезапуска:
start_block =
last_confirmed_processed_block - safety_window
Повторная обработка безопасна благодаря уникальному ключу события.
17. Определение входящего USDT-перевода
TRC20-переводы определяются по событию Transfer. Сам стандарт содержит адрес отправителя, адрес получателя и значение перевода.
Для каждого события сохраняются:
network
token_contract
transaction_hash
event_index
block_number
block_hash
block_timestamp
from_address
to_address
amount_raw
amount
observed_at
confirmed_at
17.1. Уникальность события
UNIQUE(network, transaction_hash, event_index)
Нельзя использовать только transaction_hash, потому что одна транзакция может содержать несколько событий.
17.2. Проверка контракта
if token_contract != official_usdt_contract:
do not count as payment
17.3. Поиск Payment Attempt
to_address
↓
tron_deposit_addresses
↓
crypto_payment_attempt_id
↓
payment_order_id
↓
merchant_id
Поскольку адрес уникален для попытки, сумма не используется как основной идентификатор операции.
18. Подтверждение транзакции
Первичное обнаружение события ещё не означает окончательное подтверждение оплаты.
TRON различает операции, присутствующие в текущем состоянии FullNode, и подтверждённые операции из solidified state. Для smart-contract-транзакции необходимо проверить confirmed API и успешный результат исполнения контракта.
18.1. Стадии
observed
→ confirming
→ confirmed
18.2. Условия confirmed
Транзакция считается подтверждённой, если:
-
доступна через confirmed/solidified API;
-
блок подтверждён сетью;
-
smart contract execution имеет результат
SUCCESS; -
присутствует корректное событие
Transfer; -
контракт равен официальному USDT;
-
получатель равен депозитному адресу;
-
amount больше нуля.
У Tether USD₮ TRC20 используется более старая реализация, которая может явно не возвращать Boolean из transfer. Поэтому нельзя полагаться только на возвращаемое значение метода: требуется проверять receipt, confirmed state и событие Transfer.
18.3. Время оплаты
Для определения своевременности используется:
block_timestamp
Не используются:
-
время на устройстве клиента;
-
время нажатия кнопки «Я оплатил»;
-
время отправки tx hash;
-
время обнаружения CARA Pay.
Правило:
block_timestamp <= expires_at
→ timely
block_timestamp > expires_at
→ paid_late
19. Сопоставление суммы
19.1. Точная оплата
received_amount_raw = expected_amount_raw
Результат:
payment_status = paid
review_status = not_required
execution_status = ready
19.2. Недоплата
received_amount_raw < expected_amount_raw
Результат:
payment_status = underpaid
review_status = required
execution_status = not_started
Пример:
Ожидалось: 52.00
Получено: 51.00
19.3. Переплата
received_amount_raw > expected_amount_raw
Результат:
payment_status = overpaid
review_status = required
19.4. Частичные платежи
В MVP несколько переводов автоматически не суммируются.
Например:
ожидалось 52.00
получено:
30.00
22.00
Результат:
payment_status = partial_payment
review_status = required
Даже если сумма переводов равна 52.00.
19.5. Повторный платёж
Если точный платёж уже принят, а на тот же адрес приходит ещё один перевод:
payment_status основного Order не изменяется
новый transfer:
duplicate_payment = true
review_status = required
19.6. Неправильный токен
Если на адрес пришёл другой TRC20-токен:
transfer_status = wrong_asset
review_status = required
Payment Order остаётся неоплаченным.
19.7. TRX вместо USDT
Поступление TRX:
-
сохраняется как служебное событие;
-
не считается оплатой;
-
не изменяет Payment Order;
-
отображается в админке;
-
может потребовать ручного возврата.
20. Поздний платёж
Если:
block_timestamp > expires_at
то:
payment_status = paid_late
review_status = required
execution_status = not_started
Даже если сумма точная.
Адрес мониторится бессрочно или в соответствии с утверждённой политикой хранения и on-chain reconciliation.
21. Конкуренция RUB и USDT
Клиент потенциально может оплатить:
-
RUB;
-
USDT;
-
оба способа одновременно.
В Payment Order хранится:
accepted_payment_attempt_id
accepted_payment_method
21.1. Первый окончательно подтверждённый платёж
Первый платёж, который прошёл окончательную проверку, блокирует Payment Order:
SELECT payment_order
FOR UPDATE
После этого:
accepted_payment_attempt_id = crypto_attempt_id
accepted_payment_method = USDT_TRC20
21.2. Второй платёж
Если позже обнаруживается банковская оплата:
second_payment_status = duplicate_payment
review_status = required
Автоматическое повторное исполнение запрещено.
21.3. Обнаруженный, но не подтверждённый USDT
Как только USDT-транзакция обнаружена:
-
другие способы скрываются на публичной странице;
-
Order получает временный статус
payment_detected; -
клиенту запрещается повторно платить;
-
окончательное исполнение начинается только после подтверждения.
22. Статусы
22.1. Crypto Creation Status
creating
address_allocated
activating
ready
failed
22.2. Crypto Payment Status
pending
detected
confirming
paid
paid_late
underpaid
overpaid
partial_payment
wrong_asset
duplicate_payment
refunded
22.3. Address Status
generated
available
assigned
activation_pending
active
used
retired
compromised
После публикации адрес не возвращается в available.
22.4. Blockchain Transfer Status
observed
confirming
confirmed
failed
orphaned
ignored
22.5. Sweep Status
not_started
queued
estimating_resources
awaiting_resources
signing
broadcast
confirming
completed
failed
blocked
23. Переходы статусов
Crypto Creation
creating
→ address_allocated
→ activating
→ ready
Ошибка:
creating → failed
address_allocated → failed
activating → failed
Crypto Payment
pending → detected
detected → confirming
confirming → paid
confirming → paid_late
confirming → underpaid
confirming → overpaid
confirming → partial_payment
Sweep
not_started
→ queued
→ estimating_resources
→ awaiting_resources
→ signing
→ broadcast
→ confirming
→ completed
Ошибка:
estimating_resources → failed
signing → failed
broadcast → failed
confirming → failed
Повторный запуск разрешён только идемпотентно.
24. Sweep в Treasury Wallet
24.1. Когда выполняется sweep
Для точной своевременной оплаты:
payment_status = paid
transfer_status = confirmed
После этого:
sweep_status = queued
Для поздних, ошибочных, частичных и повторных платежей средства также могут быть переведены в Treasury, но это не означает автоматического принятия платежа.
Правильное разделение:
Sweep
= внутреннее безопасное перемещение активов
Payment acceptance
= бизнес-решение по исполнению заказа
24.2. Что переводится
Рекомендуется переводить весь подтверждённый баланс официального USDT на депозитном адресе:
sweep_amount =
confirmed_usdt_balance
Если во время sweep поступил новый платёж, остаток обрабатывается следующей sweep-операцией.
24.3. Ресурсы TRON
TRC20-перевод использует Energy и Bandwidth. Потребление Energy может изменяться из-за Dynamic Energy Model, поэтому нельзя навсегда захардкодить предполагаемую себестоимость. TRON предоставляет метод estimateEnergy, который возвращает ожидаемый расход перед созданием транзакции.
24.4. Resource Strategy
Система должна поддерживать абстракцию:
resource_strategy
Значения:
DELEGATED_ENERGY
RENTED_ENERGY
TRX_FEE_FUNDING
Предпочтительный порядок:
1. Собственная делегированная Energy.
2. Арендованная Energy.
3. Пополнение TRX и прямое покрытие расходов.
TRON позволяет делегировать Energy и Bandwidth другому активированному внешнему аккаунту.
24.5. Алгоритм sweep
1. Получить exclusive lock на Deposit Address.
2. Проверить отсутствие активного sweep.
3. Получить confirmed USDT balance.
4. Если balance = 0:
завершить без транзакции.
5. Рассчитать sweep amount.
6. Вызвать estimateEnergy.
7. Получить energy_required.
8. Рассчитать безопасный fee_limit.
9. Выбрать resource strategy.
10. Предоставить ресурсы депозитному адресу.
11. Сформировать USDT transfer в Treasury.
12. Передать unsigned transaction в Signing Service.
13. Signing Service проверяет:
- сеть;
- контракт;
- destination;
- amount;
- fee limit.
14. Подписать дочерним приватным ключом.
15. Broadcast transaction.
16. Сохранить sweep_tx_hash.
17. Дождаться confirmed state.
18. Проверить receipt.result = SUCCESS.
19. Повторно проверить Deposit Address balance.
20. Сохранить фактический расход ресурсов.
21. Установить sweep_status = completed.
22. Освободить или вернуть делегированные ресурсы.
24.6. Недостаточно ресурсов
Если фактическая оценка выше допустимого операционного лимита:
sweep_status = awaiting_resources
Payment Order при этом остаётся оплаченным.
Нельзя отменять клиентскую оплату из-за того, что CARA Pay временно не смог выполнить внутренний sweep.
24.7. Идемпотентность
Один и тот же sweep нельзя отправить дважды.
Перед созданием новой транзакции проверяются:
-
активный sweep;
-
предыдущий tx hash;
-
confirmed balance;
-
текущий on-chain баланс;
-
статус предыдущего broadcast.
25. Учёт фактической комиссии
По каждой операции сохраняются:
activation_cost_trx
activation_cost_usdt
energy_required
energy_consumed
bandwidth_consumed
trx_burned
resource_rental_cost_trx
resource_rental_cost_usdt
sweep_cost_trx
sweep_cost_usdt
total_network_cost_usdt
fee_collected_usdt
fee_margin_usdt
Для пересчёта TRX в USDT сохраняется отдельный ценовой снимок:
trx_usdt_rate
rate_source
rate_fetched_at
Не допускается пересчитывать историческую себестоимость по текущему курсу.
26. Структура базы данных
26.1. payment_orders
Дополняется полями:
accepted_payment_method ENUM NULL
accepted_payment_attempt_id UUID NULL
payment_detected_at TIMESTAMP NULL
26.2. payment_options
id UUIDv7 PK
payment_order_id UUID FK
method ENUM
payment_currency VARCHAR
principal_amount DECIMAL(24,2)
fee_amount DECIMAL(24,2)
total_amount DECIMAL(24,2)
rate_snapshot_id UUID FK
created_at TIMESTAMP
expires_at TIMESTAMP
status ENUM
26.3. crypto_payment_attempts
id UUIDv7 PK
payment_order_id UUID UNIQUE FK
payment_option_id UUID FK
network VARCHAR
asset_code VARCHAR
token_standard VARCHAR
token_contract VARCHAR
token_decimals INT
principal_amount DECIMAL(24,2)
processing_fee DECIMAL(24,2)
expected_amount DECIMAL(24,2)
expected_amount_raw NUMERIC(78,0)
deposit_address_id UUID UNIQUE FK
rate_snapshot_id UUID FK
fee_snapshot_id UUID FK
creation_status ENUM
payment_status ENUM
review_status ENUM
sweep_status ENUM
created_at TIMESTAMP
expires_at TIMESTAMP
first_detected_at TIMESTAMP NULL
confirmed_at TIMESTAMP NULL
paid_at TIMESTAMP NULL
late_paid_at TIMESTAMP NULL
26.4. crypto_fee_snapshots
id UUIDv7 PK
fee_type VARCHAR
fee_amount DECIMAL(24,2)
fee_currency VARCHAR
policy_version VARCHAR
display_name VARCHAR
created_at TIMESTAMP
Пример:
fee_type = TRON_PROCESSING_FEE
fee_amount = 2.00
fee_currency = USDT
policy_version = tron-mvp-v1
26.5. wallet_key_versions
id UUIDv7 PK
key_version VARCHAR UNIQUE
network VARCHAR
purpose VARCHAR
derivation_path_template VARCHAR
status ENUM
activated_at TIMESTAMP
retired_at TIMESTAMP NULL
signing_provider_reference VARCHAR
created_at TIMESTAMP
Private key и seed отсутствуют.
26.6. tron_deposit_addresses
id UUIDv7 PK
wallet_key_version_id UUID FK
derivation_index BIGINT
derivation_path VARCHAR
address_base58 VARCHAR
address_hex VARCHAR
crypto_payment_attempt_id UUID UNIQUE NULL
address_status ENUM
activation_status ENUM
generated_at TIMESTAMP
assigned_at TIMESTAMP NULL
activated_at TIMESTAMP NULL
published_at TIMESTAMP NULL
used_at TIMESTAMP NULL
retired_at TIMESTAMP NULL
Ограничения:
UNIQUE(address_base58)
UNIQUE(address_hex)
UNIQUE(wallet_key_version_id, derivation_index)
26.7. tron_address_activations
id UUIDv7 PK
deposit_address_id UUID FK
activation_method ENUM
activation_wallet_id UUID FK
transaction_hash VARCHAR UNIQUE NULL
status ENUM
cost_trx DECIMAL(24,6) NULL
cost_usdt DECIMAL(24,8) NULL
broadcast_at TIMESTAMP NULL
confirmed_at TIMESTAMP NULL
failed_at TIMESTAMP NULL
raw_response JSONB
created_at TIMESTAMP
26.8. tron_token_transfers
id UUIDv7 PK
network VARCHAR
token_contract VARCHAR
transaction_hash VARCHAR
event_index BIGINT
block_number BIGINT
block_hash VARCHAR
block_timestamp TIMESTAMP
from_address_base58 VARCHAR
to_address_base58 VARCHAR
amount_raw NUMERIC(78,0)
amount DECIMAL(36,6)
transfer_status ENUM
is_confirmed BOOLEAN
observed_at TIMESTAMP
confirmed_at TIMESTAMP NULL
raw_event JSONB
raw_transaction JSONB
Ограничение:
UNIQUE(network, transaction_hash, event_index)
26.9. crypto_payment_matches
id UUIDv7 PK
crypto_payment_attempt_id UUID FK
tron_token_transfer_id UUID FK
contract_match BOOLEAN
address_match BOOLEAN
amount_match BOOLEAN
time_match BOOLEAN
match_result ENUM
matched_automatically BOOLEAN
created_at TIMESTAMP
reviewed_at TIMESTAMP NULL
reviewed_by_user_id UUID NULL
26.10. tron_sweeps
id UUIDv7 PK
deposit_address_id UUID FK
crypto_payment_attempt_id UUID FK
source_address VARCHAR
destination_address VARCHAR
token_contract VARCHAR
amount_raw NUMERIC(78,0)
amount DECIMAL(36,6)
resource_strategy ENUM
estimated_energy BIGINT NULL
actual_energy BIGINT NULL
actual_bandwidth BIGINT NULL
fee_limit_sun BIGINT NULL
trx_burned DECIMAL(24,6) NULL
transaction_hash VARCHAR UNIQUE NULL
status ENUM
created_at TIMESTAMP
broadcast_at TIMESTAMP NULL
confirmed_at TIMESTAMP NULL
failed_at TIMESTAMP NULL
error_code VARCHAR NULL
error_message TEXT NULL
raw_transaction JSONB NULL
raw_receipt JSONB NULL
26.11. blockchain_sync_state
id UUIDv7 PK
network VARCHAR
worker_name VARCHAR
last_seen_block BIGINT
last_confirmed_block BIGINT
last_reconciled_block BIGINT
updated_at TIMESTAMP
27. API
27.1. Выбор оплаты USDT
POST /v1/public/payment-links/{public_token}/crypto-attempt
Idempotency-Key: UUID
Тело может быть пустым, поскольку:
-
сеть фиксирована;
-
актив фиксирован;
-
сумма рассчитывается сервером.
27.2. Получение статуса
GET /v1/public/payment-links/{public_token}/crypto-attempt
27.3. Статус для мерчанта
GET /v1/payment-orders/{payment_order_id}
Пример:
{
"payment_status": "paid",
"accepted_payment_method": "USDT_TRC20",
"crypto_payment": {
"principal_amount": "301.20",
"processing_fee": "2.00",
"total_amount": "303.20",
"transaction_hash": "abc...",
"block_timestamp": "2026-07-12T12:10:22Z",
"confirmed_at": "2026-07-12T12:11:30Z",
"sweep_status": "queued"
}
}
27.4. Отмена
Отмена Payment Order разрешена, если:
payment_status = pending
AND crypto_payment_status = pending
AND no blockchain transfer detected
Адрес после отмены всё равно не переиспользуется и продолжает мониториться.
28. Webhooks
События:
crypto.payment_address_ready
crypto.payment_detected
crypto.payment_confirming
crypto.payment_paid
crypto.payment_paid_late
crypto.payment_underpaid
crypto.payment_overpaid
crypto.payment_partial
crypto.payment_duplicate
crypto.payment_wrong_asset
crypto.sweep_queued
crypto.sweep_completed
crypto.sweep_failed
Мерчанту обязательно отправляются:
crypto.payment_paid
crypto.payment_paid_late
crypto.payment_underpaid
crypto.payment_overpaid
crypto.payment_duplicate
Sweep-события могут оставаться внутренними.
Пример:
{
"event_id": "0198...",
"event_type": "crypto.payment_paid",
"created_at": "2026-07-12T12:11:30Z",
"data": {
"payment_order_id": "0198...",
"external_order_id": "ORDER-8711",
"payment_method": "USDT_TRC20",
"principal_amount": "301.20",
"processing_fee": "2.00",
"total_amount": "303.20",
"transaction_hash": "abc...",
"payment_status": "paid"
}
}
29. Административная панель
29.1. Список операций
Столбцы:
Дата
Payment Order
Мерчант
External Order ID
Сумма заказа
Principal USDT
Processing Fee
Total USDT
Deposit Address
Transaction Hash
Статус оплаты
Статус проверки
Статус sweep
Фактический расход
Fee Margin
29.2. Карточка криптоплатежа
Payment
Payment Order ID
Crypto Attempt ID
Merchant
External Order ID
Created At
Expires At
Quote
Order amount
Order currency
Market rate
Client rate
Rate source
Principal USDT
Processing Fee
Total USDT
Address
Address Base58
Address Hex
Wallet Key Version
Derivation Index
Derivation Path
Activation Status
Activation TX
Activation Cost
Published At
Incoming Transfer
Transaction Hash
Event Index
Block Number
Block Timestamp
From Address
To Address
Token Contract
Amount
Confirmation Status
Confirmed At
Sweep
Treasury Address
Sweep Amount
Resource Strategy
Estimated Energy
Actual Energy
TRX Burned
Sweep TX Hash
Sweep Status
Confirmed At
Audit
Все события
Все статусы
Все ручные решения
Все API-запросы
Все webhook delivery attempts
29.3. Ручные действия
Подтвердить поздний платёж
Отклонить поздний платёж
Принять недоплату
Отклонить недоплату
Принять переплату
Зафиксировать возврат
Повторить sweep
Заблокировать sweep
Отметить адрес compromised
Запустить reconciliation
Каждое действие содержит:
user_id
role
old_status
new_status
comment
created_at
IP address
request_id
30. Возвраты
Автоматические возвраты в MVP не выполняются.
Причины:
-
адрес отправителя может принадлежать бирже;
-
возврат на
from_addressможет не зачислиться клиенту; -
клиент может предоставить другой адрес;
-
требуется проверка личности и права на возврат;
-
возврат создаёт дополнительную сетевую комиссию.
Процесс:
1. Клиент обращается в поддержку.
2. Оператор проверяет Payment Order.
3. Клиент предоставляет TRON-адрес.
4. Адрес проходит валидацию.
5. Сотрудник подтверждает возврат.
6. Возврат выполняется из Treasury Wallet.
7. Сохраняется refund transaction hash.
Политика возврата фиксированного сбора 2.00 USDT должна быть отдельно закреплена в оферте.
Рекомендуемая техническая конфигурация:
fee_refund_policy:
NON_REFUNDABLE_AFTER_CONFIRMED_PAYMENT
Исключения допускаются вручную.
31. Безопасность
31.1. Allowlist
Жёстко фиксируются:
TRON Mainnet
Official USDT contract
Approved Treasury Address
Approved Activation Wallet
Approved Resource Wallet
Изменение Treasury Address требует:
-
повышенной роли;
-
второго подтверждения;
-
записи в аудит;
-
тестовой транзакции;
-
периода ожидания.
31.2. Signing Policy
Signing Service отклоняет транзакцию, если:
-
контракт неизвестен;
-
destination не в allowlist;
-
сумма больше on-chain баланса;
-
derivation path не соответствует адресу;
-
отсутствует связанный Payment Attempt;
-
fee limit превышает системный максимум;
-
сеть не TRON Mainnet.
31.3. Ограничение баланса
На депозитных адресах не должны долго храниться крупные суммы.
Алерты:
balance > configured_limit
paid but not swept > configured_period
unknown token received
second payment received
sweep failed repeatedly
31.4. Компрометация ключа
При подозрении:
wallet_key_version.status = compromised
После этого:
-
новые адреса из версии не создаются;
-
все балансы срочно собираются;
-
создаётся новая key version;
-
старые адреса продолжают мониториться;
-
инцидент фиксируется в audit log.
32. Reconciliation
Минимум один раз в сутки система выполняет полную сверку.
32.1. Deposit Address Reconciliation
Для каждого адреса:
database received amount
vs
on-chain confirmed transfers
vs
current USDT balance
vs
completed sweeps
Инвариант:
confirmed incoming
- confirmed outgoing
= current on-chain balance
С учётом возможных дополнительных переводов и возвратов.
32.2. Payment Reconciliation
Проверяется:
paid Payment Order
→ имеет confirmed incoming transfer
confirmed exact incoming transfer
→ имеет Payment Match
completed sweep
→ имеет confirmed outgoing transaction
32.3. Fee Reconciliation
fee collected
vs
activation cost
vs
resource cost
vs
sweep cost
vs
fee margin
33. Мониторинг и алерты
Метрики:
crypto_attempts_created
addresses_generated
addresses_activated
activation_failures
activation_cost_trx
payments_detected
payments_confirmed
payments_paid
payments_paid_late
payments_underpaid
payments_overpaid
payments_partial
duplicate_payments
wrong_asset_transfers
average_detection_delay
average_confirmation_delay
sweeps_queued
sweeps_completed
sweeps_failed
average_sweep_delay
energy_estimated
energy_consumed
trx_burned
fee_collected_usdt
actual_network_cost_usdt
fee_margin_usdt
Критические алерты:
TRON API unavailable
Blockchain cursor not moving
No payments detected for abnormal period
Activation Wallet low TRX
Resource Wallet low resources
Treasury sweep failure
Signing Service unavailable
Address generation collision
Negative fee margin spike
Deposit address balance stuck
Unknown outbound transaction
34. Сценарии
Сценарий 1. Успешная оплата
12:00 — Payment Order создан
12:02 — клиент выбрал USDT
12:02 — адрес выделен
12:03 — адрес активирован
12:03 — клиенту показано 52.00 USDT
12:10 — клиент отправил 52.00 USDT
12:10 — Transfer обнаружен
12:11 — Transfer подтверждён
12:11 — Payment Order paid
12:12 — sweep поставлен в очередь
12:15 — USDT переведены в Treasury
Результат:
payment_status = paid
execution_status = ready
sweep_status = completed
Сценарий 2. Биржа вычла комиссию
Ожидалось: 52.00
Поступило: 51.00
Результат:
payment_status = underpaid
review_status = required
Сценарий 3. Поздняя оплата
Ссылка истекла: 12:15
Block timestamp: 12:18
Результат:
payment_status = paid_late
review_status = required
Сценарий 4. Клиент отправил дважды
Первый перевод: 52.00
Второй перевод: 52.00
Результат:
первый → paid
второй → duplicate_payment
manual review
Сценарий 5. Клиент отправил TRX
Получено: 52 TRX
Результат:
Payment Order остаётся pending
wrong_asset incident создаётся
Сценарий 6. Клиент отправил поддельный USDT
Правильный адрес
Правильная сумма
Другой contract
Результат:
не является оплатой
wrong_asset
Сценарий 7. Sweep временно не выполнен
USDT payment confirmed
Resource Wallet недоступен
Результат:
payment_status = paid
execution_status = ready
sweep_status = awaiting_resources
Сценарий 8. Клиент оплатил RUB и USDT
USDT подтверждён первым
RUB обнаружен позже
Результат:
USDT → accepted payment
RUB → duplicate payment / manual review
35. Инварианты
Один Crypto Payment Attempt принадлежит одному Payment Order.
Один Crypto Payment Attempt имеет один Deposit Address.
Один опубликованный Deposit Address никогда не используется повторно.
Один Payment Order в MVP имеет не более одной Crypto Payment Attempt.
Сеть всегда TRON Mainnet.
Актив всегда официальный USDT TRC20.
Контракт проверяется по allowlist.
Все клиентские суммы имеют два знака.
Processing Fee всегда фиксируется снимком.
Изменение глобальной комиссии не меняет старые попытки.
Deposit Address показывается только после активации.
Payment определяется по адресу назначения и контракту.
Оплата подтверждается только по confirmed state.
Не подтверждённая транзакция не запускает исполнение.
Поздний платёж не исполняется автоматически.
Неправильный токен не является оплатой.
Один blockchain event не обрабатывается дважды.
Один принятый платёж не исполняется дважды.
Sweep failure не отменяет клиентский платёж.
Приватные ключи отсутствуют в основной базе.
Signing Service не может отправлять средства на произвольный адрес.
Все ручные действия сохраняются в audit log.
36. Критерии готовности MVP
Система считается готовой, если:
-
мерчант создаёт Payment Order;
-
клиент может выбрать USDT TRC20;
-
сумма USDT рассчитывается по фиксированному курсу;
-
основная сумма округляется до двух знаков;
-
к ней добавляется 2.00 USDT;
-
итоговая сумма имеет два знака;
-
создаётся уникальный TRON-адрес;
-
адрес активируется до публикации;
-
адрес никогда не переиспользуется;
-
отображаются адрес, сумма, сеть и QR;
-
клиент предупреждён о комиссии своей биржи;
-
система отслеживает официальный контракт USDT;
-
поддельный токен не засчитывается;
-
транзакция проходит confirmed-проверку;
-
точная оплата определяется автоматически;
-
недоплата определяется;
-
переплата определяется;
-
поздняя оплата определяется;
-
повторная оплата определяется;
-
Payment Order нельзя исполнить дважды;
-
подтверждённые USDT собираются в Treasury;
-
sweep подписывается изолированным Signing Service;
-
фактический расход TRX и Energy сохраняется;
-
рассчитывается маржа фиксированного сбора;
-
выполняется ежедневная reconciliation;
-
в админке видны адрес, tx hash и sweep;
-
мерчант получает подписанные webhooks;
-
все операции имеют audit trail;
-
ключи не хранятся в основной БД;
-
production-система протестирована на малых реальных суммах.
37. Этапы реализации
Этап 1. Payment Model
Реализовать:
payment_options
crypto_payment_attempts
crypto_fee_snapshots
новые статусы
формулу principal + 2.00
Этап 2. Key Infrastructure
Реализовать:
wallet_key_versions
Address Allocator
Signing Service
recovery procedure
address generation tests
Этап 3. Address Activation
Реализовать:
Activation Wallet
Activation Service
activation statuses
activation reconciliation
Этап 4. Blockchain Monitoring
Реализовать:
TRON API integration
block cursor
TRC20 Transfer parser
confirmed-state verification
backfill
deduplication
Этап 5. Payment Matching
Реализовать:
exact amount
underpayment
overpayment
late payment
duplicate payment
wrong asset
Этап 6. Sweep
Реализовать:
Resource Wallet
estimateEnergy
resource strategy
Signing Service
Treasury transfer
sweep reconciliation
Этап 7. Admin и Webhooks
Реализовать:
operation card
manual review
webhook delivery
audit history
financial fee report
Этап 8. Production Readiness
Перед запуском:
отдельные production keys
резервные копии
recovery test
лимиты кошельков
алерты
mainnet smoke tests
incident plan
daily reconciliation
38. Итоговая схема
Merchant
│
▼
Payment Order в IDR
│
▼
Публичная платёжная ссылка
│
├── Оплата RUB
│
└── Оплата USDT TRC20
│
▼
Rate Snapshot
│
├── Principal USDT
├── + 2.00 USDT
└── Total USDT, 2 знака
│
▼
Crypto Payment Attempt
│
▼
Unique Deposit Address
│
▼
Activation
│
▼
Публикация адреса и QR
│
▼
Клиент отправляет USDT
│
▼
Blockchain Monitor
│
├── официальный контракт
├── правильный адрес
├── точная сумма
├── block timestamp
└── confirmed state
│
▼
Payment confirmed
│
├── execution_status = ready
└── sweep_status = queued
│
▼
Estimate Energy
│
▼
Resource Strategy
│
▼
Signing Service
│
▼
Deposit Address ─── USDT ───► Treasury Wallet
39. Финальный стандарт CARA Pay Crypto MVP
Сеть:
TRON Mainnet
Актив:
USDT TRC20
Контракт:
TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t
Срок ссылки:
15 минут
Точность:
2 знака после запятой
Основная сумма:
рассчитывается по зафиксированному курсу
Фиксированный сбор:
2.00 USDT
Итого:
principal + 2.00 USDT
Адрес:
уникальный для каждой Crypto Payment Attempt
Активация:
до публикации клиенту
Переиспользование:
запрещено
Подтверждение:
confirmed / solidified TRON transaction
Поздняя оплата:
manual review
Недоплата:
manual review
Переплата:
manual review
Неверный токен:
не является оплатой
Sweep:
Deposit Address → Treasury Wallet
Private keys:
только в изолированном Signing Service
Основной принцип:
одна криптоплатёжная попытка —
один уникальный адрес —
одна зафиксированная сумма —
одна полная история on-chain.