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

Документация

Host-to-host (H2H): оплата криптовалютой в вашем собственном интерфейсе

Как показать платёж CryptoLike на своём экране: адрес, точная сумма, сеть, QR и таймер из инвойса, выбор монеты через API, подтверждение заказа по вебхуку.

Обновлено

Host-to-host (H2H) — это схема, в которой ваш сервер создаёт платёж через API CryptoLike, а показывает его ваш собственный интерфейс: страницу CryptoLike плательщик не открывает. В каждом ответе с инвойсом есть всё, что нужно экрану оплаты, — адрес, точная сумма, сеть, данные для QR и срок оплаты, а подписанный вебхук сообщает вашему серверу, что платёж подтверждён. Ниже — когда выбирать H2H, как идёт процесс, что обязательно показать плательщику и что делать, если он отправил меньше, больше или слишком поздно.

Когда выбирать H2H, страницу оплаты или виджет

Во всех трёх вариантах один и тот же инвойс, одни и те же статусы и вебхуки. Разница в том, кто рисует экран оплаты.

Вариант Кто рисует экран Когда выбирать Что нужно сделать
Страница оплаты CryptoLike, по checkout_url Нужен самый короткий путь: редирект и вебхук Редирект с вашего бэкенда
Виджет оплаты CryptoLike, во фрейме на вашей странице Покупатель остаётся в вашей корзине, и страница CryptoLike может появиться внутри неё Ваш origin в списке разрешённых, скрипт и правило CSP
H2H Вы Мобильное приложение, терминал, бот в мессенджере или корзина, которая целиком должна выглядеть как ваш продукт Экран оплаты со всеми элементами из списка ниже

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

Как проходит платёж

  1. Ваш сервер создаёт инвойс — POST /v1/invoices с подписью вашего API-ключа и заголовком Idempotency-Key. Передайте coin_id, чтобы сразу зафиксировать монету, или не передавайте — тогда плательщик выберет её в вашем интерфейсе.
  2. Если монета не задана, плательщик выбирает её. Сервер читает GET /v1/invoices/{id}/coin-options, интерфейс показывает список, сервер фиксирует выбор запросом POST /v1/invoices/{id}/coin.
  3. Интерфейс показывает платёж из ответа с инвойсом: сеть, адрес, payer_amount, QR, таймер до ttl_expires_at и статус.
  4. Сервер следит за статусом по вебхукам invoice.*, а счётчик подтверждений берёт из GET /v1/invoices/{id} — не чаще раза в несколько секунд. Ваша страница спрашивает ваш сервер, а не API CryptoLike.
  5. Сервер подтверждает заказ только по проверенному вебхуку invoice.paid, после чего интерфейс показывает подтверждение.

Секрет API остаётся на вашем сервере. Браузер или приложение получают только то, что решил показать ваш сервер, и запросы не подписывают. Подпись описана в разделе аутентификация.

Создание инвойса

Запрос ниже сразу фиксирует монету — USDT в сети TRON. Без coin_id в ответе поля монеты, address и платёжный блок равны null, пока плательщик не выберет монету.

http
POST /v1/invoices HTTP/1.1
Host: api.cryptolike.net
Content-Type: application/json
Idempotency-Key: order-10042
X-API-Key: <key_id>
X-Timestamp: <unix seconds>
X-Signature: <HMAC-SHA256 of the request>

{"amount": "49.90", "currency": "USD", "coin_id": "6f1c2a3e-8d4b-4c5a-9e7f-0a1b2c3d4e5f", "order_id": "10042", "description": "Order #10042 — annual plan", "ttl_seconds": 1800, "fee_paid_by": "merchant"}

В ответ приходит инвойс (значения — из примера в справочнике API):

json
{
  "id": "4e2bc88c-cb79-4afc-b864-c1ba8e9d50c9",
  "status": "new",
  "currency": "USD",
  "amount": "49.90",
  "coin_id": "6f1c2a3e-8d4b-4c5a-9e7f-0a1b2c3d4e5f",
  "ticker": "USDT",
  "network": "tron",
  "address": "TXYZopYRdj2D9XRtbG411XZZ3kM5VW9t7g",
  "amount_crypto": "49.90",
  "payer_amount": "49.90",
  "paid_amount": "0",
  "remaining_amount": "49.90",
  "overpaid_amount": "0",
  "amount_fiat": "49.90",
  "fiat_currency": "USD",
  "rate_locked": "1.0004",
  "rate_locked_at": "2026-09-16T12:00:00Z",
  "late_payment": false,
  "fee_paid_by": "merchant",
  "order_id": "10042",
  "description": "Order #10042 — annual plan",
  "return_url": null,
  "webhook_url": null,
  "metadata": null,
  "allowed_coin_ids": null,
  "checkout_url": "https://cryptolike.net/pay/4e2bc88c-cb79-4afc-b864-c1ba8e9d50c9",
  "ttl_expires_at": "2026-09-16T12:30:00Z",
  "expired_at": null,
  "created_at": "2026-09-16T12:00:00Z",
  "updated_at": "2026-09-16T12:00:00Z",
  "payment_uri": "tron:TXYZopYRdj2D9XRtbG411XZZ3kM5VW9t7g?amount=49.9&token=TR7NHqjeKQxGTCi8q8ZY4pL8otSzgjLj6t",
  "qr_payload": "TXYZopYRdj2D9XRtbG411XZZ3kM5VW9t7g",
  "confirmations": null,
  "required_confirmations": 20
}

Поля, которые нужны экрану:

  • payer_amount и ticker — ровно столько плательщик должен отправить. При fee_paid_by: payer комиссия платформы уже включена, поэтому payer_amount больше, чем amount_crypto. Показывайте payer_amount, а не amount и не amount_crypto. Комиссии — на странице тарифов.
  • network — btc, ltc, eth или tron. Один тикер бывает в двух сетях (USDT в TRON и в Ethereum), и именно сеть определяет, куда отправлять.
  • address — адрес для оплаты этого инвойса.
  • payment_uri — ссылка, которая открывает кошелёк с адресом и суммой, которую ещё осталось оплатить: BIP-21 в Bitcoin и Litecoin, EIP-681 в Ethereum, ссылка tron: в TRON.
  • qr_payload — что закодировать в QR: голый адрес во всех сетях, без суммы. Многие плательщики платят с биржи, а форма вывода на бирже принимает только адрес. Если нужен «QR с суммой» для кошельков, предложите его переключателем в Bitcoin и Litecoin и закодируйте там payment_uri.
  • ttl_expires_at — срок оплаты. Его задаёт ttl_seconds — от 60 секунд до 30 дней; без него действует настройка аккаунта (по умолчанию 30 минут). Для цены в USD или другой фиатной валюте курс в rate_locked до этого момента не меняется.
  • confirmations и required_confirmations — счётчик подтверждений; confirmations равен null, пока ничего не пришло.

Храните каждую сумму той строкой, которую вернул API: не округляйте её и не пропускайте через число с плавающей точкой.

Выбор монеты в вашем интерфейсе

Для инвойса без coin_id список монет и выбор работают через два запроса — это тот же список и та же операция, что на странице оплаты.

http
GET /v1/invoices/4e2bc88c-cb79-4afc-b864-c1ba8e9d50c9/coin-options
json
{
  "coin_options": [
    { "coin_id": "6f1c2a3e-8d4b-4c5a-9e7f-0a1b2c3d4e5f", "ticker": "USDT", "name": "Tether USD", "network": "tron", "network_name": "TRON",
      "decimals": 6, "confirmations": 20, "estimated_amount": "49.90", "rate_as_of": "2026-09-16T12:00:03Z",
      "estimate_reason": null, "estimate_message": null },
    { "coin_id": "a01d6e2b-3c4f-4a5b-8c7d-9e0f1a2b3c4d", "ticker": "BTC", "name": "Bitcoin", "network": "btc", "network_name": "Bitcoin",
      "decimals": 8, "confirmations": 2, "estimated_amount": null, "rate_as_of": null,
      "estimate_reason": "rate_stale",
      "estimate_message": { "key": "checkout.estimate.rate_stale", "default": "The exchange rate is updating. Choose this currency to see the exact amount." } }
  ]
}
  • Рядом с каждым тикером показывайте network_name, а estimated_amount — со знаком ≈: это оценка. Если она равна null, покажите вместо неё текст из estimate_message; монету всё равно можно выбрать.
  • В список попадают только монеты, которые вы предлагаете для инвойсов и которые подходят к валюте инвойса; если у инвойса есть allowed_coin_ids, список сужается до них. Монеты на обслуживании в него не входят.
  • Если монета уже выбрана, запрос отвечает 409 с details.coin_id, а в самом инвойсе уже есть адрес.

Зафиксируйте выбор со своего сервера, с Idempotency-Key:

http
POST /v1/invoices/4e2bc88c-cb79-4afc-b864-c1ba8e9d50c9/coin HTTP/1.1
Content-Type: application/json
Idempotency-Key: order-10042-coin

{"coin_id": "6f1c2a3e-8d4b-4c5a-9e7f-0a1b2c3d4e5f"}

В ответ приходит инвойс с address, payer_amount, payment_uri, qr_payload и ttl_expires_at — в той же форме, что выше. При выборе платёж оценивается заново и курс фиксируется, поэтому показывайте payer_amount из этого ответа, а не оценку. Отказы: 400 с details.field: coin_id — монеты нет среди вариантов; 409 — монета уже выбрана (даже та же самая) или инвойс уже не в статусе new; 503 — курс или новый адрес сейчас недоступны: подождите несколько секунд и повторите тот же запрос. Повтор с тем же Idempotency-Key вернёт первый ответ. Монету можно выбрать один раз; если у плательщика открыта ещё и страница оплаты, она сразу переключится на платёж.

Что обязательно показать на экране оплаты

Все эти элементы есть на странице оплаты CryptoLike; на вашем экране нужен каждый из них.

Элемент Поле Зачем
Сеть словами, для токена — со стандартом (TRON, TRC20) network, ticker Одинаковые тикеры и форматы адресов есть в нескольких сетях
Предупреждение о неправильной сети рядом с адресом ticker, network «Отправляйте на этот адрес только USDT в сети TRON. Средства, отправленные в другой сети, вернуть невозможно.»
Адрес с кнопкой копирования address Плательщик вставляет его в кошелёк; средства на адрес с опечаткой вернуть нельзя
Точная сумма с кнопкой копирования payer_amount, ticker Перевод меньше суммы становится недоплатой
QR-код и сумма рядом с ним qr_payload QR содержит только адрес во всех сетях
Ссылка «Открыть в кошельке» payment_uri Одно нажатие на телефоне, где установлен кошелёк
Сколько осталось времени ttl_expires_at После срока инвойс истекает, а более поздний перевод считается поздней оплатой
Статус и подтверждения, пока платёж ждёт status, confirmations, required_confirmations «Платёж обнаружен, 3 из 20 подтверждений» — плательщик не отправит второй раз
Экран результата status, paid_amount, remaining_amount Оплачен, недоплачен с остатком, истёк или отменён — и следующий шаг для каждого случая

Отсчитывайте таймер по часам вашего сервера, а не устройства плательщика. Когда время вышло, скройте адрес и QR и предложите создать новый платёж. Статусы — new, pending, paid, underpaid, expired и cancelled; почему перевод ждёт, прежде чем засчитаться, объясняет статья о подтверждениях.

Недоплата, переплата и поздняя оплата

  • Недоплата. Подтверждённый перевод меньше payer_amount переводит инвойс в underpaid и отправляет invoice.underpaid; нехватка в пределах допуска недоплаты вашего аккаунта всё равно считается paid. Инвойс открыт для доплаты на тот же адрес до ttl_expires_at: покажите remaining_amount и обновлённый payment_uri — теперь в нём остаток. После срока недоплата окончательная; попросите плательщика связаться с вами. Подробнее — недоплата.
  • Переплата. Статус paid, а overpaid_amount больше нуля; его несёт invoice.paid, который приходит повторно, когда следующий перевод увеличивает переплату. Вся сумма зачисляется на ваш баланс; разницу урегулируйте с покупателем.
  • Поздняя оплата. Перевод, подтверждённый после закрытия инвойса (expired, cancelled или окончательный underpaid), всё равно зачисляется на ваш баланс: инвойс получает late_payment: true, вам приходит invoice.late_payment, статус не меняется. Так же бывает, когда перевод к сроку уже обнаружен, но ещё не подтверждён: инвойс истекает, а перевод приходит как поздняя оплата. Решите, выполнить заказ или вернуть деньги. Подробнее — истёкший платёж.
  • Откат. Если реорганизация сети убирает подтверждённый перевод, вам приходит invoice.reverted с пересчитанными суммами. Остановите выполнение заказа до нового invoice.paid.

Подтверждайте заказ только по вебхуку

Выполняйте заказ, когда ваш сервер получил invoice.paid с верной подписью вебхука, — а не когда плательщик нажал «Я оплатил» и не по статусу, который увидела ваша страница. Доставка — как минимум один раз: сохраняйте event_id, отвечайте 2xx в течение 10 секунд и пропускайте повторы. Вебхуки одинаковы для H2H, страницы оплаты и виджета и не содержат payment_uri, qr_payload и счётчика подтверждений — когда они нужны, читайте GET /v1/invoices/{id}. Типы событий, повторы и тестовый вектор — в справочнике по вебхукам, пошагово — в руководстве как проверять вебхуки платежей.

Перед запуском проведите в тестовой сети по одному платежу на каждый случай: оплачен, недоплачен и потом доплачен, истёк и пришёл с опозданием. Тестовая среда описана в руководстве разработчика.

Вопросы и ответы

Нужен ли виджет для H2H?

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

Можно ли всё-таки отправить плательщика на страницу оплаты?

Да. У каждого инвойса есть checkout_url, и страница оплаты показывает тот же инвойс. Монета, выбранная через API, сразу переключает открытую страницу на платёж, поэтому страницу можно предложить как запасной вариант — например, плательщику, у которого кошелёк на другом устройстве.

Меняются ли вебхуки в H2H?

Нет. События, подпись и повторы те же, что со страницей оплаты. Платёжного блока инвойса — payment_uri, qr_payload, confirmations и required_confirmations — в теле вебхука нет; читайте инвойс через GET /v1/invoices/{id}.

Какую сумму отправляет плательщик, если комиссию платит он?

payer_amount. При fee_paid_by: payer в неё уже входит комиссия платформы, а amount_crypto — то, что получаете вы. Показывайте payer_amount и храните её той строкой, которую вернул API.