Разработчикам
От вызова API до статуса платежа.
Принимайте крипту в своём продукте: ваш сервер создаёт платёж, клиент платит на готовой странице или прямо в вашей корзине, подписанный вебхук сообщает о подтверждении. На этой странице — весь путь: от тестового ключа до первого платежа примерно десять минут.
Быстрый старт: десять минут до подтверждённого платежа
Пять шагов, один тестовый ключ, свою ноду поднимать не нужно. Все примеры выполняются на вашем сервере — браузер секрета не видит.
- 1
Создайте тестовый ключ 2 мин
Разработчикам → API-ключи → создайте ключ с правами create_invoice и read. Секрет показывается один раз: положите его в переменные окружения сервера или в менеджер секретов. Тестовые ключи (ck_test_) создаются так же в кабинете тестовой среды — доступ и тестовый адрес API даст поддержка CryptoLike (см. ниже).
- 2
Создайте платёж на сервере 3 мин
Один вызов с суммой, вашим номером заказа и адресом возврата. Передайте номер заказа как Idempotency-Key: повтор вернёт тот же платёж, а не создаст второй.
- 3
Дайте клиенту оплатить 2 мин
Перенаправьте на checkout_url или встройте ту же страницу в корзину виджетом. Страница показывает адрес, QR, точную сумму, предупреждение о сети и живой статус.
- 4
Оплатите в тестовой сети 2 мин
Попросите тестовые монеты у поддержки CryptoLike — они ничего не стоят — и отправьте точную сумму на адрес со страницы. Статус станет pending, как только перевод замечен, и paid — когда наберутся подтверждения.
- 5
Получите результат 1 мин
Проверьте подпись вебхука по сырому телу, пропустите event_id, который уже сохранён, ответьте 2xx за 10 секунд — и выполняйте заказ. Опрос GET /v1/invoices/{id} раз в несколько секунд тоже подойдёт.
Язык примеров — Примеры на странице переключаются вместе с вами.
// Your server. The secret never reaches a browser.
import { CryptoLike } from "@cryptolike/sdk";
const api = new CryptoLike(); // CRYPTOLIKE_API_KEY_ID + CRYPTOLIKE_API_SECRET
const invoice = await api.invoices.create(
{
amount: "49.90",
currency: "USD", // or a ticker: "BTC", "USDT"
order_id: "A-1001",
return_url: "https://shop.example/orders/A-1001",
},
"order-A-1001", // Idempotency-Key: a retry returns this same payment
);
res.redirect(invoice.checkout_url); // invoice.status === "new"SDK читают CRYPTOLIKE_API_KEY_ID и CRYPTOLIKE_API_SECRET из окружения. Для тестовых сетей задайте в CRYPTOLIKE_API_URL тестовый адрес API, который даст поддержка CryptoLike.
Это вся интеграция. Всё, что ниже, — подробности за этими пятью шагами.
Создание платежа
Один подписанный POST /v1/invoices. В ответ приходит платёж с id, статусом и страницей, на которую отправить клиента.
| Поле | Что делает |
|---|---|
amount | Сколько списать, десятичной строкой: "49.90". Числа с плавающей точкой не принимаются нигде — суммы остаются строками на всём пути. |
currency | Код фиата (USD, EUR, UAH, GBP или CAD) или тикер монеты (BTC, USDT). С фиатом клиент выбирает монету на странице, и курс фиксируется в этот момент. |
coin_id | Необязательно, из GET /v1/currencies. Фиксирует монету сразу: адрес и точная сумма приходят уже в ответе. |
order_id | Ваш номер заказа. Возвращается в каждом вебхуке и в каждой выгрузке. |
return_url | Куда страница предложит вернуться клиенту после оплаты. |
Idempotency-Key | Заголовок. Один и тот же ключ 24 часа возвращает тот же платёж; тот же ключ с другим телом — 422 idempotency.mismatch. |
В ответе есть id, status: new, checkout_url и ttl_expires_at — момент, после которого платёж перестаёт ждать первый перевод. Необязательные блоки присутствуют как null, поэтому любой ответ разбирается одним и тем же кодом.
Постоянным клиентам счёт каждый раз не нужен: POST /v1/addresses выдаёт постоянный адрес пополнения для одного клиента и одного актива. Каждый перевод на него зачисляется и объявляется событием deposit.confirmed.
Все поля и маршруты: обзор API →Оплата клиентом: готовая страница или встроенный виджет
Одна и та же страница оплаты, два способа её показать. Оба читают только публичные данные платежа, и ни одному не нужен секрет в браузере.
Готовая страница
Перенаправьте клиента на checkout_url. Ничего не нужно писать и оформлять: страница сама ведёт выбор монеты, QR, обратный отсчёт и живой статус — на английском или русском, в светлой или тёмной теме.
- Ноль фронтенд-кода на вашей стороне
- Возвращает клиента на ваш return_url
- Достаточно редиректа с вашего бэкенда
Встроенный виджет
Клиент остаётся на вашей странице: загрузчик примерно на 5 КБ помещает ту же страницу в iframe и сообщает вашему коду об изменениях статуса. Сначала добавьте свой origin в список: Кабинет → Настройки, карточка «Интеграция», «Разрешённые origin».
- Клиент не уходит из вашей корзины
- События onStatus / onPaid в вашей странице
- Темы и акцент бренда, без произвольных цветов
<div id="pay"></div>
<script src="https://cryptolike.net/widget/v1/cryptolike.js"></script>
<script>
CryptoLike.mount({
container: "#pay",
invoiceId: "4e2bc88c-cb79-4afc-b864-c1ba8e9d50c9", // from your server — the page gets nothing else
theme: "dark",
onStatus: function (m) { if (m.isFinal) showResult(m.status); },
});
</script>Модель статусов
Один статус, одинаково показанный в API, на странице оплаты и в вебхуках. Конечный — значит, сам по себе он уже не изменится.
| Статус | Что означает | Конечный |
|---|---|---|
new | Создан. Ничего не получено. | нет |
pending | Перевод замечен. Ожидает подтверждения. | нет |
paid | Подтверждён и зачислен на ваш баланс. Переплата — это тоже paid, с overpaid_amount больше нуля. | да |
underpaid | Пришло меньше суммы. Тот же адрес открыт для доплаты до ttl_expires_at; remaining_amount показывает, сколько не хватает. | после истечения TTL |
expired | Срок истёк, подтверждённых переводов не было. | да |
cancelled | Вы отменили платёж — это возможно только из new. | да |
Конечные состояния: paid, expired, cancelled — и underpaid после истечения TTL. Все остальные ещё могут измениться.
- Отгружайте по paid.
- Если реорганизация сети откатит подтверждение, сумма пересчитывается и приходит invoice.reverted. Остановите выполнение заказа до нового invoice.paid.
- Поздние средства всё равно зачисляются.
- Перевод, подтверждённый после закрытия платежа, попадает на ваш баланс, платёж получает late_payment: true, а вы — invoice.late_payment. Статус при этом не меняется: решайте вместе с клиентом.
- Две суммы у одного платежа.
- payer_amount — сколько должен отправить клиент, amount_crypto — сколько получите вы. Они различаются, только когда комиссию платформы платит плательщик.
Вебхуки: подпись, повторы, ровно один раз
На каждое изменение мы отправляем на ваш эндпоинт подписанное JSON-событие. Корректный обработчик держится на трёх правилах.
- 1
Проверяйте по сырым байтам
X-Signature — это "sha256=" плюс HMAC-SHA256 от X-Timestamp + "." + сырое тело. Сравнивайте за постоянное время и отклоняйте метку времени, которая расходится с вашими часами больше чем на несколько минут в любую сторону. Не пересобирайте JSON перед проверкой: порядок ключей не гарантирован.
- 2
Действуйте один раз на event_id
Доставка — не менее одного раза. То же событие может прийти снова с тем же event_id, тем же телом и новой подписью. Сохраняйте id и подтверждайте повтор, не трогая заказ дважды.
- 3
Отвечайте 2xx за 10 секунд
Сохраните событие, ответьте, обрабатывайте потом. Всё остальное — другой статус, редирект, таймаут — повторяется через 1 мин, 5 мин, 15 мин, 1 ч, 6 ч и 24 ч, после чего доставка помечается исчерпанной. Любую доставку можно переслать из кабинета.
import express from "express";
import { verifyWebhook, WebhookVerificationError } from "@cryptolike/sdk";
// express.raw: the signature covers the bytes as received, not re-serialised JSON.
app.post("/hooks/cryptolike", express.raw({ type: "application/json" }), (req, res) => {
let event;
try {
event = verifyWebhook(req.headers, req.body, process.env.CRYPTOLIKE_WEBHOOK_SECRET);
} catch (e) {
if (e instanceof WebhookVerificationError) return res.sendStatus(401);
throw e;
}
res.sendStatus(200); // answer first — 10 seconds is the budget
if (seen(event.event_id)) return; // delivery is at least once: a repeat is normal
remember(event.event_id);
if (event.type === "invoice.paid") fulfil(event.data.invoice);
});В каждом SDK это один вызов — verifyWebhook(headers, rawBody, secret) — со сравнением за постоянное время и допуском по времени внутри. Тестовые векторы, на которых закреплены все SDK, лежат в контракте вебхуков.
Доставки независимы, поэтому повторный invoice.pending может прийти после invoice.paid. Опирайтесь на created_at и статус внутри data, а не на порядок получения.
Типы событий, поля и тестовый вектор: контракт вебхуков →Ошибки: машинный код, текст для человека, действие
Один конверт на любую ошибку. Ветвитесь по code, человеку показывайте message, подробности берите из details — details.field называет неверное поле.
| Код | Что случилось | Что делать |
|---|---|---|
400 validation_error | Поле отсутствует или заполнено неверно. | Исправьте details.field и отправьте снова. |
401 auth.invalid_signature | Подпись не совпадает с полученным запросом. | Подписывайте метку времени, метод, путь с query и хеш тела — именно в этом порядке; сверьте часы (±5 минут). |
403 auth.permission_denied | У ключа нет права, названного в details.required. | Добавьте право ключу в кабинете или используйте другой ключ. |
404 not_found | Такого id нет — либо он принадлежит другому аккаунту. | Проверьте id. Оба случая отвечают одинаково, и это сделано намеренно. |
409 conflict | Текущее состояние не допускает действия: отмена оплаченного платежа, второй активный адрес. | Сначала прочитайте текущее состояние, потом решайте. |
422 idempotency.mismatch | Тот же Idempotency-Key пришёл с другим запросом. | Для нового запроса возьмите новый ключ. |
429 rate_limited | Слишком много запросов этим ключом — по умолчанию 600 в минуту. | Подождите Retry-After секунд и повторите с выдержкой. |
503 unavailable | Курс или монета временно недоступны. | Повторите с выдержкой или создайте платёж в крипте. |
504 timeout | Мы не успели обработать запрос. | Повторите с тем же Idempotency-Key — второй платёж он создать не может. |
Повторять 4xx без изменений бессмысленно. SDK повторяют 429 и 5xx (кроме 501) до трёх раз с выдержкой и джиттером и останавливаются, если Retry-After больше 30 секунд.
Все коды и их details: ошибки в обзоре API →Песочница: тестовые ключи и тестовые сети
Ключ ck_test_ подписывает точно так же, как боевой, и работает только в тестовой среде, в тестовых сетях — там и боевой ключ двигает только тестовые монеты. Тестовые монеты ничего не стоят, сценарий не отличается от боевого. Боевая среда работает в основных сетях: тестовые ключи она не выдаёт и отклоняет с 401 auth.test_key, ничего не создавая.
- 1
Направьте SDK на тестовые сети
Создайте ключ с теми же правами и задайте в CRYPTOLIKE_API_URL тестовый адрес API — его даст поддержка CryptoLike. Держите тестовые и рабочие ключи в разных конфигурациях.
- 2
Посмотрите, что доступно в тестовых сетях
GET /v1/currencies перечисляет активы, их сети, знаки после запятой и число подтверждений для каждого — те же цифры, что видит клиент на странице оплаты.
- 3
Возьмите тестовые монеты
Тестовые монеты ничего не стоят — попросите их у поддержки CryptoLike. Отправьте точный payer_amount на адрес со страницы, в той сети, которая там названа.
- 4
Пройдите весь путь
Недоплатите нарочно, чтобы увидеть underpaid и доплату; дайте одному платежу истечь — увидите expired; отмените один из new. Разработчикам → Вебхуки → эндпоинт → «Отправить тестовое событие» прогоняет ваш обработчик подписанным ping без платежа.
Чек-лист перед продом
Десять проверок перед первым реальным платежом.
- Боевой ключ создан с минимально достаточными правами (create_invoice + read); секрет лежит в хранилище секретов сервера, а не в репозитории.
- Тестовые ключи и тестовый адрес API убраны из боевой конфигурации.
- На каждом POST есть Idempotency-Key — номер заказа или UUID, созданный один раз и переиспользованный при повторах.
- Эндпоинт вебхука — https:// на публичном хосте и отвечает 2xx за 10 секунд.
- Подпись проверяется по сырому телу; метка времени сверяется с синхронизированными часами (NTP, ±5 минут).
- event_id сохраняется; повторная доставка подтверждается, не трогая заказ дважды.
- Заказ выполняется по invoice.paid, а не по событию из браузера; invoice.reverted останавливает выполнение.
- Для expired, cancelled и окончательного underpaid есть экран и следующее действие для клиента.
- Суммы — десятичные строки на всём пути: без float и без собственного округления.
- Если встраиваете виджет: боевой origin в списке разрешённых, а корзина отдаётся по https.
После запуска: в кабинете видна каждая доставка с попытками и ответом, и любую можно переслать вручную.
Вопросы, которые задают разработчики
Сколько занимает интеграция CryptoLike?
Первый платёж в тестовой сети — примерно десять минут: создать API-ключ, вызвать POST /v1/invoices с сервера, отправить клиента на checkout_url и обработать один подписанный вебхук. В проде это тот же код из трёх частей — остальная работа приходится на ваши статусы заказа.
Нужно ли поднимать ноду и хранить приватные ключи?
Нет. CryptoLike выводит адреса, следит за сетями, считает подтверждения, консолидирует средства и подписывает выплаты. Ваша интеграция — один REST API: создать платёж, прочитать статус, принимать подписанные вебхуки. Подтверждённые средства попадают на баланс в платформе.
Можно ли оставить страницу оплаты внутри своей корзины?
Да. Виджет встраивает ту же страницу оплаты в iframe загрузчиком примерно на 5 КБ и сообщает вашей странице об изменениях статуса через postMessage. Добавьте свой origin в список в кабинете; браузер получает id счёта и больше ничего.
Как обработать вебхук ровно один раз?
Проверьте подпись HMAC-SHA256 по сырому телу, затем поищите event_id в своём хранилище: если он уже был, ответьте 2xx и остановитесь. Доставка гарантируется не менее одного раза и повторяется шесть раз примерно за 31 час, так что дубликаты — норма, а не сбой.
Можно ли назначить сумму в фиате?
Да — currency принимает и код фиата (USD, EUR, UAH, GBP или CAD), и тикер монеты. С фиатом клиент выбирает монету на странице оплаты, и курс фиксируется в этот момент; он держится до истечения платежа, поэтому сумма у клиента не меняется.
Что будет, если клиент заплатит меньше, больше или позже?
Меньше: платёж переходит в underpaid, а адрес остаётся открытым для доплаты до истечения TTL. Больше: статус paid с overpaid_amount больше нуля. Позже: средства всё равно зачисляются, у платежа появляется late_payment, а вам приходит invoice.late_payment.
Читать дальше
- Обзор API — ресурсы, соглашения, версионирование →
- Аутентификация — ключи, права, подпись запросов →
- Вебхуки — типы событий, поля, тестовый вектор →
- SDK — TypeScript, Python, Go, PHP →
- Виджет оплаты — страница оплаты в вашей корзине →
Справочник API
Каждый маршрут, поле и ошибка в OpenAPI-документе, доступном как Swagger UI.
Официальные SDK
TypeScript, Python, Go и PHP: подписанные запросы, Idempotency-Key на каждом POST, повторы и проверка вебхуков. Секрет остаётся на вашем сервере.