Створення повноцінного інтернет-магазину всередині Telegram Mini Apps (TMA) вимагає не лише чутливого інтерфейсу, але й надійної архітектури на бекенді. Клієнтський додаток на Vue 3 забезпечує безшовний UX, однак довіряти фронтенду розрахунок вартості замовлення не можна. Будь-які ціни, знижки та залишки повинні проходити сувору валідацію на стороні сервера.
У цій статті ми розберемо створення надійного гібридного checkout-процесу: від ведення кошика в Pinia із збереженням стану в localStorage до генерації рахунків через Telegram Bot API (включно з Telegram Stars) та інтеграції зовнішніх платіжних шлюзів на PHP.
1. Архітектура кошика на Vue 3: Pinia та LocalStorage
Користувачі Mini Apps часто закривають додаток або перемикаються на інші чати. Щоб кошик не скидався при перезапуску WebApp, стан Pinia необхідно синхронізувати з localStorage. Нижче представлений реактивний кошик, який зберігає стан і автоматично підраховує суму.
// stores/cart.js
import { defineStore } from 'pinia';
export const useCartStore = defineStore('cart', {
state: () => ({
items: JSON.parse(localStorage.getItem('tma_cart_items')) || []
}),
getters: {
totalAmount: (state) => {
return state.items.reduce((sum, item) => sum + item.price * item.quantity, 0);
},
totalQuantity: (state) => {
return state.items.reduce((sum, item) => sum + item.quantity, 0);
}
},
actions: {
addToCart(product) {
const existing = this.items.find(i => i.id === product.id);
if (existing) {
existing.quantity++;
} else {
this.items.push({ ...product, quantity: 1 });
}
this.saveToStorage();
},
updateQuantity(productId, quantity) {
const item = this.items.find(i => i.id === productId);
if (item) {
item.quantity = Math.max(1, quantity);
}
this.saveToStorage();
},
removeFromCart(productId) {
this.items = this.items.filter(i => i.id !== productId);
this.saveToStorage();
},
clearCart() {
this.items = [];
this.saveToStorage();
},
saveToStorage() {
localStorage.setItem('tma_cart_items', JSON.stringify(this.items));
}
}
});2. Валідація кошика на PHP-бекенді
Головне правило безпеки: ніколи не вірте цінам, що прийшли від клієнта. Зловмисник може змінити ціну товару в локальному сховищі перед відправкою запиту. На бекенд ми передаємо тільки ідентифікатори товарів (id) та їхню кількість (quantity), а також рядок ініціалізації initData для перевірки автентичності користувача.
Нижче представлений скрипт оформлення замовлення, який перевіряє ціни за базою даних, генерує унікальний ідентифікатор угоди lead_id за допомогою криптографічно безпечної функції та надсилає рахунок користувачеві через Bot API.
<?php
// checkout.php
header('Content-Type: application/json');
$input = json_decode(file_get_contents('php://input'), true);
if (!$input || empty($input['items']) || empty($input['initData'])) {
http_response_code(400);
echo json_encode(['ok' => false, 'error' => 'Неверные входные данные']);
exit;
}
// Имитация базы данных товаров
$catalogDb = [
101 => ['title' => 'Набор стикеров Premium', 'price' => 500],
102 => ['title' => 'Подписка на закрытый канал', 'price' => 1500],
];
$prices = [];
$totalAmount = 0;
$leadId = bin2hex(random_bytes(7)); // Уникальный ID заказа
foreach ($input['items'] as $item) {
$id = (int)($item['id'] ?? 0);
$qty = (int)($item['quantity'] ?? 0);
if (isset($catalogDb[$id]) && $qty > 0) {
$product = $catalogDb[$id];
$prices[] = [
'label' => $product['title'] . " x{$qty}\