P2P Platform: Deals, TON and Jetton
Case study · P2P / FinTech · Laravel · TON / Jetton
A peer-to-peer crypto trading platform with listings, deals, payment details, chat, disputes, internal balances and an integration with the separate GRAM wallet service. The core engineering problem is keeping money consistent while users, background jobs and the blockchain move at different speeds.
What the platform itself does
A user posts a buy or sell offer with a rate, limits and a payment window. Another participant opens a deal, sees the stored payment details and can use the deal chat. The flow then moves through acceptance, payment confirmation and release, cancellation or dispute. Administrators have tools for disputes and withdrawals.
This is a standalone Laravel application using Inertia and Vue, not merely a wallet microservice. It has its own listings, deals, balances, payment methods and transaction history, plus a web interface and REST API v2.
Preventing two deals from spending one listing
Imagine a listing with 100 units available and two clients each trying to take 80 at the same time. A browser-side balance check could allow both. The server instead locks the listing row with lockForUpdate() inside a MySQL transaction and reduces its available amount before creating the draft deal. Once the remainder falls below the minimum, the listing closes. A background job cancels an unconfirmed draft.
After payment, only the crypto seller can release a deal, and only when its status is paid. The same database transaction deducts the seller's frozen balance and credits the buyer. Cancellation restores the listing limit and releases frozen funds according to the deal state.
Listing
Rate, available volume, minimum amount, payment window and status.
Deal
Draft → acceptance → payment → release, cancellation or dispute.
Funds
Available and frozen balances change inside database transactions.
Why wallets live in a separate service
P2P maintains obligations to users; GRAM handles TON wallets and network operations. A signed service-to-service API connects them. The platform code contains separate TON and Jetton deposit handlers, withdrawal services, ledgers and audit procedures. A network event cannot simply become a user balance: its origin, uniqueness and exact amount must first be checked.
For TON, the deposit identity derives from the network, wallet address, transaction LT and normalized hash. The request must match its Idempotency-Key. A repeated event returns the existing result; an identifier reused with different data causes a conflict. The deposit record, balance credit and ledger entry are written in one database transaction.
Jetton amounts use atomic units to avoid precision loss. A withdrawal first reserves funds; confirmation deducts both the reserve and balance, while failure releases the reserve. Each transition has an idempotency key in the ledger.
Security and operations
The application includes email verification, 2FA, a PIN for sensitive actions, account recovery, Telegram integration and administrative controls. Laravel Reverb handles real-time events. Queues, scheduler, MySQL, Redis and Nginx run as separate Docker services.
An audit of this deployment on 22 September 2026 found a testnet configuration with no users, deals or deposits. Some TON/Jetton integration flags are disabled. This case therefore describes the implemented architecture and code paths, not a verified trading volume or a live mainnet exchange. No load or commercial metrics are claimed.
Engineering outcome
The system separates three kinds of truth: the P2P deal state, its internal accounting and a confirmed blockchain event. Row locks, state transitions, reservations and idempotency connect them. This makes it possible to handle a user dispute, a retried deposit callback and a failed withdrawal as distinct events.
Related technical material: creating and linking wallets, receiving TON deposits and treasury collection. The Crypto / FinTech blog contains shorter notes on the platform mechanics.











