TON Integration Guide

This tutorial shows how to integrate T402 payments with the TON blockchain using Jetton (TEP-74) tokens.

Overview

TON uses Jettons for token transfers. The T402 TON implementation supports:

  • USDT Jetton on mainnet and testnet
  • Ed25519 signatures for payment authorization
  • Query ID based replay protection

Prerequisites

  • Node.js 18+ or Python 3.10+
  • TON wallet with testnet USDT
  • Basic understanding of TON concepts

Network Identifiers

NetworkCAIP-2 Identifier
TON Mainnetton:mainnet
TON Testnetton:testnet

USDT Addresses

NetworkJetton Master Address
MainnetEQCxE6mUtQJKFnGfaROTKOt1lZbDiiX1kCixRv7Nw2Id_sDs
TestnetkQD0GKBM8ZbryVk2aESmzfU6b9b_8era_IkvBSELujFZPsyy

Install Dependencies

npm install @t402/ton @t402/core @ton/ton @ton/crypto

Client Setup

Create a client that can sign TON payment authorizations.

import { t402Client } from '@t402/core/client';
import { ExactTonScheme, toClientTonSigner } from '@t402/ton';
import { mnemonicToPrivateKey } from '@ton/crypto';
import { WalletContractV4 } from '@ton/ton';
 
// Create wallet from mnemonic
const mnemonic = 'your 24 word mnemonic phrase here...'.split(' ');
const keyPair = await mnemonicToPrivateKey(mnemonic);
 
const wallet = WalletContractV4.create({
  publicKey: keyPair.publicKey,
  workchain: 0,
});
 
// Create TON signer
const tonSigner = toClientTonSigner({
  wallet,
  secretKey: keyPair.secretKey,
});
 
// Create t402 client
const client = new t402Client()
  .register('ton:mainnet', new ExactTonScheme(tonSigner))
  .register('ton:testnet', new ExactTonScheme(tonSigner));

Server Setup

Configure your server to accept TON payments.

import express from 'express';
import { paymentMiddleware } from '@t402/express';
import { t402ResourceServer, createFacilitatorClient } from '@t402/core/server';
import { registerExactTonServerScheme } from '@t402/ton/exact/server';
 
const app = express();
 
// Create facilitator client
const facilitator = createFacilitatorClient({
  url: 'https://facilitator.t402.io',
});
 
// Create t402 server
const server = new t402ResourceServer(facilitator);
registerExactTonServerScheme(server, {});
 
// Define routes with TON payment
const routes = {
  '/api/premium/*': {
    accepts: {
      scheme: 'exact',
      network: 'ton:mainnet',
      payTo: 'EQYourTonAddress',
      price: '$0.01',
    },
    description: 'Premium API access',
  },
};
 
// Apply middleware
app.use(paymentMiddleware(routes, server));
 
app.get('/api/premium/data', (req, res) => {
  res.json({ data: 'Premium content' });
});
 
app.listen(3000);

Make Payment Requests

import { wrapFetchWithPayment } from '@t402/fetch';
 
const fetchWithPay = wrapFetchWithPayment(fetch, client);
 
// Automatic payment handling
const response = await fetchWithPay('https://api.example.com/api/premium/data', {
  method: 'GET',
});
 
const data = await response.json();
console.log(data);

TON-Specific Concepts

Jetton Transfers

TON uses Jettons (TEP-74 standard) for token transfers. The payment flow:

  1. Client signs a Jetton transfer message
  2. Message includes query_id for replay protection
  3. Facilitator submits the message to the network
  4. Jetton master contract processes the transfer

Query ID

Each payment uses a unique query_id to prevent replay attacks:

import { generateQueryId } from '@t402/ton';
 
const queryId = generateQueryId();
// Returns: BigInt with timestamp-based unique ID

Gas Estimation

TON requires TON coins for gas fees:

import { estimateJettonTransferGas } from '@t402/ton';
 
const gasEstimate = estimateJettonTransferGas({
  amount: '1000000', // 1 USDT
  network: 'ton:mainnet',
});
// Returns: { tonAmount: '0.05', forwardAmount: '0.01' }

Address Formats

TON supports multiple address formats:

import { validateTonAddress, formatAddress } from '@t402/ton';
 
// Validate address
const isValid = validateTonAddress('EQDjv9CUEJ__D_3-3J4trQtqVklMBiNoGVSf3Fu6AaDGkEUe');
 
// Format address (bounceable/non-bounceable)
const formatted = formatAddress(address, { bounceable: true, testOnly: false });

Testing on Testnet

Get testnet USDT from the TON Testnet Faucet.

  1. Configure for testnet:
const routes = {
  '/api/test/*': {
    accepts: {
      scheme: 'exact',
      network: 'ton:testnet',
      payTo: 'kQYourTestnetAddress',
      price: '$0.001',
    },
  },
};
  1. Use testnet wallet and USDT

  2. Verify transactions on testnet.tonscan.org

Troubleshooting

Common Issues

IssueSolution
Invalid signatureEnsure wallet address matches signer
Insufficient gasAdd more TON to wallet for fees
Query ID collisionUse generateQueryId() for unique IDs
Network mismatchVerify CAIP-2 identifier matches wallet