Интеграция на Node.js
Готовый, протестированный набор для серверной интеграции (Node.js 18.13+, без зависимостей). Он ведёт себя так же, как набор Python: оба прогоняются по одним и тем же сценариям жизненного цикла, а тест чётности требует одинаковых результатов.
Скачать набор: eleven-pay-merchant-kit.zip (каталоги node/, python/, test-vectors/, curl/).
Пилотный режим
Реального приёма платежей через счета Cashier пока нет. Тесты и демонстрации набора работают на имитациях. Базовый адрес API вам выдают при подключении.
Состав набора
| Файл | Назначение |
|---|---|
elevenpay-client.mjs | HTTP-клиент (fetch): создание/чтение счёта, опрос; перенаправления запрещены |
order-store.mjs | заказы, ключи, события, очередь выдачи; exportState()/fromState() для переноса состояния |
checkout-service.mjs | адаптер сайта: AdapterSettings и CheckoutService |
webhook-verify.mjs, webhook-receiver.mjs | проверка подписи и приём вебхука |
offline-demo.mjs | демонстрация на имитациях |
test/*.test.mjs | тесты (node --test) |
order-store.mjs хранит данные в памяти и показывает логику: в продакшене это таблицы вашей базы.
Настройка
Те же переменные, что и в Python: ELEVENPAY_BASE_URL (обязательна, значения по умолчанию нет), ELEVENPAY_API_KEY, ELEVENPAY_WEBHOOK_SECRET, а также необязательные ELEVENPAY_ORDER_TTL_SECONDS (1800), ELEVENPAY_POLL_DEADLINE_SECONDS (600), ELEVENPAY_LATE_PAYMENT_POLICY (review | fulfil). Секреты в объекте настроек не перечисляются и не попадают в JSON.stringify.
Использование
import { OrderNotPayable, StartUncertain, buildService } from "./checkout-service.mjs";
import { ApiError } from "./elevenpay-client.mjs";
import { OrderStore } from "./order-store.mjs";
const service = buildService(process.env, { store: new OrderStore() });
// 1. Заказ создаёт ВАШ сайт: сумма из вашего каталога, срок — ваш.
service.placeOrder("order_1001", 1500);
// 2. Попросить Eleven Pay отправить счёт плательщику.
try {
const started = await service.startPayment("order_1001", "+7 700 123 45 67");
console.log(started.reference, started.payerStep, started.replayed);
} catch (error) {
if (error instanceof StartUncertain) {
// Ответа нет: счёт мог быть создан. Повторите ПОЗЖЕ тем же вызовом (тот же ключ).
} else if (error instanceof OrderNotPayable) {
console.log(error.reason); // unknown_order | paid | expired
} else if (error instanceof ApiError) {
console.log(error.status, error.code); // см. таблицу ошибок
} else {
throw error;
}
}
// 3. Прогресс страницы (опционально).
const progress = await service.refreshPayment("order_1001"); // { outcome, payerStep }
const waited = await service.awaitPayment("order_1001"); // "awaiting" по истечении предела = ещё не оплачено
// 4. Ваш срок: истёкшие неоплаченные заказы (ничего не отменяет в Eleven Pay).
const expired = service.expireStaleOrders();payerStep: sending_request, approve_in_kaspi, paid, not_sent, unconfirmed.
Приём вебхука
Передайте сырые байты и заголовки. С Express используйте express.raw({ type: "*/*" }), неexpress.json(): подпись покрывает точные байты.
import express from "express";
const app = express();
app.post("/webhooks/eleven-pay", express.raw({ type: "*/*" }), (request, response) => {
const { status, outcome } = service.handleWebhook(request.headers, request.body); // Buffer
response.status(status).json({ outcome });
});Исходы: fulfilled, duplicate, already_paid, late_payment, needs_review, ignored_event_type. Выдачу выполняет отдельный процесс по очереди выдачи; запись в неё делается в той же транзакции, что и смена статуса заказа.
Что проверять перед запуском
node --test— зелёный;node offline-demo.mjs— проходит.- Тесты на ваших данных: двойной клик, дубликат вебхука, поддельная подпись, просроченный заказ, поздний платёж (Чек-лист, Локальное тестирование).
- Состояние хранится в вашей базе (а не в памяти), ключ идемпотентности — вместе с заказом.