PayStars Unified API · v3.0
Один API.
Тот же баланс, что и в StarKerak.
Сайт, Telegram WebApp и внешний API используют одни и те же цены, баланс и механизм исполнения заказов StarKerak. Ответы API — в формате JSON.
Аутентификация
Создайте API-ключ в Dashboard. Ключ показывается полностью только один раз. Храните его в окружении сервера (environment), не размещайте во frontend JavaScript и не коммитьте в Git-репозиторий.
X-API-Key: ps_your_secret_api_key Content-Type: application/json Idempotency-Key: order_20260719_0001
Лимиты, проверка и idempotency
- 3 запроса в секунду на один API-ключ.
- Лимит проверки получателя через API: 120 в минуту на аккаунт; для сайта/WebApp — 12.
- Перед покупкой обязательно вызовите
/check-usernameи получитеverification_token, действующий 180 секунд. - Каждый verification token привязывается только к одному логическому заказу; безопасный повтор (retry) возможен с тем же ключом idempotency.
- При покупке Stars и Premium отправляйте уникальный
Idempotency-Keyдлиной 8–128 символов. - Если повторно отправить тот же
Idempotency-Keyс тем же payload, деньги повторно не списываются; возвращается прежний заказ. - Если тот же ключ idempotency использовать с другим payload, вернётся
409 Conflict.
Статус API
/statuscurl https://paystars.uz/api/v1/status
USD-баланс и цена
Аккаунт и долларовый баланс берутся из StarKerak. Цены — в долларах: каждый заказ стоит цену Fragment + 5%.
/accountcurl https://paystars.uz/api/v1/account \
-H "X-API-Key: $PAYSTARS_API_KEY"{
"ok": true,
"user": { "telegram_id": 123456789 },
"balances": { "USD": "12.50" },
"currency_pref": "USD"
}/pricesДля цены в USD используйте POST /usd/quote (ниже): цена = Fragment + 5%.
Проверка username
Этот эндпоинт через Fragment определяет имя, фото и статус Premium. verification_token возвращается только для получателя, которому можно совершить покупку.
/check-usernamecurl -X POST https://paystars.uz/api/v1/check-username \ -H "X-API-Key: $PAYSTARS_API_KEY" \ -H "Content-Type: application/json" \ -d '{"username":"telegram","kind":"premium"}'
{
"valid": true,
"username": "telegram",
"nickname": "Telegram User",
"photo_url": "https://cdn4.telesco.pe/...",
"premium_active": false,
"verification_token": "...",
"verification_expires_in": 180
}Значение kind — stars или premium. Если Premium уже активен, будет premium_active: true, valid: false, а токен — null.
Заказ Stars
/stars/buycurl -X POST https://paystars.uz/api/v1/stars/buy \ -H "X-API-Key: $PAYSTARS_API_KEY" \ -H "Idempotency-Key: order_20260719_0001" \ -H "Content-Type: application/json" \ -d '{"username":"telegram","quantity":100,"verification_token":"TOKEN_FROM_CHECK","currency":"USD","max_usd":1.58}'
{
"ok": true,
"order_id": 1842,
"status": "processing",
"quantity": 100,
"cost": 1.58,
"balance": 10.92,
"currency": "USD"
}Долларовый (USD) баланс и USDT
Оплата через API идёт с долларового баланса. Он пополняется USDT в сети TON или BEP20 (минимум 1 USDT). Цена в USD = цена Fragment + 5% (округляется вверх до цента).
/usd/walletДолларовый баланс, TON-адрес и персональный memo, BEP20-адрес (с QR-изображениями).
{
"ok": true,
"usd": { "balance": "12.50", "micros": 12500000 },
"min_deposit": "1.00",
"ton": { "address": "UQ...", "memo": "...", "enabled": true, "ttl_min": 30 },
"bsc": { "address": "0x...", "contract": "0x55d398326f99059fF775485246999027B3197955", "enabled": true, "ttl_min": 30 }
}usd.balance — долларовый баланс (строка), usd.micros — 1 USD = 1 000 000 micros.
/usd/invoice{"amount": 10, "chain": "ton"} или "chain": "bsc" → точная сумма, действующая 30 минут (например, 10.0437). Отправьте ровно эту сумму в USDT на адрес в выбранной сети (для TON memo не требуется). /usd/bsc-invoice работает как старое имя (по умолчанию: bsc).
/usd/invoice/{id}/cancelОтменяет ожидающий счёт (например, чтобы изменить сумму). Точная сумма отменённого счёта 24 часа никому больше не выдаётся, и платёж, пришедший за это время, всё равно зачисляется на ваш баланс.
/usd/invoice/{id}Статус: pending, paid, expired или cancelled.
/usd/quote{"kind":"stars","username":"durov","quantity":100} или {"kind":"premium","username":"durov","months":3} → {"usd": 1.58}.
Чтобы оплатить покупку с долларового баланса, добавьте в payload /stars/buy или /premium/buy поле "currency": "USD" и "max_usd": 1.58 из /usd/quote. Если цена Fragment превысит max_usd, деньги не списываются и возвращается 409 price_changed — получите новый quote. Если currency не передан, используется сохранённый выбор валюты аккаунта (pref в /usd/wallet) — поэтому всегда передавайте "currency": "USD". Поля cost и balance в ответе указаны в валюте из поля currency.
curl -X POST https://paystars.uz/api/v1/stars/buy \
-H "X-API-Key: $PAYSTARS_API_KEY" -H "Idempotency-Key: stars_8f2c1a9b" \
-H "Content-Type: application/json" \
-d '{"username":"durov","quantity":100,"verification_token":"...","currency":"USD","max_usd":1.58}'Заказы
/ordersВозвращает последние 50 заказов, созданных через сайт, WebApp и API.
/order/{order_id}curl https://paystars.uz/api/v1/order/1842 \
-H "X-API-Key: $PAYSTARS_API_KEY"Ошибки
| Status | Значение | Рекомендация |
|---|---|---|
| 400 | Ошибка в payload или ключе idempotency | Проверьте формат запроса |
| 401 | Недействительный API-ключ | Создайте новый ключ |
| 402 | Недостаточно средств на USD-балансе | Пополните в USDT |
| 409 | Verification истёк/использован или конфликт idempotency | Проверьте получателя заново |
| 422 | Username или услуга недоступны | Проверьте получателя |
| 402 | insufficient_balance (USD) | Пополните долларовый баланс через USDT |
| 409 | price_changed — цена в USD превысила max_usd | Получите новый /usd/quote |
| 429 | Превышен лимит запросов | Повторите согласно Retry-After |
| 503 | Сервис временно недоступен | Используйте exponential backoff |
{
"detail": {
"code": "insufficient_balance",
"message": "StarKerak balance is insufficient"
}
}Рабочие примеры
На разных языках программирования
Каждый пример проверяет получателя, получает краткосрочный токен и использует ключ idempotency для безопасного повтора того же заказа.
API="https://paystars.uz/api/v1"
ORDER_KEY="stars_$(uuidgen)"
VERIFY=$(curl --fail-with-body -sS -X POST "$API/check-username" \
-H "X-API-Key: $PAYSTARS_API_KEY" \
-H "Content-Type: application/json" \
-d '{"username":"telegram","kind":"stars"}')
TOKEN=$(printf '%s' "$VERIFY" | jq -r '.verification_token')
curl --fail-with-body -sS -X POST "$API/stars/buy" \
-H "X-API-Key: $PAYSTARS_API_KEY" \
-H "Idempotency-Key: $ORDER_KEY" \
-H "Content-Type: application/json" \
-d "{\"username\":\"telegram\",\"quantity\":100,\"verification_token\":\"$TOKEN\",\"currency\":\"USD\"}"import { randomUUID } from 'node:crypto';
const base = 'https://paystars.uz/api/v1';
const headers = { 'X-API-Key': process.env.PAYSTARS_API_KEY, 'Content-Type': 'application/json' };
const verify = await fetch(`${base}/check-username`, {
method: 'POST', headers, body: JSON.stringify({ username: 'telegram', kind: 'stars' })
}).then(async r => { if (!r.ok) throw new Error(await r.text()); return r.json(); });
const order = await fetch(`${base}/stars/buy`, {
method: 'POST', headers: { ...headers, 'Idempotency-Key': `stars_${randomUUID()}` },
body: JSON.stringify({ username: verify.username, quantity: 100, verification_token: verify.verification_token, currency: 'USD' })
}).then(async r => { if (!r.ok) throw new Error(await r.text()); return r.json(); });
console.log(order.order_id, order.status);import os, uuid, requests
base = "https://paystars.uz/api/v1"
headers = {"X-API-Key": os.environ["PAYSTARS_API_KEY"]}
verify = requests.post(f"{base}/check-username", headers=headers,
json={"username": "telegram", "kind": "premium"}, timeout=15)
verify.raise_for_status()
recipient = verify.json()
order = requests.post(f"{base}/premium/buy", headers={
**headers, "Idempotency-Key": f"premium_{uuid.uuid4()}"
}, json={"username": recipient["username"], "months": 3,
"verification_token": recipient["verification_token"], "currency": "USD"}, timeout=20)
order.raise_for_status()
print(order.json())<?php
$base = 'https://paystars.uz/api/v1';
$key = getenv('PAYSTARS_API_KEY');
function postJson($url, $headers, $body) {
$ch = curl_init($url);
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true,
CURLOPT_HTTPHEADER => $headers, CURLOPT_POSTFIELDS => json_encode($body), CURLOPT_TIMEOUT => 20]);
$raw = curl_exec($ch); $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); curl_close($ch);
if ($status < 200 || $status >= 300) throw new RuntimeException($raw);
return json_decode($raw, true, flags: JSON_THROW_ON_ERROR);
}
$common = ["X-API-Key: $key", 'Content-Type: application/json'];
$user = postJson("$base/check-username", $common, ['username' => 'telegram', 'kind' => 'stars']);
$order = postJson("$base/stars/buy", [...$common, 'Idempotency-Key: stars_'.bin2hex(random_bytes(16))],
['username' => $user['username'], 'quantity' => 100, 'verification_token' => $user['verification_token'], 'currency' => 'USD']);
print_r($order);package main
import ("bytes"; "encoding/json"; "fmt"; "io"; "net/http"; "os"; "github.com/google/uuid")
func post(path string, body any, idempotency string, out any) error {
data, _ := json.Marshal(body)
req, _ := http.NewRequest("POST", "https://paystars.uz/api/v1"+path, bytes.NewReader(data))
req.Header.Set("X-API-Key", os.Getenv("PAYSTARS_API_KEY")); req.Header.Set("Content-Type", "application/json")
if idempotency != "" { req.Header.Set("Idempotency-Key", idempotency) }
res, err := http.DefaultClient.Do(req); if err != nil { return err }; defer res.Body.Close()
if res.StatusCode >= 300 { raw, _ := io.ReadAll(res.Body); return fmt.Errorf("API %d: %s", res.StatusCode, raw) }
return json.NewDecoder(res.Body).Decode(out)
}
func main() {
var user struct { Username string `json:"username"`; VerificationToken string `json:"verification_token"` }
if err := post("/check-username", map[string]any{"username":"telegram", "kind":"stars"}, "", &user); err != nil { panic(err) }
var order map[string]any
if err := post("/stars/buy", map[string]any{"username":user.Username, "quantity":100,
"verification_token":user.VerificationToken, "currency":"USD"}, "stars_"+uuid.NewString(), &order); err != nil { panic(err) }
fmt.Println(order)
}Интеграция одним промптом
AI-промпт
Отправьте этот промпт в Codex, ChatGPT или другой coding-агент. Замените значения в скобках под свой проект.
Ты senior-инженер по backend и application security. Создай для меня полную, production-ready интеграцию с PayStars API.
ТИП ПРОЕКТА: [Telegram-бот / web backend / backend мобильного приложения]
ЯЗЫК И ФРЕЙМВОРК: [например: Python 3.12 + aiogram 3 + FastAPI]
НАЗВАНИЕ ПРОЕКТА: [название]
PayStars base URL: https://paystars.uz/api/v1
Бери API-ключ только из переменной окружения PAYSTARS_API_KEY. Никогда не выводи его во frontend, логи, Git или текст ошибок. Все запросы — по HTTPS и только с сервера. Добавь таймауты, structured logging (без секретов), валидацию входных данных и тесты.
Полностью реализуй следующий поток:
1. Через GET /usd/wallet получи USD-баланс. Не хардкодь цены: перед каждой покупкой получай цену в USD через POST /usd/quote (цена Fragment + 5%).
2. Перед покупкой Stars или Premium отправь в POST /check-username {"username":"...","kind":"stars|premium"}.
3. Проверь в ответе valid, nickname, photo_url, premium_active и verification_token. Покажи пользователю фото, имя, @username и статус Premium и получи отдельное подтверждение. Без подтверждения заказ не создавай.
4. verification_token действует 180 секунд. Если он истёк или вернулся 409 — проверь username заново.
5. Для Stars отправь в POST /stars/buy username, quantity и verification_token; для Premium — в POST /premium/buy username, months (3, 6 или 12) и verification_token. В каждой покупке передавай "currency": "USD" и цену из quote как "max_usd"; при 409 price_changed получи новый quote, при 402 insufficient_balance предложи пополнить USD-баланс в USDT (TON или BEP20).
6. Для каждого логического заказа создавай Idempotency-Key длиной 8–128 символов на основе криптографического UUID. При сетевом retry используй ровно тот же ключ и payload; не используй его повторно для другого заказа.
7. Опрашивай статус через GET /order/{order_id} и показывай историю через GET /orders. Корректно обрабатывай статусы processing/pending/completed/failed.
8. Лимит API — 3 запроса в секунду на ключ. При 429 соблюдай Retry-After; при 503 и временных сетевых ошибках используй ограниченный exponential backoff с jitter. Для 400, 401, 402, 409, 422, 429 и 503 показывай пользователю безопасное и понятное сообщение.
9. Не доверяй ответам API слепо: проверяй по схеме, нормализуй username, проверяй лимиты quantity/months на сервере. Показывай photo_url только как разрешённое HTTPS-изображение. Никогда не логируй verification token, API-ключ или полные заголовки запросов.
10. Учитывай параллельные нажатия одного пользователя, replay, дублирующиеся заказы, поддельные callback, race condition, таймауты и перезапуск процесса. Храни состояние в БД через транзакции и связывай подтверждение пользователя с заказом.
Если проект — Telegram-бот: создай меню /start, «Купить Stars», «Купить Premium», «Баланс», «Заказы»; запрашивай username и количество пошагово; после проверки показывай inline-кнопки «Подтвердить/Отмена»; проверяй владельца callback и TTL; настрой безопасность webhook secret или long-polling.
В результате дай не псевдокод, а полный рабочий код: точную структуру папок, файл зависимостей, .env.example (без реальных секретов), миграции БД, Dockerfile, healthcheck, README, инструкции по локальному запуску и деплою, unit/integration-тесты. Вынеси API-клиент в отдельный модуль. Считай PayStars API единственным источником истины для всех цен, балансов и заказов; не подключайся к базе StarKerak напрямую. Недостающие бизнес-значения сначала запроси коротким списком, остальные технические решения прими сам с безопасными значениями по умолчанию.