Приложение на Node.js принимает криптовалюту через CryptoLike в три шага: подписать каждый запрос к API через node:crypto, создать платёж по заказу и проверить подписанный вебхук, прежде чем отметить заказ оплаченным. Хватает Node.js 18 и новее — fetch и crypto встроены, так что интеграции не нужны зависимости, кроме вашего веб-фреймворка.
Для магазина на CMS писать код не нужно: смотрите плагин для WooCommerce и другие интеграции. Это руководство — для собственного бэкенда: SaaS, маркетплейса или приложения.
Что понадобится
Аккаунт CryptoLike, сервер с Node.js 18+ и публичный адрес https:// для вебхуков. Секреты держите в окружении: в примерах это CRYPTOLIKE_API_KEY_ID, CRYPTOLIKE_API_SECRET и CRYPTOLIKE_WEBHOOK_SECRET, а также CRYPTOLIKE_API_URL для тестового адреса API (без него — рабочий). Разрабатывайте и проверяйте сначала с тестовыми ключами в тестовых сетях.
1. Создайте тестовый API-ключ
В кабинете откройте Разработчикам → API-ключи → Создать ключ с правами create_invoice и read. Ключ ck_test_ работает с тестовыми сетями, где монеты ничего не стоят. Тестовые ключи выдаёт тестовая среда: поддержка CryptoLike даст доступ к её кабинету, где вы создадите такой же ключ, и адрес её API (CRYPTOLIKE_API_URL в примерах). Рабочий кабинет выпускает только боевые ключи, а в основных сетях тестовый ключ получает 401 auth.test_key. Секрет показывается один раз; храните его в менеджере секретов или окружении, но не во фронтенд-коде.
2. Подписывайте запросы через node:crypto
Подпись — это HMAC-SHA256 от четырёх строк: времени Unix в секундах, метода, пути с query и SHA-256 тела, с ключом — API-секретом. Этот помощник подписывает и отправляет запрос:
import { createHash, createHmac } from "node:crypto";
export async function cryptolike(method: string, path: string, payload?: unknown, idempotencyKey?: string) {
const body = payload === undefined ? "" : JSON.stringify(payload);
const ts = Math.floor(Date.now() / 1000).toString();
const canonical = [ts, method.toUpperCase(), path, createHash("sha256").update(body).digest("hex")].join("\n");
const res = await fetch(`${process.env.CRYPTOLIKE_API_URL ?? "https://api.cryptolike.net"}${path}`, {
method,
headers: {
"X-API-Key": process.env.CRYPTOLIKE_API_KEY_ID!,
"X-Timestamp": ts,
"X-Signature": createHmac("sha256", process.env.CRYPTOLIKE_API_SECRET!).update(canonical).digest("hex"),
"Content-Type": "application/json",
...(idempotencyKey ? { "Idempotency-Key": idempotencyKey } : {}),
},
body: body || undefined,
});
const data = await res.json();
if (!res.ok) throw new Error(`${data.error?.code ?? res.status}: ${data.error?.message ?? ""}`);
return data;
}Сериализуйте тело один раз и отправляйте ту же строку: если хешировать одну строку, а отправить другую, получится 401 auth.invalid_signature. Сверьте помощник с эталонным вектором из раздела аутентификация API.
3. Создайте платёж по заказу
Создавайте платёж на сервере, когда покупатель оформляет заказ. Укажите цену в USD или EUR и дайте выбрать монету на странице или передайте coin_id из GET /v1/currencies. Без coin_id страница покажет монеты, доступные для инвойсов, с ориентировочной суммой ≈ у каждой; allowed_coin_ids (id с invoice_enabled: true из подписанного GET /v1/currencies) ограничивает список для этого заказа. Номер заказа в Idempotency-Key вернёт тот же платёж при повторе:
const invoice = await cryptolike("POST", "/v1/invoices", {
amount: "49.90",
currency: "USD",
order_id: order.id,
idempotency_by_order_id: true,
return_url: `https://example.com/orders/${order.id}`,
}, `order-${order.id}`);
await orders.saveInvoiceId(order.id, invoice.id);Держите суммы строками на всём пути — "49.90", а не числом JavaScript.
4. Отправьте покупателя на страницу оплаты
Перенаправьте браузер на invoice.checkout_url, например res.redirect(303, invoice.checkout_url) в Express. На странице оплаты видны сумма, сеть, адрес с QR-кодом, оставшееся время и статус в реальном времени. Чтобы покупатель остался в вашем оформлении заказа, встройте страницу виджетом оплаты; браузер видит только ID инвойса.
5. Проверьте вебхук в Express
Добавьте вебхук-эндпоинт в разделе Разработчикам → Вебхуки с событиями invoice.*. Читайте тело «сырым» — express.json() пересериализует его и сломает подпись, — проверяйте, отсеивайте повторы по event_id и отвечайте быстро:
import express from "express";
import { createHmac, timingSafeEqual } from "node:crypto";
app.post("/webhooks/cryptolike", express.raw({ type: "application/json" }), async (req, res) => {
const ts = String(req.header("X-Timestamp") ?? "");
const got = Buffer.from(String(req.header("X-Signature") ?? ""));
const secret = process.env.CRYPTOLIKE_WEBHOOK_SECRET ?? "";
const want = Buffer.from("sha256=" + createHmac("sha256", secret).update(`${ts}.`).update(req.body).digest("hex"));
const fresh = /^\d+$/.test(ts) && Math.abs(Date.now() / 1000 - Number(ts)) <= 300;
if (!secret || !fresh || got.length !== want.length || !timingSafeEqual(got, want)) return res.sendStatus(401);
const event = JSON.parse(req.body.toString("utf8"));
if (await events.firstTime(event.event_id)) {
if (event.type === "invoice.paid") await orders.markPaid(event.data.invoice.order_id);
}
res.sendStatus(200);
});Доставка — «хотя бы один раз», и порядок может нарушаться: верьте статусу внутри data, а не порядку прихода. Полная процедура — в руководстве по проверке подписи вебхуков.
6. Проверьте в тестовой сети и запускайте
Создайте платёж тестовым ключом и оплатите его тестовыми монетами в той сети, что названа на странице. Придёт invoice.pending, затем invoice.paid. Обработайте invoice.underpaid (покупатель может доплатить на тот же адрес до срока), invoice.expired (предложите новый платёж) и invoice.reverted (приостановите выполнение заказа). Затем перейдите на рабочий ключ и пройдите чек-лист запуска из руководства разработчика.
Частые ошибки
| Ошибка | Что произошло | Что сделать |
|---|---|---|
401 auth.invalid_signature |
Подписанная строка не совпадает с запросом | Хешируйте ровно отправляемую строку тела; время — в секундах, а не миллисекунды Date.now() |
401 auth.replay |
Два одинаковых запроса в одну секунду | Дождитесь следующей секунды и подпишите заново |
401 на вашем вебхуке |
Тело разобрано до проверки | Используйте express.raw() только на маршруте вебхука |
409 conflict |
У заказа уже есть платёж на другую сумму | Возьмите details.invoice_id; второй платёж не создавайте |
422 no_allowed_coins |
Ни одна монета из allowed_coin_ids не доступна для инвойсов в этой валюте |
Передайте другие id или не передавайте поле, чтобы предложить все монеты |
429 rate_limited |
Превышен лимит запросов ключа (по умолчанию 600 в минуту) | Подождите Retry-After секунд |
Удобнее с SDK?
Официальный TypeScript SDK для Node.js 18+ оборачивает эти вызовы: типизированные ресурсы, повторы при 429 и 5xx, Idempotency-Key на каждом POST и verifyWebhook(). У него нет зависимостей, и он отказывается работать в браузере — секрет не попадёт во фронтенд.
Вопросы и ответы
Можно ли создавать платежи CryptoLike из браузера?
Нет. API-секрет подписывает запросы от имени вашего аккаунта, поэтому он должен оставаться на сервере. Фронтенд просит бэкенд создать платёж и получает только ID инвойса или checkout_url. Поэтому официальный TypeScript SDK выдаёт ошибку, если обнаружит браузер, а виджет оплаты читает только публичные данные платежа.
Почему в Express не проходит проверка подписи вебхука?
Почти всегда потому, что express.json() разобрал тело до проверки: подпись считается по сырым байтам, а пересериализованный JSON отличается. Подключите express.raw({ type: "application/json" }) на маршруте вебхука, сначала проверяйте, потом разбирайте. Ещё проверьте, что секрет — от этого эндпоинта, а часы сервера синхронизированы.
Нужно ли опрашивать статус платежа?
Нет. Вебхуки сообщают о каждой смене статуса, подписаны и повторяются примерно 31 час, если сервер не ответил. Опрос GET /v1/invoices/{id} раз в несколько секунд тоже работает, например на странице, куда возвращается покупатель, но выполняйте заказ по проверенному вебхуку invoice.paid, а не по событию в браузере.