Операции

Сверка криптоплатежей для продавца

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

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

Что вы сверяете

Три набора записей должны сходиться:

  1. Ваши заказы: что вы продали, в долларах США, и какие считаете оплаченными.
  2. Ваш журнал webhook: уведомления, которые прислал Qvard, и что вы сделали с каждым.
  3. Кабинет Qvard: платежи и баланс так, как их видит шлюз.

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

Что записывать, чтобы сверка была возможна

Сверка проста, если данные есть, и мучительна, если их нет.

  • Используйте свой номер заказа как id платежа. Он уходит в Qvard при создании платежа и возвращается в webhook, то есть это ключ, склеивающий всё.
  • Логируйте каждый webhook: id, status, время и ваше решение: применено, повтор, неизвестный заказ или ключ отклонён. Никогда не логируйте секретный ключ.
  • Храните стоимость заказа в долларах на момент заказа и время, когда пометили его оплаченным.
  • Сохраняйте id платежа Qvard из ответа, если он нужен для поддержки.

Помечайте заказ оплаченным только из webhook, который Qvard отправляет, когда платёж завершён, и делайте обработчик идемпотентным. Подробности в материале как защитить webhook криптоплатежей.

Ежедневные проверки

Три небольших запроса ловят почти всё; в примере они показаны.

  1. Неоплаченные заказы за окном курса. Курс на странице оплаты фиксируется на один час. Заказ, всё ещё ожидающий после этого, либо брошен, либо его уведомление пропущено. Посмотрите такие платежи в кабинете.
  2. Оплаченные заказы без события «completed». Если вы пометили заказ оплаченным, а в журнале нет уведомления о завершении, что-то обошло webhook, чего быть не должно. Разберитесь, прежде чем отгружать дальше.
  3. Итог за день. Сравните число и сумму в долларах заказов, помеченных оплаченными, с платежами, которые кабинет показывает за тот же день.

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

SQLreconciliation.sql
-- 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 получает ошибку.

Похожие статьи

Похожие статьи