TON Pay connects your app (prepares transfer messages and tracks status) with user wallets (approve and sign) via TonConnect. Apps keep business logic and UI; wallets keep keys and approval flow. This separation delivers secure, smooth payment UX.
Wallets discover your app via a TonConnect manifest. Create tonconnect-manifest.json and host it at a public, absolute URL:
Copy
{ "url": "https://myapp.com", "name": "My App", "iconUrl": "https://myapp.com/icon-180.png"}
Best practices: serve it at https://myapp.com/tonconnect-manifest.json and make it accessible from any origin (no CORS restrictions). Learn more in TonConnect docs: Creating the TON Connect manifest for DApp.
# APInpm i @ton-pay/api# UI (install separately from API)npm i @ton-pay/ui-react @tonconnect/ui-react
Non‑React apps: use @ton-pay/ui instead of @ton-pay/ui-react.
You can install only the API package @ton-pay/api if you don’t need React
UI. The UI package does not depend on the API; install @ton-pay/api
separately only when you need the API helpers alongside UI.
TON Pay UI relies on the official TonConnect UI to communicate with wallets.
Wrap your app with TonConnectUIProvider and pass your TonConnect manifest URL (absolute URL).See the manifest section above; full details in TonConnect docs: How TON Connect works.TonConnectUIProvider should be added to the root of the app.
Copy
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> );}
Drop in a TonPayButton and give it a handler. The handler uses useTonPay to connect a wallet (if needed), send a transaction via TonConnect, and track the result. Keep the message factory simple and readable.
The { message } you return is a TonConnect transaction message. useTonPay passes it to the wallet (via TonConnect) and initiates the send; you don’t call the wallet SDK yourself.
Client-only processing is fragile: users can close the tab before you persist
results. Prefer server-side message building and tracking for reliability. See
the recommended flow in Send Payments.
// Backend: POST /api/create-paymentimport { 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 your backend to create an order and calculate 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: "yourWalletAddress", senderAddr }, { chain: "testnet", apiKey: "yourTonPayApiKey" } ); // 2) Persist identifiers your DB (orderId, reference, bodyBase64Hash) immediately // 3) Return only the message to the client res.json({ message });});
Copy
// Frontendimport { TonPayButton, useTonPay } from "@ton-pay/ui-react";export function PayOrder({ productId }: { productId: any }) { const { pay } = useTonPay(); return ( <TonPayButton handlePay={async () => { await pay(async (senderAddr: string) => { const r = await fetch("/api/create-payment", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ productId, senderAddr }), }); const { message } = await r.json(); return { message }; // useTonPay forwards this message to the wallet via TonConnect }); }} /> );}
Optional: If you have an API key (available in your dashboard), pass it to
createTonPayTransfer options to enable automatic tracking and webhook notifications.
TonPayButton wraps wallet connect/disconnect UX and invokes your handler.
useTonPay automates wallet connection and transaction sending via TonConnect. Pass an async factory that receives senderAddr and returns { message } plus any tracking fields you want to propagate back.
Loading state and result handling
This example shows a minimal loading state and how to surface the reference. If you also have a transaction hash, render an explorer link (see snippet below).
Copy
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 you have a transaction hash, render an explorer link: <ExplorerLink txHash={result?.txHash!} /> */} </> );}
This minimal example scaffolds a React app, installs TON Pay dependencies, and renders a working button wired to TonConnect.
Replace the manifest URL and recipientAddr with your values.
Copy
npx create-react-app my-app --template typescriptcd my-appnpm i @ton-pay/api @ton-pay/ui-react @tonconnect/ui-react