How to build a transfer
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: "<RECIPIENT_ADDRESS>",
senderAddr: "<SENDER_ADDRESS>",
commentToSender: "Payment for Order #1234",
commentToRecipient: "Order #1234",
},
{
chain: "testnet",
apiKey: "<TONPAY_API_KEY>", // optional
}
);The TON Pay API key is optional, but providing 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; // optional
};stringdefault: mainnetTarget blockchain network. Use "mainnet" for production or "testnet" for development and testing.
stringThe optional TON Pay API key from the Merchant Dashboard. When provided, it enables:
- Transaction visibility in the TON Pay Merchant Dashboard.
- Webhook notifications for completed transactions.
- Receiving wallet management from the Merchant Dashboard.
Parameter details
numberrequiredHuman-readable payment amount. Decimals are allowed, for example, 10.5 TON.
stringrequiredAsset to transfer. Use "TON" for Toncoin or a jetton master address or constant, for example, USDT.
Mainnet address risk
Token constants such as USDT always reference mainnet jetton master addresses and are not affected by the chain option. Using them on testnet may send transactions to mainnet contracts.
For testnet, explicitly pass the correct testnet jetton master address instead of using token constants.
stringPayee wallet address. Optional if an API key is provided. Defaults to the merchant’s default wallet address configured in the Merchant Dashboard.
stringrequiredPayer wallet address. In UI flows, obtain it from TON Connect.
numberJetton only. Numeric identifier embedded into the jetton payload for idempotency and tracking. Ignored for Toncoin payments.
stringShort note visible to the user in the wallet while signing.
Comments are on-chain and publicly visible in blockchain explorers. Do not include confidential data. Keep comments under 120 characters to avoid increased gas fees.
stringNote visible to the recipient after the transfer is received.
Comments are on-chain and publicly visible in blockchain explorers. Do not include confidential data. Keep comments under 120 characters to avoid increased gas fees.
Predefined asset constants
Built-in constants can be used instead of raw addresses.
import { createTonPayTransfer, TON, USDT } from "@ton-pay/api";
// Toncoin transfer
await createTonPayTransfer(
{
amount: 1,
asset: TON, // same as "TON"
recipientAddr: "<RECIPIENT_ADDRESS>", // shortened example; replace with a full wallet address
senderAddr: "<SENDER_ADDRESS>", // shortened example; replace with a full wallet address
},
{
chain: "testnet",
apiKey: "<TONPAY_API_KEY>", // optional
}
);
// USDT jetton transfer
await createTonPayTransfer(
{
amount: 25,
asset: USDT, // mainnet USDT jetton master address
recipientAddr: "<RECIPIENT_ADDRESS>", // shortened example; replace with a full wallet address
senderAddr: "<SENDER_ADDRESS>", // shortened example; replace with a full wallet address
},
{
chain: "testnet",
apiKey: "<TONPAY_API_KEY>", // optional
}
);Mainnet address risk
In asset, token constants such as USDT always reference mainnet jetton master addresses and are not affected by the chain option. Using them on testnet may send transactions to mainnet contracts.
For testnet, explicitly pass the correct testnet jetton master address instead of using token constants.
Response:
type CreateTonPayTransferResponse = {
message: {
address: string;
amount: string;
payload: string;
};
bodyBase64Hash: string;
reference: string;
};Response fields
messageobjectrequiredPrebuilt TON Connect transaction message. Intended to be passed to sendTransaction as messages: [message].
bodyBase64HashstringrequiredBase64 hash of the signed message body content (payload). Used with getTonPayTransferByBodyHash.
referencestringrequiredTracking reference string. Used with getTonPayTransferByReference.
The SDK call returns a ready-to-send message along with identifiers for subsequent status lookups. Always persist tracking identifiers server-side before sending the transaction to the user.
Optional API key configuration
The TON Pay API key is optional but enables merchant features, including transaction visibility in the dashboard, webhook notifications, and centralized wallet management in the TON Pay Merchant Dashboard.
Obtain an optional API key
Open the Merchant Dashboard
Open the TON Pay Merchant Dashboard and sign in to the merchant account.
Open Developer settings
Navigate to Developer → API Keys sections to set up the optional API key.
Generate or copy an optional API key
Generate a new API key (optional) or copy an existing one and 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 the Merchant Dashboard
senderAddr: userWalletAddress,
},
{
chain: "testnet",
apiKey: "<TONPAY_API_KEY>", // optional
}
);Optional API key capabilities
| Capability | With optional API key | Without API key |
|---|---|---|
| Transaction visibility and monitoring | Transfers appear in the TON Pay Merchant Dashboard with status tracking and full transaction history. | Transfers are processed on-chain but are not visible in the Merchant dashboard. |
| Webhook notifications | Real-time HTTP POST notifications are sent for completed and failed payments. | No webhook notifications; payment status must be obtained through the manual polling. |
| Receiving wallet management | Receiving wallets are managed from the TON Pay Merchant Dashboard; addresses can be updated without code changes. | Receiving wallet addresses must be hard-coded in the application. |
recipientAddr handling | Optional; when omitted, the merchant’s default wallet from the Merchant Dashboard is used automatically. | Required for every transfer. |
With optional API key: recipientAddr is optional.
const result = await createTonPayTransfer(
{
amount: 10,
asset: "TON",
senderAddr: "<SENDER_ADDRESS>",
},
{
chain: "testnet",
apiKey: "<TONPAY_API_KEY>", // optional
}
);Without API key (the API key remains optional): recipientAddr is required.
// Works without API key (it is optional) - transaction processes normally
const result = await createTonPayTransfer(
{
amount: 10,
asset: "TON",
recipientAddr: "<RECIPIENT_ADDRESS>",
senderAddr: "<SENDER_ADDRESS>",
},
{ chain: "testnet" } // No optional apiKey - transaction works but no dashboard features
);Testnet configuration
Funds at risk
Running tests on mainnet can result in irreversible loss of real TON. Always use chain: "testnet" and testnet wallet addresses during development. Verify the network before switching to mainnet.
Set up testnet
Set chain to testnet
Configure the chain option to "testnet" in the API calls:
const { message, reference, bodyBase64Hash } = await createTonPayTransfer(
{
amount: 5.0,
asset: "TON",
recipientAddr: "<RECIPIENT_ADDRESS>",
senderAddr: "<SENDER_ADDRESS>",
},
{
chain: "testnet", // Use testnet
apiKey: "<TONPAY_API_KEY>", // optional
}
);Use testnet wallet addresses
Ensure all wallet addresses are valid testnet addresses.
Obtain testnet TON
- Use testnet wallet account: Tonkeeper or other TON wallets.
- Get testnet TON from a faucet to test transactions.
Configure testnet jettons
If testing with jettons, use the correct testnet jetton master addresses; not mainnet addresses.
// Example: Testnet USDT (use actual testnet address)
await createTonPayTransfer(
{
amount: 10,
asset: "<TESTNET_USDT_MASTER_ADDRESS>", // Testnet jetton address
recipientAddr: "<RECIPIENT_ADDRESS>",
senderAddr: "<SENDER_ADDRESS>",
},
{ chain: "testnet" }
);Configure environment
Use environment variables to 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 the application code
const { message, reference, bodyBase64Hash } = await createTonPayTransfer(
{
amount: orderAmount,
asset: "TON",
recipientAddr: "<WALLET_ADDRESS>",
senderAddr: "<CUSTOMER_WALLET_ADDRESS>",
},
{
chain: "testnet",
apiKey: "<TONPAY_API_KEY>", // optional
}
);Verify testnet transactions
Verify testnet transactions using testnet block explorers:
// Replace txHash with the actual transaction hash.
// After transaction completes:
console.log(`View on explorer: https://testnet.tonscan.org/tx/${txHash}`);Apply testing best practices
- Test all payment outcomes on testnet, including success, failures, user rejections, and edge cases.
- Verify webhook endpoint correctly processes testnet webhooks; payload structure matches mainnet.
- Validate behavior across different amounts, including small, large, and fractional values.
- Ensure
referenceandbodyBase64Hashare persisted and usable for status lookups. - Exercise error paths such as insufficient balance, invalid addresses, and network issues.
Last updated on