Сверка криптоплатежей для продавца
Как сверять криптоплатежи с заказами и балансом: что логировать, ежедневные проверки, как находить пропущенные webhook и неполные платежи и как учитывать комиссии и выводы.

Сверка отвечает на один вопрос: каждому ли заказу, который вы считаете оплаченным, соответствует реально пришедший платёж и каждому ли пришедшему платежу соответствует заказ. С криптой это нетрудно, потому что каждый платёж несёт ваш номер заказа, но работает только если вы логируете нужное и проверяете по расписанию. В материале показано, что записывать, какие проверки запускать и как объяснять найденные расхождения.
Что вы сверяете
Три набора записей должны сходиться:
- Ваши заказы: что вы продали, в долларах США, и какие считаете оплаченными.
- Ваш журнал webhook: уведомления, которые прислал Qvard, и что вы сделали с каждым.
- Кабинет Qvard: платежи и баланс так, как их видит шлюз.
В публичном API нет маршрута для списка или чтения платежей, поэтому сторону шлюза нельзя вытянуть скриптом. Вы сверяете между собой заказы и собственный журнал webhook, а затем сравниваете итоги с тем, что показывает кабинет.
Что записывать, чтобы сверка была возможна
Сверка проста, если данные есть, и мучительна, если их нет.
- Используйте свой номер заказа как id платежа. Он уходит в Qvard при создании платежа и возвращается в webhook, то есть это ключ, склеивающий всё.
- Логируйте каждый webhook:
id,status, время и ваше решение: применено, повтор, неизвестный заказ или ключ отклонён. Никогда не логируйте секретный ключ. - Храните стоимость заказа в долларах на момент заказа и время, когда пометили его оплаченным.
- Сохраняйте id платежа Qvard из ответа, если он нужен для поддержки.
Помечайте заказ оплаченным только из webhook, который Qvard отправляет, когда платёж завершён, и делайте обработчик идемпотентным. Подробности в материале как защитить webhook криптоплатежей.
Ежедневные проверки
Три небольших запроса ловят почти всё; в примере они показаны.
- Неоплаченные заказы за окном курса. Курс на странице оплаты фиксируется на один час. Заказ, всё ещё ожидающий после этого, либо брошен, либо его уведомление пропущено. Посмотрите такие платежи в кабинете.
- Оплаченные заказы без события «completed». Если вы пометили заказ оплаченным, а в журнале нет уведомления о завершении, что-то обошло webhook, чего быть не должно. Разберитесь, прежде чем отгружать дальше.
- Итог за день. Сравните число и сумму в долларах заказов, помеченных оплаченными, с платежами, которые кабинет показывает за тот же день.
Когда кабинет показывает платёж завершённым, а ваш заказ всё ещё не оплачен, уведомление пропущено: отправьте его повторно из кабинета и дайте идемпотентному обработчику применить.
-- Your side: every webhook you receive is logged with your decision.
CREATE TABLE qvard_events (
id BIGSERIAL PRIMARY KEY,
order_id TEXT NOT NULL, -- the id you sent to Qvard
status TEXT NOT NULL, -- completed, ...
decision TEXT NOT NULL, -- applied | repeat | unknown_order | rejected_key
received_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- 1. Orders still unpaid after the rate window: candidates for a missed notification.
SELECT id, amount_usd, created_at
FROM orders
WHERE state = 'awaiting_payment'
AND created_at < now() - interval '1 hour'
ORDER BY created_at;
-- 2. Orders you consider paid, with no logged "completed" event: investigate before fulfilling more.
SELECT o.id, o.amount_usd, o.paid_at
FROM orders o
LEFT JOIN qvard_events e ON e.order_id = o.id AND e.status = 'completed'
WHERE o.state = 'paid' AND e.id IS NULL;
-- 3. The daily total to compare with the Qvard cabinet.
SELECT date_trunc('day', paid_at) AS day, count(*) AS orders, sum(amount_usd) AS usd
FROM orders
WHERE state = 'paid'
GROUP BY 1
ORDER BY 1 DESC;Как объяснять найденные расхождения
У большинства разрывов короткий список причин.
- Неполный платёж. Пришло меньше полной суммы, поэтому у платежа статус
partialи webhook о завершении не отправлялся. Заказ не оплачен; попросите доплатить либо верните деньги. См. недоплата в USDT: что делать. - Пропущенное уведомление. Сервер лежал дольше окна повторов (пять попыток примерно за шесть минут) или ответил не ровно HTTP 200. См. повторы webhook в платёжных системах.
- Дубликат. То же событие пришло дважды. Правильный обработчик записывает его как повтор и ничего не меняет.
- Неизвестный заказ. Уведомление с id, которого у вас нет. Проверьте опечатки в отправляемом id и платежи, созданные из другой системы с тем же ключом.
- Возврат. Возврат это перевод, который вы отправляете сами; записывайте его на исходный заказ, чтобы итоги по-прежнему объясняли себя.
Комиссии, балансы и выводы
Долларовая стоимость заказов не то же самое, что деньги в кошельке.
- Комиссия. Абонентской платы нет; комиссия индивидуальна для проекта и по странице тарифов начинается с 1%, а может быть снижена до 0,1%. Записывайте её отдельной строкой.
- Сетевые комиссии. Сетевую комиссию перевода платит клиент. Вы платите только сетевую комиссию, когда выводите на свой кошелёк.
- Выводы. Записывайте каждый с датой, сетью и комиссией. Вывода на банковский счёт в фиате нет; если вы конвертируете в наличные, этот шаг происходит вне Qvard и имеет собственные записи.
- Монета и сеть. Платежи в разных сетях накапливаются отдельно. Проверяйте балансы по активу и сети, как в материале сетевые комиссии в криптоплатежах.
Держите в курсе и бухгалтера: режим криптопоступлений для налогов и учёта зависит от вашей страны.
FAQ
Можно ли получить платежи через API для сверки?
В публичном API нет маршрута для списка или чтения платежей. Сверяйте заказы и собственный журнал webhook, а итоги сравнивайте с платежами и балансом в кабинете Qvard.
Как сопоставить платёж с заказом?
По id. Вы передаёте номер своего заказа при создании платежа, и тот же id возвращается в webhook.
Что если кабинет показывает завершённый платёж, а заказ не оплачен?
Уведомление пропущено. Отправьте его повторно из кабинета; идемпотентный обработчик применит его безопасно.
Как часто сверяться?
Ежедневно разумно по умолчанию, плюс оповещение, когда серия запросов webhook получает ошибку.