<!-- Source: https://docs.infraio.xyz/ru/get-started/quickstart -->
<!-- Last updated: 2026-10-04 -->

# Быстрый старт

Цель: провести реальный тестовый платёж end-to-end. К концу у вас будет
сессия, созданная с вашего сервера, открытый checkout в браузере
покупателя и подписанный webhook, доставленный на ваш localhost.

> **Note:**
>
> Вам нужна **тестовая** пара API-ключей (`pk_test_…` + `sk_test_…`) и
> **секрет для подписи webhook** (`whsec_…`). Сгенерируйте их в
> [панели мерчанта](https://app.infraio.xyz) в разделах
> **Developers → API keys** и **Developers → Webhooks**.

## 1. Установите браузерный SDK

**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
```

## 2. Создайте сессию checkout (сервер)

Сессии создаются с вашего сервера, подписываются HMAC-SHA256 вашим
**secret**-ключом. Никогда не помещайте `sk_` в браузер.

### Соберите каноническую строку подписи

```
METHOD + "\n" + PATH + "\n" + TIMESTAMP + "\n" + BODY
```

Для запроса `POST /b2b/v1/checkout-sessions/quick` с unix-timestamp
`1715990400` и телом `{...}`, входная строка подписи:

```
POST
/b2b/v1/checkout-sessions/quick
1715990400
{"items":[...],"currency":"USD","success_url":"..."}
```

Подпишите её с помощью `HMAC-SHA256(secret_key, signingString)`,
вывод hex.

### Отправьте запрос

```ts filename="server/create-session.ts"
import { fetch } from "undici";
import { createHmac } from "node:crypto";

const PUBLIC_KEY = process.env.INFRAIO_PUBLIC_KEY!;   // pk_test_…
const SECRET_KEY = process.env.INFRAIO_SECRET_KEY!;   // sk_test_…
const BASE_URL   = "https://api-dev.infraio.xyz";

const path = "/b2b/v1/checkout-sessions/quick";
const body = JSON.stringify({
items: [
  {
    sku: "tee-large-blue",
    label: "Indigo Tee — L",
    unit_price: "49.00",
    quantity: 1,
    currency: "USD",
    image_url: "https://your-shop.test/img/tee.png",
  },
],
currency: "USD",
customer_email: "buyer@example.com",
success_url: "https://your-shop.test/return?status=success",
cancel_url:  "https://your-shop.test/return?status=cancel",
external_ref: "ord_1234",        // ваш id заказа; хранится на заказе, в webhook'ах не отзеркаливается — ищите через order_id
expires_in: 1800,                // секунды; по умолчанию 30 мин
idempotency_key: crypto.randomUUID(), // безопасно для повтора
});

const timestamp = Math.floor(Date.now() / 1000).toString();
const signingInput = ["POST", path, timestamp, body].join("\n");
const signature = createHmac("sha256", SECRET_KEY)
.update(signingInput)
.digest("hex");

const res = await fetch(`${BASE_URL}${path}`, {
method: "POST",
headers: {
  "Content-Type": "application/json",
  "X-Client-ID":  PUBLIC_KEY,
  "X-Timestamp":  timestamp,
  "X-Signature":  signature,
},
body,
});

// Каждый ответ API обёрнут в конверт — реальный payload
// лежит под `data`. Считайте `code`/`message` на верхнем уровне
// маркером результата (`201` / `"created"` для успеха).
const { code, message, data } = await res.json() as {
code: number;
message: string;
data: { session_key: string; checkout_url: string; order_id: string; expires_at: string };
};
const { session_key, checkout_url, order_id, expires_at } = data;
// Отправьте session_key + checkout_url в браузер. order_id и expires_at
// для вашего собственного учёта.
```

> **Warning:**
>
> Расхождение часов важно. API отклоняет запросы с `X-Timestamp`,
> отстоящим от серверного времени более чем на **5 минут**.
> Синхронизируйте серверы через NTP; не доверяйте wall clock
> долгоживущего cron.

## 3. Откройте checkout (браузер)

**Popup**

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

const sdk = await loadInfraIo(process.env.NEXT_PUBLIC_INFRAIO_PK!);

sdk.checkout({
  sessionId: session_key,
  checkoutUrl: checkout_url,
  mode: "popup",            // по умолчанию
  onReady:   () => console.log("checkout interactive"),
  onSuccess: ({ sessionId }) => {
    // Только UX — см. предупреждение ниже. Webhook авторитетен.
    location.assign("/thanks");
  },
  onCancel: () => console.log("buyer closed the popup"),
  onError:  (err) => console.error(err.code, err.message),
});
```

**Redirect**

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

const sdk = await loadInfraIo(process.env.NEXT_PUBLIC_INFRAIO_PK!);

sdk.checkout({
  sessionId: session_key,
  checkoutUrl: checkout_url,
  mode: "redirect",
});
// Браузер навигирует на checkout.infraio.xyz; покупатель возвращается на
// ваш `success_url` / `cancel_url` из payload'а создания сессии.
```

**Embed**

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

const sdk = await loadInfraIo(process.env.NEXT_PUBLIC_INFRAIO_PK!);

sdk.checkout({
  sessionId: session_key,
  checkoutUrl: checkout_url,
  mode: "embed",
  container: "#checkout-container",   // ваш DOM-таргет
  onReady: () => /* iframe загружен */ {},
});
```

## 4. Обработайте webhook (сервер)

Webhook — это **единственный** авторитетный сигнал того, что деньги
переместились. Браузерные callback'и (`onSuccess`) могут срабатывать
без реального платежа при некоторых тайминговых условиях на testnet'е
— никогда не обрабатывайте заказ по событию со стороны браузера.

```ts filename="server/webhook.ts (Next.js App Router)"
import { createHmac, timingSafeEqual } from "node:crypto";

const WEBHOOK_SECRET = process.env.INFRAIO_WEBHOOK_SECRET!; // whsec_…

export async function POST(req: Request) {
  const raw = await req.text();                    // СЫРЫЕ байты, не распарсенные
  const signature = req.headers.get("x-signature") ?? "";
  const timestamp = req.headers.get("x-timestamp") ?? "";

  // Подпись над `${timestamp}.${raw}` — НЕ над телом отдельно.
  const expected = "sha256=" + createHmac("sha256", WEBHOOK_SECRET)
    .update(`${timestamp}.${raw}`)
    .digest("hex");

  const a = Buffer.from(signature);
  const b = Buffer.from(expected);
  if (a.length !== b.length || !timingSafeEqual(a, b)) {
    return new Response("bad signature", { status: 400 });
  }

  // HTTP-тело ЕСТЬ объект данных конкретного события (без Stripe-стиля
  // внешнего конверта). Тип события + delivery ID + timestamp живут в заголовках.
  const eventType = req.headers.get("x-event") ?? "";        // напр. "payment.settled"
  const deliveryId = req.headers.get("idempotency-key")      // зеркалит X-Delivery
                   ?? req.headers.get("x-delivery") ?? "";

  const payload = JSON.parse(raw);
  switch (eventType) {
    case "payment.settled": {
      // Payload несёт order_id, payment_intent_id, tx_hash,
      // amount_received, confirmations, settlement-токен + сеть и т. д.
      // Он НЕ включает ваш `external_ref` из заказа — ищите его
      // на сервере через order_id, если нужен mapping назад.
      const { order_id, tx_hash, amount_received, confirmations } = payload;

      // Идемпотентно отметьте заказ оплаченным. Используйте deliveryId (X-Delivery)
      // как ключ дедупликации — он стабилен между всеми повторами одной доставки.
      // Мы повторяем до 6 раз (1м / 5м / 15м / 1ч / 6ч) на non-2xx.
      break;
    }
    case "checkout.expired": { /* TTL сессии истёк — освободите инвентарь */ break; }
    case "payment.refund.requested": { /* всплыть в очереди админа */ break; }
    default:
      // Forward-compat: мы можем добавлять события. Принимайте и no-op.
  }
  return new Response("ok", { status: 200 });
}
```

См. [Webhooks → Проверка подписи](https://docs.infraio.xyz/ru/webhooks/signature-verification)
для крайних случаев (нормализация тела, ротация секрета, защита от
replay).

## 5. Запустите тестовый платёж

Тестовые сессии рассчитываются против **реальных testnet'ов**
(Sepolia, Base Sepolia, BSC Testnet и т. д.) — мок-сети нет. Возьмите
тестовые средства из соответствующего крана, затем отправьте на
депозитный адрес, который показывает страница checkout. Webhook
сработает, когда подтверждения наберутся (1 confirmation на большинстве
testnet'ов).

В TRON Nile, Solana Devnet и TON Testnet (скоро) нет депозитного адреса: покупатель платит с кошелька через страницу checkout. См. [Сети с оплатой напрямую на кошелёк](https://docs.infraio.xyz/ru/concepts/chains#сети-с-оплатой-напрямую-на-кошелёк).

## Что дальше

- [Концепции → Сессии](https://docs.infraio.xyz/ru/concepts/sessions) — модель данных трёх
  уровней (CheckoutSession + Order + PaymentIntent).
- [Концепции → Сети и активы](https://docs.infraio.xyz/ru/concepts/chains) — какие сети и
  токены поддерживаются live vs test.
- [Webhooks → Обзор](https://docs.infraio.xyz/ru/webhooks/overview) — каждый тип события и
  что его запускает.
- [Безопасность → API-ключи](https://docs.infraio.xyz/ru/security/api-keys) — scope'ы,
  ротация, rate-limit'ы.
