Skip to content

Payments

TMA.sh supports two payment methods: Telegram Stars for Telegram’s in-app currency and TON Connect for cryptocurrency payments.

Telegram Stars is Telegram’s built-in digital currency. The SDK provides a first-class payments namespace that handles invoice creation, opening the payment sheet, and tracking the result — all without exposing your bot token to the client.

The simplest way to accept Stars payments. Creates an invoice via the platform endpoint, opens the native Telegram payment sheet, and returns the result:

import { createTMA } from '@tma.sh/sdk';
const tma = createTMA({ projectId: 'my-project' });
const result = await tma.payments.stars.pay({
title: 'Premium Subscription',
description: 'Access premium features for 30 days',
payload: JSON.stringify({ userId: user.telegramId, plan: 'premium' }),
prices: [{ label: 'Premium (30 days)', amount: 100 }],
});
if (result.status === 'paid') {
// Payment successful
}

The pay() method:

  1. Sends the invoice params to POST /sdk/v1/payments/stars/invoice (the platform uses your project’s stored bot token)
  2. Opens the native Telegram payment sheet via WebApp.openInvoice()
  3. Returns a StarsPaymentResult with status: 'paid' | 'cancelled' | 'failed' | 'pending'

If you need the invoice URL without immediately opening it (for example, to share or store it):

const invoiceUrl = await tma.payments.stars.createInvoice({
title: 'Premium Subscription',
description: 'Access premium features for 30 days',
payload: JSON.stringify({ plan: 'premium' }),
prices: [{ label: 'Premium (30 days)', amount: 100 }],
});
// Open it later
window.Telegram.WebApp.openInvoice(invoiceUrl, (status) => {
if (status === 'paid') { /* ... */ }
});
ParameterTypeRequiredDescription
titlestringYesProduct name (max 32 chars)
descriptionstringYesProduct description (max 255 chars)
payloadstringYesOpaque data returned in webhooks (max 128 chars)
pricesLabeledPrice[]YesArray of { label, amount } where amount is in Stars
photoUrlstringNoURL of product photo
photoWidthnumberNoPhoto width in pixels
photoHeightnumberNoPhoto height in pixels
import { useStarsPayment, createTMA } from '@tma.sh/sdk/react';
const tma = createTMA({ projectId: 'my-project' });
function PurchaseButton() {
const { pay, status, isLoading, error } = useStarsPayment();
const handlePurchase = async () => {
await pay(tma, {
title: 'Premium',
description: '30-day access',
payload: JSON.stringify({ plan: 'premium' }),
prices: [{ label: 'Premium', amount: 100 }],
});
};
return (
<button onClick={handlePurchase} disabled={isLoading}>
{isLoading ? 'Processing...' : 'Buy Premium (100 Stars)'}
</button>
);
}
<script>
import { createStarsPayment, createTMA } from '@tma.sh/sdk/svelte';
const tma = createTMA({ projectId: 'my-project' });
const payment = createStarsPayment();
const handlePurchase = async () => {
await payment.pay(tma, {
title: 'Premium',
description: '30-day access',
payload: JSON.stringify({ plan: 'premium' }),
prices: [{ label: 'Premium', amount: 100 }],
});
};
</script>
<button on:click={handlePurchase} disabled={$payment.isLoading}>
{$payment.isLoading ? 'Processing...' : 'Buy Premium (100 Stars)'}
</button>

If you need to create invoices or handle pre-checkout queries in your own API routes (rather than via the platform endpoint), use the typed methods on TelegramApiClient:

import { createTelegramApiClient } from '@tma.sh/sdk/server';
const telegram = createTelegramApiClient(c.env.BOT_TOKEN);
// Typed method (preferred over generic call())
const invoiceUrl = await telegram.createInvoiceLink({
title: 'Premium',
description: '30 days of premium',
payload: JSON.stringify({ plan: 'premium' }),
currency: 'XTR',
prices: [{ label: 'Premium', amount: 100 }],
});
// Answer pre-checkout query (must respond within 10 seconds)
await telegram.answerPreCheckoutQuery(query.id, true);

In your bot handler, send an invoice directly to a chat:

import { defineBot } from '@tma.sh/sdk/bot';
export default defineBot({
commands: [
{
command: 'buy',
description: 'Purchase premium',
handler: async (ctx) => {
await ctx.sendInvoice({
title: 'Premium',
description: '30-day access',
payload: JSON.stringify({ userId: ctx.from?.id }),
currency: 'XTR',
prices: [{ label: 'Premium', amount: 100 }],
});
},
},
],
onPreCheckoutQuery: async (ctx) => {
await ctx.answerPreCheckoutQuery(true);
},
});

After a successful Stars payment, Telegram sends a pre_checkout_query and then a successful_payment update to your bot. TMA.sh automatically tracks stars_payment analytics events with campaign attribution when payments complete through the bot webhook.

TON Connect lets users pay with TON cryptocurrency from their wallet. Use the @tonconnect/ui library directly — TON Connect is independent of the TMA SDK.

Terminal window
bun add @tonconnect/ui

Before sending a transaction, the user must connect their TON wallet:

import { TonConnectUI } from '@tonconnect/ui';
const tonConnect = new TonConnectUI({
manifestUrl: 'https://your-app.tma.sh/tonconnect-manifest.json',
});
// Opens the TON Connect wallet selector
await tonConnect.connectWallet();

This opens the standard TON Connect modal. The user selects their wallet app (Tonkeeper, MyTonWallet, etc.) and approves the connection.

Once connected, send a transaction with the destination address and amount in nanotons:

const result = await tonConnect.sendTransaction({
validUntil: Math.floor(Date.now() / 1000) + 300, // 5 minutes
messages: [
{
address: 'UQ...', // recipient address
amount: '1500000000', // nanotons (1.5 TON)
},
],
});

The user sees a confirmation screen in their wallet app before the transaction is submitted.

To disconnect the wallet (for example, when the user logs out):

await tonConnect.disconnect();

TON amounts are specified in nanotons (1 TON = 1,000,000,000 nanotons). Some common values:

TONNanotons
0.1100000000
1.01000000000
5.05000000000
10.010000000000
TON ConnectTelegram Stars
CurrencyTON cryptocurrencyTelegram Stars
User experienceWallet confirmationNative Telegram payment sheet
Server requiredNo (client-side)Yes (invoice creation via API route)
SettlementOn-chain (instant)Telegram balance
SDK dependency@tonconnect/ui (independent)@tma.sh/sdk/server
Best forCrypto-native users, NFTsSubscriptions, in-app items