Skip to main content
Use this server-side helper to generate a canonical TON message payload and a tracking reference.
import { createTonPayTransfer } from "@ton-pay/api";

const { message, bodyBase64Hash, reference } = await createTonPayTransfer(
  {
    amount: 10.5,
    asset: "TON",
    recipientAddr: "EQC...RECIPIENT",
    senderAddr: "EQC...SENDER",
    commentToSender: "Payment for Order #1234",
    commentToRecipient: "Order #1234",
  },
  {
    chain: "mainnet",
    apiKey: "yourTonPayApiKey",
  }
);
The API key is optional but recommended. It enables transaction visibility in the TON Pay Merchant Dashboard, webhook notifications, and wallet management features.

Function Signature

createTonPayTransfer(
  params: CreateTonPayTransferParams,
  options?: APIOptions
): Promise<CreateTonPayTransferResponse>

Transfer Parameters

type CreateTonPayTransferParams = {
  amount: number;
  asset: string;
  recipientAddr?: string;
  senderAddr: string;
  queryId?: number;
  commentToSender?: string;
  commentToRecipient?: string;
};

API Options

type APIOptions = {
  chain?: "mainnet" | "testnet";
  apiKey?: string;
};
chain
string
default:"mainnet"
Target blockchain network. Use "mainnet" for production or "testnet" for development and testing.
apiKey
string
Your TON Pay API key from the Merchant Dashboard. Optional, but required to:
  • View transactions in the TON Pay Merchant Dashboard
  • Receive webhook notifications about completed transactions
  • Manage receiving wallets from the admin panel

Parameter details

amount
number
required
Human-readable payment amount. Decimals allowed (e.g., 10.5).
asset
string
required
Asset to transfer. Use “TON” for TON coin or a jetton master address/constant (e.g., USDT).
Token constants (e.g., USDT) contain MAINNET jetton master addresses and are not affected by the chain option. For testnet, pass the correct master address explicitly.
recipientAddr
string
Payee wallet address. Optional if API key is provided - defaults to the merchant’s default wallet address configured in the admin panel.
senderAddr
string
Payer wallet address. Required for jetton transfers; optional for TON coin. In UI flows obtain it from TonConnect.
queryId
number
Jetton only. Numeric identifier embedded into the jetton payload for idempotency/tracking. Ignored for TON coin.
commentToSender
string
Short note visible to the user in their wallet while signing.
Comments are on-chain and publicly visible in blockchain explorers. Do not include confidential data. We also recommend keeping comments under 120 characters to avoid increased gas fees.
commentToRecipient
string
Note visible to the recipient after the transfer is received.
Comments are on-chain and publicly visible in blockchain explorers. Do not include confidential data. We also recommend keeping comments under 120 characters to avoid increased gas fees.

Predefined assets (constants)

You can use built-in constants instead of raw addresses.
import { createTonPayTransfer, TON, USDT } from "@ton-pay/api";

// TON coin transfer
await createTonPayTransfer(
  {
    amount: 1,
    asset: TON, // same as "TON"
    recipientAddr: "EQC...RECIPIENT",
    senderAddr: "EQC...SENDER",
  },
  {
    chain: "mainnet",
    apiKey: "yourTonPayApiKey", // optional
  }
);

// USDT jetton transfer
await createTonPayTransfer(
  {
    amount: 25,
    asset: USDT, // mainnet USDT jetton master address
    recipientAddr: "EQC...RECIPIENT",
    senderAddr: "EQC...SENDER",
  },
  {
    chain: "mainnet",
    apiKey: "yourTonPayApiKey",
  }
);
Token constants (e.g., USDT) contain MAINNET jetton master addresses and are not affected by the chain option. If you target testnet or a different deployment, pass the correct jetton master address explicitly in asset.
Response
type CreateTonPayTransferResponse = {
  message: {
    address: string;
    amount: string;
    payload: string;
  };
  bodyBase64Hash: string;
  reference: string;
};

Response fields

message
object
required
Built transaction message for TonConnect. Pass it to sendTransaction as messages: [message].
bodyBase64Hash
string
required
Base64 hash of the signed message body content (payload). Persist and use with getTonPayTransferByBodyHash.
reference
string
required
Tracking reference string. Persist and use with getTonPayTransferByReference.
The SDK call returns a ready-to-send message plus identifiers for later lookups. Always persist tracking identifiers server-side before sending the transaction to the user.

API Key Configuration

The TON Pay API key is optional but highly recommended for production use. It unlocks essential merchant features including transaction tracking, webhook notifications, and centralized wallet management.

Obtaining Your API Key

1

Access Merchant Dashboard

Navigate to tonpay.tech/dashboard and log in to your merchant account.
2

Open Developer Settings

Go to DeveloperAPI Keys section.
3

Generate or Copy API Key

Generate a new API key or copy your existing key. Store it securely.

Usage in Code

import { createTonPayTransfer } from "@ton-pay/api";

const { message, reference, bodyBase64Hash } = await createTonPayTransfer(
  {
    amount: 10.5,
    asset: "TON",
    // recipientAddr is optional when API key is provided
    // Will use merchant's default wallet from admin panel
    senderAddr: userWalletAddress,
  },
  {
    chain: "testnet",
    apiKey: "yourTonPayApiKey",
  }
);

Why Use an API Key

The API key is optional, but using it provides access to important merchant features:
When you create transfers with an API key, they appear in your TON Pay Merchant Dashboard. You can monitor all transactions, track payment statuses, and access detailed transaction history.Without API key: Transactions are processed but not visible in your merchant account.
API key enables webhook notifications for completed transactions. Receive real-time HTTP POST requests when payments succeed or fail.Without API key: No webhook notifications will be sent, requiring manual status polling.
API key allows you to manage receiving wallet addresses from the TON Pay admin panel. Centralize wallet configuration and update addresses without code changes. When an API key is provided and recipientAddr is omitted, the merchant’s default wallet address from the admin panel will be used automatically.Without API key: You must hard-code recipient addresses in your application.
// With API key - recipientAddr is optional
const result = await createTonPayTransfer(
  {
    amount: 10,
    asset: "TON",
    // recipientAddr omitted - uses merchant's default wallet
    senderAddr: "EQC...SENDER",
  },
  { 
    chain: "mainnet",
    apiKey: "yourTonPayApiKey" 
  }
);
You can create and send TON Pay transfers without an API key. The blockchain transaction will work normally, but you won’t have access to merchant dashboard features.
// Works without API key - transaction processes normally
const result = await createTonPayTransfer(
  {
    amount: 10,
    asset: "TON",
    recipientAddr: "EQC...RECIPIENT",
    senderAddr: "EQC...SENDER",
  },
  { chain: "mainnet" } // No apiKey - transaction works but no dashboard features
);

Testnet Configuration

For development and testing purposes, configure your integration to use the TON testnet before deploying to mainnet.

Setting Up Testnet

1

Set Chain to Testnet

Configure the chain option to "testnet" in your API calls:
const { message, reference, bodyBase64Hash } = await createTonPayTransfer(
  {
    amount: 5.0,
    asset: "TON",
    recipientAddr: "EQC...TESTNET_RECIPIENT",
    senderAddr: testnetWalletAddress,
  },
  {
    chain: "testnet", // Use testnet
    apiKey: "yourTonPayApiKey",
  }
);
2

Use Testnet Wallet Addresses

Ensure all wallet addresses are valid testnet addresses. Testnet addresses have the same format as mainnet but represent different accounts on the test network.
3

Obtain Testnet TON

Get testnet TON from a faucet to test transactions:
4

Configure Testnet Jettons

If testing with jettons (tokens), use the correct testnet jetton master addresses, not mainnet addresses.
// Example: Testnet USDT (use actual testnet address)
await createTonPayTransfer(
  {
    amount: 10,
    asset: "EQC...TESTNET_USDT_MASTER", // Testnet jetton address
    recipientAddr: "EQC...RECIPIENT",
    senderAddr: "EQC...SENDER",
  },
  { chain: "testnet" }
);

Environment-Based Configuration

Use environment variables to easily switch between mainnet and testnet:
// .env.development
TON_CHAIN=testnet
MERCHANT_WALLET_ADDRESS=EQC...TESTNET_ADDRESS

// .env.production
TON_CHAIN=mainnet
MERCHANT_WALLET_ADDRESS=EQC...MAINNET_ADDRESS
// In your application code
const { message, reference, bodyBase64Hash } = await createTonPayTransfer(
  {
    amount: orderAmount,
    asset: "TON",
    recipientAddr: "EQ....yourWalletAddress",
    senderAddr: customerWalletAddress,
  },
  {
    chain: "testnet",
    apiKey: "yourTonPayApiKey",
  }
);

Testnet Limitations and Differences

Important Differences Between Testnet and Mainnet:
  • Testnet transactions have no real monetary value
  • Testnet may be less stable and can be reset periodically
  • Some third-party services may not support testnet
  • Webhook delivery may be slower on testnet
  • Rate limits may differ between testnet and mainnet

Verifying Testnet Transactions

You can verify testnet transactions using testnet block explorers:
// After transaction completes
console.log(`View on explorer: https://testnet.tonscan.org/tx/${txHash}`);

Best Practices for Testing

Test successful payments, failed transactions, user rejections, and edge cases on testnet before mainnet deployment.
Ensure your webhook endpoint correctly processes testnet webhooks. The payload structure is identical to mainnet.
Test with various amounts including small values, large values, and decimal amounts to ensure proper handling.
Verify that reference and bodyBase64Hash are correctly stored and can be used to query transaction status.
Intentionally test failure cases like insufficient balance, invalid addresses, and network errors.