> ## Documentation Index
> Fetch the complete documentation index at: https://oblodai.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# SDK — библиотеки для вашего языка

> Библиотеки для PHP, TypeScript, Python, Go и Rust.

SDK — это готовая библиотека, которая берёт на себя всю «рутину» интеграции с Oblodai: подписывает
запросы, разбирает ответы, проверяет подписи вебхуков, повторяет запросы при сбоях. Вам остаётся
вызвать пару методов.

<Note>
  **Новичку:** если вы пишете свой сайт/бэкенд — берите SDK, а не «голый» HTTP. Так вы не ошибётесь в
  подписи запроса (самый частый источник проблем на старте).
</Note>

***

## Какой SDK выбрать

Все SDK устроены одинаково — одни и те же понятия и почти одинаковые названия методов. Освоив один,
вы сразу поймёте остальные.

<CardGroup cols={3}>
  <Card title="PHP" icon="php" href="/sdk/php">
    `composer require oblodai/sdk`
  </Card>

  <Card title="TypeScript / Node.js" icon="node-js" href="/sdk/typescript">
    `npm install @oblodai-npm/sdk`
  </Card>

  <Card title="Python" icon="python" href="/sdk/python">
    `pip install oblodai`
  </Card>

  <Card title="Go" icon="golang" href="/sdk/go">
    `go get github.com/oblodai/oblodai-go`
  </Card>

  <Card title="Rust" icon="rust" href="/sdk/rust">
    `cargo add oblodai`
  </Card>
</CardGroup>

<Info>
  **v1.1.0 — текущая версия в реестрах** (Packagist · npm · PyPI · Go modules · crates.io).
  Ломающее изменение против v1.0.x: идемпотентность переехала с авто-`order_id` на заголовок
  `Idempotency-Key` — см. принцип 3 и CHANGELOG вашего SDK. npm-пакет: `@oblodai-npm/sdk`.
</Info>

Чтобы устанавливать пакеты, понадобятся сами инструменты вашего языка: [Composer](https://getcomposer.org/download/) (PHP), [npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) (Node.js), [pip](https://pip.pypa.io/en/stable/installation/) (Python), [Go](https://go.dev/doc/install) (go), [Cargo](https://doc.rust-lang.org/cargo/getting-started/installation.html) (Rust).

***

## Что вам понадобится (для любого SDK)

1. **`public_id` и `secret`** — пара ключей вашего мерчанта. `public_id` — несекретный идентификатор,
   `secret` — тайна, которой подписываются запросы. Один ключ на весь функционал (и приём, и выплаты).
   Ключи берутся в кабинете [my.oblodai.com](https://my.oblodai.com) — см.
   [Регистрация и ключи](/guides/get-keys).
2. **Сервер.** SDK работает **только на сервере** (бэкенд). Секрет никогда не должен попадать в
   браузер или мобильное приложение.
3. **Публичный URL для вебхуков** — адрес на вашем сервере, куда Oblodai будет присылать уведомления
   об оплате (например `https://ваш-сайт.ру/oblodai/webhook`).

***

## Шесть принципов за минуту

Эти правила одинаковы во всех SDK — понимание их избавит от 90 % ошибок.

1. **Ключи — из переменных окружения.** Не пишите секрет в коде. Задайте `OBLODAI_PUBLIC_ID` и
   `OBLODAI_SECRET`, и создавайте клиента через `fromEnv()` / `from_env()`. Базовый URL по умолчанию —
   боевой `https://api.oblodai.com`; переопределить можно переменной `OBLODAI_BASE_URL` (необязательно).
2. **Суммы — строками.** `"25.00"`, не число `25.0`. Так не теряется точность.
3. **Повтор безопасен — но защита от дублей устроена по-разному в двух версиях.** Это главное отличие
   v1.1.0 от v1.0.x.

   |                           | **v1.0.x** — предыдущая версия                             | **v1.1.0** — текущая версия                                                                            |
   | ------------------------- | ---------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
   | Что защищает от дублей    | `order_id`: если вы его **не задали, SDK подставлял свой** | Заголовок `Idempotency-Key`: генерируется один раз на вызов и **одинаков во всех внутренних повторах** |
   | Ваш `order_id`            | если не задан — в платеже окажется **сгенерированный SDK** | уходит **как есть**; SDK ничего не подставляет и не переписывает                                       |
   | Свой ключ идемпотентности | параметра `idempotency_key` **не существует**              | передайте `idempotency_key` в вызов создания — уйдёт в **заголовок**, не в тело                        |

   * **Ловушка при обновлении на v1.1.0.** Если вы полагались на то, что `order_id` «появится сам» в
     ответе, — теперь его там **не будет**. Задавайте `order_id` **явно** (см. следующий раздел). Сама
     защита от дублей при этом работает и без него — уже заголовком.
   * **`order_id` — это ВАШ бизнес-идентификатор**, по которому вы потом находите платёж через
     `payments.info` и сопоставляете со своим заказом. Ключом идемпотентности он быть перестал.
   * **Выплаты:** `order_id` **обязателен в обеих версиях** — этого требует сам API (без него вернётся
     `payout.order_id_required`). Задавайте свой уникальный номер выплаты.
   * **Ходите в API напрямую, без SDK?** Тогда шлите заголовок `Idempotency-Key` сами (или полагайтесь
     на `order_id`) — иначе повтор задублирует операцию. Подробно —
     [Идемпотентность](/reference/basics-idempotency).
4. **Оплату подтверждает вебхук, а не редирект.** Покупателя редиректит на «спасибо», но статус
   заказа меняйте **только** получив вебхук (и перепроверив статус через `payments.info`).
5. **Вебхуки надо зарегистрировать.** Один раз вызовите «регистрацию вебхука» — получите webhook-секрет
   и храните его. Именно им проверяются входящие уведомления (это НЕ ваш API-секрет). Без регистрации
   уведомления не приходят.
6. **Валюта цены — это не валюта расчёта.** Два разных поля, и их постоянно путают:

   * **`currency` — валюта ЦЕНЫ.** Может быть одной из **23 фиатных валют** — USD, EUR, GBP, RUB, UAH,
     PLN, CZK, TRY, CNY, INR, BRL, CAD, AUD, CHF, AED, ZAR, MXN, IDR, THB, VND, NGN, JPY, KRW — **или
     любой монетой** (USDT, BTC, TRX, …). Указать цену в рублях или евро можно, и это надёжно: курс
     монеты приходит сразу в нужной валюте, ничего не перемножается.
   * **`to_currency` — валюта РАСЧЁТА.** Здесь **только крипта, фиата не бывает никогда.** Баланс,
     выплаты и возвраты — тоже всегда в крипте: шлюз не хранит фиат.
   * У **JPY и KRW ноль знаков после запятой**: `"amount": "10000"`, а не `"10000.00"`. У остальных — 2.
   * **Тенге (KZT), сом (KGS), сум (UZS) пока не поддерживаются** — вернётся `payment.unknown_currency`.
   * **Правило, о которое спотыкаются:** цена в фиате + задана только `network` **без** `to_currency`
     → `400 payment.to_currency_required`. Либо задайте `to_currency`, либо **не задавайте ни то, ни
     другое** — тогда получится валюто-агностичный счёт и монету выберет сам покупатель.

   Полный список того, в чём можно назначать цену, отдаёт `rates.currencies()` — в ответе поле
   `pricing_currencies` (монеты + фиат с признаком `"fiat": true`).

***

## Как связать платёж со своим заказом

Всегда возвращается `uuid` платежа — по нему платёж ищется через `payments.info`. Но удобнее искать
по **своему** `order_id`, поэтому задавайте его сами и **сохраняйте до вызова**:

<CodeGroup>
  ```python Python theme={null}
  import uuid
  order_id = "ord-" + uuid.uuid4().hex
  db.save(order_id=order_id, status="pending")            # сохранили ПЕРЕД отправкой
  payment = client.payments.create(amount="10", currency="USDT", order_id=order_id, network="tron")
  db.update(order_id, payment_uuid=payment.uuid)
  ```

  ```ts Node.js theme={null}
  import { randomUUID } from "node:crypto";
  const orderId = "ord-" + randomUUID();
  await db.save({ orderId, status: "pending" });          // сохранили ПЕРЕД отправкой
  const payment = await client.payments.create({
    amount: "10", currency: "USDT", order_id: orderId, network: "tron",
  });
  await db.update(orderId, { paymentUuid: payment.uuid });
  ```
</CodeGroup>

<Note>
  **Почему «до вызова».** Если `create()` бросит исключение (сеть отвалилась после того, как SDK
  исчерпал повторы), ответа у вас не будет — и записать `uuid` будет неоткуда. Дубля при этом не
  возникнет (все повторы шли с одним `Idempotency-Key`), но чтобы потом **найти** этот платёж, вам
  нужен ключ, который вы придумали сами. Поэтому: сначала записали свой `order_id`, потом отправили.
</Note>

**Где взять `order_id` из ответа:**

| Язык    | Доступ                 |
| ------- | ---------------------- |
| TS/Node | `payment.order_id`     |
| Python  | `payment.order_id`     |
| Rust    | `payment.order_id`     |
| Go      | `payment.OrderID`      |
| PHP     | `$payment['order_id']` |

***

## Что нового в v1.1.0

* **Массовые операции.** До 5000 платежей / возвратов / выплат **одним** запросом — одна отметка
  rate-limit вместо тысячи. Обработка в фоне: постановка возвращает `batch_id`, результаты по каждому
  элементу забираются через «инфо о пачке». Есть режим `on_error`: `continue` (по умолчанию) или
  `stop` (остановиться на первой ошибке). См. [Массовые операции](/guides/batch-operations),
  [Пачка](/reference/payment-batch), [Инфо о пачке](/reference/batch-info).
* **Платёжные ссылки (донаты).** Переиспользуемая ссылка: по ней платят много людей, каждый платёж —
  свой инвойс со своим адресом. Сумма фиксированная, свободная или в диапазоне; валюту и сеть можно
  закрепить или дать выбрать клиенту; ссылка может быть **бессрочной**. Это **единственный способ
  принимать платежи вообще без бэкенда** (Tilda, Wix и т. п.). См.
  [Платёжные ссылки](/guides/payment-links), [Ссылка](/reference/payment-link),
  [Публичный доступ к ссылке](/reference/link-public).
* **Сплит-платежи.** Доля каждого платежа автоматически уходит партнёру — на внешний адрес
  (необратимо) или аккаунту на платформе (обратимо: возврат отзовёт долю). См. предупреждение о
  возвратах ниже, [Сплит-платежи](/guides/split-payments),
  [Правило сплита](/reference/split-rule), [Настройки сплита](/reference/split-config).
* **Счёт на e-mail** — письмо покупателю с кнопкой «Оплатить». Чек об оплате уходит автоматически на
  `payer_email`, если вы задали его при создании платежа. См.
  [Счета на e-mail](/guides/email-invoices), [Отправка счёта](/reference/payment-send-email).
* **Крипто-чеки (payout links)** — выплата **без адреса получателя**: вы резервируете сумму,
  получатель открывает `claim_url` и сам вводит свой адрес. Группа `payout_links` / `payoutLinks` /
  `PayoutLinks`: `create`, `create_batch` (до 500 ссылок), `list`, `info`, `cancel` + публичные
  `claim_info` / `claim` (без подписи). См. [Крипто-чеки](/guides/payout-links),
  [Payout-ссылка](/reference/payout-link), [Публичный claim](/reference/claim-public).
* **Резолв недоплаты** — `payments.resolve(action=accept|refund)`: недоплаченный платёж можно
  принять как есть (`accept`, глушит авто-возврат) или вернуть плательщику (`refund`). См.
  [Резолв платежа](/reference/payment-resolve).
* **Новые поля платежа:** `payer_address` (откуда пришли деньги), `refunds[]` и `refund_status`
  (`none|partial|full`).
* **`address` в возврате больше не обязателен** — по умолчанию вернём на адрес плательщика. Для
  Bitcoin/UTXO, где адрес плательщика неизвестен, `address` по-прежнему нужен.

### Сплиты и возвраты — прочитайте, прежде чем включать

Возврат списывается с **вашего** баланса на всю сумму, что прислал плательщик. Если бы доля партнёра
уходила сразу, вам могло бы стать нечем возвращать.

Поэтому отправка партнёрам **откладывается на окно удержания** (`refund_hold_hours`), и в момент
отправки база пересчитывается как «оплачено − возвращено». Возврат внутри окна сам уменьшает или
отменяет отчисление. Возврат **после** отправки: долю на внешний адрес вернуть нельзя (придётся
пополнить баланс), а долю партнёра-на-платформе мы отзовём автоматически.

***

## Типичный сценарий приёма платежа

Одинаков во всех языках:

```
1. (однократно) Зарегистрировать вебхук → сохранить webhook-секрет
2. Покупатель нажал «Оплатить»
3. Ваш сервер: client.payments.create({ amount, currency, order_id, url_callback })
4. Редиректите покупателя на payment.url (hosted-страница оплаты Oblodai)
5. Покупатель платит в крипте
6. Oblodai шлёт вебхук на ваш url_callback
7. Ваш сервер: проверяете подпись вебхука → payments.info(uuid) → если "paid", отмечаете заказ оплаченным
```

Пошагово с кодом — на странице вашего языка. Начните с [приёма первого платежа](/guides/accept-first-payment),
если хотите сперва понять сам API.

***

## Логи (диагностика)

Логирование во всех SDK **выключено по умолчанию** и включается одинаково — переменной окружения или
своим логгером. Секреты (`secret`, `X-Signature`, webhook-секрет) и тела запросов **не логируются
никогда** — только метод, путь, HTTP-статус, время, номер попытки, задержка ретрая и код ошибки.

**Самый простой способ — переменная окружения** (значения `debug` · `info` · `warn` · `error`):

```bash theme={null}
export OBLODAI_LOG=debug
```

Тогда в stderr пойдут понятные строки:

```
oblodai: -> POST /v1/payment (attempt 1/4)
oblodai: <- 200 POST /v1/payment 123ms
oblodai: retrying POST /v1/payment in 60000ms (429 rate limit; attempt 2/4)
oblodai: webhook signature ok
```

**Свой логгер** (в прод — направьте в свою систему логов) задаётся в конфиге клиента:

* **TypeScript** — `new OblodaiClient({ ..., logger: (level, msg, fields) => … })`
* **Python** — стандартный `logging`: настройте логгер `logging.getLogger("oblodai")`
* **Go** — `oblodai.Config{ ..., Logger: slog.Default() }`
* **Rust** — `Config::new(...).logger(Arc::new(|level, msg| …))`
* **PHP** — `new Client($publicId, $secret, ['logger' => function (string $level, string $msg) { … }])`

***

## Лимиты частоты и повторы (встроено)

Все пять SDK **сами** переживают временные сбои, и повторы **включены по умолчанию** — отдельно
настраивать ничего не нужно:

* **Что повторяется:** `HTTP 429` (лимит частоты) и `5xx`, а также сетевые сбои/таймауты. Ошибки
  запроса (`4xx`, кроме 429) не повторяются.
* **`Retry-After`:** на `429` шлюз присылает `Retry-After: 60` — SDK ждёт именно столько; иначе —
  экспоненциальный backoff с джиттером.
* **Дефолты:** до 4 попыток, старт 500 мс, потолок задержки 30 с. Настраивается через `RetryConfig`/
  `RetryOptions` (или отключается) в конфиге клиента.
* Лимит самого API — **120 запросов/мин на IP**. Подробно — [Ограничение частоты](/reference/basics-ratelimit).

<Note>
  **Go, внимание.** Раньше в Go-SDK повторов по умолчанию **не было** (`Retry: nil` означало «не
  повторять») — из пяти библиотек ошибалась одна. Это **исправлено**: теперь `nil` = повторы включены,
  как везде. Отключить их можно осознанно — `Retry: oblodai.NoRetry()`.
</Note>

<Tip>
  **Упираетесь в лимит частоты?** Не разгоняйте параллелизм — используйте
  [массовые операции](/guides/batch-operations) (с v1.1.0): до 5000 элементов **одним**
  подписанным запросом = одна отметка rate-limit вместо тысяч.
</Tip>

***

## Куда дальше

Выберите язык из карточек выше и откройте его страницу — там установка, рабочий пример и все методы.

<CardGroup cols={2}>
  <Card title="Справочник API" href="/reference/overview" icon="arrow-right" horizontal>
    Точные поля каждого метода API.
  </Card>

  <Card title="Глоссарий" href="/reference/glossary" icon="arrow-right" horizontal>
    Незнакомый термин — смотрите здесь.
  </Card>

  <Card title="Модули для CMS" href="/modules/overview" icon="arrow-right" horizontal>
    Не пишете код — возьмите готовый модуль.
  </Card>
</CardGroup>
