Возвраты, отмены и холдирование в платежных API
Практическое руководство по проектированию и внедрению операций возврата, отмены и холдирования средств в платежных интеграциях. Разберем, как работают двухстадийные платежи, capture и partial refund, когда нужен reverse/cancel API и как избежать ошибок в продакшене.
Table of contents
- Зачем нужны возвраты, отмены и холдирование
- Базовые понятия: авторизация, capture, refund, reversal/void
- Жизненный цикл: одностадийный vs двухстадийный платеж
- Дизайн API: ресурсы, идемпотентность, вебхуки
- Частичные операции: partial refund и partial capture
- Ограничения по времени и правила провайдеров
- Безопасность: PCI DSS и 3‑D Secure
- Сверка и учет: как не потерять деньги
- Мобильные и рекуррентные сценарии
- Тестирование и отладка в песочнице
- Мониторинг, метрики и обработка ошибок
- Выбор провайдера и архитектуры
- Чек-лист внедрения
- Заключение и что дальше
Зачем нужны возвраты, отмены и холдирование
- Возврат платежа API (refund) — когда заказ отменен после списания, требуется вернуть средства полностью или частично.
- Отмена/реверс (reversal/void, или reverse/cancel API) — быстрое «разблокирование» средств до их финального списания, если передумали после авторизации.
- Холдирование средств API — безопасно «заморозить» сумму на карте клиента, чтобы гарантировать оплату позже (например, подготовка заказа, проверка наличия, динамическая доставка).
Эти операции помогают снизить риски, улучшить UX (нет мгновенного списания при сомнительных заказах) и соответствовать требованиям банков/эквайеров.
Базовые понятия: авторизация, capture, refund, reversal/void
- Авторизация (hold, preauth) — банк подтверждает, что на карте есть средства, и «замораживает» сумму. Деньги еще не у мерчанта.
- Capture (списание) — перевод «холда» в реальное списание. На счет мерчанта поступает сумма, авторизация закрывается.
- Refund — возврат списанных средств клиенту. Возможен полный или partial refund.
- Reversal/Void — отмена авторизации до capture. Средства «размораживаются», комиссия обычно ниже, а деньги возвращаются быстрее, чем при refund.
Ниже — краткое сравнение операций.
| Операция |
Когда использовать |
Деньги на счете клиента |
Результат для мерчанта |
Окно времени (типично) |
Частичность |
Типичный endpoint |
| Авторизация (Hold) |
Нужна проверка/подготовка |
Заблокированы |
Резерв средств |
Минуты–дни |
Да (по сумме) |
POST /payments/{id}/authorize |
| Capture |
Готовы отгрузить |
Списаны |
Доход зафиксирован |
Пока авторизация активна |
Да (partial capture) |
POST /payments/{id}/capture |
| Refund |
Отмена после списания |
Возвращены |
Доход уменьшен |
Месяцы (зависит от провайдера) |
Да (partial refund) |
POST /payments/{id}/refunds |
| Reversal/Void |
Отмена до списания |
Разблокированы |
Комиссия ниже |
Часы–дни (до истечения hold) |
Часто да |
POST /payments/{id}/void |
Жизненный цикл: одностадийный vs двухстадийный платеж
- Одностадийный: авторизация и списание происходят сразу. Удобно для цифровых товаров и мгновенных услуг. Если возникла отмена — используется refund.
- Двухстадийный платеж: сначала холдирование средств API, затем capture по готовности. Оптимально для логистики, ресторанов, бронирований, маркетплейсов. Если заказ отменен до capture — используйте reverse/cancel API (void). Если после — делайте refund.
Дизайн API: ресурсы, идемпотентность, вебхуки
Правильный контракт API уменьшает ошибки и повышает предсказуемость интеграции.
- Иерархия ресурсов. Возвраты и капчуры — подресурсы платежа: /payments/{payment_id}/refunds, /capture, /void. Удобно для трейсинга.
- Идемпотентность. Операции refund/capture/void должны поддерживать idempotency-key, чтобы повторные запросы (ретраи) не создавали дубли. См. рекомендации в patterns/webhooks-idempotency.
- Асинхронность. Capture/refund часто выполняются асинхронно. Возвращайте 202 Accepted и уведомляйте через вебхуки.
- Состояния. Явно описывайте статусы: authorized, captured, partially_captured, refunded, partially_refunded, voided, failed.
- Корреляция. Сохраняйте внешние идентификаторы провайдера (rrn, approval_code, acquirer_reference) для сверки. Подробнее — в analytics/reconciliation.
Пример минимального контракта (псевдо):
- POST /payments/{id}/capture { amount, idempotency_key }
- POST /payments/{id}/refunds { amount, reason, idempotency_key }
- POST /payments/{id}/void { reason, idempotency_key }
- Webhook: payment.captured, payment.refunded, payment.voided, payment.failed
Частичные операции: partial refund и partial capture
- Partial capture: списывайте меньше заблокированной суммы, если позиции частично отгружены. Оставшийся hold можно void-ить. Учитывайте, что некоторые провайдеры разрешают один capture на авторизацию, другие — множественные.
- Partial refund: возврат части суммы (например, нет одной позиции). Формируйте понятный reason и метаданные для бухгалтерии.
- Комиссия и НДС: как правило, комиссию удерживают пропорционально или не возвращают — уточняйте у провайдера.
Практические советы:
- Не делайте partial refund до финального статуса capture, иначе возможны конфликтующие состояния.
- Храните инвариант: refunded_amount <= captured_amount.
- Покрывайте пограничные случаи округления при валюте с 0/3 знаками после запятой.
Ограничения по времени и правила провайдеров
Окна жизни авторизаций и условия возвратов отличаются по эквайерам и схемам карт.
- Hold TTL: от нескольких часов до 7–14 дней. По истечении — автоматически освобождается, capture уже невозможен.
- Void до capture: обычно быстрое «размораживание», но возможно только пока hold активен.
- Refund после capture: сроки зачисления клиенту — от минут до 3–10 рабочих дней.
Смотрите специфику в разделах провайдеров: providers/yoomoney-api, providers/sberbank-acquiring-api, providers/tbank-api, providers/belarusbank-api. Различия включают поддержку двухстадийных платежей, множественного capture и правила partial refund.
Безопасность: PCI DSS и 3‑D Secure
Операции refund/void не заменяют аутентификацию клиента, но должны соблюдаться базовые меры:
- Не храните PAN и CVV. Делегируйте ввод карт SDK или хостед‑флоу. Подробнее — security/pci-3ds.
- 3DS на authorize снижает риск чарджбеков, особенно в одностадийных схемах.
- Ограничьте доступ к refund/void в админке: MFA, ролевые модели, журналирование.
- Валидация сумм и валюты на стороне сервера, серверные ключи — только с backend.
Сверка и учет: как не потерять деньги
Возвраты и отмены требуют аккуратной бухгалтерии.
- Ведите раздельный учет captured_amount, refunded_amount, fees.
- Сверяйте события вебхуков с отчетами провайдера (дневные/по транзакциям). См. analytics/reconciliation.
- Храните ссылочные идентификаторы платежной системы (RRN/ARN) для расследований и чарджбеков.
- Старайтесь завершать «подвисшие» авторизации void-ом по таймеру, если capture не планируется.
Мобильные и рекуррентные сценарии
- Мобильные SDK: для минимизации frictions используйте готовые компоненты и токенизацию. См. mobile/sdk-integration. Холд на стороне провайдера позволяет снизить отказ от оплаты при долгих потоках.
- Рекуррентные списания: при продлении подписок refund — основной инструмент корректировок, а preauth помогает перед авторизацией крупного продления. См. patterns/recurring-subscriptions.
Тестирование и отладка в песочнице
- Используйте тестовые карты и сценарии: успешный capture, отказ capture, partial refund, void просроченной авторизации.
- Валидируйте идемпотентность: 10 повторов одного запроса не должны создавать дубликаты.
- Проверяйте вебхуки на сетевые сбои и ретраи с подписью. См. testing/sandbox и patterns/webhooks-idempotency.
Мониторинг, метрики и обработка ошибок
Ключевые показатели:
- Доля двухстадийных платежей с успешным capture.
- Процент refund к объему продаж (по причинам: отмена клиента, out-of-stock, SLA).
- Среднее время от void/refund до подтверждения вебхука.
- Доля ошибок reverse/cancel API и повторные попытки.
Практики:
- Централизуйте логирование с корреляцией по payment_id и idempotency_key.
- Классифицируйте ошибки провайдера и маппьте их на собственные коды. См. troubleshooting/errors-monitoring.
- Делайте деградацию: если capture временно недоступен, держите очередь ретраев с экспоненциальной задержкой.
Выбор провайдера и архитектуры
- Банковский эквайринг vs агрегатор: агрегаторы часто дают удобный reverse/cancel API, унифицированные refund-и и отчеты; банки — более прямой клиринг и ставки. Сравнение — comparisons/banks-vs-aggregators.
- Унификация слоев: спроектируйте собственный абстрактный интерфейс (authorize/capture/refund/void) и адаптеры под каждого провайдера.
- SLA и окно hold: это критичные параметры для отраслей с долгим фулфилментом.
Чек-лист внедрения
- Определите бизнес-правила: когда делаем void, когда refund, как работаем с partial.
- Реализуйте эндпоинты: capture, refund, void, с поддержкой idempotency-key.
- Добавьте вебхуки и гарантированную доставку, подписи, ретраи.
- Покройте статусы и переходы, авто-void просроченных холдов.
- Постройте сверку: отчеты провайдера ↔ события вебхуков ↔ ваша БД.
- Настройте роли и права на возвраты, журналирование действий операторов.
- Пропишите таймауты и ретраи для reverse/cancel API и refund операций.
- Тесты в песочнице: edge cases, конкурентные запросы, потеря сети.
Примеры пользовательских историй
- Клиент оформил доставку «сегодня». Вы делаете холд на 2 000 ₽, затем частичный capture на 1 700 ₽ (часть отменили), остаток — void. Если курьер не приехал — полный void.
- В маркетплейсе покупатель вернул одну позицию. Вы делаете partial refund на 590 ₽, добавляете reason=item_returned и отправляете клиенту уведомление о сроках зачисления.
- Ошибка в цене после capture. Вы быстро инициируете refund и отправляете корректирующие документы. Метрики показывают рост refund-rate — проводите RCA.
Часто задаваемые вопросы
- Чем отличается refund от reversal/void? Refund — после списания (деньги уже у мерчанта), reversal/void — до списания (раскрытие холда). Во втором случае деньги возвращаются быстрее, комиссия ниже.
- Поддерживается ли partial refund? Зависит от провайдера, но чаще да. Разрешайте множественные partial refund до суммы captured.
- Можно ли сделать capture двумя частями? Некоторые провайдеры разрешают множественные capture, другие — нет. Закладывайте абстракцию.
Заключение и что дальше
Грамотно реализованные возвраты, отмены и холдирование средств API снижают риск потерь, улучшают клиентский опыт и упрощают учет. Используйте двухстадийный платеж там, где важна гибкость, применяйте reverse/cancel API для отмен до списания и поддерживайте partial refund для постпродажного сервиса.
Готовы внедрять? Начните с базовой схемы интеграции в guides/integration-basics, изучите требования безопасности security/pci-3ds и подключите нужного провайдера: providers/yoomoney-api, providers/sberbank-acquiring-api, providers/tbank-api, providers/belarusbank-api. Затем протестируйте сценарии в testing/sandbox и настройте сверку в analytics/reconciliation.
Если понадобятся шаблоны контрактов или помощь с архитектурой — следуйте нашим паттернам и гайдам на api-platezhi.online.