Skip to Content
Начало работыБыстрый старт

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

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

Вам нужна тестовая пара API-ключей (pk_test_… + sk_test_…) и секрет для подписи webhook (whsec_…). Сгенерируйте их в панели мерчанта  в разделах Developers → API keys и Developers → Webhooks.

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

npm install @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.

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

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: "[email protected]", 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 // для вашего собственного учёта.

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

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

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), });

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

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

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 → Проверка подписи для крайних случаев (нормализация тела, ротация секрета, защита от replay).

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

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

Что дальше