Гібридний checkout у Telegram Mini App на Vue 3: кошик у Pinia та процесинг платежів на PHP

Створення повноцінного інтернет-магазину всередині 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}\

Нові статті — у Telegram

Розбираємо, що автоматизувати в бізнесі та як це працює на практиці. Без спаму.