Metadata-Version: 2.5
Name: pystakan
Version: 0.2.0
Summary: Python client for the Stakanex API: RUB ↔ USDT exchange, deposits, withdrawals and partner clients
Project-URL: Homepage, https://stakanex.io
Project-URL: Documentation, https://api.stakanex.io/v1/docs
Author: Stakanex
License-Expression: MIT
License-File: LICENSE
Keywords: api,exchange,sdk,stakanex,usdt
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: httpx>=0.25
Requires-Dist: pydantic>=2.5
Description-Content-Type: text/markdown

# pystakan

Python-клиент [Stakanex API](https://api.stakanex.io/v1/docs): обмен RUB ↔ USDT,
пополнения, выводы, KYC и всё для партнёров — клиенты, работа от их имени и наценка.

```bash
pip install pystakan
```

Python 3.10+. Синхронный `Stakan` и асинхронный `AsyncStakan` с одинаковыми методами.

## Начало

```python
from stakan import Stakan

stakan = Stakan("stx_ab12cd34_…")      # или переменная окружения STAKAN_API_KEY
print(stakan.account.balances())

quote = stakan.exchange.quote("9000")                 # рубли → USDT
deal = stakan.exchange.create("9000", quote.rate_version)
print(deal.reference, deal.status)
```

Ключ выпускается в кабинете: **Настройки → API** (нужна 2FA). Права: `read`,
`trade` (обмен, пополнение, KYC, клиенты, наценка), `withdraw` (адреса и вывод).

## Партнёрам

Ключ партнёра открывает аккаунты клиентов и работает от их имени —
`for_client(id)` добавляет к каждому запросу `X-Client-Id`:

```python
client = stakan.partner.clients.create("client@example.com", external_id="u-1842")
as_client = stakan.for_client(client.id)

session = as_client.kyc.start()          # access_token для WebSDK или redirect_url
print(as_client.kyc.status().status)     # pending → submitted → approved

quote = as_client.exchange.quote("1020") # курс уже с вашей наценкой
as_client.exchange.create("1020", quote.rate_version)

entry = as_client.address_book.add("trc20", "TUEZ…", code="123456")  # ваш код 2FA
as_client.withdrawals.create(entry.id, "30")

stakan.partner.set_markup("2")           # наценка для клиентов, до потолка
print(stakan.partner.get().summary.earned)
```

### Своя 2FA клиента

Пока её нет, адреса клиента подтверждает ваш код, а вывод идёт без кода. Включите —
и вывод и новые адреса клиента пойдут только с **его** кодом:

```python
setup = as_client.totp.setup()          # покажите клиенту QR из setup.otpauth_url
enabled = as_client.totp.confirm("123456")  # первый код из его приложения
print(enabled.backup_codes)             # резервные коды — показать клиенту один раз

as_client.address_book.add("trc20", "TUEZ…", code="654321")   # код клиента
as_client.withdrawals.create(entry.id, "30", code="112233")   # код клиента
as_client.totp.disable("ab12-cd34")     # его код или резервный
```

Потерял телефон и резервные коды — сброс у поддержки Stakanex.

Найти клиента по своему id — `stakan.partner.clients.find("u-1842")`, обойти всех —
`for c in stakan.partner.clients.iter()`.

## Деньги и повторы

- Суммы — `str`, `int` или `Decimal`. `float` не принимается: в нём теряются копейки.
- Обмен, пополнение и вывод получают `Idempotency-Key` сами (или ваш —
  `idempotency_key=`). При сетевом сбое, 429 и 5xx запрос повторяется **с тем же
  ключом** — вторая операция не создастся. `max_retries=2` по умолчанию.

## Ошибки

Каждый отказ API — исключение с `code` (стабильный код API), `message`, `status` и
`reference` (номер операции, если она осталась в истории):

```python
from stakan import RateChanged, Refused, StakanError

try:
    as_client.exchange.create("1020", quote.rate_version)
except RateChanged:
    ...  # курс или наценка сменились — возьмите новую котировку
except Refused as refusal:
    print(refusal.code, refusal.message)   # insufficient_funds, bad_amount, …
```

`AuthError` (401), `PermissionDenied` (403), `NotFound` (404), `Conflict` /
`RateChanged` (409), `Refused` (422), `RateLimited` (429), `ServerError` (5xx),
`ConnectionFailed` — ответа не было.

## Асинхронно

```python
from stakan import AsyncStakan

async with AsyncStakan() as stakan:
    async for tx in stakan.for_client(client_id).transactions.iter():
        print(tx.reference, tx.kind, tx.status)
```

Документация API: <https://api.stakanex.io/v1/docs>. Лицензия MIT.
