Skip to main content

TON Connect manifest

Before installing and setting up the TON Pay SDK, the application must provide a TON Connect manifest, which is a JSON file that defines application metadata. Wallets use this manifest to discover the application.

First payment

1

Install necessary libraries

# API
npm i @ton-pay/api

# UI (install separately from API)
npm i @ton-pay/ui-react @tonconnect/ui-react
The UI package does not depend on the API. Install @ton-pay/api only when API helpers are required.
2

Add TON Connect provider

TON Pay UI uses TON Connect UI for wallet communication.The application must be wrapped with TonConnectUIProvider and configured with an absolute URL to the TON Connect manifest. Add TonConnectUIProvider at the root of the application.
import { TonConnectUIProvider } from '@tonconnect/ui-react';
import AppContent from "./AppContent";

export default function App() {
  return (
    <TonConnectUIProvider
      manifestUrl="https://myapp.com/tonconnect-manifest.json">
      <AppContent />
    </TonConnectUIProvider>
  );
}
3

Add a payment button

Add a TonPayButton and provide a handler. The handler uses useTonPay to connect a wallet if needed, send a transaction through TON Connect, and track the transaction result.
  • TonPayButton wraps wallet connect and disconnect UX and invokes the provided handler.
  • useTonPay accepts an async message factory that receives senderAddr and returns { message } along with any tracking fields to propagate.
The returned { message } is a TON Connect transaction message. useTonPay forwards it to the wallet through TON Connect and initiates the transaction send; direct calls to the wallet SDK are not required.
import { TonPayButton, useTonPay } from "@ton-pay/ui-react";
import { useTonConnectUI } from "@tonconnect/ui-react";
import { createTonPayTransfer } from "@ton-pay/api";

export default function PayButton() {
  const { pay } = useTonPay();
  const [tonConnectUI] = useTonConnectUI();

  async function createMessage(senderAddr: string) {
    const { message, reference } = await createTonPayTransfer(
      {
        amount: 12.34,
        asset: "TON",
        recipientAddr: "<WALLET_ADDRESS>",
        senderAddr,
        commentToSender: "<THE_ORDER>",
      },
      {
        chain: "testnet", // use "mainnet" for production
        apiKey: "<TONPAY_API_KEY>" // optional
      }
    );
    return { message, reference };
  }

  return (
    <TonPayButton handlePay={() => pay(createMessage)} />
  );
}
Client-only processing is fragileUsers can close the tab before transaction results are persisted, which breaks tracking and confirmation logic. Build messages and track transactions on the server side.
// Backend: POST /api/create-payment
import { createTonPayTransfer } from "@ton-pay/api";
import { TON } from "@ton-pay/api";

app.post("/api/create-payment", async (req, res) => {
    const { productId, senderAddr } = req.body;

    // Use the backend to create an order and calculate the amount using productId price
    const amount = 12.23; // order amount
    const orderId = 1; // ID of a new order

    // 1) Create transfer + get tracking identifiers
    const { message, reference, bodyBase64Hash } = await createTonPayTransfer(
        { amount, asset: TON, recipientAddr: "<WALLET_ADDRESS>", senderAddr },
        { chain: "testnet", apiKey: "<TONPAY_API_KEY>" }
    );

    // 2) Persist identifiers the DB (orderId, reference, bodyBase64Hash) immediately

    // 3) Return only the message to the client
    res.json({ message });
});

// Frontend
import { TonPayButton, useTonPay } from "@ton-pay/ui-react";

export function PayOrder({ productId }: { productId: any }) {
  const { pay } = useTonPay();

  async function createMessage(senderAddr: string) {
    const response = await fetch("/api/create-payment", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ productId, senderAddr }),
    });
    const { message } = await response.json();
    return { message };
  }

  return <TonPayButton handlePay={() => pay(createMessage)} />;
}
If an API key is available from the dashboard, pass it to createTonPayTransfer options to enable automatic tracking and webhook notifications.
This example shows a minimal loading state and how to surface the reference. If a transaction hash is available, an explorer link can be rendered.
import { useState } from "react";
import { TonPayButton, useTonPay } from "@ton-pay/ui-react";
import { createTonPayTransfer, getTonPayTransferByReference, type CompletedTonPayTransferInfo } from "@ton-pay/api";

export default function Checkout() {
    const { pay } = useTonPay();
    const [loading, setLoading] = useState(false);
    const [reference, setReference] = useState<string | null>(null);
    const [result, setResult] = useState<CompletedTonPayTransferInfo | null>(null);

    const options = { chain: "testnet", apiKey: "yourTonPayApiKey" } as const;

    async function createMessage(senderAddr: string) {
      const { message, reference } = await createTonPayTransfer(
        { amount: 12.34, asset: "TON", recipientAddr: "EQC...yourWalletAddress", senderAddr },
        options
      );
      setReference(reference);
      return { message, reference };
    }

    async function handlePay() {
      setLoading(true);
      try {
        const { reference } = await pay(createMessage);
          // Poll status until success
          for (;;) {
            try {
              const t = await getTonPayTransferByReference(reference, options);
              if (t.status === "success") {
                setResult(t);
                break;
              }
            } catch {}
            await new Promise(r => setTimeout(r, 1000));
          }
      } finally {
        setLoading(false);
      }
    }

    return (
      <>
        <TonPayButton handlePay={handlePay} isLoading={loading} />

        {reference && !result && <div>Payment sent. Reference: {reference}</div>}
        {result && <div>Payment successful! Tx hash: {result.txHash}</div>}
        {/* If a transaction hash is available, render an explorer link:
              <ExplorerLink txHash={result?.txHash!} />
         */}
      </>
    );
}
import { useTonWallet } from "@tonconnect/ui-react";
import { CHAIN } from "@tonconnect/ui";

function ExplorerLink({ txHash }: { txHash: string }) {
    const wallet = useTonWallet();
    const isTestnet = wallet?.account?.chain === CHAIN.TESTNET;
    const tonviewer = isTestnet
        ? `https://testnet.tonviewer.com/transaction/${txHash}`
        : `https://tonviewer.com/transaction/${txHash}`;
    const tonscan = isTestnet
        ? `https://testnet.tonscan.org/tx/${txHash}`
        : `https://tonscan.org/tx/${txHash}`;

    return (
        <div>
            <a href={tonviewer} target="_blank" rel="noreferrer">TonViewer</a>
            {" • "}
            <a href={tonscan} target="_blank" rel="noreferrer">Tonscan</a>
        </div>
    );
}

Full example

This minimal example scaffolds a React app, installs TON Pay dependencies, and renders a working button wired to TON Connect. Replace the manifest URL and recipientAddr with the necessary values.
npx create-react-app my-app --template typescript
cd my-app
npm i @ton-pay/api @ton-pay/ui-react @tonconnect/ui-react
// src/App.tsx
import { TonConnectUIProvider } from "@tonconnect/ui-react";
import { TonPayButton, useTonPay } from "@ton-pay/ui-react";
import { createTonPayTransfer } from "@ton-pay/api";

function AppContent() {
    const { pay } = useTonPay();

    async function createMessage(senderAddr: string) {
        const { message, reference } = await createTonPayTransfer(
            {
                amount: 12.34,
                asset: "TON",
                recipientAddr: "<WALLET_ADDRESS>",
                senderAddr,
                commentToSender: "Order #123",
            },
            {
                chain: "testnet",
                apiKey: "<TONPAY_API_KEY>"
            }
        );
        return { message, reference };
    }

    return (
        <TonPayButton handlePay={() => pay(createMessage)} />
    );
}

export default function App() {
    return (
        <TonConnectUIProvider
          manifestUrl="https://ton-connect.github.io/demo-dapp-with-wallet/tonconnect-manifest.json"
        >
          <AppContent />
        </TonConnectUIProvider>
    );
}

See also