Приймайте платежі Telegram Stars PHP: sendInvoice, pre_checkout_query та Successful_payment верифікація сервера

Що ми будуємо і чого нам не вистачає

У цьому посібнику показано мінімальний шлях PHP для впровадження Telegram Stars через API бота. Ви:

  1. Надіслати рахунок sendInvoice У валюті XTR.
  2. Рукоятка pre_checkout_query і зателефонуйте answerPreCheckoutQuery вчасно
  3. Отримати message.successful_payment та підтвердьте ідентифікатор стягнення, суму Stars та корисне навантаження рахунку.
  4. Уникайте поширених помилок: відповідайте занадто пізно, ставтеся до зірок як до банківської готівки та довіряйте підсумкам діяльності клієнта.

Ми не покриваємо 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);

Перегляньте його:

  1. currency === 'XTR'
  2. invoice_payload співвідноситься з відкладеним замовленням
  3. Загальна вартість відповідає очікуваним зіркам
  4. 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 для XTRsendInvoice -
  • Ставлення до зірок як до готівки в банку.
  • Повернення помилок до платіж успішно здійснено назавжди. Журнал, повернення 200, і охоронець з унікальним telegram_payment_charge_id.

6. Наскрізна форма

  1. Користувач натискає Купити. Ви answerCallbackQuery і зателефонуйте sendInvoice де currency=XTR.
  2. Telegram показує аркуш Stars. Після підтвердження надсилає pre_checkout_query; ви відповідаєте лише після повторної перевірки замовлення.
  3. Для зарядки Telegram надсилає платіж успішно здійсненоВи підтверджуєте валюту, суму, корисне навантаження та ідентифікатор платежу, кредитуєте один раз, а потім підтверджуєте за допомогою SendMessage.

Зберігайте факти про гроші лише в двох місцях: invoice_payload ви створили, і платіж успішно здійснено Telegram відправляє назад.

Як перетворити Stars на виробничого бота або мини-приложение? botservice.biz - чудовий початок.

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

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