# Quick start (https://docs-fpm2731fy-ton-core-docs.vercel.app/llms/ecosystem/ton-pay/quick-start/content.md)



## TON Connect manifest [#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 the [TON Connect manifest](/llms/ecosystem/ton-connect/manifest/content.md) to discover the application.

## First payment [#first-payment]

<Steps>
  <Step>
    ### Install necessary libraries [#install-necessary-libraries]

    <Tabs items="[&#x22;React&#x22;, &#x22;Vanilla JS&#x22;]">
      <Tab value="React">
        ```bash
        # API
        npm i @ton-pay/api

        # UI (install separately from API)
        npm i @ton-pay/ui-react @tonconnect/ui-react
        ```
      </Tab>

      <Tab value="Vanilla JS">
        ```bash
        # API
        npm i @ton-pay/api

        # UI (install separately from API)
        npm i @ton-pay/ui @tonconnect/ui
        ```
      </Tab>
    </Tabs>

    <Callout type="tip">
      The UI package does not depend on the API. Install `@ton-pay/api` only when API helpers are required.
    </Callout>
  </Step>

  <Step>
    ### Add TON Connect provider [#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.

    ```tsx
    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>
      );
    }
    ```
  </Step>

  <Step>
    ### Add a payment button [#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 return tracking data for the next step.

    * `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.
    * Return `reference` from `createTonPayTransfer` so it can be used later with `getTonPayTransferByReference`.

    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.

    In the examples below, replace `<WALLET_ADDRESS>` with the recipient wallet address, `<TONPAY_API_KEY>` with an optional dashboard API key, and `<ORDER_REFERENCE>` with an order label or ID.

    ```tsx
    import { TonPayButton, useTonPay } from "@ton-pay/ui-react";
    import { createTonPayTransfer } from "@ton-pay/api";

    const recipientAddr = "<WALLET_ADDRESS>";
    const orderReference = "<ORDER_REFERENCE>";

    // Set chain to "mainnet" in production.
    const options = {
      chain: "testnet",

      // Pass an API key from the dashboard when available.
      apiKey: "<TONPAY_API_KEY>",
    } as const;

    export default function PayButton() {
      const { pay } = useTonPay();

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

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

    <Callout type="danger" title="Client-only processing is fragile">
      Users can close the tab before transaction results are persisted, which breaks tracking and confirmation logic. Build messages and track transactions in the [Send payments using React](/llms/ecosystem/ton-pay/payment-integration/payments-react/content.md) guide.
    </Callout>

    <Accordions>
      <Accordion title="Minimal server-side flow example">
        ```ts
        // Backend: POST /api/create-payment
        import { createTonPayTransfer, TON } from "@ton-pay/api";

        const recipientAddr = "<WALLET_ADDRESS>";

        // Set chain to "mainnet" in production.
        const options = {
          chain: "testnet",

          // Pass an API key from the dashboard when available.
          apiKey: "<TONPAY_API_KEY>",
        } as const;

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

          // Create an order and calculate the amount from the product price.
          const amount = 12.23;
          const orderId = 1;

          // Create the transfer and get tracking identifiers.
          const { message, reference, bodyBase64Hash } = await createTonPayTransfer(
            { amount, asset: TON, recipientAddr, senderAddr },
            options
          );

          // Persist identifiers in the database immediately.

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

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

        export function PayOrder({ productId }: { productId: string }) {
          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)} />;
        }
        ```
      </Accordion>
    </Accordions>

    <Callout>
      The TON Pay API key is optional. If available from the dashboard, pass it to `createTonPayTransfer` options.
    </Callout>
  </Step>

  <Step>
    ### Handle loading state and results [#handle-loading-state-and-results]

    Wallet approval does not mean the payment is finalized yet. After `pay(createMessage)` resolves, use the `reference` returned by `createTonPayTransfer` to query TON Pay until the transfer leaves the `pending` state.

    The example below uses React. The same flow applies in other clients: keep the `reference`, poll status, and update the UI when the transfer leaves `pending`.

    Use the SDK flow below:

    1. Return `reference` from the `createMessage` function together with `message`.
    2. Call `pay(createMessage)` and, once it resolves, read the propagated `reference` from its return value.
    3. Call `getTonPayTransferByReference(reference, options)` with the same `chain` and optional `apiKey` used during transfer creation.
    4. While the SDK returns `status: "pending"`, keep the UI in a loading or "confirming payment" state.
    5. When the status becomes `success`, show the confirmation UI and persist any result fields the application needs, such as `txHash` or `traceId`.
    6. When the status becomes `error`, show the failure state and capture `errorCode` or `errorMessage` for diagnostics.

    In a client-only flow, persist the `reference` after `pay(createMessage)` resolves and returns it. This is enough to resume status checks after a reload during polling, but it does not cover the period while the wallet approval screen is open. To avoid that gap, create the transfer on the server and persist the `reference` before opening the wallet.

    ```tsx title="Result handling example" expandable
    import { useState } from "react";
    import { TonPayButton, useTonPay } from "@ton-pay/ui-react";
    import {
      createTonPayTransfer,
      getTonPayTransferByReference,
      type CompletedTonPayTransferInfo,
    } from "@ton-pay/api";

    type PaymentState = "idle" | "sending" | "pending" | "success" | "error";

    const amount = 12.34;
    const recipientAddr = "<WALLET_ADDRESS>";

    // Set chain to "mainnet" in production.
    const options = {
      chain: "testnet",

      // Pass an API key from the dashboard when available.
      apiKey: "<TONPAY_API_KEY>",
    } as const;

    export default function Checkout() {
      const { pay } = useTonPay();
      const [paymentState, setPaymentState] = useState<PaymentState>("idle");
      const [reference, setReference] = useState<string | null>(null);
      const [result, setResult] = useState<CompletedTonPayTransferInfo | null>(null);
      const [errorMessage, setErrorMessage] = useState<string | null>(null);

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

        setReference(reference);
        return { message, reference };
      }

      // Polls TON Pay until the transfer gets a final status.
      async function waitForTransferResult(reference: string) {
        for (;;) {
          const transfer = await getTonPayTransferByReference(reference, options);

          if (transfer.status === "pending") {
            await new Promise((resolve) => setTimeout(resolve, 1000));
            continue;
          }

          return transfer;
        }
      }

      async function handlePay() {
        setPaymentState("sending");
        setErrorMessage(null);
        setReference(null);
        setResult(null);

        try {
          const { reference } = await pay(createMessage);
          setPaymentState("pending");

          const transfer = await waitForTransferResult(reference);

          if (transfer.status === "success") {
            setResult(transfer);
            setPaymentState("success");
            return;
          }

          setPaymentState("error");
          setErrorMessage(transfer.errorMessage ?? "Payment failed");
        } catch (error) {
          setPaymentState("error");
          setErrorMessage(error instanceof Error ? error.message : "Payment failed");
        }
      }

      return (
        <>
          <TonPayButton
            handlePay={handlePay}
            isLoading={paymentState === "sending" || paymentState === "pending"}
          />

          {paymentState === "pending" && reference && (
            <div>
              Payment submitted. Waiting for blockchain confirmation. Reference: {reference}
            </div>
          )}

          {paymentState === "success" && result && (
            <div>
              Payment confirmed. Tx hash: {result.txHash}
            </div>
          )}

          {paymentState === "error" && errorMessage && (
            <div>
              Payment failed: {errorMessage}
            </div>
          )}
        </>
      );
    }
    ```

    The [status lookup guide](/llms/ecosystem/ton-pay/payment-integration/status-info/content.md) describes the response fields and lookup methods used in this step.
  </Step>
</Steps>

## Full example [#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.

```bash
npx create-react-app my-app --template typescript
cd my-app
npm i @ton-pay/api @ton-pay/ui-react @tonconnect/ui-react
```

```tsx
// src/App.tsx
import { TonConnectUIProvider } from "@tonconnect/ui-react";
import { TonPayButton, useTonPay } from "@ton-pay/ui-react";
import { createTonPayTransfer } from "@ton-pay/api";

const recipientAddr = "<WALLET_ADDRESS>";
const commentToSender = "Order #123";

// Set chain to "mainnet" in production.
const options = {
  chain: "testnet",

  // Pass an API key from the dashboard when available.
  apiKey: "<TONPAY_API_KEY>",
} as const;

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

  async function createMessage(senderAddr: string) {
    const { message, reference } = await createTonPayTransfer(
      {
        amount: 12.34,
        asset: "TON",
        recipientAddr,
        senderAddr,
        commentToSender,
      },
      options
    );
    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 [#see-also]

* [Build a transfer](/llms/ecosystem/ton-pay/payment-integration/transfer/content.md)
* [Send payments using React](/llms/ecosystem/ton-pay/payment-integration/payments-react/content.md)
* [Webhooks](/llms/ecosystem/ton-pay/webhooks/content.md)
* [API reference](/llms/ecosystem/ton-pay/api-reference/content.md)
