Разработка P2P-платформы: сделки, TON и Jetton

P2P Crypto Exchange Platform · FinTech · Laravel · TON / Jetton

P2P-платформа с объявлениями, сделками, чатом и спорами, отдельным финансовым учётом и интеграцией с кошельковым микросервисом GRAM. Инженерная задача проекта — согласовать действия пользователей, внутренние балансы и асинхронные события блокчейна.

P2Pдвижок сделок
Ledgerучёт Jetton
HMACграница API
Outboxдоставка депозитов
TONкошельки и токены

Задача проекта

Объединить пользовательский P2P-маркет и финансовый backend: объявления на покупку и продажу, согласование сделки, отметку об оплате, release, отмены и разбор споров. При этом ввод TON и Jetton должен доходить до баланса пользователя даже после временной недоступности основного приложения, а повторная доставка события не должна создавать новое зачисление.

Важная часть задачи — разделить учёт платформы и работу с блокчейном. P2P отвечает за пользовательские сценарии и обязательства перед пользователями; отдельный GRAM обслуживает кошельки, синхронизацию транзакций, доставку депозитов и исполнение заявок на вывод.

Архитектура: P2P-приложение и GRAM

Vue / Inertia → Laravel P2P

Интерфейс связан с Laravel через Inertia. В backend выделены маршруты маркета, сделок, профиля, безопасности, банковских реквизитов и административной части. REST API v2 предоставляет отдельные операции для объявлений, сделок, сообщений и настроек пользователя.

GRAM → TON blockchain

Кошельковый микросервис ведёт собственные модели кошельков и транзакций, работает с blockchain API и передаёт в P2P подтверждённые события по подписанному service-to-service API. TON и Jetton имеют отдельные потоки обработки.

Архитектура P2P: Vue и Inertia, Laravel с P2P Engine, Security и Ledger, API к GRAM, TON и Jetton
Схема компонентов. Депозиты идут из GRAM в P2P, заявки на вывод — из P2P в GRAM.

Граница между приложениями проходит по API. Сбой внешнего провайдера или очереди доставки отражается в состоянии операции; пользовательский баланс и баланс кошелька рассматриваются как разные уровни учёта.

P2P Engine: от объявления до завершения сделки

Мейкер создаёт объявление на покупку или продажу, редактирует его и управляет доступностью: предусмотрены пауза, возобновление и отмена объявлений. Тейкер открывает сделку по выбранному объявлению. Для API сделки используется публичный UID, для исходного объявления — его ID.

  1. Создание и подтверждение

    Для покупки и продажи предусмотрены отдельные сценарии создания. Затем пользователь проходит checkout и подтверждает сделку. В backend это отдельные операции, а не изменение произвольного статуса из интерфейса.

  2. Принятие или отклонение

    После создания доступны самостоятельные действия accept и reject. Для сопровождения сделки есть её страница, история переписки и банковские реквизиты пользователя.

  3. Отметка об оплате

    Операция markAsPaid фиксирует подтверждение оплаты участником. Она отделена от release: отметка пользователя об отправке денег сама по себе не является подтверждением банковского зачисления.

  4. Release или отмена

    Для release и cancel используются отдельные серверные обработчики. Также предусмотрено действие продления времени сделки. Точная матрица допустимых переходов зависит от бизнес-логики контроллеров.

  5. Спор и его закрытие

    У сделки есть действия dispute и closeDispute. Административная часть содержит раздел споров и справочник причин, а также просмотр P2P-сделок.

Это последовательность пользовательских действий, подтверждённая маршрутами проекта. Названия внутренних enum-статусов и правила арбитражного распределения средств здесь не подменяются названиями API-команд.

Чат, история операций и реквизиты

Переписка привязана к сделке: backend предоставляет получение и отправку сообщений, отметку прочтения и загрузку вложения. Отдельно доступны история операций, список банков и создание, редактирование, удаление банковских реквизитов. Эти сценарии позволяют сопровождать обмен в одном приложении.

Real-time инфраструктура на Laravel Reverb

В составе инфраструктуры P2P выделен контейнер Laravel Reverb — сервер WebSocket-соединений. Он запускается отдельно от PHP-FPM, обработчика очередей и планировщика. Vue/Inertia обслуживает пользовательский интерфейс, Reverb — канал real-time взаимодействия.

Переданная конфигурационная документация описывает WebSocket-host, порт и схему подключения через HTTPS. Конкретный перечень broadcast-событий и подписок клиента в предоставленных исходниках отсутствует, поэтому здесь не заявляется, что каждое изменение баланса или статуса уже доставляется через WebSocket.

Security: аккаунт и межсервисный API

Пользовательский контур

Выделены сценарии установки и смены PIN, включения и отключения 2FA, привязки Telegram и восстановления доступа. Доступ к чувствительным данным вынесен в отдельные действия профиля, включая пошаговый сценарий раскрытия seed и подготовку файла восстановления.

API v2 использует auth:sanctum. Web-маркет находится в группе с security.keys.viewed и email.linked; административные маршруты — за auth и admin. Это границы доступа в маршрутах, а не заявление о проведённом пентесте.

Service-to-service контур

Endpoint приёма депозитов находится в группе api.hmac с rate limiting. В GRAM подпись HMAC-SHA256 охватывает HTTP-метод, путь, query string, хеш тела, timestamp, nonce и Idempotency-Key. Подмена подписанных данных приводит к несовпадению подписи.

Timestamp ограничивает срок запроса, nonce используется однократно, IP allowlist ограничивает адреса клиентов. Обязательность HTTPS и allowlist задаётся конфигурацией окружения.

Nonce и Idempotency-Key решают разные задачи

При повторной доставке депозита GRAM создаёт новый nonce и timestamp, но сохраняет прежний Idempotency-Key, равный external_id события. Nonce защищает от воспроизведения запроса; стабильный идентификатор операции позволяет распознать повтор на уровне финансовой логики. Одна лишь корректная HMAC-подпись не делает зачисление идемпотентным.

В публичном описании и схеме нет ключей, seed-фраз, токенов, пользовательских реквизитов или внутренних адресов сервисов.

TON deposits: подтверждение, outbox и повторная доставка

GRAM создаёт событие депозита для подтверждённой входящей транзакции пользовательского кошелька. До постановки в outbox проверяются успешность операции, отсутствие bounce, минимальная сумма и тип сообщения. Технические сообщения Jetton, включая transfer_notification и excesses, а также внутренние управляемые переводы не должны становиться новым TON-зачислением.

  1. Стабильная идентичность события

    external_id TON-депозита строится из сети, адреса кошелька, logical time и нормализованного хеша транзакции. В таблице outbox уникальны и исходная транзакция, и external_id.

  2. Доставка через очередь

    Задача блокирует outbox-запись в транзакции БД, проверяет состояние и отправляет подписанное событие в P2P. Сохраняются число попыток, время следующей попытки, HTTP-статус и код ошибки.

  3. Подтверждение приёма

    GRAM проверяет успешный ответ и совпадение external_id в acknowledgement. Временные ошибки приводят к повторным попыткам с задержкой; исчерпание попыток переводит доставку на ручную проверку.

В P2P, согласно документации интеграции, TON хранится отдельно от legacy-баланса USDT: user_currency_balances содержит валютные балансы, crypto_deposits — журнал зачислений. Повторная доставка того же external_id не должна создавать второе начисление. Endpoint приёма TON зарегистрирован отдельно от endpoint Jetton.

Backfill и reconciliation

GRAM умеет восстанавливать исторические транзакции и недостающие outbox-записи. Команды backfill, dispatch и reconcile разделяют поиск пропущенных событий, доставку и сверку. Для истории транзакций используется отдельный курсор: восстановление прошлого не подменяет опрос новых операций.

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

Jetton: atomic amounts, ledger и вывод с резервом

Поддержка Jetton выделена в отдельный контур. В GRAM используются активные активы из allowlist, собственные записи транзакций и балансов. Идентичность депозита включает сеть, кошелёк, адрес Jetton master и идентификатор операции: одинакового символа токена недостаточно.

Документация P2P фиксирует три структуры: jetton_deposits для зачислений, user_jetton_balances для отдельных пользовательских балансов и jetton_ledger_entries для неизменяемого журнала операций. Суммы передаются в атомарных единицах; отображение с учётом decimals отделено от финансового расчёта.

Жизненный цикл заявки на вывод

  • При создании заявки P2P атомарно резервирует сумму вывода и комиссию в пользовательском Jetton-балансе.
  • Комиссия платформы задаётся в basis points: 100 bps означает 1%. По документации сумма комиссии округляется вверх до минимальной единицы токена; фактическая ставка зависит от конфигурации.
  • GRAM создаёт идемпотентную заявку, проверяет доступный токен и отдельно резервирует TON для gas. Резерв на сетевые расходы и комиссия P2P относятся к разным уровням учёта.
  • После подтверждения GRAM платформа проводит списание резерва через ledger. При failed или rejected резерв P2P освобождается.
  • Команда gram:jetton-withdrawals:sync в P2P документирована как ежеминутное восстановление недоставленных заявок и синхронизация активных статусов после перезапуска очереди.

В модели вывода GRAM определены состояния created, queued, processing, sent, confirmed, failed, rejected и manual_review. Состояние sent отделено от confirmed: отправка транзакции и подтверждение результата — разные этапы. Это статусы заявки на вывод, а не P2P-сделки.

Custody audit: сверка активов и обязательств

Защищённый custody endpoint GRAM возвращает агрегированный снимок: баланс, резерв и доступную сумму по управляемым кошелькам, отдельно пользовательские, collector и standalone кошельки, сведения казначейства. Вместе с суммами передаются признаки ошибок и давность синхронизации.

На стороне P2P документирована плановая сверка активов GRAM с обязательствами перед пользователями каждые 30 минут. Для неё предусмотрены допустимое расхождение в nanoTON, отдельный флаг включения и интервал повторного оповещения администраторов.

Сверка доставки депозитов и custody reconciliation отвечают на разные вопросы: все ли события дошли до P2P и согласуются ли учтённые обязательства с активами кошелькового контура. Снимок зависит от свежести синхронизации; сам по себе он не является доказательством платёжеспособности в реальном времени.

Связанный проект: собственный testnet Jetton TGRAM

Для интеграционного тестирования GRAM и P2P создан отдельный проект gram-test-jetton. Его токен GRAM Integration Test (TGRAM, decimals 9) работает в TON testnet. Он используется для проверки сценариев токенов и не представлен как mainnet-актив.

По документации контрактного проекта в него входят Jetton master/minter и Jetton Wallet, скрипты deploy, mint и transfer, чтение supply, metadata и balance, передача управления через change-admin/claim-admin и изменение on-chain metadata. Отдельно описаны тесты комиссий, bounce и административных операций, а также GitHub Actions CI с build, format, check и test.

Контракты и инструменты TGRAM находятся в собственной кодовой базе. В P2P интегрируется результат работы токена через GRAM; контрактные скрипты не являются частью Laravel-приложения. Исходники контрактов и выполнение CI в рамках этого разбора не проверялись.

Стек и инфраструктура

Приложение

PHP · Laravel · Inertia · Vue · REST API · P2P Engine · Security · Jetton Ledger.

Данные и процессы

MySQL 8 · Redis · Laravel Queue · Laravel Scheduler · Laravel Reverb · WebSocket.

Развёртывание

Docker · Nginx · PHP-FPM. В документации P2P web, app, queue, scheduler и reverb описаны как отдельные контейнеры.

Blockchain

GRAM wallet microservice · TON · Jetton · blockchain API · webhooks · outbox delivery · custody snapshot. TGRAM — отдельный testnet-проект.

Технически сложные моменты и результат

Основная сложность находится на стыке независимых состояний: участник отметил оплату, блокчейн подтвердил входящую транзакцию, очередь доставила событие, платформа отразила его в учёте. Для этих действий выделены разные операции и идентификаторы; сетевой retry не должен становиться новой финансовой операцией.

В проекте соединены P2P-сценарии, защищённый межсервисный API, раздельный учёт TON и Jetton, резервирование вывода и восстановление доставки. GRAM дополняет приложение кошельковой инфраструктурой и проверяемыми журналами обработки. Отдельный TGRAM даёт тестовый токен для проверки интеграции в TON testnet.

Это кейс backend и FinTech-разработки с конкретными механизмами учёта и согласования. Метрики нагрузки, оборота и коммерческого результата здесь не приводятся: подтверждённых измерений в материалах нет.

Основа технического разбора

Описание подготовлено по предоставленным маршрутам и README P2P, исходникам микросервисов GRAM и TON Wallet Lab и документации отдельного проекта TGRAM. Полная кодовая база P2P в этот разбор не входила: детали ledger, комиссии, custody audit и инфраструктуры P2P приведены по документации. Наличие маршрутов не заменяет проверку внутренних контроллеров, а наличие тестов в GRAM — отчёт об их выполнении.


Давайте обсудим проект

Расскажите, что хотите сделать. Я отвечу на вашу почту.

Или напишите в Telegram @ifwcom