Как работает оплата
Preview / Stable, test_mode / prod_mode, US / WW и multicurrency — порядок выбора и запасные варианты.
Проверено по редактору:
Оплата определяется последовательностью: префикс /test в адресе → платёжный режим → региональный провайдер → валюта и цены каждого пути → подтверждение. На каждом этапе используются результаты предыдущего.
Например: /preview/cosmiciq → prod_mode → US → stripe_us → USD. Но для того же посетителя из US результатом может стать stripe → EUR, если US-настройки в Production не заполнены, а у WW-пути доступна только базовая цена в евро.
Ниже подробно разобрана оплата подписки через Simple Plans + Checkout или Composite. Особенности One Click и Cancellation приведены в конце страницы.
Какие настройки за что отвечают
| Понятие | На что влияет |
|---|---|
| Preview / Stable | Какое содержимое воронки открыто; независимо от режима оплаты |
| test_mode / prod_mode | /test включает test_mode; без него prod_mode. Какие пути плана, API платёжного сервиса и ключ Stripe используются |
| US / WW | Какой региональный провайдер и его Payment App выбираются |
| Multicurrency | Какая готовая цена из выбранного пути подходит стране посетителя |
| Intro offer | Есть ли вводная цена в той же валюте, что и подписка |
| Локализация | Язык и форматирование текста; язык сам по себе не выбирает платёжную валюту |
Здесь WW означает общий провайдер Stripe (stripe), а US — Stripe US (stripe_us). В редакторе вкладки называются Stripe и Stripe US. Страна берётся из IP lookup посетителя; региональный провайдер и валюта выбираются раздельно.
1. Адрес → test_mode или prod_mode
В header редактора доступны три ссылки. Обе Preview-ссылки открывают текущий выбранный экран одной и той же версии воронки.
| Ссылка | Адрес | Содержимое | Настройки и ключ Stripe | API оплаты | Регистрация |
|---|---|---|---|---|---|
| Link to stable | /cosmiciq | Stable | prod_mode, prod_mode_publishable_key | pay.mentalgrowth.app/api/v1 | Production URL |
| Link to preview prod | /preview/cosmiciq?step=Welcome+1 | Preview | prod_mode, prod_mode_publishable_key | pay.mentalgrowth.app/api/v1 | Production URL |
| Link to preview test | /test/preview/cosmiciq?step=Welcome+1 | Preview | test_mode, test_mode_publishable_key | dev.pay.mentalgrowth.app/api/v1 | Staging URL |
Режим определяется только ведущим сегментом /test, независимо от домена staging или production. Обычный Preview теперь использует боевые настройки. Например, https://dev.constructor.youth-app.com/preview/cosmiciq — это prod_mode, а тот же адрес с /test/preview/ — test_mode.
Preview / Stable выбирают содержимое и опубликованный renderer. Они не выбирают платёжный режим. В редакторе вкладки Test mode и Production выбирают редактируемые настройки; режим посетителя задаёт его ссылка. Публикация не преобразует тестовые priceId в боевые.
Префикс сохраняется при переходах по экранам, перезагрузке и возврате на старт. Cancellation и Manage Subscription работают по тому же правилу: например, /test/preview/manage-subscription/<alias>. Preview на телефоне из редактора открывается в тестовом режиме. Тестовая идентификация клиента и Stripe не подставляются в Preview prod.
2. Страна → US или WW
Stripe US выбирается, только когда одновременно выполнены три условия:
- Страна посетителя — US.
- У коллекции заполнен Payment App для Stripe US.
- У выбранного плана есть хотя бы один непустой US priceId в активном режиме.
Во всех остальных случаях выбирается WW / Stripe. Для других стран Stripe US не выбирается, даже если он полностью настроен. Если страна неизвестна, используется WW и далее базовая валюта пути.
Матрица четырёх конфигураций
В таблице US-ветка предполагает, что её условия выше выполнены.
| Режим ссылки | Выбранный провайдер | Какие пути читаются | Общий Payment App |
|---|---|---|---|
С /test | WW / Stripe | paymentPaths.test_mode.stripe | providerSettings.stripe.appName |
С /test | US / Stripe US | paymentPaths.test_mode.stripe_us | providerSettings.stripe_us.appName |
Без /test | WW / Stripe | paymentPaths.prod_mode.stripe | providerSettings.stripe.appName |
Без /test | US / Stripe US | paymentPaths.prod_mode.stripe_us | providerSettings.stripe_us.appName |
У коллекции два общих Payment App — Stripe и Stripe US. Каждый используется в обоих режимах. Пути и цены внутри плана разделены на четыре конфигурации из таблицы. Изменение Payment App во вкладке Test mode также меняет общий App для Production этого провайдера.
Выбранный Payment App связывается с интеграцией проекта по app_name; из неё берётся ключ нужного режима. При отсутствии подходящей интеграции или ключа текущий клиент использует встроенный тестовый ключ. Для Production необходимо явно настроить совпадающий App и production-ключ: заполненные пути сами по себе этого не гарантируют.
Где заканчивается переход US → WW
Переход на WW происходит при выборе провайдера, если US App или US-пути не заполнены. Затем проверяется каждый путь уже выбранного провайдера.
Если Stripe US выбран, но у одного из его путей неправильная сумма, нет совместимой валюты или не хватает intro-цены, оплата получает blocked. Автоматического перехода на WW после такой ошибки нет. Также нет перехода с prod_mode на test_mode при незаполненных Production-путях.
3. Multicurrency → валюта и сумма
Multicurrency использует сохранённые цены каталога для выбранного priceId. В клиенте нет пересчёта по курсу обмена: сумма USD, EUR или CAD берётся из соответствующей настройки цены.
При выборе Subscription редактор загружает дополнительные валюты и сохраняет их в currency_options текущего пути. Для Intro Offer ID отдельно загружаются introOfferCurrencyOptions. Во время запроса виден индикатор Fetching multicurrency.... Каталог запрашивается для выбранного Payment App и режима; загрузка валют для Test mode не заполняет Production.
Порядок выбора цены
- Страна сопоставляется с валютой по таблице клиента: например, US → USD, DE → EUR, CA → CAD, GB → GBP, JP → JPY.
- В
currency_optionsищется подходящая цена этой валюты. - Если её нет или она не подходит, проверяется опция базовой валюты
path.currency, затем базоваяpath.price. - При включённом intro offer проверяется совместимость двух цен по схеме ниже.
- Итоговый код валюты и сумма подписки валидируются до подготовки оплаты. Отсутствие подходящей цены блокирует попытку.
Любая другая доступная валюта не выбирается случайно. Например, наличие только CAD в дополнительных опциях не означает, что посетителю из US покажут CAD: будет использована подходящая базовая цена либо оплата заблокируется.
Intro offer должен совпасть по валюте
Допустим, подписка имеет цену USD 29.99 и базовую EUR 24.99, а intro offer существует только в EUR 1.99. Для посетителя из US сначала находится USD-подписка, но USD intro отсутствует. Тогда проверяется базовая пара, и весь путь переходит на EUR 24.99 + EUR 1.99.
Если подходящей базовой пары тоже нет, результат — blocked. Клиент не смешивает USD-подписку с EUR intro и не убирает включённое вводное предложение, чтобы провести оплату по обычной цене.
US не гарантирует USD, а WW не означает EUR. И US-, и WW-путь могут содержать несколько валют. Выбор провайдера отвечает за платёжную конфигурацию; выбор валюты — за совместимые цены внутри неё.
Суммы из unit_amount_minor приводятся к денежным единицам с учётом валюты: например, 1999 → USD 19.99, а 3000 → JPY 3000. Число minor units нельзя всегда делить на 100. Сумма подписки должна быть положительной; для настроенного intro offer допускается нулевая цена.
Примеры прохождения схемы
Цены ниже условные, для объяснения правил. Во всех строках предполагаются корректные данные клиента и ключи интеграций.
| Ситуация | Что выберется | Почему |
|---|---|---|
| Preview test, страна US; тестовый US-путь заполнен и содержит USD | test_mode → stripe_us → USD | Проверяются US-настройки Test mode |
| Stable, страна US; US заполнен только в Test mode; Production WW содержит USD | prod_mode → stripe → USD | Тестовый US-путь не участвует в prod_mode |
| Stable, страна DE; оба провайдера заполнены, WW содержит EUR | prod_mode → stripe → EUR | Для страны DE выбирается WW |
| Stable, страна CA; WW не содержит CAD, базовая USD корректна, intro выключен | prod_mode → stripe → USD | Используется базовая цена WW-пути |
| Stable, страна US; US-путь заполнен, USD-подписка без USD intro; есть базовая пара EUR | prod_mode → stripe_us → EUR | Меняется валюта, провайдер остаётся US |
| Stable, страна US; US выбран, но совместимой пары подписка + intro нет | blocked | Поздняя ошибка не переключает оплату на WW |
| Stable; выбранный Production-провайдер не содержит корректного пути | blocked | Настройки Test mode не подставляются |
Для каждого плана и режима проверьте отдельные сценарии US, другой страны, отсутствующей валюты и несовпадающего intro offer. Каждый дополнительный платёжный путь разрешает свою валюту отдельно; ошибка любого пути блокирует подготовку всей попытки.
Как настройки доходят до Checkout
Simple Plans сохраняет выбор посетителя. Связанный Checkout читает этот выбор из указанного источника. Composite объединяет обе роли. В обоих случаях Payment App принадлежит коллекции, а платёжные пути и действия после результата — выбранному Payment Plan.
До подготовки оплаты нужны идентификатор и email зарегистрированного клиента. После готовности данных клиента и определения местоположения runtime проверяет конфигурацию и фиксирует параметры попытки. Неполный Payment App, отсутствующий путь или неподходящая сумма/валюта блокируют подготовку.
Воронка обращается к платёжному сервису напрямую. CMS backend хранит и отдаёт конфигурацию воронки и интеграции проекта. Внутренняя обработка платёжного сервиса, webhooks и дальнейшие автоматические продления подписок находятся за границей этой схемы.
Настройки блоков и записи интерфейса собраны в разделе Платёжные элементы.
Как проходит Checkout
Ниже показан успешный сценарий. Первый платёжный путь создаёт основную подписку; остальные, если они есть, выполняются последовательно как дополнительные подписки.
- Выбор и подготовка. Checkout получает выбранный план и вызывает
subscriptions/init. В ответ приходят данные для подтверждения оплаты. Подготовка сама по себе не является командой подтверждения списания. - Подтверждение посетителем. Для карты сначала создаётся Payment Method, затем вызывается подтверждение Stripe. Для Apple Pay / Google Pay открывается нативное окно кошелька и подтверждается тот же платёж. Доступность кошельков зависит от устройства и окружения.
- Фиксация результата. Успех основной операции требует статуса
succeededи идентификатора PaymentIntent. После подтверждения отправляются события покупки и устанавливается защита от повторной оплаты в этой вкладке. - Дополнительные пути. Каждый следующий путь вызывает
subscriptions/add. Подтверждённые операции отмечаются выполненными. Если очередной путь завершается ошибкой, уже выполненные автоматически не повторяются и не откатываются. - Действие экрана. Настроенное
paymentSucceedsвыполняется после успеха всех операций. Подтверждение основной подписки ещё не означает завершение всей цепочки.
Что происходит при ошибке
| Результат | Поведение |
|---|---|
| Успех · succeeded | Все операции завершены; выполняется действие успеха |
| Отказ · failed | При ошибке выполнения запускается настроенное действие ошибки; допустима явная повторная попытка |
| Отмена · cancelled | Посетитель или Stripe отменил подтверждение; выполняется настроенное действие отмены, повторная попытка допустима |
| Нет сети · offline | До отправки запрос не ставится в очередь. После восстановления связи требуется явная попытка |
| Неопределённый результат · unknown | Запрос мог выполниться, но окончательный результат не получен. Повтор заблокирован до выяснения статуса |
| Блокировка · blocked | Не хватает настроек/данных клиента либо сработала защита после подтверждённой оплаты. Сначала нужно устранить причину |
При offline, unknown и blocked действия успеха и ошибки не запускаются. Ошибка подготовки subscriptions/init также не запускает действие экрана и допускает повтор: подтверждение списания ещё не начиналось. Для команды списания обрыв связи, HTTP 408/5xx или нечитаемый/неокончательный ответ означают unknown.
Повтор в рамках текущей попытки продолжает невыполненные операции. Перезагрузка страницы не является способом восстановить платёж: Stripe-ссылки на клиента и подписку хранятся только в памяти, а отметка о подтверждённой оплате в текущей вкладке блокирует новую попытку. Автоматического восстановления спорного результата через backend в этом клиентском потоке пока нет.
После основной оплаты
One-Click Button
Кнопка выполняет только первый настроенный платёжный путь и использует Stripe-данные, полученные в текущей сессии оплаты.
Связь Preview / Stable с режимом и правило выбора US сохраняются. Но настройки One Click имеют собственную структуру paymentMethods[provider][mode]: Payment App и пути находятся внутри выбранной пары провайдер + режим. Правило об общем App коллекции к этой отдельной структуре не относится.
| Техника | Действие | Что требуется |
|---|---|---|
| oneTime | Разовый платёж | Stripe customer ID |
| additionalSubscription | Дополнительная подписка | Stripe customer ID |
| modify | Изменение подписки с выбранным правилом перерасчёта | Stripe subscription ID |
onPress выполняется только после успеха операции. Если изменение подписки прошло без списания, отправляется subscription_modified, а не событие покупки. При нехватке Stripe-данных после перезагрузки операция блокируется.
Отмена и заморозка
Cancellation — отдельный сценарий: по email и user_id из ссылки он запрашивает список подписок, применяет отмену или заморозку к доступным для действия подпискам и обновляет их статус. Отмена действует в конце оплаченного периода; заморозка задаётся на 1–365 дней. Итоговое действие экрана зависит от общего результата запросов.
Здесь US / WW — уже буквальные секции cancellationConfig.us и cancellationConfig.ww. Для страны US выбирается US-секция, если заполнены оба поля app и projectSlug; иначе используется заполненная WW-секция. Выбора нового priceId и multicurrency в отмене нет. Хост API по-прежнему зависит от Preview / Stable.
Manage Subscription имеет отдельный вход с проверкой email. Сам по себе этот вход не запускает Cancellation и не восстанавливает данные One Click.
Запросы к платёжному сервису
Хост выбирается по варианту воронки из таблицы выше.
| Операция | Метод и путь |
|---|---|
| Подготовка основной подписки | POST /api/v1/constructor/stripe/subscriptions/init |
| Дополнительная подписка | POST /api/v1/constructor/stripe/subscriptions/add |
| Разовый платёж | POST /api/v1/constructor/stripe/payments/one-time |
| Изменение подписки | POST /api/v1/constructor/stripe/subscriptions/modify |
| Получение подписок | GET /api/v3/status_subscription |
| Отмена | POST /api/v1/constructor/stripe/subscriptions/cancel |
| Заморозка | POST /api/v1/constructor/stripe/subscriptions/freeze |