# How to add a TON Pay button using JS (https://docs-kyrm16yq7-ton-core-docs.vercel.app/llms/ecosystem/ton-pay/ui-integration/button-js/content.md)



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.

<div style="{ display: &#x22;flex&#x22;, justifyContent: &#x22;center&#x22; }">
  <Image src="/images/ton-pay/button.png" alt="TON Pay button" width="300" noZoom="true" />
</div>

## Installation [#installation]

<Steps>
  <Step>
    ### Choose an installation method [#choose-an-installation-method]

    Choose one of the installation methods:

    1. npm

       ```bash
       npm install @ton-pay/ui @tonconnect/ui
       ```

    2. CDN: no installation needed

       ```html
       <!-- Use the unpkg CDN directly in HTML -->
       <script src="https://unpkg.com/@ton-pay/ui@0.1.1/dist/ton-pay-embed.js"></script>
       ```

    3. Local copy

       After npm install, copy `node_modules/@ton-pay/ui/dist/ton-pay-embed.js` to a public assets folder. For example, the site's `public/` directory.
       Then include it with a `<script src="...">` tag in HTML.
  </Step>

  <Step>
    ### Create a TON Connect manifest [#create-a-ton-connect-manifest]

    Create a [`tonconnect-manifest.json` file](/llms/ecosystem/ton-connect/manifest/content.md) and host it on a public HTTPS URL.

    ```json
    {
      "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.
  </Step>
</Steps>

## Option 1: embed script [#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.

<Steps>
  <Step>
    ### Add the container [#add-the-container]

    ```html
    <div id="<CONTAINER_ID>"></div>
    ```
  </Step>

  <Step>
    ### Add the embed script [#add-the-embed-script]

    ```html
    <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>
    ```
  </Step>

  <Step>
    ### Add the import map [#add-the-import-map]

    ```html
    <script type="importmap">
      {
          "imports": {
          "@tonconnect/ui": "https://esm.sh/@tonconnect/ui@2.0.9"
        }
      }
    </script>
    ```
  </Step>

  <Step>
    ### Create the payment handler [#create-the-payment-handler]

    ```html
    <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>
    ```
  </Step>
</Steps>

<Callout type="note">
  The `callback` parameter in the embed script must match the global function name in the handler.
</Callout>

## Option 2: use TON Pay client [#option-2-use-ton-pay-client]

Use `createTonPay` for custom UI and payment flow control.

<Steps>
  <Step>
    ### Set up the HTML [#set-up-the-html]

    ```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>
    ```
  </Step>

  <Step>
    ### Import the TON Pay client and create an instance [#import-the-ton-pay-client-and-create-an-instance]

    ```html
    <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>
    ```
  </Step>

  <Step>
    ### Implement the payment logic [#implement-the-payment-logic]

    ```html
    <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>
    ```

    <Callout type="note">
      `createTonPay` opens TON Connect modal when `pay()` is called and no wallet is connected.
    </Callout>
  </Step>
</Steps>

## Create messages with createTonPayTransfer [#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.

<Steps>
  <Step>
    ### Add the API package script [#add-the-api-package-script]

    ```html
    <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>
    ```
  </Step>

  <Step>
    ### Use the API in the payment handler [#use-the-api-in-the-payment-handler]

    ```html
    <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>
    ```
  </Step>
</Steps>

<Callout type="note">
  Store `reference` and `bodyBase64Hash` to track payment status via the [webhooks guide](/llms/ecosystem/ton-pay/webhooks/content.md).
</Callout>

## Embed script parameters [#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 [#examples]

<CodeGroup>
  <CodeBlockTabs defaultValue="Default blue button">
    <CodeBlockTabsList>
      <CodeBlockTabsTrigger value="Default blue button">
        Default blue button
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Gradient button">
        Gradient button
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Custom colors">
        Custom colors
      </CodeBlockTabsTrigger>

      <CodeBlockTabsTrigger value="Custom size">
        Custom size
      </CodeBlockTabsTrigger>
    </CodeBlockTabsList>

    <CodeBlockTab value="Default blue button">
      ```html
      <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>
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Gradient button">
      ```html
      <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>
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Custom colors">
      ```html
      <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>
      ```
    </CodeBlockTab>

    <CodeBlockTab value="Custom size">
      ```html
      <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>
      ```
    </CodeBlockTab>
  </CodeBlockTabs>
</CodeGroup>

<Callout type="caution">
  URL-encode special characters in parameters. For example, `#` becomes `%23` in color values.
</Callout>

## TonPayEmbed API [#tonpayembed-api]

The embed script exposes a global `TonPayEmbed` object for programmatic control.

### `TonPayEmbed.mount(config)` [#tonpayembedmountconfig]

Update button configuration dynamically.

```js
TonPayEmbed.mount({
  preset: "gradient",
  variant: "short",
  borderRadius: 12,
  width: 350
});
```

### `TonPayEmbed.setCallback(functionName)` [#tonpayembedsetcallbackfunctionname]

Change the callback function.

```js
TonPayEmbed.setCallback("newPaymentHandler");
```

### `TonPayEmbed.setAddress(address)` [#tonpayembedsetaddressaddress]

Update the displayed wallet address in the menu.

```js
TonPayEmbed.setAddress("<RECIPIENT_ADDR>");
```

### `TonPayEmbed.click()` [#tonpayembedclick]

Trigger a button click programmatically.

```js
TonPayEmbed.click();
```

### Example: dynamic configuration [#example-dynamic-configuration]

```html
<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 [#tonpay-client-api]

`createTonPay` returns a client for wallet connection and payments.

### Properties [#properties]

* `address: string | null`

  Current wallet address.

  ```js
  const tonPay = createTonPay({ manifestUrl: "https://<APP_URL>/tonconnect-manifest.json" });
  console.log(tonPay.address); // null or wallet address
  ```

### Methods [#methods]

* `waitForWalletConnection(): Promise<string>`

  Wait for a wallet connection and open the modal if needed.

  ```js
  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.

  ```js
  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.

  ```js
  await tonPay.disconnect();
  ```

## Framework integration [#framework-integration]

### WordPress [#wordpress]

```html
<!-- 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 [#plain-htmlcssjs]

Use the complete example of the embed script in a single HTML file.

### Build tools: Webpack and Vite [#build-tools-webpack-and-vite]

```js
import { createTonPay } from "@ton-pay/ui/vanilla";

const tonPay = createTonPay({
  manifestUrl: "https://<APP_URL>/tonconnect-manifest.json"
});

// Use tonPay.pay() in event handlers
```

## Best practices [#best-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 `reference` and `bodyBase64Hash` from `createTonPayTransfer` to track payments via webhooks.
* Test with testnet first and handle error scenarios before going live.

## Troubleshooting [#troubleshooting]

<Accordions>
  <Accordion title="If the button does not render">
    1. Verify that `containerId` matches the target div ID
    2. Check the browser console for errors
    3. Ensure the script loads after the container div
    4. Verify that the script URL is correct and accessible
  </Accordion>

  <Accordion title="If the callback is not invoked">
    1. Ensure the callback function is defined on the `window` object
    2. Verify that the function name matches the `callback` parameter exactly
    3. Define the callback before the embed script runs
  </Accordion>

  <Accordion title="If import map erros occur">
    1. Add the import map before any module scripts
    2. Verify that the `@tonconnect/ui` version is compatible
    3. Check that the import map syntax is valid
  </Accordion>

  <Accordion title="If CORS errors occur">
    1. Serve the manifest from the same domain or enable CORS
    2. Use HTTPS in production
    3. Verify that CDN resources are accessible
  </Accordion>
</Accordions>
