Операции возврата Cloud
Mockarty Cloud может вернуть всю сумму или часть подтверждённого платежа, не выдумывая успешный результат. Возврат резервируется до вызова провайдера и привязывается к точным счёту, заказу, платежу, Space, аккаунту провайдера, версии коннектора, валюте, сумме и ключу идемпотентности. Лицензия или entitlement меняется только после подтверждённого провайдером завершения возврата. У покупки Team, оплаченной бонусами, нет платежа у провайдера: для неё возможен точный полный возврат в исходные промо-лоты, каждый с прежним сроком действия, без нового периода использования.
Отмена вместо возврата
Кнопки возврата в кабинете нет. Чтобы перестать платить, отмените тариф: он работает до конца уже оплаченного периода, а затем переходит на бесплатный. Неиспользованная часть периода на карту не возвращается.
Если деньги списаны по ошибке или с платежом что-то пошло не так, напишите в поддержку. Поддержка проверит платёж и может его вернуть; ниже описано, как проходит такой возврат.
Пока возврат обрабатывается, счёт показывает его в обработке, пока платёжный провайдер не подтвердит результат. Для текущего периода Team Mockarty сначала переводит общее Пространство в режим чтения и ждёт подтверждения от общего сервера; если он недоступен, возврат остаётся в ожидании, а деньги на карте или бонусном балансе не меняются. Однозначный отказ провайдера оставляет счёт как был; обычный доступ к ещё оплаченному периоду Team возвращается после подтверждения восстановления общим сервером. Возможности провайдеров различаются: частичный возврат, который провайдер не умеет, отклоняется и никогда не превращается незаметно в полный.
Дублирующий платёж возвращается автоматически
Если провайдер подтверждает платёж с опозданием — когда Mockarty уже закрыл этот checkout, а тот же период подписки оплачен другим платежом, — поздний платёж считается дублем. Он никогда не применяется второй раз: план остаётся за платежом, который его активировал, заказ дубля переходит в refunded, его счёт остаётся аннулированным, а вся сумма возвращается на исходный способ оплаты с причиной duplicate_settlement. Оператор не нужен; возврат виден в истории счетов как обычный и завершается, когда провайдер его подтвердит.
Возврат дубля не задерживается удержанием расходов. Если собственные попытки оплаты аккаунта подняли удержание в разделе Проверки и обращения, новые платежи ждут проверки, но деньги, которые система возвращает, уходят независимо от него — удержание защищает от трат, а не от возврата лишнего списания.
Восстановление неоднозначного платежа
Если checkout или продление пересекли границу провайдера, но Mockarty не может доказать итог, поддержка видит платёжный инцидент operator_required. Отдельный операторский токен с точным scope operator:commerce:write читает только редактированные инциденты:
curl -fsS 'https://cloud.mockarty.ru/api/v1/cloud/operator/payment-incidents' \
-H "Authorization: Bearer $MOCKARTY_TOKEN"
Проверьте платёж в кабинете провайдера и используйте возвращённые operation ID и generation. Допустимы только два решения:
reject: authoritative-данные провайдера доказывают, что списание не создано.retry: закреплённый провайдер поддерживает exact replay или authoritative readback этого платежа.
Ручного действия succeeded нет. Retry возвращает ту же замороженную операцию в ограниченный provider recovery; активировать или продлить подписку могут только аутентифицированные данные провайдера. Reject атомарно отменяет замороженный заказ, аннулирует открытый счёт и освобождает связанную risk-резервацию.
curl -fsS -X POST \
'https://cloud.mockarty.ru/api/v1/cloud/operator/payments/PAYMENT_OPERATION_ID/resolve' \
-H "Authorization: Bearer $MOCKARTY_TOKEN" \
-H 'Content-Type: application/json' \
-H 'Idempotency-Key: payment-resolution:SUP-1042:g7' \
--data '{"action":"retry","reason_code":"provider_readback_confirmed","generation":7}'
После потери ответа повторите тот же ключ идемпотентности. 409 stale_generation означает, что инцидент изменился и его нужно прочитать заново. 409 payment_recovery_unsupported означает, что закреплённый провайдер не умеет безопасно восстановить этот точный эффект; не создавайте новый платёж для обхода ограничения.
Разрешение операторского инцидента
Если автоматический recovery не может доказать итог, поддержка получает refund-инцидент operator_required. Используйте отдельный API-токен Cloud operator с точным scope operator:commerce:write. Операторские маршруты исключены из автоматически генерируемых MCP tools.
Тот же scope разрешает прочитать редактированный список и разрешить инцидент. Начните с команды:
mockarty-cli --server 'https://cloud.mockarty.ru' --token "$MOCKARTY_TOKEN" cloud-refunds list
Команда читает массив refunds из ответа отдельного refund-only маршрута GET /api/v1/cloud/operator/refunds. Тот же токен не может читать более широкий межтенантный платёжный журнал. Проверьте запись провайдера и возвращённую generation, затем выберите только одно действие:
reject: данные провайдера доказывают, что возврат не применён; Mockarty освобождает зарезервированную risk-ёмкость.retry: закреплённый провайдер поддерживает authoritative readback или exact replay; Mockarty снова открывает ограниченный recovery.
Оператор не может вручную указать успешный денежный результат.
mockarty-cli --server 'https://cloud.mockarty.ru' --token "$MOCKARTY_TOKEN" \
cloud-refunds resolve REFUND_OPERATION_ID \
--action retry \
--generation 4 \
--reason-code provider_readback_confirmed \
--idempotency-key refund-resolution:SUP-1042:g4
Ключ идемпотентности обозначает точное решение и после потери ответа повторяется без изменений. Для другой action, причины или generation нужен новый ключ. 409 stale_generation означает, что инцидент уже изменил другой worker или оператор: прочитайте его заново. 409 refund_resolution_conflict означает противоречие durable provider- или risk-authority. У каждой строки инцидента есть provider_status — последнее слово провайдера: succeeded значит, что деньги уже ушли и повтор догонит ledger; canceled — отклонить. Отклонённый возврат отправляет клиенту письмо «Возврат не удался» с просьбой написать в поддержку; отклонённый платёж — «Оплата не прошла» для разовой покупки или «Не удалось продлить план» для автопродления.
Эквиваленты в SDK: CloudRefunds().ListRefunds / ResolveRefund для Go, client.cloud_refunds.list_refunds / resolve_refund для Python и client.cloudRefunds().listRefunds / resolveRefund для Java.
Успешное завершение также создаёт неизменяемый intent фискальной коррекции. Доставка в настроенный фискальный провайдер асинхронна; наличие intent ещё не доказывает, что чек коррекции доставлен.
Разрешение инцидента с фискальным чеком
Чек, который касса не смогла выпустить или подтвердить, ждёт оператора в Денежных инцидентах строкой Чек (а также GET /api/v1/cloud/operator/fiscal-intents, деталь строки — /fiscal-intents/{id}). Деньги уже прошли; чек — это долг по 54-ФЗ, а не сбой оплаты. У каждой строки есть reason, консоль показывает ту же подпись:
reason |
Что случилось | Что делать |
|---|---|---|
terminal_readback |
чек выпущен, но провайдер не подтверждает его | проверьте чек у провайдера и приложите подтверждённый чек |
retry_exhausted, provider_rejected, receipt_status, dispatch_ambiguous, claim_authority |
выпуск раз за разом падал или исход неизвестен | переиздайте чек, убедившись, что он не был выпущен |
contact_unavailable, connector_unavailable, route_unresolved, authority_unavailable, request_sealing_failed |
чек не удалось даже подготовить (нет контакта плательщика, кассового коннектора, фискального маршрута) | устраните причину (коннектор, маршрут продавца) и перепривяжите полномочие |
Каждое решение подтверждается step-up (fiscal_operator_resolution), помечается Idempotency-Key и записывается против generation строки:
# чек есть у провайдера — приложить
curl -X POST https://cloud.example.com/api/v1/cloud/operator/fiscal-intents/INTENT_ID/resolve \
-H "Authorization: Bearer $OPERATOR_TOKEN" -H "Idempotency-Key: fiscal-attach-INTENT_ID" \
--data '{"action":"attach_verified_receipt","reason_code":"receipt_recovered","evidence_kind":"provider_readback","evidence_digest":"<sha256 ответа провайдера>","receipt_reference":"<uuid чека у провайдера>","generation":13}'
# чек не выпускался — выпустить заново
curl ... --data '{"action":"reissue_receipt","reason_code":"receipt_reissued","evidence_kind":"operator_attestation","evidence_digest":"<sha256 вашего подтверждения>","generation":13}'
# чек не удалось подготовить — подготовить заново после устранения причины
curl ... --data '{"action":"rebind_authority","reason_code":"authority_restored","evidence_kind":"operator_attestation","evidence_digest":"<sha256 вашего подтверждения>","generation":0}'
Устаревший generation отвечает 409 fiscal_resolution_conflict; перепривязка строки без блокировки полномочия или переиздание заблокированной отвечают тем же 409 и называют состояние, которому служит решение. Каждое решение открывает воркеру новый бюджет попыток и свежий дедлайн в 24 часа. Когда чек подтверждён, клиент получает его письмом.
Повтор неудавшегося автопродления
Когда сохранённый способ оплаты отвергнут окончательно, цикл продления завершается как failed. Деньги не списываются; платные возможности доступны только до оплаченной даты, а не до конца льготного периода для оплаты. Клиент получает письмо «Не удалось продлить план» (и такое же уведомление в кабинете) с этой датой и просьбой обновить способ оплаты. Если оплаченная дата прошла без продления, отдельное уведомление сообщает срок окончания льготного периода. Цикл попадает в очередь и как operator required — когда списание удержала служба рисков или когда его резерв риска больше нельзя возобновить. В любом случае цикл остаётся виден операторам в Денежных инцидентах строкой Автопродление (а также GET /api/v1/cloud/operator/renewal-incidents).
У каждой строки есть reason; консоль показывает ту же подпись рядом с провайдером:
reason |
Что случилось |
|---|---|
provider_refused |
сохранённый способ оплаты отклонил списание |
provider_unanswered |
провайдер так и не подтвердил и не отклонил списание |
risk_held |
проверка рисков удержала списание для оператора |
risk_reservation_dead |
резерв риска этой попытки нельзя возобновить; повтор откроет новый |
provider_not_recurring |
закреплённый провайдер не умеет списывать с сохранённого способа |
Повторить возвращает цикл в расписание для новой попытки со своим резервом риска. Действие принимается, только если связанный платёжный инцидент закрыт, подписка активна и не отменена, а поколение строки совпадает с загруженным — иначе консоль отвечает 409 и просит перезагрузить. Цикл, чей период уже оплатил более поздний цикл, повторить нельзя вовсе: API отвечает 409 renewal_period_superseded, а ближайший свип закрывает цикл. Дальше воркер продлений спишет сохранённый способ снова в ближайшем цикле.
curl -X POST https://cloud.example.com/api/v1/cloud/operator/renewals/RENEWAL_ID/retry \
-H "Authorization: Bearer $OPERATOR_TOKEN" -H "Idempotency-Key: renewal-retry-20260905-1" \
-H "Content-Type: application/json" \
-d '{"generation": 4, "reason_code": "provider_recovery_retry"}'
Операторский MCP отдаёт ту же очередь инструментом cloud_renewal_incidents_list.
Суточная сверка коммерческих операций
Откройте Консоль оператора → Сверка коммерческих операций, чтобы сопоставить три независимых журнала: authoritative readback платёжного провайдера, доставку фискального чека и бухгалтерскую проекцию Mockarty. Запуск остаётся в состоянии Проверка у провайдеров, пока все запланированные readback не получат терминальный результат. Частичные данные провайдера означают, что хотя бы одного провайдера проверить не удалось; такой запуск нельзя считать чистым.
Расхождения содержат стабильные ссылки на сущности, суммы, валюты и односторонние дайджесты доказательств. В них нет личности клиента, секретов коннектора, тела фискального запроса и сырого ответа провайдера. Открытое расхождение закрывается автоматически только после следующей сверки, доказавшей устранение проблемы. Оператор не может вручную объявить оплату или возврат успешными с этого экрана.
Для автоматической проверки без изменений данных оператор Cloud может вызвать GET /api/v1/cloud/operator/commerce/reconciliation/runs и GET /api/v1/cloud/operator/commerce/reconciliation/findings. Допустимые значения status: open и resolved; severity: warning и critical; limit: от 1 до 500. Нужны права оператора Cloud; в пользовательских SDK, CLI и MCP-инструментах этих запросов нет.