Что проверить при добавлении оплаты и подписок в продукт
Полный чеклист для команды: от первого PR до первого платёжного инцидента
Введение: почему платёжная интеграция — отдельный разговор
Добавление оплаты кажется стандартной задачей: подключи Stripe, набрось пару эндпоинтов, проверь в sandbox, задеплой. На практике это одна из наиболее рискованных областей разработки продукта — одновременно с точки зрения безопасности, надёжности и соответствия регуляторным требованиям.
Ошибки здесь стоят дорого: средняя стоимость утечки данных в финансовом секторе составила 6,08 млн долларов по данным отчёта IBM Cost of a Data Breach за 2024 год [1]. В 2024 году глобальная средняя стоимость утечки данных любой отрасли достигла 4,88 млн долларов [2]. Но даже без громкого инцидента непродуманная реализация означает: двойные списания, пользователи с доступом к фичам, которые уже не оплатили, потерянный revenue из-за неправильного retry, и — в худшем случае — компрометация платёжных данных клиентов.
Эта статья — структурированный чеклист для Tech Lead, Senior-разработчика и всей команды, которая добавляет в продукт первый или очередной платёжный флоу.
1. Прежде чем писать код: архитектурный выбор
1.1 Scope карточных данных — ключевой вопрос
Первое, что нужно решить до написания первой строчки: будет ли ваше приложение прикасаться к карточным данным вообще?
Любое прикосновение к номеру карты, CVV или PIN-коду автоматически помещает вас в область действия PCI DSS — стандарта безопасности, актуальная версия которого (4.0.1) вступила в полную силу с января 2025 года [3]. Полный аудит PCI DSS включает более 500 требований, изложенных примерно на 400 страницах [4]. Для большинства продуктовых команд это нереалистичная нагрузка.
Правильный ответ почти всегда: делегировать хранение карточных данных провайдеру — использовать hosted payment page (Stripe Checkout, PayPal, Adyen Hosted Fields) или JS-библиотеки с токенизацией. В таком случае ваш сервер никогда не видит номер карты, а PCI Scope радикально сужается. Это не просто удобство — это архитектурное решение с прямым влиянием на безопасность и compliance [5].
Что проверить:
- Карточные данные нигде не проходят через ваш сервер.
- Вы не логируете ничего похожего на PAN (номер карты), CVV, дату истечения.
- Токен от провайдера — не сами карточные данные — это то, что хранится в вашей БД.
- Используете ли вы Stripe Elements / Checkout / аналоги — форма отрисовывается на стороне провайдера, не в вашем DOM.
1.2 Выбор модели интеграции
Hosted page (redirect) против embedded form — разные точки риска. Hosted page минимизирует PCI Scope и ускоряет реализацию; embedded form даёт больше контроля над UX, но требует тщательной реализации токенизации на клиенте [6].
Что проверить:
- Если используете embedded-форму — publishable key безопасен на клиенте, secret key — только на сервере.
- Команда понимает разницу между publishable и secret ключами и никогда не перепутывает их в коде.
2. Управление секретами и ключами
2.1 Проблема хардкода
Анализ 13 000 мобильных приложений, проведённый компанией CloudSEK, показал: около 5% приложений, использующих платёжные API, содержат захардкоженные API-ключи и секреты прямо в исходном коде [7]. Это не уникальная ошибка начинающих разработчиков — крупные компании попадают в ту же ловушку.
Проблема усугубляется историей git: даже если ключ убрали из последнего коммита, он остаётся в истории репозитория и продолжает быть эксплуатируемым [8].
Что проверить:
- Ни один ключ (Stripe secret key, webhook signing secret, ключ любого другого провайдера) не захардкожен в коде или конфигурационных файлах в репозитории.
- Секреты хранятся в переменных окружения или в dedicated secrets manager (AWS Secrets Manager, Azure Key Vault, HashiCorp Vault, Doppler) [9].
- Настроен автоматический secrets scanning в CI/CD (GitGuardian, truffleHog, встроенные механизмы GitHub/GitLab).
- Проверена история git на предмет случайных коммитов с секретами — для этого недостаточно только code review текущего PR [8].
- Ключи разделены по средам: отдельные ключи для test/sandbox и production.
- Определён процесс ротации ключей при компрометации: кто получает алерт, как быстро ключ можно инвалидировать, что происходит с работающими интеграциями в этот момент.
2.2 Принцип минимальных привилегий
Что проверить:
- Не все разработчики имеют доступ к production-ключам.
- Для разных сервисов — разные ключи (если провайдер поддерживает restricted keys).
- CI/CD имеет доступ только к тем ключам, которые нужны на конкретном этапе.
3. Безопасность вебхуков
Вебхуки — именно то место, где ломается большинство платёжных интеграций. Ошибки в их реализации приводят к двойным списаниям, пропущенным событиям и уязвимостям типа event spoofing.
3.1 Верификация подписи
Каждый платёжный провайдер подписывает вебхуки HMAC-подписью. Верификация должна производиться по сырому (raw) телу запроса — не по распарсенному JSON. Это частая ошибка: если в Express.js применить express.json() глобально до обработки вебхука, сырое тело будет потеряно и верификация всегда будет проваливаться [10].
Без верификации подписи любой может отправить POST-запрос на ваш эндпоинт с фиктивным событием payment_succeeded и вызвать несанкционированные действия [11].
Что проверить:
- Подпись вебхука верифицируется по raw body, до любого парсинга.
- Webhook signing secret хранится как секрет — не в коде.
- Добавлена проверка timestamp (окно 5–10 минут) для защиты от replay-атак [12].
- Любой запрос с невалидной подписью немедленно отклоняется с 401/400 без дополнительной обработки.
3.2 Идемпотентность
Платёжные провайдеры работают по принципу «at least once delivery» — одно и то же событие может быть доставлено несколько раз [13]. Обработчик вебхука должен быть идемпотентным: повторная доставка того же события не должна приводить к повторной бизнес-операции (двойному начислению, повторной отправке письма, дублированию заказа).
Что проверить:
- Перед обработкой события проверяется наличие event.id в базе данных.
- event.id сохраняется в БД атомарно с выполнением бизнес-операции (или до неё — с last-write-wins логикой).
- Используется unique constraint на поле event_id в таблице processed events.
- Обработчик возвращает 2xx быстро (< 5–10 сек) и передаёт тяжёлую обработку в фоновую очередь — иначе провайдер начнёт retry [14].
3.3 Подписка только на нужные события
Что проверить:
- Вы подписаны только на события, которые реально обрабатываете. Лишние события — лишний шум, дополнительная нагрузка, и потенциальные вектора атак.
- Есть обработка хотя бы основных событий жизненного цикла подписки:
payment_intent.succeeded,invoice.paid,customer.subscription.created,customer.subscription.updated,customer.subscription.deleted,checkout.session.completed[15].
4. Бизнес-логика: самое уязвимое место
OWASP, Portswigger и большинство практикующих пентестеров сходятся в одном: уязвимости бизнес-логики — самые дорогостоящие и одновременно наименее заметные для автоматических сканеров [16]. В платёжных системах они проявляются особенно часто.
4.1 Манипуляция ценой и параметрами на клиенте
Если ваш checkout принимает от клиентского приложения сумму, валюту, количество товаров или идентификатор тарифа — это уязвимость. Сервер должен самостоятельно рассчитывать итоговую сумму на основании данных из базы данных, а не доверять тому, что пришло от клиента.
Задокументированный пример: передача currency code напрямую от клиента в payment gateway без серверной валидации позволила изменить валюту с USD на JPY и существенно снизить реальную стоимость заказа [17].
Что проверить:
- Сумма заказа рассчитывается на сервере — никогда не принимается от клиента.
- Валюта определяется на сервере.
- Идентификатор тарифа/плана верифицируется на сервере.
- Скидки и промокоды применяются на сервере, с проверкой срока действия и ограничения использования.
4.2 Race condition и двойное использование
Классический сценарий: пользователь отправляет параллельные запросы на использование промокода. Если проверка «использован ли промокод» и запись «помечаем как использованный» не атомарны, оба запроса пройдут проверку одновременно и промокод применится дважды [18].
Исследованный пример: промокод был активирован 30 раз параллельными запросами из-за отсутствия атомарной блокировки на уровне базы данных — несмотря на то что логика приложения казалась корректной [19].
Что проверить:
- Операции «проверить доступность» и «использовать» для промокодов, лимитов, trial-периодов — атомарны (SELECT ... FOR UPDATE или аналог).
- В критичных транзакциях (создание заказа, активация подписки) применяется pessimistic или optimistic locking.
- Эндпоинты с финансовыми операциями имеют rate limiting и защиту от одновременных параллельных запросов от одного пользователя.
4.3 Пропуск шагов в многошаговом флоу
Что проверить:
- Подтверждение заказа / активация доступа проверяет факт оплаты — не только наличие валидной сессии.
- Порядок шагов в checkout не может быть нарушен прямым вызовом эндпоинта.
- Статусы заказов и подписок машина состояний — переходы между ними валидируются на сервере.
5. Жизненный цикл подписки и провижининг доступа
5.1 Маппинг событий на доступ
Самая распространённая ошибка: провижининг доступа реализован только на создание подписки — а что происходит при даунгрейде, отмене, паузе, истечении trial или смене тарифа?
Что проверить:
- Обработаны все события жизненного цикла: создание, обновление, отмена, пауза, возобновление, истечение пробного периода, неуспешная оплата.
- При отмене подписки доступ отзывается в нужный момент — немедленно или по истечении оплаченного периода (в зависимости от вашей модели).
- При даунгрейде тарифа доступ к фичам старшего тарифа отзывается — проверяйте это руками, не только через юнит-тесты.
- Trial period: пользователь не может получить полный доступ без введения платёжных данных, если это требует ваша модель.
5.2 Провижининг в коде, а не в голове
Логика «кому какой доступ принадлежит» не должна быть размазана по разным местам кодовой базы. Рекомендуется единый entitlement layer — сервис или модуль, который отвечает на вопрос «имеет ли текущий пользователь доступ к фиче X» на основании актуального состояния подписки [20].
Что проверить:
- Проверка доступа к платным фичам происходит в runtime, а не только при логине.
- Изменение подписки (upgrade, downgrade, отмена) немедленно отражается в проверке доступа — без перелогина пользователя.
- Нет мест в коде, где доступ к платной фиче проверяется по флагу в JWT или cookie без проверки актуального состояния в БД.
5.3 Тестирование граничных состояний
Что проверить:
- Что происходит, если подписка истекает прямо во время активной сессии пользователя?
- Что происходит при webhook о неуспешной оплате — пользователь немедленно теряет доступ или есть grace period?
- Что происходит при апгрейде в середине платёжного периода — правильно ли рассчитывается пропорциональная оплата (proration)?
6. Dunning и recovery неуспешных платежей
Неуспешные платежи — неизбежная реальность в подписочной модели. По данным исследований SaaS-рынка, инволюнтарный чёрн (потеря клиентов из-за технических проблем с платежом, а не из-за осознанного решения отказаться) составляет 20–40% от общего оттока [21]. Это revenue, который можно вернуть правильной реализацией.
6.1 Умный retry
Не все отказы одинаковы. Hard decline (карта заблокирована, фрод) не имеет смысла ретраить немедленно. Soft decline (временно недостаточно средств, технический сбой) — имеет смысл.
Что проверить:
- Есть ли логика ретрая с экспоненциальным бэкоффом — не одновременная многократная попытка в течение минуты.
- Ретраи остановлены после разумного количества попыток (3–4 за 1–2 недели) — дальнейшие попытки маловероятно успешны и ведут к дополнительным комиссиям [22].
- Время ретрая оптимизировано: ночные ретраи менее эффективны, так как в это время активнее работают антифрод-системы [23].
6.2 Коммуникация с пользователем
Что проверить:
- Пользователь получает уведомление о неуспешном платеже — не только системный алерт.
- Уведомление содержит чёткий CTA: обновить платёжные данные.
- Есть ссылка на self-service portal для обновления карты.
- Pre-dunning: уведомление до истечения карты (если провайдер предоставляет эту информацию).
6.3 Account Updater
Что проверить:
- Подключён ли Account Updater (Stripe поддерживает это автоматически для части карточных сетей) — автоматическое обновление реквизитов при перевыпуске карты без участия пользователя.
7. Безопасность на уровне API
7.1 OWASP API Top 10 применительно к платёжным эндпоинтам
Обновлённый OWASP API Security Top 10 выделяет несколько рисков, особенно актуальных для платёжных систем [24]:
Что проверить:
- Broken Object Level Authorization (BOLA): пользователь A не должен иметь доступ к данным заказа, истории платежей, методам оплаты пользователя B — даже зная его ID.
- Broken Authentication: эндпоинты, обрабатывающие платёжные данные, защищены корректной аутентификацией; нет эндпоинтов, доступных без аутентификации, которые выполняют финансовые операции.
- Excessive Data Exposure: API возвращает только необходимые данные — last4 цифры карты, тип, дату истечения — но не полный токен, не raw payment method object с лишними полями.
- Rate Limiting: платёжные эндпоинты имеют rate limiting — защита от card testing attacks (перебора карточных данных).
7.2 3D Secure и SCA
PCI DSS 4.0 и регуляции типа PSD2 (Европа) требуют Strong Customer Authentication для определённых типов транзакций. Stripe и другие провайдеры реализуют это через 3D Secure 2 (3DS2).
Что проверить:
- Интеграция корректно обрабатывает 3DS challenge flow — пользователь не получает обрезанный UX или ошибку при redirect.
- Обрабатываются случаи, когда 3DS требуется на этапе recurring-платежа (не только при первичной оплате).
- Для подписок в Индии реализовано соответствие требованиям RBI e-mandate [25].
8. Логирование, мониторинг и алерты
8.1 Что логировать
Что проверить:
- Все платёжные события логируются с event ID, timestamp, типом события, результатом обработки.
- Логи не содержат карточных данных, CVV, полных номеров карт — только токены и маскированные значения.
- Есть correlation ID между вебхук-событием и внутренней транзакцией в вашей системе.
- Логи хранятся достаточно долго для расследования инцидентов (PCI DSS требует минимум 12 месяцев для аудит-логов).
8.2 Алерты
Что проверить:
- Алерт на аномальное количество неуспешных платежей (возможный card testing).
- Алерт на вебхук-события с невалидной подписью (возможная атака).
- Алерт на нетипичный объём транзакций (возможный фрод или ошибка интеграции).
- Алерт на downtime платёжного провайдера.
9. Тестирование: что обязательно нужно проверить
Sandbox-тестирования «happy path» недостаточно. Платёжная интеграция требует обязательного тестирования негативных и граничных сценариев [26].
9.1 Тест-кейсы для платёжного флоу
Что проверить:
- Успешный платёж — проверить в обход только UI, но и через прямую проверку состояния в БД и ответ провайдера.
- Decline card (недостаточно средств) — пользователь получает понятное сообщение, а не техническую ошибку.
- 3DS challenge — корректный redirect и обратный redirect.
- Timeout платёжного провайдера — что происходит, если провайдер не ответил?
- Webhook c задержкой / дублированный webhook — обрабатывается корректно (идемпотентность).
- Webhook с невалидной подписью — отклоняется без выполнения бизнес-логики.
9.2 Тест-кейсы для подписок
Что проверить:
- Создание подписки — правильно ли провижинится доступ?
- Апгрейд — немедленный доступ к новым фичам?
- Даунгрейд — немедленный или после периода?
- Отмена — когда отзывается доступ?
- Неуспешная рекуррентная оплата — что происходит с доступом?
- Истечение trial — переходит в paid или блокируется?
9.3 Security-тесты
Что проверить:
- Попытка передать сумму транзакции с клиента — сервер игнорирует её?
- Попытка обратиться к заказам другого пользователя по ID — BOLA?
- Попытка применить промокод дважды параллельными запросами — race condition?
- Попытка пропустить шаг оплаты и напрямую обратиться к эндпоинту подтверждения.
10. PCI DSS 4.0.1: что важно знать разработчику
С марта 2025 года все требования PCI DSS 4.0.1 обязательны [3]. Ключевые изменения, релевантные для команды разработки:
- Клиентская безопасность (требования 6.4.3 и 11.6.1): скрипты на payment page должны быть явно авторизованы и проверены на целостность. Это означает: знать, какие сторонние скрипты загружаются на checkout-странице, и контролировать их.
- MFA для всего CDE (Cardholder Data Environment): многофакторная аутентификация теперь обязательна для всех, кто имеет доступ к данным держателей карт — не только для администраторов.
- Пароли минимум 12 символов для доступа к CDE [27].
- Непрерывный мониторинг вместо ежегодных проверок — как минимум для скриптов и конфигурации.
Для большинства продуктовых команд правильная стратегия: минимизировать PCI Scope через делегирование хранения карточных данных провайдеру, а не погружаться в полный аудит самостоятельно.
11. Типичные ошибки и как их избежать
Ошибка 1: Исполнение бизнес-логики по событию redirect, а не по вебхуку
Пользователь закрыл вкладку до redirect. Платёж прошёл, но заказ не создан. Или наоборот — redirect отработал, но платёж не прошёл.
Решение: никакой критичной бизнес-логики на redirect. Только на вебхуке с верификацией подписи. Stripe прямо предупреждает об этом в документации [6].
Ошибка 2: Хранение состояния подписки только в JWT
JWT не обновляется автоматически при изменении подписки. Пользователь с отменённой подпиской будет иметь доступ до истечения токена.
Решение: проверять актуальное состояние подписки в базе данных при каждом запросе к защищённым ресурсам.
Ошибка 3: Sandbox = production
Sandbox не воспроизводит реальные задержки банков, специфику антифрод-систем, SCA-флоу для европейских карт.
Решение: после sandbox — staging с production-like окружением, и smoke-тесты с реальными маленькими транзакциями перед полным запуском.
Ошибка 4: Нет процесса реакции на инцидент
Компрометация API-ключа или вебхук-секрета без заранее подготовленного плана превращается в панику с непредсказуемым результатом.
Решение: задокументированный playbook: кто получает алерт, что делает в первые 5 минут, как инвалидируется ключ, как уведомляются клиенты при необходимости.
12. Код и PR: что проверять перед merge
Когда в команду приходит PR с платёжной интеграцией или изменениями в её логике, он требует особого внимания. Стандартный code review может пропустить:
- секреты в конфигах или env-файлах, закоммиченных случайно;
- логику, принимающую сумму транзакции с клиента;
- обработчик вебхука без верификации подписи;
- недостаточную проверку прав доступа к платёжным объектам;
- отсутствие идемпотентности в критических операциях;
- новую зависимость, добавленную в payment service.
Дополнительный слой контроля изменений перед merge/release — особенно для PR, затрагивающих платёжный код, конфиги и служебные файлы — помогает заметить риски, которые дочитывают не до конца. Именно для этого существует HighVibe: он анализирует конкретное изменение в коде, новые зависимости и конфигурацию, объясняет проблему и предлагает следующий шаг — не длинный отчёт, а практический ответ на вопрос «можно ли безопасно принять это изменение».
Заключение: что делать дальше
Добавление оплаты — это не разовая задача, а постоянная ответственность. Несколько конкретных шагов для команды:
- Проведите аудит текущей интеграции по этому чеклисту — даже если она работает. «Работает» не значит «безопасно».
- Настройте secrets scanning в CI/CD прямо сейчас, если его ещё нет.
- Проверьте идемпотентность ваших вебхук-обработчиков — это можно проверить вручную за час.
- Пройдитесь по всем событиям жизненного цикла подписки и убедитесь, что каждое обрабатывается корректно.
- Задокументируйте процесс реакции на платёжный инцидент — до того, как он произойдёт.
Платёжная инфраструктура — не то место, где стоит надеяться на «авось пронесёт».
Источники
[1] APIsec — "API Security Testing for Payment Systems: PSD2 and Open Banking Requirements" — https://www.apisec.ai/blog/api-security-testing-payment-systems
[2] G2 — "10 Best Practices To Measure Payment Processing Security" — https://learn.g2.com/payment-processing-security
[3] Cynomi — "PCI DSS Compliance Checklist: Full Requirements Guide" — https://cynomi.com/learn/pci-dss-compliance-checklist/
[4] Ignyteplatform — "PCI DSS 4.0 Requirements Checklist for 2026" — https://www.ignyteplatform.com/blog/security/pci-dss-requirements-checklist/
[5] Qualysec — "What is Digital Payment Security? A Complete Guide for 2025" — https://qualysec.com/digital-payment-security/
[6] Digital Applied — "Stripe Payment Integration: Complete Dev Guide 2026" — https://www.digitalapplied.com/blog/stripe-payment-integration-developer-guide-2026
[7] Infosecurity Magazine — "Payment API Vulnerabilities Exposed 'Millions' of Users" — https://www.infosecurity-magazine.com/news/payment-api-vulnerabilities/
[8] GitGuardian Blog — "API Key Management Best Practices for Secure Secrets Storage" — https://blog.gitguardian.com/secrets-api-management/
[9] Sourcery — "Hardcoded API Keys in JavaScript/Node.js Applications" — https://www.sourcery.ai/vulnerabilities/hardcoded-api-keys-javascript
[10] Medium / Sohail — "Handling Payment Webhooks Reliably (Idempotency, Retries, Validation)" — https://medium.com/@sohail_saifii/handling-payment-webhooks-reliably-idempotency-retries-validation-69b762720bf5
[11] Refgrow — "10 Critical Webhook Security Best Practices" — https://refgrow.com/blog/webhook-security-best-practices
[12] PentestTesting — "9 Powerful Webhook Security Patterns That Stop Breaches" — https://www.pentesttesting.com/webhook-security-best-practices/
[13] Hookdeck — "How to Implement Webhook Idempotency" — https://hookdeck.com/webhooks/guides/implement-webhook-idempotency
[14] Integrate.io — "How to Apply Webhook Best Practices to Business Processes" — https://www.integrate.io/blog/apply-webhook-best-practices/
[15] Codehooks — "Stripe Webhook Security: Signature Verification, Idempotency, and Local Testing" — https://codehooks.io/docs/examples/webhooks/stripe
[16] OWASP Foundation — "Business Logic Vulnerability" — https://owasp.org/www-community/vulnerabilities/Business_logic_vulnerability
[17] Intigriti — "Exploiting Business Logic Error Vulnerabilities" — https://www.intigriti.com/researchers/blog/hacking-tools/exploiting-business-logic-error-vulnerabilities
[18] StackHawk — "Business Logic Vulnerability Testing: How to Find What Scanners Miss" — https://www.stackhawk.com/blog/testing-for-business-logic-vulnerabilities/
[19] Beagle Security — "Business Logic Vulnerability Testing" — https://beaglesecurity.com/blog/article/business-logic-vulnerability-testing.html
[20] Schematic HQ — "Entitlement Management System for SaaS (2026 Guide)" — https://schematichq.com/blog/entitlement-management-system
[21] Whop — "Ultimate guide to SaaS billing" — https://whop.com/blog/saas-billing/
[22] Kinde — "Dunning Strategies for SaaS: Email Flows and Retry Logic" — https://www.kinde.com/learn/billing/churn/dunning-strategies-for-saas-email-flows-and-retry-logic/
[23] SubscriptionFlow — "The Ultimate Guide to Handling SaaS Failed Payments" — https://www.subscriptionflow.com/2023/03/ultimate-guide-to-handling-saas-failed-payments/
[24] CloudDefense AI — "OWASP API Security Top 10 Vulnerabilities – 2025" — https://www.clouddefense.ai/api-security-top-10-vulnerabilities/
[25] Stripe Docs — "SCA migration guide for Billing" — https://stripe.com/es/docs/billing/migration/strong-customer-authentication
[26] Testfort — "Payment Gateway Testing: The Ultimate Guide & Test Cases" — https://testfort.com/blog/payment-gateway-testing-the-ultimate-guide
[27] Linford & Company — "PCI DSS 4.0 Mandatory Requirements: 2025 Compliance Guide" — https://linfordco.com/blog/pci-dss-4-0-requirements-guide/