<!-- Source: https://docs.infraio.xyz/ru/sdks/javascript -->
<!-- Last updated: 2026-10-04 -->

# JavaScript / Браузерный SDK

`@lartech/infraio-checkout-js` — единственный SDK, который мы публикуем
сегодня. Он работает в браузере и открывает наш размещённый checkout.
Backend-SDK пока нет. Вызывайте API напрямую; [Быстрый старт](https://docs.infraio.xyz/ru/get-started/quickstart)
содержит хелпер HMAC-подписи.

- Текущая версия: `0.1.1-beta.17` (pre-1.0; возможны минорные ломки)
- Форматы: **ESM** (`index.js`), **CJS** (`index.cjs`), **IIFE** (`index.global.js`)
- Типы в комплекте (`index.d.ts`)
- Нулевые runtime-peers — нет React, jQuery или других зависимостей

> **Note:**
>
> Серверной точки входа **нет**. Хелперы проверки подписи для
> webhook'ов не поставляются в бандле — реализуйте их сами через
> `crypto` (на [странице проверки подписи](https://docs.infraio.xyz/ru/webhooks/signature-verification)
> есть copy-paste код на 4 языках).

## Установка

**npm**

```bash
npm install @lartech/infraio-checkout-js
```

**pnpm**

```bash
pnpm add @lartech/infraio-checkout-js
```

**yarn**

```bash
yarn add @lartech/infraio-checkout-js
```

**bun**

```bash
bun add @lartech/infraio-checkout-js
```

**CDN**

```html
<script src="https://unpkg.com/@lartech/infraio-checkout-js/dist/index.global.js"></script>
<script>
  const sdk = await InfraIo.loadInfraIo("pk_live_yourkeyhere");
  sdk.checkout({ sessionId, checkoutUrl });
</script>
```

## `loadInfraIo(publicKey, options?)`

Возвращает `Promise<InfraIoInstance>`.

```ts
import { loadInfraIo } from "@lartech/infraio-checkout-js";

const sdk = await loadInfraIo("pk_live_yourkeyhere", {
  // Опционально. Переопределяйте только при указании на non-prod окружение.
  checkoutUrl: "https://checkout-dev.infraio.xyz",
});
```

| Параметр | Тип | Обязательный | Заметки |
| --- | --- | --- | --- |
| `publicKey` | `string` | ✓ | Должен соответствовать `pk_(live\|test)_…` |
| `options.checkoutUrl` | `string` | — | Переопределение базового URL checkout. По умолчанию: `https://checkout.infraio.xyz`. Страница checkout автоматически подключается к соответствующему API. |

## `sdk.checkout({ … })`

Открывает размещённый checkout. Возвращает `void` (используйте
callback'и для состояния).

| Поле | Тип | Обязательное | Заметки |
| --- | --- | --- | --- |
| `sessionId` | `string` | ✓ | `session_key`, возвращённый `POST /b2b/v1/checkout-sessions/quick` |
| `checkoutUrl` | `string` | — | Полный URL, возвращённый тем же endpoint'ом. Если опущен, SDK конструирует его из `checkoutUrl` `loadInfraIo()` (или дефолта) + `sessionId` |
| `mode` | `"popup" \| "redirect" \| "embed"` | — | По умолчанию `"popup"` |
| `container` | `string \| HTMLElement` | только embed | CSS-селектор или DOM-элемент, куда монтируется iframe |
| `width` | `number` | — | Только popup. По умолчанию `560`. Ограничен `[320, 1280]` |
| `height` | `number` | — | Только popup. По умолчанию `780`. Ограничен `[400, 1000]` |
| `timeoutMs` | `number` | — | Только popup. Таймаут загрузки iframe. По умолчанию `30000`. Передайте `0`, чтобы отключить |
| `locale` | `string` | — | BCP-47 тег, передаётся как `?locale=` на страницу checkout (`en`, `vi`, `ja`, `ko`, `es`, `pt-BR`, `ru`, `tr`, `zh-CN`, `zh-TW`) |
| `hideSummary` | `boolean` | — | Скрыть колонку со сводкой заказа. По умолчанию `false` |
| `hideHeader` | `boolean` | — | Скрыть заголовок InfraIO Pay и встроенную кнопку wallet-connect. По умолчанию `false`. Сочетайте с `walletAddress` для полного white-label |
| `walletAddress` | `string` | — | Предподключить кошелёк покупателя. Требует `onSignRequest` |
| `walletChainId` | `number` | — | EVM chain ID для предподключённого кошелька |
| `onReady` | `() => void` | — | Срабатывает, когда iframe становится интерактивным. Только popup/embed |
| `onSignRequest` | `(req: { method: string; params: unknown[] }) => Promise<string>` | при заданном `walletAddress` | SDK проксирует wallet RPC к вашему обработчику; возвращайте подписанный hex |
| `onSuccess` | `({ sessionId }) => void` | — | Срабатывает при успешной оплате. **Не авторитетно — webhook авторитетен** |
| `onCancel` | `() => void` | — | Срабатывает, когда покупатель закрывает popup/embed без оплаты |
| `onError` | `(err: InfraIoError) => void` | — | Срабатывает при сбое загрузки iframe (режимы popup + embed). Некорректные аргументы выбрасываются синхронно, они **не** доставляются сюда. У redirect нет runtime-уровня ошибок — сбой наблюдается на редиректнутой странице. |

> **Warning:**
>
> `onSuccess` **не** авторитетен. Он может сработать, даже когда
> webhook позже определит, что платёж не прошёл (реорги testnet'а,
> тайминги на стороне покупателя). Используйте его только для UX
> (показать «Спасибо!», редирект). Всегда подтверждайте через webhook
> до обработки заказа.

## `sdk.close()`

Программно закрыть открытый popup или embed. No-op для режима
redirect.

```ts
sdk.checkout({ sessionId, mode: "popup", /* … */ });
// позже, например когда пользователь уходит со страницы
sdk.close();
```

## `sdk.openRefundRequest({ … })`

Открывает размещённую **форму refund-request** для одноразового
токена, который ваш backend выпустил через
`POST /b2b/v1/merchants/{merchant_id}/refund-requests`. Покупатель
вводит адрес назначения возврата + причину + (опциональные)
метаданные на нашей странице; ваша страница только обрабатывает
жизненный цикл открыть/закрыть. Возвращает функцию **`close()`** —
вызовите её, чтобы программно закрыть popup или открепить embed
iframe. В режиме redirect возвращаемая функция — no-op.

> **Note:**
>
> Форма живёт по адресу
> `https://checkout.infraio.xyz/refund-request/:token`. Этот метод
> просто обёртывает этот URL в popup / redirect / embed, чтобы
> покупатель никогда не покидал ваш домен (в popup / embed) или
> возвращался автоматически (в redirect). Endpoint, на который
> обращается ваш backend для выпуска токена —
> `POST /b2b/v1/merchants/{merchant_id}/refund-requests` — подписан
> HMAC вашим secret-ключом, та же аутентификация, что и у остального
> B2B-уровня. См. [Концепции → Возвраты](https://docs.infraio.xyz/ru/concepts/refunds#mint-via-b2b-api).

```ts
import { loadInfraIo } from "@lartech/infraio-checkout-js";

const sdk = await loadInfraIo("pk_live_yourkeyhere");

// Выпустите токен на сервере, затем передайте в SDK в браузере.
const { token } = await fetch("/api/mint-refund-token", { method: "POST" }).then(r => r.json());

const close = sdk.openRefundRequest({
  token,
  mode: "popup",
  onSuccess: ({ linkToken, refundId }) => {
    // Покупатель отправил форму.
    // linkToken → /r/:linkToken страница статуса (поделитесь с покупателем).
    // refundId  → используйте с B2B API для approve / reject.
    window.location.href = `/r/${linkToken}`;
  },
  onCancel: () => { /* покупатель закрыл без отправки */ },
});

// Программно закрыть popup позже, если нужно:
// close();
```

| Поле | Тип | Обязательное | Заметки |
| --- | --- | --- | --- |
| `token` | `string` | ✓ | Токен `rfqt_…`, возвращённый `POST /b2b/v1/merchants/{merchant_id}/refund-requests` |
| `mode` | `"popup" \| "redirect" \| "embed"` | — | По умолчанию `"popup"`. Та же семантика поверхности, что и у `sdk.checkout()` — см. [Заметки по режимам](#mode-notes) |
| `container` | `string \| HTMLElement` | только embed | CSS-селектор или DOM-элемент, куда монтируется iframe |
| `locale` | `string` | — | BCP-47 тег, передаётся как `?locale=` (`en`, `vi`, `ja`, `ko`, `es`, `pt-BR`, `ru`, `tr`, `zh-CN`, `zh-TW`) |
| `hideHeader` | `boolean` | — | Скрыть заголовок InfraIO Pay внутри iframe. В popup/embed SDK рисует собственный modal chrome, поэтому заголовок страницы обычно не нужен. По умолчанию `false` |
| `hideSummary` | `boolean` | — | Скрыть колонку Order Summary, показав только форму возврата. По умолчанию `false` |
| `walletAddress` | `string` | — | Предзаполнить поле адреса назначения (`?wallet_address=`). Позволяет мерчанту, уже знающему кошелёк покупателя, пропустить ручной ввод |
| `onSuccess` | `(data: { linkToken: string; refundId: string }) => void` | — | Срабатывает после того, как покупатель отправил форму. `linkToken` → `/r/:linkToken` страница статуса для покупателя. `refundId` → используйте с B2B API для approve / reject |
| `onCancel` | `() => void` | — | Срабатывает, когда покупатель закрывает popup/embed без отправки |
| `onError` | `(err: InfraIoError) => void` | — | Срабатывает при сбое загрузки iframe или некорректных аргументах. Истечение/отмена токена обрабатывается размещённой страницей, не через `onError` |

**Состояния токена, всплывающие через `onError`**

Если покупатель открывает устаревший токен, страница сама обрабатывает
отображение (отрисовывает приглашение «Истёк — запросите новую ссылку»
и т. д.), и SDK **не** отправляет `onError` для этих случаев —
покупатель внутри потока формы, и вашему коду не нужно реагировать.
`onError` срабатывает только для того, на что ваш код может реагировать
(плохие аргументы, сетевой сбой при загрузке iframe).

> **Warning:**
>
> `openRefundRequest` записывает намерение возврата — он **не** двигает
> средства. После `onSuccess` строка возврата в `PENDING` (или
> `APPROVED`, если ваша мерчантская конфигурация авто-одобряет
> клиентские возвраты). Вам всё ещё нужно подписать и транслировать
> on-chain перевод с вашего мерчантского кошелька, затем отправить tx
> hash на `POST /b2b/v1/refunds/:id/submit-tx`. См.
> [Концепции → Возвраты](https://docs.infraio.xyz/ru/concepts/refunds) для полного жизненного
> цикла.

## Класс ошибки

```ts
import { InfraIoError } from "@lartech/infraio-checkout-js";

sdk.checkout({
  sessionId,
  onError: (err: InfraIoError) => {
    switch (err.code) {
      case "invalid_request_error":   /* плохой sessionId / аргументы */ break;
      case "iframe_load_error":       /* iframe не загрузился */ break;
      case "iframe_timeout_error":    /* превышен timeoutMs */ break;
      case "already_open_error":      /* другой checkout уже открыт */ break;
      case "network_error":           /* временная сетевая проблема при общении с origin checkout */ break;
      case "api_error":               /* backend вернул non-2xx для вызова, инициированного SDK */ break;
    }
  },
});
```

`sdk.openRefundRequest()` выбрасывает только `invalid_request_error`
(отсутствующий или некорректный токен / аргументы), синхронно.
`iframe_load_error` (iframe refund-request не загрузился) доставляется
**асинхронно через `onError`**, а не выбрасывается. Он **не**
отправляет `iframe_timeout_error` или `already_open_error` — у popup
refund-request нет load-timeout, и разрешено несколько одновременных
popup'ов.

## Заметки по режимам

### Popup
- Центрированный overlay с тёмным полупрозрачным backdrop
- `z-index: 2147483647` (максимум int32) — сидит над всем остальным
- Скролл body заблокирован пока открыт; восстанавливается при закрытии
- Кнопка закрытия получает начальный фокус; Tab захвачен в popup
- Закрывается через: кнопку закрытия, Escape, клик снаружи, `sdk.close()`.
  Все они вызывают `onCancel`
- Страница checkout может запросить resize через `postMessage` — SDK
  обрезает в пределах ограничений `width`/`height`

### Redirect
- Жёсткая навигация через `window.location.href`
- Автоматически добавляет `?return_url=<current-page>`, чтобы
  покупатель вернулся туда, откуда пришёл. Если `success_url` /
  `cancel_url` в сессии уже покрывают это, round-trip игнорирует
  `return_url`

### Embed
- iframe с `allow="payment; clipboard-write"` (директивы HTML5
  Feature Policy — не атрибут `sandbox`). iframe обслуживается с
  origin checkout, поэтому wallet popup'ы и запись в clipboard со
  стороны покупателя работают без дополнительного opt-in.
- Ширина контейнера 100%; высота авто-настраивается через
  `INFRAIO_RESIZE` postMessage, ограничена `[200, 2000]px`
- Никакой CSS-изоляции за границей iframe — стили вашей родительской
  страницы не протекают внутрь
- Всегда подключайте `onReady`, чтобы скрыть собственный loading-state,
  когда checkout становится интерактивным

## TypeScript

Все типы в комплекте. Самые полезные экспорты:

```ts
import type {
  CheckoutOptions,
  RefundRequestOptions,
  LoadOptions,
  InfraIoInstance,
  InfraIoErrorCode,
} from "@lartech/infraio-checkout-js";
import { loadInfraIo, InfraIoError, VERSION } from "@lartech/infraio-checkout-js";
```

`VERSION` — строка версии самого SDK — полезна в bug-репортах.
