Recommended Free Tools
Build the catalog and cart in Vue, use Pinia for cart state shared across screens, and let a trusted backend—not the Mini App—verify the user, recalculate prices, create the order, and confirm payment. Telegram provides the host interface and invoice-opening capability; it does not make browser-submitted cart data authoritative.
How the parts fit together
A sound implementation separates display, shared state, Telegram integration, and order authority:
As an Amazon Associate I earn from qualifying purchases.
- Vue renders the catalog, cart, and checkout screens.
- Pinia holds cart data and cart actions used by more than one component.
- Telegram’s WebApp API supplies Mini App host information, interface features, and an invoice-opening interface.
- Your backend validates Telegram launch data, loads current product records and prices, creates orders, and tracks payment and fulfillment.
The browser can show a provisional subtotal, but the server must determine the final amount. Treat submitted product IDs and quantities as a request, not proof of price, availability, identity, or payment.
Set up Vue and Pinia
Install Pinia and register a Pinia instance on the Vue application before mounting it. The official Pinia Getting Started guide shows the app setup and introduces stores as a place for shared state and business logic.
#1 Best Overall
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import App from './App.vue'
const app = createApp(App)
app.use(createPinia())
app.mount('#app')
Create a cart store with explicitly declared initial state, actions for mutations, and getters for values derived from that state. For example, store product identifiers and quantities in cart lines; derive the displayed item count from those lines. Pinia requires state properties to be declared in the state function; see its state documentation.
import { defineStore } from 'pinia'
export const useCartStore = defineStore('cart', {
state: () => ({
lines: []
}),
getters: {
itemCount: (state) =>
state.lines.reduce((total, line) => total + line.quantity, 0)
},
actions: {
add(productId) {
const line = this.lines.find(item => item.productId === productId)
if (line) line.quantity += 1
else this.lines.push({ productId, quantity: 1 })
},
setQuantity(productId, quantity) {
const line = this.lines.find(item => item.productId === productId)
if (!line) return
if (quantity <= 0) this.remove(productId)
else line.quantity = quantity
},
remove(productId) {
this.lines = this.lines.filter(item => item.productId !== productId)
}
}
})
This example stores identifiers and quantities rather than treating browser-held product names or prices as authoritative. A UI may combine the cart lines with catalog data for display, but checkout should send only the requested items needed by the server to reconstruct the order.
Decide what belongs in Pinia
Use Pinia when the same cart must be read or changed by the catalog, cart, and checkout components. Keep one-off interface details—such as whether a particular menu is open—inside the component that owns that interface. This distinction avoids turning every temporary UI condition into global state.
Do these 3 things before closing this tab:
1Fix the driver behind crashes, sound loss and screen glitches2Clear out junk files and repair common Windows errors3Scan for outdated or missing drivers - takes under a minuteIf you use server-side rendering, do not keep a mutable store singleton in module scope across server requests: it can lead to state being shared between users. Vue’s state management guide discusses this risk and recommends Pinia for new applications that need a store.
Connect to Telegram’s Mini App environment
Telegram’s Mini Apps documentation describes loading telegram-web-app.js and using window.Telegram.WebApp to interact with the host. Account for the possibility that the same app is opened in an ordinary browser preview, where the Telegram host object may be absent; guard calls that depend on it and provide a useful fallback rather than assuming every environment is Telegram.
Design for a mobile-first embedded view. Use Telegram’s live theme parameters instead of hard-coding a light or dark palette, respond to viewport changes, and respect safe-area insets so fixed cart or checkout controls do not collide with host UI. Keep controls usable on small screens, and provide labels for inputs and meaningful text alternatives for images, as Telegram’s design guidance recommends.
Rank #4
Validate launch data before identifying the user
Do not use initDataUnsafe as proof of identity. Telegram explicitly warns that it should not be trusted. Send the raw initData string to your backend and validate it there using Telegram’s documented procedure before relying on the identity or other fields it contains. Apply an appropriate freshness check as part of that validation.
A user ID copied from frontend state is not authentication. The backend should associate an order with a Telegram user only after validating the launch data; see Telegram’s current Mini Apps documentation for the validation requirements.
Best Value
Build checkout around a server-authoritative order
A practical flow keeps order decisions on the server while letting Vue handle the interaction:
- Review: Show the cart lines and a clearly provisional subtotal in the Mini App.
- Submit: Send product identifiers and quantities to the backend along with the raw Telegram
initData. - Validate and price: The backend validates the launch data, reloads current product records, checks availability, and calculates the amount from authoritative prices.
- Create the order and invoice: The backend creates an order and obtains the applicable Telegram invoice or payment flow for the product and selected provider.
- Open the invoice: The Mini App invokes the documented invoice-opening interface.
- Reconcile: Record payment and fulfillment state using the backend’s order/payment record before displaying a confirmed order or fulfilling it.
Telegram documents payment support through third-party providers and the Mini App openInvoice interface. Requirements depend on what is being sold and the selected provider, so check the current Telegram documentation and provider terms rather than assuming one universal payment route, fee, or regional availability.
Handle invoice outcomes without guessing
Keep pending, successful, and cancelled outcomes distinct in the interface. Telegram documents invoice status reporting through the corresponding close event, but closing the invoice UI alone is not proof that an order was paid. Reconcile the reported outcome with the backend order/payment record before presenting success or starting fulfillment. If the record is still pending, show that state and let the backend’s payment handling determine the next step.
Quick Recap
Choose the right implementation boundaries
| Decision | Use this approach | Reason |
|---|---|---|
| Cart state | Pinia for cart contents shared across screens; component-local state for isolated UI details. | Shared cart operations belong in shared application state; transient controls need not be global. |
| Prices and order validity | Backend recalculation and validation; browser totals are display-only. | Client state can be changed and is not proof of a valid price or order. |
| Rendering mode | Client rendering is straightforward; if using SSR, avoid a mutable cross-request store singleton. | Server requests must not share user-specific mutable state. |
| Host integration | Use Telegram’s host API in the Mini App and guard for its absence in browser previews. | The host object is not available in every environment where the web app may be opened. |
| Payment route | Choose the supported invoice/payment route and provider based on the product, geography, and current requirements. | Platform and provider rules are not universal across products or regions. |
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

