Оптовое API · benefit.ggWholesale API · benefit.gg

Прилавок
для своих
сервисов.
A counter
for your
service.

Клиент платит вам. Вы создаёте заказ. Мы списываем ваш баланс и покупаем Telegram Stars, Premium, розыгрыш канала или Steam.Your customer pays you. You place the order. We charge your balance and buy Telegram Stars, Premium, a channel giveaway, or Steam.

https://api.benefit.gg/api/v1
Наценка APIAPI markupниже, чем в ботеlower than in the bot
Stars5%
Premium5%
Розыгрыш Stars / PremiumStars / Premium giveaway5%
Steam1%
01БалансBalance

Один кошелёк на бота и API. Пополнение в боте или депозитом CryptoBot.One wallet for the bot and the API. Top up in the bot or with a CryptoBot deposit.

02ЗаказOrder

Ключ, ваш external_id и Idempotency-Key. Повтор не создаёт второй заказ.Your key, external_id and Idempotency-Key. A retry does not create a second order.

03СписаниеCharge

Stars, Premium и розыгрыш — в момент покупки. Steam — сразу.Stars, Premium and giveaways are charged when the purchase starts. Steam is charged immediately.

04СтатусStatus

Вебхук с подписью или GET /orders/:id. Ошибка до оплаты — возврат на баланс.A signed webhook, or GET /orders/:id. If payment never goes out, the balance is refunded.

GET/meБаланс, права ключа, наценкиBalance, key permissions, markups
GET/stars/priceЦена звёзд до заказаStars price before you order
POST/stars/ordersКупить Stars. Списание в момент покупкиBuy Stars. Charged when the purchase starts
GET/premium/priceЦена Premium на 3, 6 или 12 месяцевPremium price for 3, 6 or 12 months
POST/premium/ordersПодарить PremiumGift Premium
GET/giveaways/stars/packagesПакеты розыгрыша звёздStars giveaway packages
GET/giveaways/stars/priceЦена пакета и лимит победителейPackage price and winner limit
POST/giveaways/stars/ordersОплатить розыгрыш Stars для каналаPay a Stars giveaway for a channel
GET/giveaways/premium/priceЦена розыгрыша PremiumPremium giveaway price
POST/giveaways/premium/ordersОплатить розыгрыш Premium для каналаPay a Premium giveaway for a channel
GET/steam/ratesКурсы RUB, KZT, UAHRUB, KZT, UAH rates
POST/steam/check-loginПроверить логин SteamCheck a Steam login
POST/steam/ordersПополнить Steam. Списание сразуTop up Steam. Charged immediately
POST/depositsПополнить баланс через CryptoBotTop up the balance with CryptoBot
GET/orders и /deposits/orders and /depositsСтатус заказа или платежаOrder or payment status

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.

Пример кодаCode sample
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
pendingprocessingcompletedfailedpending_verificationrefunded
Пример кодаCode sample
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"},
)

04

Premium

Подарок Telegram Premium на 3, 6 или 12 месяцев. Себестоимость фиксирована в долларах Fragment, к ней 5%. Живая цена: GET /premium/price?months=3.A Telegram Premium gift for 3, 6 or 12 months. Fragment’s cost is fixed in dollars, plus 5%. Live price: GET /premium/price?months=3.

Списание такое же, как у Stars: в момент покупки, возврат если покупка не прошла. Если у человека уже есть Premium, ответ 409 already_has_premium и деньги не списываются.Charged like Stars: when the purchase starts, refunded if it does not go through. If the person already has Premium, the response is 409 already_has_premium and nothing is charged.

username
Кому дарим, без @Who receives it, without @
months
3, 6 или 123, 6 or 12
external_id
Ваш номер заказаYour order id

В объекте заказа quantity и months — это срок в месяцах.In the order object, quantity and months are the term in months.

Пример кодаCode sample
curl -X POST https://api.benefit.gg/api/v1/premium/orders \
  -H "X-API-Key: bnf_..." \
  -H "Idempotency-Key: prem-42" \
  -H "Content-Type: application/json" \
  -d '{"username":"durov","months":3,"external_id":"prem-42"}'
await fetch("https://api.benefit.gg/api/v1/premium/orders", {
  method: "POST",
  headers: {
    "X-API-Key": process.env.BENEFIT_KEY,
    "Idempotency-Key": "prem-42",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({ username: "durov", months: 3, external_id: "prem-42" }),
});
requests.post(
    "https://api.benefit.gg/api/v1/premium/orders",
    headers={"X-API-Key": key, "Idempotency-Key": "prem-42"},
    json={"username": "durov", "months": 3, "external_id": "prem-42"},
)

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
Пример кодаCode sample
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
Пример кодаCode sample
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
Пример кодаCode sample
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.

заказorder
{
  "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.

order.processingorder.completedorder.failed order.pending_verificationorder.refunded payment.completedpayment.expired
Пример кодаCode sample
# заголовок 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