Центр разработчика
Интеграция с NineLogix
Начните в Test. Одобренный Live API создаёт размещённый Checkout для разовых Price, но не списывает средства при создании ссылки. Для подписок используйте только одобренный Checkout вашего Store. Для размещённой ссылки с ручной выдачей API не обязателен.
Ключи и среды
- Храните API-ключи и секреты webhook только на сервере. Ключ показывается один раз. Не отправляйте его в журналы, клиентский код или поддержку.
- Ключи nlx_test_ и nlx_live_ определяют среду и Store. Поля запроса не могут включить Live. Для Test и Live используйте отдельные секреты и записи; один URL обработчика допустим при строгом разделении сред.
- Возможности подтверждаются для конкретного Store, рынка, валюты и модели оплаты. Публикация Checkout, готовность вашей интеграции и официальное разрешение запуска — разные факты.
Создание и чтение Checkout
- POST https://www.ninelogix.com/api/v1/checkout-sessions принимает order_id (ваш стабильный номер, 1–64 безопасных ASCII-символа) и одобренный price_id. Store определяется ключом. Не отправляйте amount_minor, currency, product_name, store_id, environment или способ оплаты.
- Обязателен заголовок Idempotency-Key (1–128 символов). Допустимы буквы, цифры, точка, подчёркивание, двоеточие и дефис; первый символ — буква или цифра. После тайм-аута повторяйте тот же order_id, price_id и ключ. Другие данные с тем же ключом дают 409.
- Test создаёт симуляцию. Live поддерживает одобренные разовые Price и требует активного Live webhook order.paid. Для подписки используйте опубликованный Checkout. Создание ссылки не является оплатой.
- Ответ 201: {ok, request_id, data}. Сохраните data.id, data.order_id, data.price_id и request_id. GET /checkout-sessions/{session_id} читает сессию в том же Store и среде; отдельный публичный API чтения заказов/подписок здесь не заявлен.
- 400 — неверные поля; 401 — ключ; 403 — доступ; 404 — объект недоступен; 409 — конфликт состояния/идемпотентности; 429 — ограничение частоты. При неизвестном результате сначала запросите состояние, не создавайте повторную оплату.
curl https://www.ninelogix.com/api/v1/checkout-sessions \
-H "Authorization: Bearer nlx_test_REPLACE" \
-H "Idempotency-Key: order-demo-001" \
-H "Content-Type: application/json" \
-d '{"order_id":"demo-001","price_id":"prc_APPROVED"}'Подпись, приём и повторы
- Читайте точные исходные байты ДО JSON-парсинга. X-NineLogix-Signature имеет формат v1=<hex>. HMAC-SHA256 подписывает X-NineLogix-Timestamp + точку + исходное тело. Сравнивайте равные по длине байты за постоянное время; отклоняйте время более чем на 300 секунд в прошлом или будущем.
- Идентификатор JSON — id, не event_id. После проверки подписи сравните его с X-NineLogix-Event-Id и проверьте environment/livemode. Заголовок содержит время доставки; created — время события, которое при повторе может быть старым.
- Атомарно сохраните (Store, среда, id) и надёжную задачу до ответа 2xx. Уже принятый дубликат подтвердите без повторной выдачи. Worker также обеспечивает одну выдачу на бизнес-заказ или оплаченный invoice_id.
- 2xx означает только транспортный приём, не проверку подписи, обработку заказа или фактическую выдачу. Текст ответа не является доказательством бизнес-успеха. Если безопасный приём не состоялся, верните не-2xx.
- Для order.paid сверьте Store, merchant_reference, Product, Price, amount_minor и currency с локальным заказом. В Live API исходный order_id возвращается как merchant_reference; order_id события — идентификатор заказа NineLogix. Сохраните оба.
- Для размещённой ссылки сначала создайте локальный заказ и добавьте merchant_reference (1–120 безопасных ASCII-символов, без персональных данных). Сохраняйте ссылку на пользователя и заказ; для подписки ссылка стабильна весь срок.
- Тайм-аут доставки — пять секунд, максимум восемь плановых попыток. Возможны повторы, задержки и иной порядок. Разрешённая повторная доставка сохраняет id/тело, меняет время и подпись и никогда не повторяет списание.
Проверка подписи исходного тела в Node.js
import { createHmac, timingSafeEqual } from "node:crypto";
// headers: Web Headers; rawBody: Buffer read BEFORE JSON parsing.
// secret and expectedEnvironment come from your server configuration.
export function verifyNineLogixWebhook(
rawBody, headers, secret, expectedEnvironment,
nowSeconds = Math.floor(Date.now() / 1000)
) {
const timestamp = headers.get("x-ninelogix-timestamp") ?? "";
const signature = headers.get("x-ninelogix-signature") ?? "";
const headerId = headers.get("x-ninelogix-event-id");
if (!["test", "live"].includes(expectedEnvironment) ||
!Buffer.isBuffer(rawBody) || !secret ||
!/^\d{1,12}$/.test(timestamp) ||
Math.abs(nowSeconds - Number(timestamp)) > 300 ||
!/^v1=[0-9a-f]{64}$/.test(signature)) {
throw new Error("Invalid webhook headers");
}
const expected = createHmac("sha256", secret)
.update(timestamp + ".").update(rawBody).digest();
const received = Buffer.from(signature.slice(3), "hex");
if (received.length !== expected.length ||
!timingSafeEqual(received, expected)) {
throw new Error("Invalid webhook signature");
}
const event = JSON.parse(rawBody.toString("utf8"));
// Some legacy Test transport probes omit environment, not livemode.
if (!event || typeof event.id !== "string" || !event.id ||
event.id !== headerId || typeof event.type !== "string" ||
event.livemode !== (expectedEnvironment === "live") ||
(event.environment !== expectedEnvironment &&
!(expectedEnvironment === "test" &&
event.environment === undefined))) {
throw new Error("Webhook identity or environment mismatch");
}
return event;
}
// Signature verification is not fulfillment.
// Validate the event's business fields against your local pending order.
// Atomically persist (Store, environment, event.id) and enqueue work.
// Only then return 2xx; duplicates already durably accepted return 2xx.
// A durable worker grants once per order_id (or paid invoice_id),
// with a unique business key and transactional/outbox protection.Идентификаторы иллюстративные: согласуйте Test-данные с локальным ожидающим заказом. Обычное завершение Test Checkout и транспортная проверка не доказывают выдачу по order.paid. Функция проверяет подлинность сообщения, но не бизнес-поля и надёжную обработку.
Синтетический пример order.paid
{
"id": "evt_test_example",
"type": "order.paid",
"created": 1793070000,
"environment": "test",
"livemode": false,
"data": {
"object": {
"id": "ord_test_example",
"object": "order",
"environment": "test",
"livemode": false,
"status": "paid",
"order_id": "ord_test_example",
"merchant_id": "nlx_merchant_example",
"store_id": "sto_example",
"merchant_reference": "demo-001",
"product_id": "prd_example",
"price_id": "prc_APPROVED",
"amount_minor": 1000,
"currency": "USD",
"payment_method": "test",
"paid_at": "2026-10-27T03:00:00.000Z",
"test": true
}
}
}События и бизнес-результат
- checkout.session.completed и checkout.session.failed описывают сессию. Обычный Test Checkout отправляет completed, но не доказывает обработку order.paid. Не выдавайте доступ второй раз по обоим событиям.
- order.paid — основание выдачи после проверки подписи и заказа. Для подписки выдавайте один раз на invoice_id; храните subscription_id, invoice_id, order_id, merchant_reference и UTC period_start/period_end (конец исключён).
- subscription.active, subscription.updated, subscription.ended меняют состояние, но не дают новые права. Отмена в конце периода сохраняет оплаченный доступ и не означает возврат. Не отменяйте оплаченный период из-за более позднего неуспешного продления.
- refund.succeeded и refund.failed — результат возврата. Успешный возврат отменяет только связанные права по согласованной политике, однократно; неуспешный не подтверждает возврат. Запись API Checkout не даёт права на возвраты, отмены, расчёты и выплаты.
- Оплата, выдача, сверка, расчёт и выплата — отдельные факты. expired не доказывает отсутствие оплаты. В поддержку передавайте среду, время, request_id, id события, заказ/сессию и безопасный код ошибки, без секретов и персональных данных.
Проверка интеграции без реального платежа
- Для автоматизации сопоставьте Test-заказ и пользователя, проверьте неверную подпись/секрет, изменённое тело, старое/будущее время и неверную среду: ни один отказ не должен выдавать доступ.
- Повторите одно событие и подтвердите ровно одну выдачу. Проверьте отдельный бизнес-ключ заказа/invoice, восстановление прерванного worker и одинаковую сессию при повторном Idempotency-Key.
- Проверьте фактическую Test-выдачу по согласованному order.paid, а не только HTTP 200. Сохраните безопасные идентификаторы, UTC-время и результат; не создавайте реальный платёж для этой проверки.
Подтверждение готовности и запуск
- Повторно не заполняйте одобренные политики. Подтвердите настоящий URL/канал продаж и ручную либо автоматическую выдачу. Ручная выдача не требует API.
- Готовность платформы и публикация не доказывают установку ссылки продавцом. Готовность интеграции не заменяет официального разрешения запуска NineLogix.
- После запуска наблюдайте первый естественный Live-заказ. Дополнительная реальная покупка до запуска не обязательна.