Как подключиться

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

Пошаговое подключение и справочник по API — то же, что лежит в репозитории.

Подключение сайта к «Приёму»

Пошагово, от пустого кабинета до работающей кнопки оплаты. Займёт около получаса.

Всё, что здесь написано, проверено на боевом.


Шаг 0. Регистрация

Зарегистрируйтесь на https://app.priem.io/signup — название бизнеса, почта, пароль. Кабинет откроется сразу.

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

Проверка занимает от нескольких часов. Пока она идёт, всё остальное настраивать можно — проект, адрес коллбэка, ключи, ценники. Как подтвердим, они заработают без каких-либо действий с вашей стороны.

Пароль пока нельзя сменить самому — напишите нам, если понадобится.

Что это стоит

Кому платится Сколько
Нам, с каждого платежа 1,5%, удерживается при зачислении
Сети, при выводе Фиксированно, около 1 USDT в TRON
Сети, при оплате Платит клиент сверх ценника

Комиссия сети при оплате не зависит от суммы, поэтому на мелком чеке она заметна, а на крупном почти нет. Клиент видит её до подтверждения:

Чем платит Чек $5 Чек $30 Чек $100
USDT в BSC +1,3% +0,5% +0,3%
USDT в TON +9,3% +1,8% +0,8%
USDT в TRON +16,3% +3,0% +1,1%
USDT в Ethereum +19,5% +3,5% +1,3%

Ниже $10 продавать через криптовалюту тяжело: на трёх долларах даже недорогая сеть съедает заметную долю, а TRON и Ethereum добавляют четверть цены. Если у вас мелкие тарифы, имеет смысл продавать их пакетами.


Шаг 1. Проект

Первый проект создаётся при регистрации сам. Откройте https://app.priem.io/projects и проверьте настройки:

Поле Что это
Валюта выплаты В чём хотите получать. По умолчанию USDT
Адрес коллбэка Куда сообщить об оплате. Заполните на шаге 4

Чем платить, клиент выбирает сам на странице оплаты — доступны все валюты и сети Changelly. Ограничить набор пока нельзя.

Там же лежит секрет подписи — длинная строка, ею вы проверяете, что коллбэк пришёл от нас. Понадобится на шаге 4.

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

Шаг 2. Ценник — самый быстрый способ

Если нужна просто кнопка «оплатить» с фиксированной ценой, кода не потребуется.

На https://app.priem.io/price-tags создайте ценник:

Скопируйте ссылку вида https://api.priem.io/pay/<идентификатор> и поставьте её кнопкой:

<a href="https://api.priem.io/pay/ВАШ-ИДЕНТИФИКАТОР"
   style="display:inline-block;padding:.8rem 1.4rem;background:#17171a;
          color:#fff;border-radius:8px;text-decoration:none">
  Оплатить криптовалютой
</a>

На этом приём платежей уже работает. Дальше — если нужна сумма из корзины.

Оплата картой

На кассе рядом с «Криптовалютой» есть кнопка «Картой». Клиент покупает биткоин у Changelly, тот приходит прямо на адрес счёта, и платёж проводится как обычный — вам ничего настраивать не нужно.

Биткоин выбран из-за однозначности: у него одна сеть, и доставка не может уйти не туда.

Минимальная сумма покупки картой — 10 долларов. Измерено на живом виджете. Ниже порога кнопка «Картой» на кассе не показывается вовсе, и клиенту сказано почему.

Учтите: клиент отправляет больше ценника, сверху ложится комиссия сети. За счёт в $30 он заплатит около $34.

Шаг 3. Динамическая сумма через API

Выпустите ключ на https://app.priem.io/api-keys. Он показывается один раз.

Ваш сервер создаёт платёж и ведёт клиента на полученную ссылку:

const response = await fetch('https://api.priem.io/v1/payments', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.PRIEM_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    amount: order.total.toFixed(2),   // строкой, не числом
    currency: 'USD',
    idempotencyKey: `order-${order.id}`,   // ваш номер заказа
  }),
});

const payment = await response.json();
// payment.id       — сохраните в заказе, по нему придёт коллбэк
// payment.paymentUrl — сюда отправляйте клиента

idempotencyKey берите из номера заказа, а не из времени и не случайный. Повторный запрос с тем же ключом вернёт тот же платёж вместо второго счёта. Ключ из Date.now() не защищает ни от чего.

Ключ API держите на сервере. В браузер он попадать не должен: с ним создают платежи от вашего имени.

Шаг 4. Коллбэк — как узнать об оплате

В карточке проекта впишите адрес коллбэка: https://ваш-сайт/priem/hook. Только https, только публичное имя — внутренние адреса мы отклоняем.

Когда деньги дошли, мы шлём туда POST:

{
  "event": "payment.settled",
  "paymentId": "f0a528fa-...",
  "state": "settled",
  "amount": "49.00",
  "currency": "USD",
  "payCurrency": "USDC",
  "payoutCurrency": "USDT",
  "creditedAmount": "48.26"
}

Обработчик на Express:

import express from 'express';
import { createHmac, timingSafeEqual } from 'node:crypto';

const app = express();

// ВАЖНО: сырое тело, до разбора JSON. Подпись считается по байтам,
// и разобранный-собранный JSON её не пройдёт.
app.post('/priem/hook', express.raw({ type: 'application/json' }), (req, res) => {
  const timestamp = req.get('X-Priem-Timestamp');
  const signature = req.get('X-Priem-Signature');

  const expected = createHmac('sha256', process.env.PRIEM_WEBHOOK_SECRET)
    .update(`${timestamp}.${req.body}`, 'utf8')
    .digest('hex');

  const a = Buffer.from(expected, 'utf8');
  const b = Buffer.from(signature ?? '', 'utf8');
  if (a.length !== b.length || !timingSafeEqual(a, b)) {
    return res.sendStatus(401);
  }

  // не старше пяти минут: иначе это повтор перехваченного запроса
  if (Math.abs(Date.now() / 1000 - Number(timestamp)) > 300) {
    return res.sendStatus(401);
  }

  const event = JSON.parse(req.body.toString('utf8'));

  // Тот же платёж может прийти дважды — мы повторяем при неудаче.
  // Отгружайте по факту, а не по приходу коллбэка.
  markOrderPaid(event.paymentId, event.creditedAmount);

  res.sendStatus(200);   // ответьте 2xx, иначе будем повторять
});

Секрет подписи — из карточки проекта. Положите его в переменную окружения, не в код.

Шаг 5. Проверка до боевого запуска

Пройдите этот список — каждый пункт закрывает ошибку, которую легко сделать:

Затем сделайте настоящий платёж на маленькую сумму. Не на $2 — на такой сумме комиссия сети даёт около 40% накрутки, и вы решите, что что-то сломано. Возьмите $20–30.

Шаг 6. Вывод денег

На https://app.priem.io/payouts заведите адрес кошелька. Выводить на него можно через сутки — отсрочка защищает на случай, если кабинетом завладеет кто-то другой.

Сеть указывается отдельно и обязательна: USDT в TRON и USDT в BSC — разные адреса, отправка не в ту сеть теряет деньги безвозвратно.

Комиссию сети платите вы, она списывается сверх суммы, которую получит адресат. Она фиксированная — около 1 USDT в TRON независимо от суммы, — поэтому выводить накопленное разом дешевле, чем каждый платёж отдельно.


Если что-то не сходится

Платёж не создаётся, ответ говорит про подтверждение. Проверка ещё не пройдена. Настраивать всё остальное можно, приём включится сам.

Клиент заплатил, а коллбэк не пришёл. Посмотрите платёж через GET /v1/payments/{id} — состояние там всегда актуально. Коллбэк мы повторяем семь раз в течение полутора суток, но полагаться только на него не стоит.

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

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

Подробности по API — в разделе «Документация» кабинета.


Приём: как подключиться

Приём принимает у вашего клиента криптовалюту или фиат и зачисляет вам выбранную монету. Клиент платит чем хочет — вы получаете то, что заказали.

Есть два пути, и они не исключают друг друга:

Дальше в обоих случаях приходит коллбэк на ваш адрес: платёж прошёл.

Что нужно завести

  1. Проектhttps://app.priem.io/projects. Задайте валюту, в которой хотите получать деньги, и валюты, которыми клиент может платить.
  2. Адрес коллбэка — там же. Только https, только публичное имя.
  3. Ключ APIhttps://app.priem.io/api-keys. Показывается один раз; восстановить его неоткуда, только выпустить новый.

Ценник без кода

На https://app.priem.io/price-tags создайте ценник и скопируйте ссылку вида https://api.priem.io/pay/<идентификатор>. Поставьте её кнопкой на сайт.

Два свойства решаются при создании:

API

Адрес: https://api.priem.io. Аутентификация — заголовок Authorization: Bearer <ключ>. Проект определяется ключом, поэтому в теле его указывать не нужно и нельзя.

Создать платёж

curl -X POST https://api.priem.io/v1/payments \
  -H "Authorization: Bearer priem_..." \
  -H "Content-Type: application/json" \
  -d '{
        "amount": "49.00",
        "currency": "USD",
        "idempotencyKey": "zakaz-12345"
      }'
{
  "id": "f0a528fa-5292-4f04-a4a0-747b6da94873",
  "state": "created",
  "amount": "49.00",
  "currency": "USD",
  "paymentUrl": "https://app.pay.changelly.com/payment/a3e5b893-..."
}

Ведите клиента на paymentUrl — там он выберет монету и сеть.

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

Если предыдущий запрос с этим ключом ещё выполняется, ответ будет 400 с текстом payment is already being created, try again — повторите через мгновение.

Отказы

Все поля тела — строки, включая сумму: "25.00", а не 25.00.

Код Когда Что делать
400 поля не хватает, сумма не строка, валюта незнакомая исправить запрос, текст в message
400 payment is already being created, try again повторить через мгновение
401 ключ не тот или не передан проверить заголовок Authorization
403 merchant verification is pending… — проверка ещё идёт ждать, приём включится сам
403 merchant is suspended… — приём приостановлен написать нам
502 / 503 подвёл провайдер платежей повторить с тем же idempotencyKey
500 наш сбой прислать нам requestId из тела ответа

Каждый ответ несёт заголовок x-request-id, а тело отказа 500 — то же значение полем requestId. Это самое полезное, что можно приложить к письму о поломке: по нему мы находим нужную строку в журнале сразу. Свой идентификатор тоже принимается — пришлите его тем же заголовком, и он вернётся в ответе.

Прочитать платёж

curl https://api.priem.io/v1/payments/<id> \
  -H "Authorization: Bearer priem_..."
{
  "id": "f0a528fa-...",
  "state": "settled",
  "amount": "49.00",
  "currency": "USD",
  "paymentUrl": "https://app.pay.changelly.com/payment/...",
  "payCurrency": "USDC",
  "depositAddress": "0x..."
}

Чужой платёж и несуществующий отвечают одинаково — 404.

Пока не подтверждены

До прохождения проверки создание платежа отвечает 403 с текстом merchant verification is pending, payments are disabled until it completes. Ссылка ценника в это же время отвечает покупателю «приём платежей у этого продавца пока не подключён».

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

Состояния платежа

Состояние Что это значит
created Платёж заведён, клиент ещё не выбрал монету
awaiting_payment Монета выбрана, ждём перевода
onramp_pending Клиент платит картой, ждём покупку крипты
detected Перевод виден в сети, ещё не подтверждён
confirming / confirmed Набираются подтверждения
converting Меняем полученное на вашу валюту
conversion_retry Обмен не прошёл с первого раза, пробуем снова
settled Деньги на вашем балансе. Отгружайте заказ
underpaid / overpaid Заплатили меньше или больше нужного
expired / failed Не состоялся
refunded Возвращён клиенту

Отгружайте заказ по settled и только по нему.

Коллбэк

Когда платёж дошёл до settled, мы шлём POST на ваш адрес.

{
  "event": "payment.settled",
  "paymentId": "f0a528fa-...",
  "projectId": "34c80b2d-...",
  "state": "settled",
  "amount": "49.00",
  "currency": "USD",
  "payCurrency": "USDC",
  "payoutCurrency": "USDT",
  "creditedAmount": "48.26"
}

creditedAmount — сколько зачислено вам после нашей комиссии, в payoutCurrency.

Заголовки:

Заголовок Что в нём
X-Priem-Timestamp Время подписи, секунды Unix
X-Priem-Signature HMAC-SHA256, шестнадцатеричная строка

Ответьте 2xx. Любой другой ответ мы считаем неудачей и повторяем: всего семь попыток с промежутками 1 минута, 5 минут, 30 минут, 2 часа, 6 часов и сутки — последняя примерно через 33 часа после первой. Дальше мы перестаём: бесконечные повторы одного сломанного мерчанта забили бы очередь всем остальным.

Обработчик должен терпеть повтор — один и тот же paymentId может прийти дважды. И не полагайтесь на один лишь коллбэк: если ваш сервер лежал больше полутора суток, состояние надо забрать опросом.

Проверка подписи

Подписывается строка {timestamp}.{тело как есть}. Секрет — в карточке проекта в кабинете.

Считайте подпись по сырому телу, до разбора JSON. Если разобрать и собрать обратно, изменится порядок ключей или пробелы, и подпись не сойдётся.

import { createHmac, timingSafeEqual } from 'node:crypto';

export function verify(secret, rawBody, timestamp, signature) {
  const expected = createHmac('sha256', secret)
    .update(`${timestamp}.${rawBody}`, 'utf8')
    .digest('hex');

  const a = Buffer.from(expected, 'utf8');
  const b = Buffer.from(signature, 'utf8');
  // сравнение за постоянное время: обычное `===` выдаёт длину общего префикса
  return a.length === b.length && timingSafeEqual(a, b);
}
import hmac, hashlib

def verify(secret: str, raw_body: bytes, timestamp: str, signature: str) -> bool:
    expected = hmac.new(
        secret.encode(), f"{timestamp}.".encode() + raw_body, hashlib.sha256
    ).hexdigest()
    return hmac.compare_digest(expected, signature)

Проверяйте и возраст: слишком старый X-Priem-Timestamp — повтор перехваченного запроса. Пять минут разумны.

Смена секрета

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

Недоплата

Клиент может прислать чуть меньше — из-за скачка курса или комиссии кошелька. В настройках проекта задан допуск: насколько меньше вы согласны принять.

Допуск по умолчанию — 1%. Учтите, что доплата стоит клиенту ещё одной комиссии сети: при недоплате на 0.09 USDT в TRON с него попросят почти 0.9. Поэтому слишком узкий допуск оборачивается не доплатами, а брошенными заказами.

Деньги

Комиссия удерживается при зачислении и видна в creditedAmount.

Выводhttps://app.priem.io/payouts. Сначала заведите адрес; выводить на него можно через сутки после добавления. Отсрочка неприятна ровно один раз, а защищает от того, кто получил доступ к вашему кабинету и хочет увести деньги на свой кошелёк немедленно.

Комиссию сети платите вы: она списывается сверх суммы, которую получит адресат.

Сеть у адреса указывается отдельно и обязательна. USDT в TRON и USDT в BSC — разные адреса, и отправка не в ту сеть теряет деньги безвозвратно.

Что стоит проверить до боевого запуска