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

Разработчикам

Почему «обнаружен» ≠ «оплачен»: реорганизация блокчейна и откат блоков

Что такое реорганизация блокчейна (реорг), почему обнаруженный платёж ещё не оплачен и что делает CryptoLike при откате блока с платежом: статусы, леджер, вебхуки.

Главное

  • Реорг — замена последних блоков цепочки другой веткой; перевод, который вы уже видели, может исчезнуть или попасть в другой блок.
  • CryptoLike держит обнаруженный перевод на балансе pending и зачисляет его только по достижении порога подтверждений актива; откат до порога не трогает доступный баланс.
  • Перевод, откатившийся до порога, возвращает платёж в прежний статус без вебхука по инвойсу; откат подтверждённого перевода сторнируется в леджере, и приходит invoice.reverted или deposit.reverted.
  • Отгружайте по paid, останавливайтесь на reverted и обрабатывайте каждый вебхук один раз по event_id. Если перевод снова попадёт в блок, придёт invoice.paid, пока инвойс открыт, или invoice.late_payment, если он уже истёк.

Реорганизация блокчейна, или реорг, — это момент, когда сеть заменяет свои последние блоки другой веткой. Перевод, который уже был виден в блоке, после такого отката блоков может исчезнуть или оказаться в другом блоке. Поэтому CryptoLike ставит платёж в pending, как только перевод обнаружен, а в paid — только после подтверждений, заданных для актива. Ниже — как устроен реорг, что CryptoLike делает, если блок с вашим платежом откатился до порога или после него, и как написать интеграцию, которая это переживёт.

Что такое реорганизация блокчейна?

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

Конкурирующие ветки возникают потому, что блоки создают много независимых участников. В Bitcoin и Litecoin два майнера могут найти блок почти одновременно, и узлы идут за тем, который увидели первым, пока одна из веток не станет длиннее. Ethereum и TRON выпускают блоки по расписанию, но и их последние блоки могут быть заменены, пока сеть не считает их окончательными. В любой сети чем глубже блок, тем меньше шанс, что его заменят. Короткое определение — в глоссарии: реорганизация блоков.

Почему «обнаружен» — ещё не «оплачен»?

«Обнаружен» значит, что CryptoLike увидел перевод в блоке. «Оплачен» — что перевод набрал порог подтверждений актива и деньги на вашем балансе. Между этими моментами реорг ещё может убрать перевод, поэтому CryptoLike разделяет эти стадии везде, где вы их видите.

Стадия Статус платежа Что видит плательщик Вебхук Ваш баланс
Перевод виден в блоке pending «Платёж обнаружен. Ожидаем подтверждения.» invoice.pending или deposit.pending Вся сумма в pending, вывести её нельзя
Порог набран paid (или underpaid, если суммы не хватает) «Платёж подтверждён.» invoice.paid или deposit.confirmed Сумма за вычетом комиссии платформы в available
Подтверждённый перевод откатился Пересчитан: paid, underpaid, pending или new Страница обновляется сама invoice.reverted или deposit.reverted Зачисление и комиссия сторнированы

Переводы, которые ещё не попали в блок, CryptoLike не читает, поэтому pending уже означает «в блоке». Страница оплаты показывает и detected_amount — сумму переводов, которые видны, но не подтверждены; в документации API прямо сказано, что она не зачислена и может исчезнуть при реорганизации. Сколько подтверждений нужно каждому активу и почему числа в сетях разные, разобрано в статье сколько подтверждений нужно криптоплатежу; текущие значения — в GET /v1/currencies и на страницах монет, например Bitcoin.

Как CryptoLike замечает реорг?

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

Дальше шаги всегда одни и те же:

  1. Все переводы на ваши адреса в блоках выше общего помечаются как осиротевшие (orphaned), и для леджера публикуется событие об этом.
  2. CryptoLike читает новую ветку от общего блока вверх, по одному блоку.
  3. Перевод, который снова появился в новой ветке, обнаруживается заново и считает подтверждения от своего нового блока; получатель и сумма берутся из нового блока.
  4. Записи, которые выглядят подтверждёнными, перед подсчётом сверяются с сохранённой цепочкой заголовков блоков, поэтому подтверждению из заменённого блока не доверяют.

У автоматической обработки есть предел. CryptoLike хранит окно последних блоков каждой сети: удвоенный самый строгий порог подтверждений среди её активов, но не меньше 8 блоков. Реорганизация глубже этого окна останавливает обработку этой сети, пока её не проверит команда CryptoLike: из ветки, которую нельзя проверить, ничего не зачисляется и не сторнируется. Платежи в этой сети на это время ждут; те, у которых истёк срок оплаты, как обычно закрываются как expired.

Что происходит с платежом, если его блок откатился?

Действия CryptoLike зависят от того, как далеко платёж успел продвинуться. Правило одно для всех четырёх сетей — Bitcoin, Litecoin, Ethereum и TRON; различается только порог подтверждений актива.

Ситуация Статус Леджер Вебхук
Инвойс, перевод ещё ниже порога Снова new или остаётся pending, если на адрес ждёт другой перевод Сумма списывается из pending Нет; событие записано в истории платежа в кабинете
Инвойс в paid или underpaid, откатился подтверждённый перевод Пересчитан по тому, что ещё подтверждено: paid, underpaid, pending или new Сторнированы подтверждение, комиссия и зачисление invoice.reverted с новыми paid_amount и remaining_amount
Статический адрес, любая стадия В разделе «Транзакции» перевод в статусе «Отменена сетью» Сторнируется всё, что было проведено deposit.reverted; confirmed_reversed: true, если сумма уже была доступна
Перевод снова попал в новую ветку Открытый инвойс: pending, затем paid по достижении порога. Если срок инвойса истечёт раньше, он закрывается как expired и так и остаётся Зачисляется заново Открытый инвойс — invoice.paid, истёкший — invoice.late_payment, статический адрес — deposit.confirmed; у каждого новый event_id
Подтверждённая сумма уже выведена Статус инвойса не меняется до разбора Автоматического сторно нет; баланс не уходит в минус Нет; случай разбирает команда CryptoLike

Две детали. Если откатился только излишек и инвойс остался paid, invoice.reverted не приходит: то, на что вы опирались, не изменилось. А на закрытом инвойсе — истёкшем или отменённом — откат уже подтверждённого позднего платежа всё равно даёт invoice.reverted, хотя статус остаётся прежним.

Что записывается в леджер?

Леджер никогда не редактирует и не удаляет записи: реорг проводится новыми записями сторно, зеркальными исходным. CryptoLike сторнирует в порядке, обратном зачислению: сначала подтверждение (available обратно в pending, комиссия платформы возвращается), затем само зачисление (pending обратно за пределы платформы).

Так учёт остаётся проверяемым. Каждая проводка сбалансирована, зачисление и его сторно лежат рядом со ссылкой на один и тот же перевод, а счёт по-прежнему сходится: «зачислено − сторно − выплаты − баланс = 0». Если перевод снова попадёт в блок, он зачисляется под следующим поколением того же ключа, поэтому повтор старого события не зачислит его дважды.

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

Как интеграции обрабатывать реорг?

Считайте каждый вебхук описанием текущего состояния, а не шагом в одну сторону. Если откатившийся перевод снова попадёт в блок, открытый инвойс пройдёт paid → reverted → paid. Но часто срок к этому моменту уже вышел: подтверждения отсчитываются заново от нового блока, а инвойс по умолчанию живёт 30 минут. Тогда инвойс закрывается как expired, а вернувшиеся деньги зачисляются и приходят событием invoice.late_payment. Код должен справляться с обоими вариантами без ручной помощи.

  • Отгружайте по paid, а не по pending. invoice.pending — сигнал о ходе оплаты. Если перевод откатится до порога, отдельного вебхука по инвойсу не будет: платёж может просто вернуться в new и позже истечь.
  • Обрабатывайте invoice.reverted. Если заказ ещё не отгружен, держите его в ожидании; если отгружен — отправьте на проверку. Плагины для CMS делают именно так: оплаченный заказ после reverted переходит в статус проверки. Если затем придёт invoice.late_payment, деньги снова на балансе, а статус остаётся expired: отгрузите заказ или верните деньги покупателю.
  • Применяйте каждое событие один раз по event_id. Доставка — «хотя бы один раз» и без фиксированного порядка: сохраняйте event_id, пропускайте повторы и сравнивайте created_at, а не порядок прихода. Как это сделать в коде, показано в статье об идемпотентности в платёжных API.
  • Сначала проверяйте подпись. Поддельный invoice.paid опаснее реорга — см. как проверить подпись вебхука.
  • Доверяйте суммам из события, а не своим подсчётам. Каждое событие инвойса несёт платёж целиком с текущими paid_amount и remaining_amount. В Ethereum позиция перевода токена внутри блока может измениться, когда транзакция попадёт в блок заново, поэтому собственный ключ из txid и output_index может не совпасть с новым появлением.
  • Если сомневаетесь — спросите API. GET /v1/invoices/{id} возвращает текущий статус и суммы.

Если вы ещё выбираете провайдера, вопрос о том, как он обрабатывает реорги, вполне уместен — он есть в списке из статьи как выбрать криптопроцессинг.

Как встроить обработку реорга в интеграцию

Отдельной настройки для реоргов в CryptoLike нет: статусы и вебхуки, описанные выше, работают для каждого платежа и статического адреса. С вашей стороны нужен обработчик вебхуков.

  1. В кабинете откройте «Вебхуки», добавьте эндпоинт и подпишите его на invoice.paid, invoice.reverted, invoice.late_payment, deposit.confirmed и deposit.reverted или на все события (*).
  2. Пусть обработчик проверяет подпись, сохраняет event_id, а invoice.paid переводит заказ в «готов к отгрузке», invoice.reverted — в «ожидание».
  3. Проверьте сценарий в тестовых сетях с тестовым ключом — шаги есть в руководстве по тестовым криптоплатежам, — а список событий держите под рукой в справочнике по вебхукам.

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

Что такое реорг в блокчейне?

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

Может ли оплаченный криптоплатёж откатиться?

Со стороны сети — только если реорг отбросит блок с уже подтверждённым переводом. Тогда CryptoLike сторнирует зачисление в леджере, пересчитывает платёж и отправляет invoice.reverted (или deposit.reverted для статического адреса). Если перевод снова попадёт в блок, пока инвойс открыт, придёт новый invoice.paid; если инвойс за это время истёк, статус останется expired, а вместо paid придёт invoice.late_payment. Остановите выполнение заказа на reverted и решайте заново по тому из двух событий, которое придёт.

Придёт ли вебхук, если ожидающий платёж исчез?

Для инвойса — нет. Перевод, откатившийся до порога, не был зачислен, поэтому платёж тихо возвращается в new (или остаётся pending, если ждёт другой перевод), а событие записывается в его историю. Для статического адреса deposit.reverted приходит на любой стадии.

Что будет, если деньги из откатившегося платежа уже выведены?

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

Одинаково ли CryptoLike обрабатывает реорг во всех сетях?

Да. В Bitcoin, Litecoin, Ethereum и TRON работают одна и та же проверка блок за блоком, одни и те же статусы, сторно в леджере и вебхуки. Различается порог подтверждений каждого актива, опубликованный в GET /v1/currencies, — потому что сети по-разному выпускают блоки и по-разному делают их окончательными.

Термины на странице

Читать дальше

Начните с тестового платежа

Создайте аккаунт, попросите у поддержки доступ к тестовой среде и посмотрите, как платёж проходит путь от pending до paid в тестовой сети.