# How to build a transfer (https://docs-kyrm16yq7-ton-core-docs.vercel.app/llms/ecosystem/ton-pay/payment-integration/transfer/content.md)



Use this server-side helper to generate a canonical TON message payload and a tracking reference.

```ts
import { createTonPayTransfer } from "@ton-pay/api";

const { message, bodyBase64Hash, reference } = await createTonPayTransfer(
  {
    amount: 10.5,
    asset: "TON",
    recipientAddr: "<RECIPIENT_ADDRESS>",
    senderAddr: "<SENDER_ADDRESS>",
    commentToSender: "Payment for Order #1234",
    commentToRecipient: "Order #1234",
  },
  {
    chain: "testnet",
    apiKey: "<TONPAY_API_KEY>", // optional
  }
);
```

<Callout type="tip">
  The TON Pay API key is optional, but providing it enables transaction visibility in the TON Pay Merchant Dashboard, webhook notifications, and wallet management features.
</Callout>

## Function signature [#function-signature]

```ts
createTonPayTransfer(
  params: CreateTonPayTransferParams,
  options?: APIOptions
): Promise<CreateTonPayTransferResponse>
```

### Transfer parameters [#transfer-parameters]

```ts
type CreateTonPayTransferParams = {
  amount: number;
  asset: string;
  recipientAddr?: string;
  senderAddr: string;
  queryId?: number;
  commentToSender?: string;
  commentToRecipient?: string;
};
```

### API options [#api-options]

```ts
type APIOptions = {
  chain?: "mainnet" | "testnet";
  apiKey?: string; // optional
};
```

<ParamField body="chain" type="string" default="mainnet">
  Target blockchain network. Use `"mainnet"` for production or `"testnet"` for development and testing.
</ParamField>

<ParamField body="apiKey" type="string">
  The optional TON Pay API key from the Merchant Dashboard. When provided, it enables:

  * Transaction visibility in the TON Pay Merchant Dashboard.
  * Webhook notifications for completed transactions.
  * Receiving wallet management from the Merchant Dashboard.
</ParamField>

## Parameter details [#parameter-details]

<ParamField body="amount" type="number">
  Human-readable payment amount. Decimals are allowed, for example, 10.5 TON.
</ParamField>

<ParamField body="asset" type="string">
  Asset to transfer. Use "TON" for Toncoin or a jetton master address or constant, for example, USDT.
</ParamField>

<Callout type="danger" title="Mainnet address risk">
  Token constants such as `USDT` always reference mainnet jetton master addresses and are not affected by the `chain` option. Using them on testnet may send transactions to mainnet contracts.

  For testnet, explicitly pass the correct testnet jetton master address instead of using token constants.
</Callout>

<ParamField body="recipientAddr" type="string">
  Payee wallet address. Optional if an API key is provided. Defaults to the merchant’s default wallet address configured in the Merchant Dashboard.
</ParamField>

<ParamField body="senderAddr" type="string">
  Payer wallet address. In UI flows, obtain it from TON Connect.
</ParamField>

<ParamField body="queryId" type="number">
  Jetton only. Numeric identifier embedded into the jetton payload for idempotency and tracking. Ignored for Toncoin payments.
</ParamField>

<ParamField body="commentToSender" type="string">
  Short note visible to the user in the wallet while signing.

  <Callout>
    Comments are on-chain and publicly visible in blockchain explorers. Do not include confidential data. Keep comments under 120 characters to avoid increased gas fees.
  </Callout>
</ParamField>

<ParamField body="commentToRecipient" type="string">
  Note visible to the recipient after the transfer is received.

  <Callout>
    Comments are on-chain and publicly visible in blockchain explorers. Do not include confidential data. Keep comments under 120 characters to avoid increased gas fees.
  </Callout>
</ParamField>

## Predefined asset constants [#predefined-asset-constants]

Built-in constants can be used instead of raw addresses.

```ts
import { createTonPayTransfer, TON, USDT } from "@ton-pay/api";

// Toncoin transfer
await createTonPayTransfer(
  {
    amount: 1,
    asset: TON, // same as "TON"
    recipientAddr: "<RECIPIENT_ADDRESS>", // shortened example; replace with a full wallet address
    senderAddr: "<SENDER_ADDRESS>",       // shortened example; replace with a full wallet address
  },
  {
    chain: "testnet",
    apiKey: "<TONPAY_API_KEY>", // optional
  }
);

// USDT jetton transfer
await createTonPayTransfer(
  {
    amount: 25,
    asset: USDT, // mainnet USDT jetton master address
    recipientAddr: "<RECIPIENT_ADDRESS>", // shortened example; replace with a full wallet address
    senderAddr: "<SENDER_ADDRESS>",       // shortened example; replace with a full wallet address
  },
  {
    chain: "testnet",
    apiKey: "<TONPAY_API_KEY>", // optional
  }
);
```

<Callout type="danger" title="Mainnet address risk">
  In `asset`, token constants such as `USDT` always reference mainnet jetton master addresses and are not affected by the `chain` option. Using them on testnet may send transactions to mainnet contracts.

  For testnet, explicitly pass the correct testnet jetton master address instead of using token constants.
</Callout>

Response:

```ts
type CreateTonPayTransferResponse = {
  message: {
    address: string;
    amount: string;
    payload: string;
  };
  bodyBase64Hash: string;
  reference: string;
};
```

## Response fields [#response-fields]

<ResponseField name="message" type="object">
  Prebuilt TON Connect transaction message. Intended to be passed to `sendTransaction` as `messages: [message]`.
</ResponseField>

<ResponseField name="bodyBase64Hash" type="string">
  Base64 hash of the signed message body content (payload). Used with `getTonPayTransferByBodyHash`.
</ResponseField>

<ResponseField name="reference" type="string">
  Tracking reference string. Used with `getTonPayTransferByReference`.
</ResponseField>

The SDK call returns a ready-to-send message along with identifiers for subsequent status lookups. Always persist tracking identifiers server-side before sending the transaction to the user.

## Optional API key configuration [#optional-api-key-configuration]

The TON Pay API key is optional but enables merchant features, including transaction visibility in the dashboard, webhook notifications, and centralized wallet management in the TON Pay Merchant Dashboard.

### Obtain an optional API key [#obtain-an-optional-api-key]

<Steps>
  <Step>
    #### Open the Merchant Dashboard [#open-the-merchant-dashboard]

    Open the TON Pay Merchant Dashboard and sign in to the merchant account.
  </Step>

  <Step>
    #### Open Developer settings [#open-developer-settings]

    Navigate to <kbd>Developer</kbd> → <kbd>API Keys</kbd> sections to set up the optional API key.
  </Step>

  <Step>
    #### Generate or copy an optional API key [#generate-or-copy-an-optional-api-key]

    Generate a new API key (optional) or copy an existing one and store it securely.
  </Step>
</Steps>

### Usage in code [#usage-in-code]

```typescript
import { createTonPayTransfer } from "@ton-pay/api";

const { message, reference, bodyBase64Hash } = await createTonPayTransfer(
  {
    amount: 10.5,
    asset: "TON",
    // recipientAddr is optional when API key is provided
    // Will use merchant's default wallet from the Merchant Dashboard
    senderAddr: userWalletAddress,
  },
  {
    chain: "testnet",
    apiKey: "<TONPAY_API_KEY>", // optional
  }
);
```

### Optional API key capabilities [#optional-api-key-capabilities]

| Capability                            | With optional API key                                                                                             | Without API key                                                                       |
| ------------------------------------- | ----------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
| Transaction visibility and monitoring | Transfers appear in the TON Pay Merchant Dashboard with status tracking and full transaction history.             | Transfers are processed on-chain but are not visible in the Merchant dashboard.       |
| Webhook notifications                 | Real-time HTTP POST notifications are sent for completed and failed payments.                                     | No webhook notifications; payment status must be obtained through the manual polling. |
| Receiving wallet management           | Receiving wallets are managed from the TON Pay Merchant Dashboard; addresses can be updated without code changes. | Receiving wallet addresses must be hard-coded in the application.                     |
| `recipientAddr` handling              | Optional; when omitted, the merchant’s default wallet from the Merchant Dashboard is used automatically.          | Required for every transfer.                                                          |

With optional API key: `recipientAddr` is optional.

```ts
const result = await createTonPayTransfer(
  {
    amount: 10,
    asset: "TON",
    senderAddr: "<SENDER_ADDRESS>",
  },
  {
    chain: "testnet",
    apiKey: "<TONPAY_API_KEY>", // optional
  }
);
```

Without API key (the API key remains optional): `recipientAddr` is required.

```ts
// Works without API key (it is optional) - transaction processes normally
const result = await createTonPayTransfer(
{
  amount: 10,
  asset: "TON",
  recipientAddr: "<RECIPIENT_ADDRESS>",
  senderAddr: "<SENDER_ADDRESS>",
},
{ chain: "testnet" } // No optional apiKey - transaction works but no dashboard features
);
```

## Testnet configuration [#testnet-configuration]

<Callout type="danger" title="Funds at risk">
  Running tests on mainnet can result in irreversible loss of real TON. Always use `chain: "testnet"` and testnet wallet addresses during development. Verify the network before switching to mainnet.
</Callout>

### Set up testnet [#set-up-testnet]

<Steps>
  <Step>
    #### Set chain to testnet [#set-chain-to-testnet]

    Configure the `chain` option to `"testnet"` in the API calls:

    ```typescript
    const { message, reference, bodyBase64Hash } = await createTonPayTransfer(
    {
      amount: 5.0,
      asset: "TON",
      recipientAddr: "<RECIPIENT_ADDRESS>",
      senderAddr: "<SENDER_ADDRESS>",
    },
    {
      chain: "testnet", // Use testnet
      apiKey: "<TONPAY_API_KEY>", // optional
    }
    );
    ```
  </Step>

  <Step>
    #### Use testnet wallet addresses [#use-testnet-wallet-addresses]

    Ensure all wallet addresses are valid [testnet addresses](/llms/foundations/addresses/formats/content.md).
  </Step>

  <Step>
    #### Obtain testnet TON [#obtain-testnet-ton]

    * Use testnet wallet account: [Tonkeeper](/llms/ecosystem/wallet-apps/tonkeeper/content.md) or other TON wallets.
    * [Get testnet TON](/llms/ecosystem/wallet-apps/get-coins/content.md) from a faucet to test transactions.
  </Step>

  <Step>
    #### Configure testnet jettons [#configure-testnet-jettons]

    If testing with jettons, use the correct testnet jetton master addresses; not mainnet addresses.

    ```typescript
    // Example: Testnet USDT (use actual testnet address)
    await createTonPayTransfer(
    {
      amount: 10,
      asset: "<TESTNET_USDT_MASTER_ADDRESS>", // Testnet jetton address
      recipientAddr: "<RECIPIENT_ADDRESS>",
      senderAddr: "<SENDER_ADDRESS>",
    },
    { chain: "testnet" }
    );
    ```
  </Step>
</Steps>

### Configure environment [#configure-environment]

Use environment variables to switch between mainnet and testnet:

```typescript
// .env.development
TON_CHAIN=testnet
MERCHANT_WALLET_ADDRESS=EQC...TESTNET_ADDRESS

// .env.production
TON_CHAIN=mainnet
MERCHANT_WALLET_ADDRESS=EQC...MAINNET_ADDRESS
```

```typescript
// In the application code
const { message, reference, bodyBase64Hash } = await createTonPayTransfer(
  {
    amount: orderAmount,
    asset: "TON",
    recipientAddr: "<WALLET_ADDRESS>",
    senderAddr: "<CUSTOMER_WALLET_ADDRESS>",
  },
  {
    chain: "testnet",
    apiKey: "<TONPAY_API_KEY>", // optional
  }
);
```

### Verify testnet transactions [#verify-testnet-transactions]

Verify testnet transactions using testnet block [explorers](/llms/ecosystem/explorers/overview/content.md):

* [Testnet Tonviewer](https://testnet.tonviewer.com/)
* [Testnet Tonscan](https://testnet.tonscan.org/)

```typescript
// Replace txHash with the actual transaction hash.
// After transaction completes:
console.log(`View on explorer: https://testnet.tonscan.org/tx/${txHash}`);
```

### Apply testing best practices [#apply-testing-best-practices]

* Test all payment outcomes on testnet, including success, failures, user rejections, and edge cases.
* Verify webhook endpoint correctly processes testnet webhooks; payload structure matches mainnet.
* Validate behavior across different amounts, including small, large, and fractional values.
* Ensure `reference` and `bodyBase64Hash` are persisted and usable for status lookups.
* Exercise error paths such as insufficient balance, invalid addresses, and network issues.
