Що ми будуємо і чого нам не вистачає
У цьому посібнику показано мінімальний шлях PHP для впровадження Telegram Stars через API бота. Ви:
- Надіслати рахунок
sendInvoiceУ валютіXTR. - Рукоятка
pre_checkout_queryі зателефонуйтеanswerPreCheckoutQueryвчасно - Отримати
message.successful_paymentта підтвердьте ідентифікатор стягнення, суму Stars та корисне навантаження рахунку. - Уникайте поширених помилок: відповідайте занадто пізно, ставтеся до зірок як до банківської готівки та довіряйте підсумкам діяльності клієнта.
Ми не покриваємо KYC, фрагменти та податки. Це рівень інтеграції, а не фінансовий рівень.
1. Надсилання рахунку до XTR
Рахунки Stars використовують валюту XTRСЕРП provider_token — Telegram відхиляє його для Stars. Користувач сплачує зі свого балансу Stars. Ваш полезная нагрузка непрозорий рядок (до 128 байтів), який прив 'язує платіж успішно здійснено повернутися до замовлення.
<?php
// send_invoice.php — start a Stars checkout
$token = getenv('BOT_TOKEN');
$chatId = (int) $update['message']['chat']['id'];
$payload = bin2hex(random_bytes(7)); // order key, ≤128 bytes
$stars = 1; // integer only
// Persist the order BEFORE sendInvoice
$orderId = saveOrder([
'payload' => $payload,
'chat_id' => $chatId,
'stars' => $stars,
'status' => 'pending',
'created_at' => gmdate('c'),
]);
$body = [
'chat_id' => $chatId,
'title' => 'Pro plan (1 month)',
'description' => 'Unlocks Pro features for 30 days.',
'payload' => $payload,
'currency' => 'XTR',
'prices' => json_encode([['label' => 'Pro 1m', 'amount' => $stars]]),
// provider_token must be OMITTED for XTR
];
$ch = curl_init('https://api.telegram.org/bot'.$token.'/sendInvoice');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query($body),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
]);
$raw = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_RESPONSE_CODE);
curl_close($ch);
$resp = json_decode($raw, true);
if ($status !== 200 || !($resp['ok'] ?? false)) {
markOrderFailed($orderId, $raw);
throw new RuntimeException('sendInvoice failed: '.$raw);
}
цих Правил
ціни[].amountцілі зірки — без дробів.корисне навантаженняє лише ключем кореляції. Не встановлюйте фальшиву ціну.- Спочатку збережіть, а потім надішліть. Якщо
sendInvoiceне вдається. Зверніть увагу, що замовлення не вдалося виконати, тому ви ніколи не стягуватимете плату, яку не зможете знайти.
2. Відповідь на pre_checkout_query
Коли користувач підтвердить, Telegram надішле pre_checkout_query На його invoice_payload і Загальна вартість. У вас є близько 10 сек зателефонувати answerPreCheckoutQuery. Тайм-аут і Telegram скасовує платіж. Перевірте ціну та доступність тут — ніколи не довіряйте даним зворотного дзвінка з клавіатури за гроші.
<?php
// pre_checkout.php
$token = getenv('BOT_TOKEN');
$pcq = $update['pre_checkout_query'];
$pcqId = $pcq['id'];
$payload = $pcq['invoice_payload'];
$totalStars = (int) $pcq['total_amount'];
$order = loadOrderByPayload($payload);
$ok = $order
&& $order['status'] === 'pending'
&& (int) $order['stars'] === $totalStars
&& empty($order['expires_at_gmt']); // optional TTL check
$body = [
'pre_checkout_query_id' => $pcqId,
'ok' => $ok ? 'true' : 'false',
];
if (!$ok) {
$body['error_message'] = 'This order is no longer available.';
}
$ch = curl_init('https://api.telegram.org/bot'.$token.'/answerPreCheckoutQuery');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query($body),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 5,
]);
$raw = curl_exec($ch);
curl_close($ch);
$resp = json_decode($raw, true);
if (!($resp['ok'] ?? false)) {
error_log('answerPreCheckoutQuery failed: '.$raw);
}
Перевіряє, що важливо:
invoice_payloadмає прийняти рішення про реальне замовлення.Загальна вартістьмає відповідати зіркам, які ви зберегли.- Статус все ще має бути
в очікуванні.. Другий запит на вже затверджене корисне навантаження - повторити — відхилити його.
3. підтвердження успішного_платежу
Після заряджання ваш вебхук отримує message.successful_payment. Цей блок включає:
currency === 'XTR'Загальна вартість— заряджаються зірки (ціле число);invoice_payload— рядок, який ви вказали в рахунку-фактуріtelegram_payment_charge_id— Унікальний ідентифікатор стягнення. зберігати йогоprovider_payment_charge_id— порожній для зірок; не припускайте, що він існує
Розглядайте це оновлення лише як доказ отримання зірок. Зірки - це баланс на стороні Telegram, а не банківський депозит. Щоб перевірити заробіток бота, зателефонуйте getStarTransactions.
<?php
// successful_payment.php
$token = getenv('BOT_TOKEN');
$sp = $update['message']['successful_payment'];
$payload = $sp['invoice_payload'];
$totalStars = (int) $sp['total_amount'];
$currency = $sp['currency'];
$chargeId = $sp['telegram_payment_charge_id'];
$userId = (int) $update['message']['from']['id'];
if ($currency !== 'XTR') {
error_log('Unexpected currency on successful_payment: '.json_encode($sp));
http_response_code(200);
exit;
}
if (alreadyCredited($chargeId)) {
http_response_code(200);
exit;
}
$pdo->beginTransaction();
try {
$order = loadOrderByPayloadForUpdate($payload); // SELECT ... FOR UPDATE
if (!$order || $order['status'] !== 'pending') {
throw new RuntimeException('Order not pending for payload '.$payload);
}
if ((int) $order['stars'] !== $totalStars) {
throw new RuntimeException('Stars mismatch on payload '.$payload);
}
creditUser($userId, $order);
markOrderPaid($order['id'], $chargeId);
recordCharge($chargeId, $order['id']); // unique index on charge_id
$pdo->commit();
} catch (Throwable $e) {
$pdo->rollBack();
error_log('successful_payment error: '.$e->getMessage());
// Still return 200 — Telegram will retry on errors. Reconcile later.
}
http_response_code(200);
Перегляньте його:
currency === 'XTR'invoice_payloadспіввідноситься з відкладеним замовленнямЗагальна вартістьвідповідає очікуваним зіркамtelegram_payment_charge_idунікальний у вашій робочій книзі (ключ ідемпотентності)
Після реєстрації ви можете надіслати короткий SendMessage ПІДТВЕРДЖЕННЯ
4. Виробничі примітки
- Секретний код вебхука. Випробування пройдено:
секретний токенвідsetWebhookДодати та підтвердитиX-Telegram-Bot-Api-Secret-Token. - Оновлення ідемпотентності. Продовжувати
update_idі пропускати дублікати, щоб веб-перехоплювачі не могли позичати двічі. - Повернення Ставтеся до повернень Stars як до власних подій зі списаними ідентифікаторами та узгоджуйте їх, як до будь-яких зворотних подій.
- Подробиці в журналі Утримувати RAW
платіж успішно здійсненоJSON щонайменше на 90 днів. - Зняття коштів Зірки не фіатні. Використовуйте
getStarTransactionsі Фрагмент для виплати — не обіцяйте користувачам, що оплата дорівнює вашому банківському депозиту.
Типові пастки
- Повернути http 200 веб-перехоплювачу без виклику
answerPreCheckoutQuery— Telegram все ще скасовано після тайм-ауту. - Читання ціни з кнопки «Купити»
callback_query. Гроші живуть вpre_checkout_queryіплатіж успішно здійснено. - Внесення ціни всередину
корисне навантаження. Зберігайте його непрозорим; перерахуйте на сервері. - У тому числі
provider_tokenдляXTR—sendInvoice- - Ставлення до зірок як до готівки в банку.
- Повернення помилок до
платіж успішно здійсненоназавжди. Журнал, повернення200, і охоронець з унікальнимtelegram_payment_charge_id.
6. Наскрізна форма
- Користувач натискає Купити. Ви
answerCallbackQueryі зателефонуйтеsendInvoiceдеcurrency=XTR. - Telegram показує аркуш Stars. Після підтвердження надсилає
pre_checkout_query; ви відповідаєте лише після повторної перевірки замовлення. - Для зарядки Telegram надсилає
платіж успішно здійсненоВи підтверджуєте валюту, суму, корисне навантаження та ідентифікатор платежу, кредитуєте один раз, а потім підтверджуєте за допомогоюSendMessage.
Зберігайте факти про гроші лише в двох місцях: invoice_payload ви створили, і платіж успішно здійснено Telegram відправляє назад.
Як перетворити Stars на виробничого бота або мини-приложение? botservice.biz - чудовий початок.