Один кошелёк на бота и API. Пополнение в боте или депозитом CryptoBot.One wallet for the bot and the API. Top up in the bot or with a CryptoBot deposit.
01
Ключ и адресKey and address
Ключ выпускается в @BenefitStarsRobot → Профиль → API. Формат bnf_ и 48 hex. Показывается один раз, у нас хранится только sha256. Заголовок X-API-Key или Authorization: Bearer.Get the key in @BenefitStarsRobot → Profile → API. Format bnf_ plus 48 hex. It is shown once; we store only the sha256. Send X-API-Key or Authorization: Bearer.
Лимит по умолчанию: 120 запросов в минуту и 20 создающих запросов. Сверх лимита — 429.Default limits: 120 requests per minute and 20 creating requests. Above that — 429.
На каждый POST заказа передавайте Idempotency-Key до 128 символов. Тот же ключ после таймаута вернёт уже созданный заказ, HTTP 200 вместо 201.Send Idempotency-Key (up to 128 characters) on every order POST. The same key after a timeout returns the existing order, HTTP 200 instead of 201.
curl https://api.benefit.gg/api/v1/me \
-H "X-API-Key: bnf_..."
const res = await fetch("https://api.benefit.gg/api/v1/me", { headers: { "X-API-Key": process.env.BENEFIT_KEY }, }); console.log(await res.json());
import os, requests
r = requests.get(
"https://api.benefit.gg/api/v1/me",
headers={"X-API-Key": os.environ["BENEFIT_KEY"]},
)
print(r.json())
Curl, Node.js или Python. Выбор на любом примере меняет код во всех блоках.Curl, Node.js or Python. The choice on any sample changes the code in every block.
02
Один балансOne balance
GET/me отдаёт баланс в долларах, права ключа и ваши наценки. Отдельного API-счёта нет: депозит в боте и депозит по API попадают в одно место.returns the balance in dollars, the key permissions and your markups. There is no separate API wallet: a deposit in the bot and a deposit via the API land in the same place.
POST/deposits — пока только CryptoBot. amount_usd — сколько зачислим, от $1 до $10 000. pay_amount — сумма инвойса: комиссия шлюза около 3% лежит на плательщике. Ссылка живёт 30 минут. После оплаты приходит payment.completed.— CryptoBot only for now. amount_usd is what we credit, from $1 to $10,000. pay_amount is the invoice: the gateway fee of about 3% is paid by the payer. The link lasts 30 minutes. After payment you get payment.completed.
- amount_usd
- Сумма зачисления на балансAmount credited to the balance
- method
- cryptobot
- external_id
- Ваш id платежа, вернётся в вебхукеYour payment id, echoed in the webhook
03
Stars
Цена Fragment в долларах плюс 5%. Узнайте её до заказа: GET /stars/price?quantity=100.Fragment price in dollars plus 5%. Check it before ordering: GET /stars/price?quantity=100.
Заказ списывает баланс в момент покупки. Если Fragment не принял заказ и оплата не ушла, сумма возвращается сама. Статус приходит вебхуком, обычно быстрее 30 секунд.The order charges the balance when the purchase starts. If Fragment does not accept it and no payment goes out, the amount returns on its own. The status arrives by webhook, usually within 30 seconds.
- username
- Получатель, без @. 4–32 символаRecipient, without @. 4–32 characters
- quantity
- От 50 до 10 000From 50 to 10,000
- external_id
- Ваш номер заказаYour order id
curl -X POST https://api.benefit.gg/api/v1/stars/orders \ -H "X-API-Key: bnf_..." \ -H "Idempotency-Key: order-1001" \ -H "Content-Type: application/json" \ -d '{"username":"durov","quantity":100,"external_id":"order-1001"}'
await fetch("https://api.benefit.gg/api/v1/stars/orders", { method: "POST", headers: { "X-API-Key": process.env.BENEFIT_KEY, "Idempotency-Key": "order-1001", "Content-Type": "application/json", }, body: JSON.stringify({ username: "durov", quantity: 100, external_id: "order-1001" }), });
requests.post(
"https://api.benefit.gg/api/v1/stars/orders",
headers={"X-API-Key": key, "Idempotency-Key": "order-1001"},
json={"username": "durov", "quantity": 100, "external_id": "order-1001"},
)
05
РозыгрышиGiveaways
Мы оплачиваем призы. Запуск остаётся у канала.We pay for the prizes. The channel launches the giveaway.
Розыгрыш Stars или Premium для публичного канала. После оплаты он появляется в настройках канала: условия, дату и старт админ задаёт сам в Telegram. Личный аккаунт Fragment не примет.A Stars or Premium giveaway for a public channel. After payment it appears in the channel settings: the admin sets the rules, the date and the start in Telegram. Fragment will not accept a personal account.
Если розыгрыши ещё не включены, заказ ответит 503 sales_paused и деньги не спишутся. Цену смотреть можно и в этом случае.If giveaways are still turned off, the order returns 503 sales_paused and nothing is charged. You can still request the price.
Наценка 5%. Списание в момент покупки, возврат если оплата не прошла. Право ключа: giveaway.Markup 5%. Charged when the purchase starts, refunded if payment does not go out. Key permission: giveaway.
Stars на каналStars for a channel
GET /giveaways/stars/packages — пакеты, которые проходят лимит одной оплаты, с ценой и числом бустов. Затем цена конкретного пакета: GET /giveaways/stars/price?stars=500&winners=5. В ответе max_winners и per_user — сколько звёзд достанется одному победителю.— packages that fit in one payment, with price and boosts. Then the price of a package: GET /giveaways/stars/price?stars=500&winners=5. The response includes max_winners and per_user — stars per winner.
- channel
- @username канала, без @Channel @username, without @
- stars
- Пакет из списка packagesA package from the packages list
- winners
- Победители, от 1 до max_winners пакетаWinners, from 1 to the package max_winners
curl -X POST https://api.benefit.gg/api/v1/giveaways/stars/orders \ -H "X-API-Key: bnf_..." \ -H "Idempotency-Key: gw-9" \ -H "Content-Type: application/json" \ -d '{"channel":"durov","stars":500,"winners":5,"external_id":"gw-9"}'
await fetch("https://api.benefit.gg/api/v1/giveaways/stars/orders", { method: "POST", headers: { "X-API-Key": process.env.BENEFIT_KEY, "Idempotency-Key": "gw-9", "Content-Type": "application/json", }, body: JSON.stringify({ channel: "durov", stars: 500, winners: 5, external_id: "gw-9", }), });
requests.post(
"https://api.benefit.gg/api/v1/giveaways/stars/orders",
headers={"X-API-Key": key, "Idempotency-Key": "gw-9"},
json={"channel": "durov", "stars": 500, "winners": 5, "external_id": "gw-9"},
)
Premium на каналPremium for a channel
Несколько подписок одного срока. Цена: GET /giveaways/premium/price?months=3&quantity=4. Поле max_quantity — сколько подписок проходит в один заказ. boosts — бусты канала, четыре на подписку.Several subscriptions of one term. Price: GET /giveaways/premium/price?months=3&quantity=4. max_quantity is how many fit in one order. boosts are the channel boosts, four per subscription.
- channel
- Публичный каналPublic channel
- months
- 3, 6 или 123, 6 or 12
- quantity
- Сколько подписок разыгратьHow many subscriptions to give away
curl -X POST https://api.benefit.gg/api/v1/giveaways/premium/orders \ -H "X-API-Key: bnf_..." \ -H "Idempotency-Key: gwp-3" \ -H "Content-Type: application/json" \ -d '{"channel":"durov","months":3,"quantity":4,"external_id":"gwp-3"}'
await fetch("https://api.benefit.gg/api/v1/giveaways/premium/orders", { method: "POST", headers: { "X-API-Key": process.env.BENEFIT_KEY, "Idempotency-Key": "gwp-3", "Content-Type": "application/json", }, body: JSON.stringify({ channel: "durov", months: 3, quantity: 4, external_id: "gwp-3", }), });
requests.post(
"https://api.benefit.gg/api/v1/giveaways/premium/orders",
headers={"X-API-Key": key, "Idempotency-Key": "gwp-3"},
json={"channel": "durov", "months": 3, "quantity": 4, "external_id": "gwp-3"},
)
В заказе тип giveaway_stars или giveaway_premium. У звёзд quantity — пакет, winners — победители. У Premium quantity — число подписок, months — срок.The order type is giveaway_stars or giveaway_premium. For stars, quantity is the package and winners is the winner count. For Premium, quantity is the number of subscriptions and months is the term.
06
Steam
Пополнение с наценкой 1%. Валюты USD, RUB, KZT, UAH. Минимум — эквивалент $0.15. Перед заказом можно проверить логин: POST /steam/check-login с полем login. Курсы: GET /steam/rates.Top-up with a 1% markup. Currencies: USD, RUB, KZT, UAH. Minimum is the equivalent of $0.15. Check a login first with POST /steam/check-login and the field login. Rates: GET /steam/rates.
Здесь списание происходит сразу, до поставщика. Если заказ у поставщика не создался — возврат. Если создался, статус догоняет вебхук, деньги уже удержаны.The balance is charged immediately, before the supplier. If the supplier does not create the order, it is refunded. If it is created, the webhook catches up with the status and the money stays charged.
- login
- Логин SteamSteam login
- amount
- Сумма в выбранной валютеAmount in the chosen currency
- currency
- USD, RUB, KZT или UAHUSD, RUB, KZT or UAH
curl -X POST https://api.benefit.gg/api/v1/steam/orders \ -H "X-API-Key: bnf_..." \ -H "Idempotency-Key: st-7" \ -H "Content-Type: application/json" \ -d '{"login":"steamuser","amount":800,"currency":"RUB","external_id":"st-7"}'
await fetch("https://api.benefit.gg/api/v1/steam/orders", { method: "POST", headers: { "X-API-Key": process.env.BENEFIT_KEY, "Idempotency-Key": "st-7", "Content-Type": "application/json", }, body: JSON.stringify({ login: "steamuser", amount: 800, currency: "RUB", external_id: "st-7" }), });
requests.post(
"https://api.benefit.gg/api/v1/steam/orders",
headers={"X-API-Key": key, "Idempotency-Key": "st-7"},
json={"login": "steamuser", "amount": 800, "currency": "RUB", "external_id": "st-7"},
)
07
Как читать заказHow to read an order
GET/orders/:id — один заказ этого ключа. /orders?external_id= — поиск по вашему id. /orders?limit=20&offset=0 — список, не больше 100 за раз.— one order for this key. /orders?external_id= finds it by your id. /orders?limit=20&offset=0 lists up to 100 at a time.
Платёж: GET /deposits/:id. В объекте заказа смотрите тип: у Premium months — срок, у розыгрыша звёзд winners — победители, у Steam currency — валюта пополнения.Payment: GET /deposits/:id. In the order, read the type: Premium months is the term, a stars giveaway winners is the winner count, Steam currency is the top-up currency.
{
"ok": true,
"order": {
"id": 512,
"type": "stars",
"status": "completed",
"external_id": "order-1001",
"recipient": "durov",
"quantity": 100,
"price_usd": 1.58,
"markup_percent": 5,
"error": null
}
}
08
ВебхукиWebhooks
URL задаётся в боте: Профиль → API → Webhook URL. На каждую смену статуса мы шлём POST. Ответьте 2xx за 10 секунд. Иначе повтор с растущей паузой, до 20 раз. Одно событие может прийти дважды — обрабатывайте по id заказа.Set the URL in the bot: Profile → API → Webhook URL. We POST on every status change. Reply 2xx within 10 seconds, or we retry with a growing pause, up to 20 times. The same event can arrive twice — handle it by order id.
Подпись: заголовок X-Benefit-Signature: sha256=<hex>. Это HMAC-SHA256 сырого тела, ключ — секрет вебхука из меню API.Signature: header X-Benefit-Signature: sha256=<hex>. HMAC-SHA256 of the raw body, keyed with the webhook secret from the API menu.
# заголовок X-Benefit-Signature: sha256=<hex> # ключ — webhook secret из меню API
# header X-Benefit-Signature: sha256=<hex> # key — webhook secret from the API menu
const crypto = require("crypto"); function verify(rawBody, signature, secret) { const expected = "sha256=" + crypto.createHmac("sha256", secret).update(rawBody).digest("hex"); return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(expected)); }
import hmac, hashlib
def verify(raw: bytes, signature: str, secret: str) -> bool:
expected = "sha256=" + hmac.new(secret.encode(), raw, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature)
09
ОшибкиErrors
Тело всегда { "ok": false, "error": { "code", "message" } }.The body is always { "ok": false, "error": { "code", "message" } }.
- 401 unauthorized
- Нет ключа или ключ отозванMissing or revoked key
- 402 insufficient_balance
- На балансе меньше ценыBalance is below the price
- 403 forbidden
- Нет права или получатель заблокированMissing permission, or the recipient is blocked
- 404
- Нет пользователя, канала или заказа. Код channel_not_found или recipient_not_foundNo user, channel or order. Code channel_not_found or recipient_not_found
- 409 already_has_premium
- Premium уже активен, баланс не трогаетсяPremium is already active, balance is untouched
- 400
- Неверный срок, пакет, число победителей или суммаBad term, package, winner count or amount
- 429 rate_limited
- Слишком часто: 120 запросов или 20 заказов в минутуToo often: 120 requests or 20 orders per minute
- 503 sales_paused
- Этот товар сейчас выключен. Деньги не списываютсяThis product is turned off. Nothing is charged
- 503 prices_unavailable
- Fragment не отдал тариф. Повторите позжеFragment did not return a tariff. Try again later
- 502 upstream_error
- Fragment или поставщик не ответилFragment or the supplier did not respond