К содержимому

Разработчикам

От вызова API до статуса платежа.

Принимайте крипту в своём продукте: ваш сервер создаёт платёж, клиент платит на готовой странице или прямо в вашей корзине, подписанный вебхук сообщает о подтверждении. На этой странице — весь путь: от тестового ключа до первого платежа примерно десять минут.

Быстрый старт: десять минут до подтверждённого платежа

Пять шагов, один тестовый ключ, свою ноду поднимать не нужно. Все примеры выполняются на вашем сервере — браузер секрета не видит.

  1. 1

    Создайте тестовый ключ 2 мин

    Разработчикам → API-ключи → создайте ключ с правами create_invoice и read. Секрет показывается один раз: положите его в переменные окружения сервера или в менеджер секретов. Тестовые ключи (ck_test_) создаются так же в кабинете тестовой среды — доступ и тестовый адрес API даст поддержка CryptoLike (см. ниже).

  2. 2

    Создайте платёж на сервере 3 мин

    Один вызов с суммой, вашим номером заказа и адресом возврата. Передайте номер заказа как Idempotency-Key: повтор вернёт тот же платёж, а не создаст второй.

  3. 3

    Дайте клиенту оплатить 2 мин

    Перенаправьте на checkout_url или встройте ту же страницу в корзину виджетом. Страница показывает адрес, QR, точную сумму, предупреждение о сети и живой статус.

  4. 4

    Оплатите в тестовой сети 2 мин

    Попросите тестовые монеты у поддержки CryptoLike — они ничего не стоят — и отправьте точную сумму на адрес со страницы. Статус станет pending, как только перевод замечен, и paid — когда наберутся подтверждения.

  5. 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>
Origin, события и CSP-фрейминг: руководство по виджету →

Модель статусов

Один статус, одинаково показанный в 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. 1

    Проверяйте по сырым байтам

    X-Signature — это "sha256=" плюс HMAC-SHA256 от X-Timestamp + "." + сырое тело. Сравнивайте за постоянное время и отклоняйте метку времени, которая расходится с вашими часами больше чем на несколько минут в любую сторону. Не пересобирайте JSON перед проверкой: порядок ключей не гарантирован.

  2. 2

    Действуйте один раз на event_id

    Доставка — не менее одного раза. То же событие может прийти снова с тем же event_id, тем же телом и новой подписью. Сохраняйте id и подтверждайте повтор, не трогая заказ дважды.

  3. 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. 1

    Направьте SDK на тестовые сети

    Создайте ключ с теми же правами и задайте в CRYPTOLIKE_API_URL тестовый адрес API — его даст поддержка CryptoLike. Держите тестовые и рабочие ключи в разных конфигурациях.

  2. 2

    Посмотрите, что доступно в тестовых сетях

    GET /v1/currencies перечисляет активы, их сети, знаки после запятой и число подтверждений для каждого — те же цифры, что видит клиент на странице оплаты.

  3. 3

    Возьмите тестовые монеты

    Тестовые монеты ничего не стоят — попросите их у поддержки CryptoLike. Отправьте точный payer_amount на адрес со страницы, в той сети, которая там названа.

  4. 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

Каждый маршрут, поле и ошибка в OpenAPI-документе, доступном как Swagger UI.

Открыть Swagger UI ↗

Официальные SDK

TypeScript, Python, Go и PHP: подписанные запросы, Idempotency-Key на каждом POST, повторы и проверка вебхуков. Секрет остаётся на вашем сервере.

Быстрый старт с SDK →

Текстовая версия этой страницы для ИИ-ассистентов ↗