How to add a TON Pay button using JS
Add a TON Pay button to a plain HTML/JavaScript page using the embed script or TON Pay client. The default button appearance is shown below.

Installation
Choose an installation method
Choose one of the installation methods:
-
npm
npm install @ton-pay/ui @tonconnect/ui -
CDN: no installation needed
<!-- Use the unpkg CDN directly in HTML --> <script src="https://unpkg.com/@ton-pay/ui@0.1.1/dist/ton-pay-embed.js"></script> -
Local copy
After npm install, copy
node_modules/@ton-pay/ui/dist/ton-pay-embed.jsto a public assets folder. For example, the site'spublic/directory. Then include it with a<script src="...">tag in HTML.
Create a TON Connect manifest
Create a tonconnect-manifest.json file and host it on a public HTTPS URL.
{
"url": "<APP_URL>",
"name": "<APP_NAME>",
"iconUrl": "<APP_ICON_URL>"
}Placeholders:
<APP_URL>– public HTTPS URL of the app.<APP_NAME>– display name shown in the wallet.<APP_ICON_URL>– public HTTPS URL of the app icon.
Option 1: embed script
Placeholders:
<CONTAINER_ID>– HTML element ID where the button is mounted.<CALLBACK_NAME>– global function name invoked by the embed script.<RECIPIENT_ADDR>– recipient wallet address.
Add the container
<div id="<CONTAINER_ID>"></div>Add the embed script
<script src="https://unpkg.com/@ton-pay/ui@0.1.1/dist/ton-pay-embed.js?preset=gradient&variant=long&borderRadius=8&containerId=<CONTAINER_ID>&callback=<CALLBACK_NAME>"></script>Add the import map
<script type="importmap">
{
"imports": {
"@tonconnect/ui": "https://esm.sh/@tonconnect/ui@2.0.9"
}
}
</script>Create the payment handler
<script type="module">
import { createTonPay } from "https://unpkg.com/@ton-pay/ui@0.1.1/dist/ton-pay-vanilla.mjs";
// Initialize TonPay client
const tonPay = createTonPay({
manifestUrl: "https://<APP_URL>/tonconnect-manifest.json"
});
// Define payment handler
window["<CALLBACK_NAME>"] = async () => {
try {
const result = await tonPay.pay(async (senderAddr) => {
// Build the payment message
const message = {
address: "<RECIPIENT_ADDR>",
amount: "1000000", // 0.001 TON in nanotons
payload: "dGVzdA==", // optional base64 payload
};
return { message };
});
console.log("Payment successful:", result.txResult);
alert("Payment sent!");
} catch (error) {
console.error("Payment failed:", error);
alert("Payment failed: " + error.message);
}
};
</script>The callback parameter in the embed script must match the global function name in the handler.
Option 2: use TON Pay client
Use createTonPay for custom UI and payment flow control.
Set up the HTML
<!DOCTYPE html>
<html>
<head>
<title>My TON Payment App</title>
<script type="importmap">
{
"imports": {
"@tonconnect/ui": "https://esm.sh/@tonconnect/ui@2.0.9"
}
}
</script>
</head>
<body>
<button id="pay-btn">Pay with TON</button>
<div id="status"></div>
</body>
</html>Import the TON Pay client and create an instance
<script type="module">
import { createTonPay } from "https://unpkg.com/@ton-pay/ui@0.1.1/dist/ton-pay-vanilla.mjs";
const tonPay = createTonPay({
manifestUrl: "https://<APP_URL>/tonconnect-manifest.json",
connectTimeoutMs: 300000 // 5 minutes (optional)
});
</script>Implement the payment logic
<script type="module">
const payButton = document.getElementById("pay-btn");
const statusDiv = document.getElementById("status");
payButton.addEventListener("click", async () => {
payButton.disabled = true;
payButton.textContent = "Processing...";
try {
const result = await tonPay.pay(async (senderAddr) => {
statusDiv.textContent = `Sending from ${senderAddr}...`;
// Build payment message
const message = {
address: "<RECIPIENT_ADDR>",
amount: "1000000",
};
return { message };
});
statusDiv.textContent = "Payment successful!";
console.log("Transaction:", result.txResult);
} catch (error) {
statusDiv.textContent = "Payment failed: " + error.message;
console.error(error);
} finally {
payButton.disabled = false;
payButton.textContent = "Pay with TON";
}
});
</script>createTonPay opens TON Connect modal when pay() is called and no wallet is connected.
Create messages with createTonPayTransfer
Use createTonPayTransfer to build a canonical payment message with tracking identifiers.
Combine the snippets in this section into one HTML page.
Add the API package script
<script type="module">
import { createTonPayTransfer } from "https://unpkg.com/@ton-pay/api@0.2.1/dist/index.mjs";
import { createTonPay } from "https://unpkg.com/@ton-pay/ui@0.1.1/dist/ton-pay-vanilla.mjs";
</script>Use the API in the payment handler
<script type="module">
const tonPay = createTonPay({
manifestUrl: "https://<APP_URL>/tonconnect-manifest.json"
});
window["<CALLBACK_NAME>"] = async () => {
try {
const result = await tonPay.pay(async (senderAddr) => {
// Create payment with tracking
const { message, reference, bodyBase64Hash } =
await createTonPayTransfer(
{
amount: 3.5,
asset: "TON",
recipientAddr: "<RECIPIENT_ADDR>",
senderAddr,
commentToSender: "Order #12345",
},
{ chain: "mainnet" }
);
// Store tracking identifiers
console.log("Reference:", reference);
console.log("Hash:", bodyBase64Hash);
return { message, reference, bodyBase64Hash };
});
alert("Payment sent! Reference: " + result.reference);
} catch (error) {
alert("Payment failed: " + error.message);
}
};
</script>Store reference and bodyBase64Hash to track payment status via the webhooks guide.
Embed script parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
containerId | string | "ton-pay-btn" | Target element ID where the button renders. |
preset | "default" | "gradient" | - | Built-in theme preset. |
bgColor | string | "#0098EA" | Background color in hex or CSS gradient. URL-encode the value. |
textColor | string | "#FFFFFF" | Text and icon color. |
variant | "long" | "short" | "long" | Button text variant. |
text | string | - | Custom button text. Overrides variant. |
loadingText | string | "Processing..." | Text shown during loading. |
borderRadius | number | 8 | Border radius in pixels. |
fontFamily | string | "inherit" | CSS font-family value. |
width | number | 300 | Button width in pixels. |
height | number | 44 | Button height in pixels. |
showMenu | boolean | true | Show dropdown menu with wallet actions. |
callback | string | - | Global function name called on click. |
Examples
<script src="https://unpkg.com/@ton-pay/ui@0.1.1/dist/ton-pay-embed.js?preset=default&variant=long&borderRadius=8&containerId=btn-1&callback=handlePayment"></script><script src="https://unpkg.com/@ton-pay/ui@0.1.1/dist/ton-pay-embed.js?preset=gradient&variant=short&borderRadius=12&containerId=btn-2&callback=handlePayment"></script><script src="https://unpkg.com/@ton-pay/ui@0.1.1/dist/ton-pay-embed.js?bgColor=%23000000&textColor=%23FFFFFF&borderRadius=99&containerId=btn-3&callback=handlePayment"></script><script src="https://unpkg.com/@ton-pay/ui@0.1.1/dist/ton-pay-embed.js?preset=gradient&width=400&height=56&containerId=btn-4&callback=handlePayment"></script>URL-encode special characters in parameters. For example, # becomes %23 in color values.
TonPayEmbed API
The embed script exposes a global TonPayEmbed object for programmatic control.
TonPayEmbed.mount(config)
Update button configuration dynamically.
TonPayEmbed.mount({
preset: "gradient",
variant: "short",
borderRadius: 12,
width: 350
});TonPayEmbed.setCallback(functionName)
Change the callback function.
TonPayEmbed.setCallback("newPaymentHandler");TonPayEmbed.setAddress(address)
Update the displayed wallet address in the menu.
TonPayEmbed.setAddress("<RECIPIENT_ADDR>");TonPayEmbed.click()
Trigger a button click programmatically.
TonPayEmbed.click();Example: dynamic configuration
<div id="<CONTAINER_ID>"></div>
<button onclick="changeTheme()">Change Theme</button>
<script src="https://unpkg.com/@ton-pay/ui@0.1.1/dist/ton-pay-embed.js?containerId=<CONTAINER_ID>&callback=<CALLBACK_NAME>"></script>
<script>
function changeTheme() {
TonPayEmbed.mount({
preset: "gradient",
variant: "short",
borderRadius: 99
});
}
</script>TonPay client API
createTonPay returns a client for wallet connection and payments.
Properties
-
address: string | nullCurrent wallet address.
const tonPay = createTonPay({ manifestUrl: "https://<APP_URL>/tonconnect-manifest.json" }); console.log(tonPay.address); // null or wallet address
Methods
-
waitForWalletConnection(): Promise<string>Wait for a wallet connection and open the modal if needed.
try { const address = await tonPay.waitForWalletConnection(); console.log("Connected:", address); } catch (error) { console.error("Connection failed:", error); } -
pay(getMessage): Promise<PayResult>Execute a payment transaction.
const result = await tonPay.pay(async (senderAddr) => { const message = { address: "<RECIPIENT_ADDR>", amount: "1000000" }; return { message }; }); console.log(result.txResult); -
disconnect(): Promise<void>Disconnect the current wallet.
await tonPay.disconnect();
Framework integration
WordPress
<!-- Add to the theme footer or a custom HTML block -->
<div id="<CONTAINER_ID>"></div>
<script type="importmap">
{
"imports": {
"@tonconnect/ui": "https://esm.sh/@tonconnect/ui@2.0.9"
}
}
</script>
<script type="module">
import { createTonPay } from "https://unpkg.com/@ton-pay/ui@0.1.1/dist/ton-pay-vanilla.mjs";
const tonPay = createTonPay({ manifestUrl: "https://<APP_URL>/tonconnect-manifest.json" });
window["<CALLBACK_NAME>"] = async () => { /* payment handler */ };
</script>
<script src="https://unpkg.com/@ton-pay/ui@0.1.1/dist/ton-pay-embed.js?containerId=<CONTAINER_ID>&callback=<CALLBACK_NAME>"></script>Plain HTML/CSS/JS
Use the complete example of the embed script in a single HTML file.
Build tools: Webpack and Vite
import { createTonPay } from "@ton-pay/ui/vanilla";
const tonPay = createTonPay({
manifestUrl: "https://<APP_URL>/tonconnect-manifest.json"
});
// Use tonPay.pay() in event handlersBest practices
- Wrap payment calls in try-catch blocks and display clear error messages.
- Update the UI during payment processing to prevent repeated clicks.
- Check amounts, addresses, and input before calling the payment function.
- Use HTTPS in production. TON Connect requires HTTPS for the manifest URL and callbacks.
- Save
referenceandbodyBase64HashfromcreateTonPayTransferto track payments via webhooks. - Test with testnet first and handle error scenarios before going live.
Troubleshooting
Last updated on