Yadreno :
/

🔌 Руководство по интеграции

Используйте Ya.SellerBot как платёжный шлюз для своих проектов

2. Webhook уведомления

При каждой успешной оплате бот отправляет 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 оплаты

3. Проверка подписи (Signature)

Для защиты от поддельных запросов передаётся заголовок X-Callback-Signature. Вам необходимо проверить его валидность.

Алгоритм проверки

  1. Получите сырое тело запроса (raw body).
  2. Вычислите HMAC-SHA256 хеш от тела с вашим Secret Key (ПрофильКлюч подписи).
  3. Возьмите первые 11 байт бинарного результата.
  4. Закодируйте эти байты в Base62 (~15 символов).
  5. Сравните с заголовком X-Callback-Signature.

PHP

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');
}

Python

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)

Node.js

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 (если подпись не сходится)

💡 Когда это нужно?

В большинстве случаев канонизация не нужна! Мы отправляем JSON уже в каноническом формате. Используйте этот раздел, только если ваш прокси, WAF или фреймворк переформатирует JSON (добавляет пробелы, меняет порядок ключей).

PHP

// Замените 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;
}

Python

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)

Node.js

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;
}

4. Callback-кнопка

После покупки покупатель увидит кнопку «Вернуться в магазин». При клике он перейдёт на ваш URL с параметром start.

⚙️ Где настроить

Callback URL настраивается в боте: ПозицияУведомленияОбратная ссылка

Формат ссылки

Вы указываете только базовый URL. Параметр start будет добавлен автоматически.

Пример:
Вы указываете:      https://t.me/Ya_FooterBot
Покупатель получит: https://t.me/Ya_FooterBot?start=bill1-{данные}-{подпись}

📋 Требования

  • Только HTTPS
  • Длина от 10 до 255 символов
  • Без пробелов и спецсимволов
  • Не добавляйте ?start= в конце ссылки

Расшифровка параметра 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")

5. Тестовый режим (Sandbox)

Для отладки интеграции включите «Тестовый режим» в Профиле бота.

Как это работает

  • Используйте item0 вместо item в ссылках
  • Бот позволит «оплатить» товар без реальных транзакций
  • Вы получите Webhook-уведомление в том же формате
  • Callback-ссылка также будет работать штатно

Идеально для

  • Проверки цепочки: webhook → обработка → ответ
  • Тестирования интеграции без реальных денег
  • Отладки callback-ссылок
  • Демонстрации функционала клиентам

⚠️ Ограничения

Платформа имеет следующие технические ограничения:

💰 Лимиты оплат

  • Минимальная сумма оплаты: 0.1 USDT
  • Максимальная сумма оплаты: 1 000 USDT

⚠️ Важно: Платежи свыше 1 000 USDT будут заблокированы.

💸 Вывод средств

  • Минимальная сумма вывода: 10 USDT

Для автоматических и ручных выплат на внешний кошелёк.

Готовы интегрировать?

Начните принимать криптовалютные платежи уже сегодня.

Открыть бота Техническая поддержка