Надёжность интеграции
Платёжная интеграция работает поверх сети, которая теряет пакеты, и очередей, которые доставляют сообщения дважды. Эта страница — про то, как построить обработку так, чтобы ни одна из этих особенностей не превратилась в потерянный или задвоенный платёж.
Идемпотентность создания сделок
Заголовок раздела «Идемпотентность создания сделок»Ключ идемпотентности — ваш merchantTransactionId. Он должен быть уникален в рамках кабинета.
Если сделка с таким идентификатором уже есть, повторный запрос вернёт ORDER_WITH_SPECIFIED_MERCHANT_TRANSACTION_ID_ALREADY_EXISTS, а не создаст вторую. Это не ошибка вашей логики — это защита, и обрабатывать её нужно как «сделка уже создана».
POST /payment/pay-in { merchantTransactionId: "order-1001" } → сделка созданаPOST /payment/pay-in { merchantTransactionId: "order-1001" } → ORDER_..._ALREADY_EXISTSЧто делать при тайм-ауте запроса
Заголовок раздела «Что делать при тайм-ауте запроса»Самый опасный случай: вы отправили запрос на создание, но ответа не получили. Сделка могла быть создана, а могла не быть.
Правильный порядок:
-
Не повторять запрос вслепую.
-
Запросить сделку по вашему идентификатору:
GET /payment/merchant/{merchantTransactionId} -
Если сделка вернулась — работать с ней, запрос удался.
-
Если пришло
TRADE_NOT_FOUND— повторить создание с тем жеmerchantTransactionId.
Тот же порядок применим к отмене и к любым другим изменяющим запросам.
Обработка webhook
Заголовок раздела «Обработка webhook»Уведомления доставляются как минимум один раз. Практические следствия:
Одно и то же уведомление придёт повторно. Например, если ваш сервер ответил 200, но соединение оборвалось до того, как ответ дошёл до нас. Обработчик должен быть безопасен при повторном вызове.
Уведомления могут прийти не по порядку. Сначала success, потом active — нормальная ситуация при ретраях. Не выстраивайте логику на последовательности; смотрите на текущий статус.
Уведомление может прийти после того, как вы уже узнали результат из GET. Это не повод считать его лишним — обработайте так же, как первое.
Рабочая схема обработчика:
1. Прочитать raw body2. Проверить x-signature → не сошлось: 401, выход3. Ответить 200 → сразу, до бизнес-логики4. Положить событие в очередь5. В фоне: - взять блокировку по id сделки - если сохранённый статус уже финальный — ничего не делать - иначе применить новый статусТайм-ауты и очереди
Заголовок раздела «Тайм-ауты и очереди»| Что | Значение |
|---|---|
| Тайм-аут на ваш ответ по webhook | 10 секунд |
| Успешный ответ | 200 или 201 |
| Попыток доставки | 6 |
| Паузы между попытками | ~5 с, ~30 с, ~2 мин, ~10 мин, ~30 мин, ~2 ч, с разбросом ±20 % |
Если обработка не укладывается в 10 секунд, отвечайте 200 сразу и обрабатывайте асинхронно. Иначе доставка будет засчитана как неудачная, и вы получите то же уведомление ещё раз — уже параллельно первой обработке.
После шести неудачных попыток доставка прекращается. Восстановить её можно только через поддержку, поэтому надёжнее иметь собственную сверку.
Webhook — основной канал, но не единственный источник истины. Раз в сутки полезно сверять состояние сделок за период.
GET /payment?begin=2026-08-22T00:00:00.000Z&end=2026-08-23T00:00:00.000Z&blockIndex=1&blockCapacity=100| Параметр | Описание |
|---|---|
begin, end | Границы периода, ISO 8601 |
status | Фильтр по статусу |
blockIndex | Номер страницы, с 1 |
blockCapacity | Размер страницы, по умолчанию 10 |
sortKey, sortDirection | Поле и направление сортировки, ASC или DESC |
Ответ содержит массив result и блок elements с метаданными пагинации: aggregateVolume — всего записей, segmentCount — всего страниц, activeSegment — текущая.
Сверяйте по трём признакам: у сделки, которая у нас финальная, должен быть финальный статус и у вас; суммы должны совпадать (с учётом success_changed); сделок, которых нет у вас, быть не должно.
Работа со временем
Заголовок раздела «Работа со временем»createdAtприходит в UTC в формате ISO 8601 с миллисекундами.paymentLifetime— в минутах, отсчёт отcreatedAt.- Не полагайтесь на локальное время сервера при вычислении момента истечения: считайте от
createdAt. - Часовой пояс, выбранный в кабинете, влияет только на отображение и выгрузки, но не на значения в API.
Работа с деньгами
Заголовок раздела «Работа с деньгами»cryptoиfeeприходят строками, в минимальных единицах USDT. Делите на1 000 000.amount— фиатная сумма. Приsuccess_changedона отличается от той, что вы отправляли при создании.- Не приводите денежные значения к числам с плавающей точкой в промежуточных расчётах: используйте decimal или целые числа минимальных единиц.
Типовые ошибки
Заголовок раздела «Типовые ошибки»| Ошибка | К чему приводит |
|---|---|
Ретрай создания с новым merchantTransactionId | Двойная сделка, для Pay-Out — двойная выплата |
Обработка success без обработки success_changed | Часть успешных платежей зависает необработанной |
Отгрузка на статусе checking или active | Отгрузка по неподтверждённому платежу |
| Пересборка JSON перед проверкой подписи | Подпись не сходится никогда |
| Тяжёлая логика внутри обработчика webhook | Тайм-аут 10 с, лавина повторных доставок |
| Отсутствие блокировки по сделке | Гонка при параллельной доставке одного события |
| Доверие порядку уведомлений | Финальный статус перезаписывается промежуточным |
Чек-лист перед запуском
Заголовок раздела «Чек-лист перед запуском»-
merchantTransactionIdуникален и стабилен при ретраях - Проверка
x-signatureвыполняется на raw body до бизнес-логики - Обработчик webhook отвечает
200быстрее 10 секунд - Повторная доставка одного события не меняет результат
-
success_changedобрабатывается наравне сsuccess - При тайм-ауте выполняется поиск сделки, а не повторное создание
- Финальные статусы не перезаписываются промежуточными
- Настроена ежедневная сверка через
GET /payment - Все IP-адреса ваших серверов согласованы и внесены в whitelist
- API-ключ хранится только на сервере
Что дальше
Заголовок раздела «Что дальше»- Webhook — формат уведомлений и требования к endpoint
- Коды ошибок — что повторять, а что нет
- Статусы сделок — какие статусы считать финальными