API для партнёров
Базовый адрес https://persshop.com/api/v1. Всё общение — JSON. Суммы в сомони, две цифры после запятой.
Ключ
Ключ выдаётся после одобрения заявки и выглядит как ps_live_abc123.секрет. Он показывается один раз — храните его на сервере, а не в коде бота, который лежит в общем репозитории. Если ключ утёк, напишите в чат: старый отзовём, выдадим новый.
Authorization: Bearer ps_live_abc123.ваш_секретНе больше 120 запросов в минуту на ключ. Сверх лимита — 429 с полем retryAfterSeconds.
Ошибки
У любой ошибки есть машинный code и человеческий error. Разбирайте первый: тексты мы можем переписать, коды — нет.
{ "ok": false, "code": "insufficient_balance", "error": "Недостаточно средств на балансе." }unauthorized— ключ не подходит ·key_revoked— ключ отозванrate_limited— слишком часто ·not_found— нет такой категории или заказаmissing_field— не заполнено поле заказа (какое — в полеfield)insufficient_balance— не хватает денег ·fulfillment_failed— поставщик не выдал, деньги уже вернулись на балансidempotency_key_reuse,in_progress— см. ниже
Ручки
GET /api/v1/catalog
q — поиск по названию, limit, offset. Цен здесь нет — они зависят от позиции, а не от категории.GET /api/v1/catalog/{id}
priceTjs — ваша, retailPriceTjs — наша розница.{
"ok": true,
"category": {
"id": "free-fire",
"kind": "topup",
"title": "Free Fire",
"fields": [{ "key": "player_id", "label": "ID игрока", "type": "text" }],
"offers": [
{ "offerId": "110_diamonds", "title": "110 Diamonds",
"priceTjs": 8.75, "retailPriceTjs": 9.95 }
],
"steamTopup": null
}
}GET /api/v1/balance
POST /api/v1/orders
POST /api/v1/orders
Authorization: Bearer ps_live_abc123.секрет
Idempotency-Key: 7f0c1e2a-2c1b-4a55-9f10-3e4d5c6b7a80
Content-Type: application/json
{
"categoryId": "free-fire",
"offerId": "110_diamonds",
"fields": { "player_id": "123456789" }
}Для пополнения Steam вместо offerId шлите сумму:{ "categoryId": "steam-topup", "amountUsd": 20, "fields": { "login": "steamlogin" } }В ответ — заказ со статусом. Для подарочных карт и ключей код лежит в delivery.{
"ok": true,
"order": {
"id": "0f2c…", "number": 128,
"categoryId": "free-fire", "offerId": "110_diamonds",
"priceTjs": 8.75, "status": "fulfilled",
"delivery": null, "error": null
}
}GET /api/v1/orders/{id}
Про Idempotency-Key
Рано или поздно ваш запрос оборвётся по таймауту уже после того, как мы списали деньги и заказали у поставщика. Ключ идемпотентности — то, что делает повтор безопасным.
- Повтор с тем же ключом и тем же телом вернёт тот же самый ответ, а не второй заказ.
- Пока первый запрос ещё выполняется, повтор получит
409 in_progress— подождите несколько секунд и спросите статус. - Тот же ключ с другим телом — это ошибка на вашей стороне, вернётся
422 idempotency_key_reuse. Берите новый ключ на каждый заказ, проще всего UUID. - Если запрос не дошёл до списания (не хватило баланса, не заполнено поле), ключ освобождается — можно исправить и повторить с ним же.
Что важно знать до интеграции
- Работаем только с предоплаты. Заказ списывается с баланса в момент оформления. Кончились деньги —
402 insufficient_balance, заказ не создаётся. - Сбой поставщика возвращает деньги сам. Если поставщик не выдал товар, заказ получает статус
refunded, а сумма возвращается на баланс — вручную ничего запрашивать не нужно. - ID покупателя проверяйте до оплаты. Пополнение уходит на тот аккаунт, который вы прислали. Ошиблись цифрой — вернуть нельзя, это уже деньги на чужом аккаунте.
- Цены меняются. Курс и наценка правятся, поэтому берите цену из
/catalog/{id}перед каждым заказом, а не из своей таблицы, заполненной однажды.
