Реорганизация блокчейна, или реорг, — это момент, когда сеть заменяет свои последние блоки другой веткой. Перевод, который уже был виден в блоке, после такого отката блоков может исчезнуть или оказаться в другом блоке. Поэтому 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 спускается вниз до последнего блока, общего для обеих веток.
Дальше шаги всегда одни и те же:
- Все переводы на ваши адреса в блоках выше общего помечаются как осиротевшие (orphaned), и для леджера публикуется событие об этом.
- CryptoLike читает новую ветку от общего блока вверх, по одному блоку.
- Перевод, который снова появился в новой ветке, обнаруживается заново и считает подтверждения от своего нового блока; получатель и сумма берутся из нового блока.
- Записи, которые выглядят подтверждёнными, перед подсчётом сверяются с сохранённой цепочкой заголовков блоков, поэтому подтверждению из заменённого блока не доверяют.
У автоматической обработки есть предел. 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 нет: статусы и вебхуки, описанные выше, работают для каждого платежа и статического адреса. С вашей стороны нужен обработчик вебхуков.
- В кабинете откройте «Вебхуки», добавьте эндпоинт и подпишите его на
invoice.paid,invoice.reverted,invoice.late_payment,deposit.confirmedиdeposit.revertedили на все события (*). - Пусть обработчик проверяет подпись, сохраняет
event_id, аinvoice.paidпереводит заказ в «готов к отгрузке»,invoice.reverted— в «ожидание». - Проверьте сценарий в тестовых сетях с тестовым ключом — шаги есть в руководстве по тестовым криптоплатежам, — а список событий держите под рукой в справочнике по вебхукам.
Вопросы и ответы
Что такое реорг в блокчейне?
Реорг, или реорганизация блокчейна, — замена сетью последних блоков конкурирующей веткой. Переводы из отброшенных блоков могут исчезнуть или снова попасть в один из следующих блоков. Чем глубже блок, тем меньше шанс его замены, поэтому платёж засчитывается только после заданного числа подтверждений.
Может ли оплаченный криптоплатёж откатиться?
Со стороны сети — только если реорг отбросит блок с уже подтверждённым переводом. Тогда 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, — потому что сети по-разному выпускают блоки и по-разному делают их окончательными.