Кальтер / Блог

Оплата в приложении с ИИ: как проверить реальный checkout, а не кнопку

Разбираем серверный checkout: какие состояния заказа хранить, как подтвердить платёж webhook-событием и какие отказы проверить до выдачи доступа.

Материал подготовлен с помощью ИИ; источники и расчёты проверены редакцией ZASLON.

Доступ выдают после подтверждённого события оплаты от провайдера; на схеме: Кнопка покупки, Checkout провайдера, Подтверждённый webhook.
Редакционная схема ZASLON. Условная иллюстрация механизма; не рыночные данные и не интерфейс сервиса.

Страница «Оплатить» не подтверждает, что заказ оплачен. В приложении с hosted checkout сервер создаёт заказ и платёжную сессию, а результат получает от платёжного провайдера отдельно. Поэтому основной маршрут здесь — связать заказ, checkout session и одно подтверждённое событие провайдера.

Один путь от кнопки до доступа

  1. Браузер отправляет серверу ID выбранного тарифа, но не задаёт доверенную сумму.
  2. Сервер ищет товар в своём каталоге, рассчитывает сумму и валюту, создаёт заказ со статусом pending и запрашивает checkout session.
  3. Браузер переходит по URL, возвращённому сервером. Страница возврата показывает состояние заказа из backend; параметр success в URL не открывает платный доступ.
  4. Провайдер отправляет webhook. Сервер проверяет подпись по исходному телу запроса, тип события, session/order IDs, сумму и валюту.
  5. В одной транзакции приложение фиксирует ID события и допустимый переход заказа в paid. Только после этого оно выдаёт доступ.

Именно такой раздел обязан оставаться серверным: ключ checkout не попадает в bundle, а браузер не может подменить цену. Stripe Checkout quickstart показывает hosted checkout с созданием сессии сервером; webhook guide описывает доставку событий приложению.

Не обработай одно действие дважды

Сбой соединения может оставить неизвестным, создал ли провайдер объект. Для запроса создания session используй поддерживаемый провайдером idempotency key и сохраняй связь между ключом, заказом и ответом. Для webhook храни обработанные event IDs с уникальным ограничением. Это разные уровни защиты: первый предотвращает повтор создания объекта при сетевом retry, второй — повторную обработку доставленного события.

При повторном webhook обработчик должен подтвердить получение, но не выдавать товар снова. Если пришло событие с незнакомым заказом или несовпадающей суммой, оставь заказ без доступа и отправь запись на разбор. Не превращай ошибку проверки в автоматическое «успешно».

Stripe отдельно документирует idempotent requests, включая повтор запроса после ошибки соединения. Детали хранения результата и правила ошибок относятся к API провайдера; схема статусов заказа остаётся ответственностью приложения.

Проверка на тестовом окружении

Проведи четыре сценария: закрыть Checkout до оплаты; получить успешное тестовое событие; повторить тот же event ID; отправить событие с неверной подписью. Сверь запись заказа и доступ, а не только текст на странице. Используй sandbox/test mode и локальный webhook forwarder из Stripe testing; это не доказывает конфигурацию production. Для возвратов и подписок отдельно определи переходы по своим правилам продукта.

Не сохраняй платёжные реквизиты в собственной форме, если используешь hosted checkout. И не выдавай тестовый успешный event за реальную транзакцию.