Используйте Ya.SellerBot как платёжный шлюз для своих проектов
Чтобы привести покупателя к товару, используйте ссылку вида:
https://t.me/Ya_SellerBot?start=item-{item_id}-{ref_code}-{promo}-{invoice}-{price}
| Параметр | Обязательный | Описание |
|---|---|---|
item_id |
✅ Да | ID товара в кодировке Base62 (выдаётся при создании) |
ref_code |
❌ Нет | Реферальный код. Формат: R + 6 символов или U + user_id в Base62 |
promo |
❌ Нет | Промокод для автоприменения |
invoice |
❌ Нет | Идентификатор инвойса (для внешних систем). Разрешены: A-Z, a-z, 0-9. Макс 8 символов. |
price |
❌ Нет | Фиксированная цена в центах (для Invoice с произвольной суммой) |
Если нужно пропустить промежуточный параметр (например, указать промокод без реф-кода), просто оставьте пустое значение между дефисами: item-aZ--PROMO
• Простая ссылка:
https://t.me/Ya_SellerBot?start=item-aZ
• Ссылка с реф-кодом:
https://t.me/Ya_SellerBot?start=item-aZ-RaBcDeF
• Ссылка с промокодом (без реф-кода):
https://t.me/Ya_SellerBot?start=item-aZ--SALE10
• Полная ссылка:
https://t.me/Ya_SellerBot?start=item-aZ-RaBcDeF-SALE10-myinv123-1500
Где:
• aZ — ID товара
• RaBcDeF — реферальный код
• SALE10 — промокод
• myinv123 — идентификатор инвойса
• 1500 — цена 15.00 USDT (1500 центов)
Для тестовых ссылок используйте команду item0 вместо item:
https://t.me/Ya_SellerBot?start=item0-aZ
При каждой успешной оплате бот отправляет POST-запрос на указанный URL.
Webhook URL настраивается в боте: Позиция → Уведомления → Webhook
Заголовок: Content-Type: application/json
{
"version": 1,
"invoice_or_order_id": "aZ1",
"item_id": "bY",
"item_type": "digital",
"tariff_id": 1,
"tariff_global_id": "5",
"tariff_name_en": "Monthly",
"buyer_id": 987654321,
"seller_id": 123456789,
"amount_cents": 1000,
"final_amount_cents": 900,
"promo_code": "SALE10",
"promo_discount_percent": 10,
"status": "paid",
"paid_at": 1735689600,
"delivered_at": 1735689605
}
| Поле | Тип | Описание |
|---|---|---|
version |
integer | Версия формата (сейчас 1) |
invoice_or_order_id |
string | ID заказа (Base62) |
item_id |
string | ID товара (Base62) |
tariff_id |
integer? | Локальный ID тарифа (1-9) или null |
tariff_global_id |
string | Глобальный ID тарифа в БД |
tariff_name_en |
string? | Английское название тарифа или null |
buyer_id |
integer | Telegram ID покупателя |
seller_id |
integer | Telegram ID продавца |
amount_cents |
integer | Исходная сумма в центах USDT |
final_amount_cents |
integer | Итоговая сумма с учётом скидки |
status |
string | paid или delivered |
paid_at |
integer | Unix timestamp оплаты |
Для защиты от поддельных запросов передаётся заголовок X-Callback-Signature. Вам необходимо проверить его валидность.
Профиль → Ключ подписи).X-Callback-Signature.function check_signature($raw_body, $signature, $secret_key) {
$hmac = hash_hmac('sha256', $raw_body, $secret_key, true);
$truncated = substr($hmac, 0, 11);
$calc_sig = encode_base62($truncated);
return hash_equals($calc_sig, $signature);
}
function encode_base62($binary_data) {
$alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
$hex = bin2hex($binary_data);
$dec = gmp_init($hex, 16);
if (gmp_cmp($dec, 0) == 0) return '0';
$result = '';
while (gmp_cmp($dec, 0) > 0) {
list($dec, $rem) = gmp_div_qr($dec, 62);
$result .= $alphabet[gmp_intval($rem)];
}
return strrev($result);
}
// Использование
$secret = 'ВАШ_КЛЮЧ';
$payload = file_get_contents('php://input');
$sig = $_SERVER['HTTP_X_CALLBACK_SIGNATURE'] ?? '';
if (!check_signature($payload, $sig, $secret)) {
http_response_code(403);
die('Invalid signature');
}
import hmac
import hashlib
ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
def encode_base62(data: bytes) -> str:
if not data: return ""
num = int.from_bytes(data, 'big')
if num == 0: return "0"
res = []
while num > 0:
num, rem = divmod(num, 62)
res.append(ALPHABET[rem])
return "".join(reversed(res))
def check_signature(raw_body: str, signature: str, secret_key: str) -> bool:
h = hmac.new(secret_key.encode('utf-8'), raw_body.encode('utf-8'), hashlib.sha256).digest()
truncated = h[:11]
calc_sig = encode_base62(truncated)
return hmac.compare_digest(calc_sig, signature)
const crypto = require('crypto');
const ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
function encodeBase62(buffer) {
let value = BigInt("0x" + buffer.toString('hex'));
if (value === 0n) return "0";
let res = "";
while (value > 0n) {
let rem = value % 62n;
res = ALPHABET[Number(rem)] + res;
value /= 62n;
}
return res;
}
function checkSignature(rawBody, signature, secretKey) {
const hmac = crypto.createHmac('sha256', secretKey).update(rawBody).digest();
const truncated = hmac.subarray(0, 11);
const calcSig = encodeBase62(truncated);
return calcSig === signature;
}
В большинстве случаев канонизация не нужна! Мы отправляем JSON уже в каноническом формате. Используйте этот раздел, только если ваш прокси, WAF или фреймворк переформатирует JSON (добавляет пробелы, меняет порядок ключей).
// Замените check_signature на эту версию:
function check_signature_canonical($raw_body, $signature, $secret_key) {
$data = json_decode($raw_body, true);
if ($data === null) return false;
$canonical = canonical_json($data);
$hmac = hash_hmac('sha256', $canonical, $secret_key, true);
$truncated = substr($hmac, 0, 11);
$calc_sig = encode_base62($truncated);
return hash_equals($calc_sig, $signature);
}
function canonical_json($data) {
$data = canonical_sort($data);
return json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
}
function canonical_sort($value) {
if (is_array($value)) {
if (array_keys($value) !== range(0, count($value) - 1)) {
ksort($value, SORT_STRING);
}
foreach ($value as $k => $v) {
$value[$k] = canonical_sort($v);
}
}
return $value;
}
import json
def canonical_json(data) -> str:
return json.dumps(data, sort_keys=True, separators=(',', ':'), ensure_ascii=False)
def check_signature_canonical(raw_body: str, signature: str, secret_key: str) -> bool:
data = json.loads(raw_body)
canonical = canonical_json(data)
h = hmac.new(secret_key.encode('utf-8'), canonical.encode('utf-8'), hashlib.sha256).digest()
truncated = h[:11]
calc_sig = encode_base62(truncated)
return hmac.compare_digest(calc_sig, signature)
function sortKeys(obj) {
if (typeof obj !== 'object' || obj === null) return obj;
if (Array.isArray(obj)) return obj.map(sortKeys);
return Object.keys(obj).sort().reduce((sorted, key) => {
sorted[key] = sortKeys(obj[key]);
return sorted;
}, {});
}
function canonicalJson(data) {
return JSON.stringify(sortKeys(data));
}
function checkSignatureCanonical(rawBody, signature, secretKey) {
const data = JSON.parse(rawBody);
const canonical = canonicalJson(data);
const hmac = crypto.createHmac('sha256', secretKey).update(canonical).digest();
const truncated = hmac.subarray(0, 11);
const calcSig = encodeBase62(truncated);
return calcSig === signature;
}
После покупки покупатель увидит кнопку «Вернуться в магазин». При клике он перейдёт на ваш URL с параметром start.
Callback URL настраивается в боте: Позиция → Уведомления → Обратная ссылка
Вы указываете только базовый URL. Параметр start будет добавлен автоматически.
Пример:
Вы указываете: https://t.me/Ya_FooterBot
Покупатель получит: https://t.me/Ya_FooterBot?start=bill1-{данные}-{подпись}
?start= в конце ссылки| Сегмент | Описание |
|---|---|
bill1 |
Префикс протокола (всегда bill1) |
ORDER_ID |
ID заказа (Base62) |
ITEM_ID |
ID товара (Base62) |
TARIFF |
Локальный ID тарифа (1-9) или _ если нет |
PROMO |
Промокод (_ если не использовался) |
PRICE |
Цена в центах (_ если не применимо) |
SIGNATURE |
Подпись (последний сегмент) |
Подпись вычисляется от строки из всех сегментов кроме последнего:
# Python
start_param = "bill1-aZ1-bY-1-_-1000-SIG"
data_part, received_sig = start_param.rsplit('-', 1)
# data_part = "bill1-aZ1-bY-1-_-1000"
is_valid = check_signature(data_part, received_sig, "YOUR_SECRET")
Для отладки интеграции включите «Тестовый режим» в Профиле бота.
item0 вместо item в ссылкахПлатформа имеет следующие технические ограничения:
⚠️ Важно: Платежи свыше 1 000 USDT будут заблокированы.
Для автоматических и ручных выплат на внешний кошелёк.
Начните принимать криптовалютные платежи уже сегодня.