S Subster

Диагностика

Вы пройдёте путь от оплаты до доступа в приложении по двум журналам кабинета и одному запросу — и найдёте место, где он оборвался.

Перед началом

Оба журнала живут в кабинете владельца проекта, в разделе «Подключение приложения».

Что смотретьГде этоКому видно
Логи SDK — что присылало приложениеподвкладка «Логи SDK», секция «Что приходило от приложения»всем, кроме участника проекта
Журнал доставок — что мы отправляли на ваш серверподвкладка «Для разработчика» → «События на ваш сервер» → кнопка «Журнал» в строке получателявсем ролям проекта

Если кабинета у вас нет, попросите владельца проекта позвать вас в проект. Роль администратора нужна отдельно — только ею выпускаются ключи доступа и настраиваются получатели событий. Кто кого зовёт и какие бывают роли — «Проект и команда» и «Передача разработчику» на дорожке владельца.

С чего начать

СимптомКуда смотретьЧто это значит
приложение говорит «доступа нет», а деньги списанысначала «Логи SDK» по этому устройствузаписи есть — идите к запросу о праве доступа; записей нет вовсе — приложение до нас не дошло
в ответе о праве есть действующая запись, а библиотека говорит «доступа нет»«SDK», шаг 4библиотека читает только самую свежую запись; если она истекла или отозвана, старая действующая не видна
iOS: оплата прошла в веб-воронке, ссылка возврата открыла приложение, а доступа нет«Логи SDK»: шаг identify.cached_guid вместо обмена кодабиблиотека уже хранила другой идентификатор и код не обменяла — «SDK», шаг 3
Android: после оплаты в окне браузера колбэк пришёл пустым«SDK», шаг 5библиотека ждала право минуту с момента открытия окна; спросите право заново
экран с ценами из приложения не открылся: unavailable или nil«SDK», шаг 5нет configure, либо экран по ID не нашёлся: не опубликован или домен привязан к воронке, а не к нему
Android: экран с ценами или квиз открылся, а потом закрылся сам с Unavailable«Логи SDK»: шаг paywall.webview_process_terminated_twiceс версии 0.7.2 страница во встроенном окне упала два раза подряд, и библиотека закрыла показ; если оплата успела пройти, пришёл бы Paid — «SDK», шаг 5
ваш сервер не получил событие о покупкежурнал доставок получателястрока-попытка с кодом ответа — не принял ваш сервер; строка решения — мы не отправляли, причина в строке
любой запрос /s2s/v1/* отвечает 403, в теле код bridge_tier_expiredраздел «Дверь закрыта по тарифу» нижеотключена не одна операция, а вся интеграция: кончился оплаченный период
тот же 403, но код bridge_temporarily_unavailableникуда: проверять нечегоэто не про ваш тариф. Сбой на нашей стороне: тариф проверить не удалось. С вашими ключами и оплатой всё в порядке — повторите запрос позже
платное открыто и тем, кто не платилполе testMode в ответе о праве доступавключён режим проверки: настоящие покупки не читаются вовсе
режим проверки включили, а в ответе testMode: falseраздел «Проверить, не дожидаясь настоящей оплаты» нижережим не сохранён, на вашем пути подключения его нет, либо идентификатор проекту незнаком

Дошёл ли запрос от приложения

Подвкладка «Логи SDK» — лента того, что присылает наша библиотека: время по всемирному, уровень, имя шага (identify.resolve_failed и подобные), сообщение и устройство. Отбор — по периоду, уровню, имени шага и идентификатору.

Второй вид ленты, «По устройствам», собирает строки по тому же идентификатору покупателя (guid), по которому вы спрашиваете право доступа; кабинет подписывает его словом «Устройство». С этого вида и начинайте, когда разбираете жалобу конкретного человека.

Пустая лента — это диагноз, а не поломка журнала. Приём записей отвечает «принято» всегда и ничего не проверяет: пачка с чужим или несуществующим номером проекта отбрасывается молча. Значит, одно из четырёх: приложение ещё не запускали, в нём стоит номер другого проекта, библиотека в нём не используется или её версия старше журнала — на iOS журнал ведётся с 0.6.0, на Android с 0.7.0. Первое отличает подвкладка «Основное»: там написано либо «Приложение ещё не обращалось к нам», либо «Связка работает» с датой последней установки.

Записи уходят пачками, поэтому последние секунды могут ещё не доехать — обновите ленту через полминуты. Версию библиотеки лента пишет рядом с платформой. Имена шагов и как увидеть их в консоли на своём устройстве — «SDK».

Если вы собрали интеграцию на своём сервере, без нашей библиотеки, этой ленты у вас не будет: разбор идёт по двум оставшимся местам.

Что отвечает право доступа

curl "https://api.subster.ai/s2s/v1/entitlement?guid=<GUID>" \
  -H "Authorization: Bearer sk_live_…"

Ключ годится любой: режим ключа и среда оплаты Stripe — разные вещи. Право доступа читает и тестовый ключ, и покупки, сделанные в песочнице, в ответе видны.

Что в ответеЧто это значит
есть запись со статусом activeоплата у нас есть — смотрите свою сторону: как приложение читает ответ. Наша библиотека смотрит только самую свежую запись — «SDK»
список записей (grants) пустк этому идентификатору оплата не привязана. Право могло ещё не выдаться — повторите через несколько секунд. Пусто и тогда — идентификатор не тот: посмотрите в логах SDK, какой отдала библиотека этому человеку, а если покупателя заводил ваш сервер — в своей базе
testMode: trueвключён режим проверки, настоящие покупки не читаются
404идентификатор неизвестен или принадлежит другому проекту

Разбирайте весь список записей, а не первую: доступ открыт, если есть хотя бы одна со статусом active. Что означает каждое поле — «Проверить право доступа».

Доступ доставляете через Adapty или RevenueCat? Тогда проверьте ещё и привязку профиля: GET /s2s/v1/identity/link-profile?guid=<GUID> показывает, какой профиль мы сохранили. Ответ linked: false — это диагноз «профиль не привязан», а не ошибка запроса: оплата у нас есть, а в подписочную платформу она не поехала, потому что отдавать её было некому. Как привязать профиль — «Adapty и RevenueCat».

Доехало ли наше событие

Кнопка «Журнал» в строке получателя открывает его доставки за 30 суток. Строки бывают двух видов, и это главная развилка разбора.

Попытка — мы отправляли. В строке код ответа вашего сервера и номер попытки. Ошибка здесь означает, что не принял ваш сервер: повторяем до восьми раз с удвоением паузы от 30 секунд, укладываясь примерно в час.

Строка решения — мы не отправляли. Кода ответа у неё нет, вместо него причина и счётчик, сколько раз такое решение повторилось за час. Причин шесть, перечень закрытый — других в строке не появится.

ПричинаЧто это значит
consent_deniedпосетитель отказался от отслеживания; касается только аналитических событий, транзакционные уходят всегда
not_subscribedтип события не включён у этого получателя — либо имя свойства не попало в его список
bridge_tier_expiredкончился оплаченный период, см. ниже
rate_limitedпотолок 300 событий в минуту на адрес; он стоит только на потоке шагов воронки (funnel.analytics_event) и остальных типов не касается. Сверх потолка события пропускаются без повторов
duplicate_eventэто же событие уже стоит в доставке
not_live_trafficслужебный трафик — наш замер скорости воронки; такие события получателям не отправляются, делать ничего не нужно

Строки нет вовсе — значит, получатель выключен: строки пишутся только включённым адресам. Адрес выключаем мы сами: если за трое суток по нему были доставки и ни одна не прошла. Владельцу проекта уходит письмо, а включить адрес обратно может он сам или администратор проекта — кнопкой в той же строке. Ручные проверки в этот счёт не идут.

Дверь закрыта по тарифу

Работа интеграции входит в платный тариф. Когда организация перестаёт быть оплаченной, всё продолжает работать ещё семь дней, и только потом интеграция отключается целиком: серверные запросы, приём событий и отправка событий на ваш сервер — сразу все, а не по одной операции.

Семь дней считаются с первого дня неоплаты — с первого неудачного списания, а не с закрытия подписки. Дату отключения владелец проекта получает письмом.

Где видноЧто приходит
/s2s/v1/*403, в теле код bridge_tier_expired
/public/event и /public/identity/resolve-by-email404 без кода: отказ по тарифу там неотличим от любого другого
журнал доставоксобытия просто перестают приходить, в журнале — строка решения bridge_tier_expired

Ключи при этом не отзываются. Вернулись на платный тариф — интеграция работает с теми же ключами и тем же секретом, перевыпускать нечего.

Остальные коды отказа закрывают одну операцию, а не всю интеграцию — они разобраны в «Деньгах из приложения».

Кабинет прячет раздел до того, как интеграция отключится. Как только тариф стал бесплатным, подвкладка «Для разработчика» перестаёт показывать ключи и получателей событий — а запросы в это время ещё работают, пока идёт льготный срок.

Проверить, не дожидаясь настоящей оплаты

Что проверяемЧем
приложение открывает платные экранырежим проверки — только на пути «Наша библиотека в приложении»
весь путь целиком: оплата, возврат в приложение, право доступа, событиятестовые ключи Stripe
ваш обработчик событий и проверка подписикнопка «Отправить тестовое событие»

Режим проверки включают в «Подключении приложения», вкладка «Основное»: в группе «Что ещё умеет подключение» раскройте ветку «Проверить связку до первой оплаты», нажмите «Включить», затем «Сохранить настройки» внизу вкладки. Кнопка в ветке и ссылка «Выключить» в оранжевой полосе меняют только несохранённые настройки: пока их не сохранили, на сервере режим прежний. Включают владелец или администратор проекта.

Пока режим включён, право доступа отвечает выдуманной действующей записью по любому идентификатору, который проект знает, — посетителя воронки или покупателя, заведённого вашим сервером. Незнакомый идентификатор получает обычный ответ — пустой grants на публичном адресе и 404 на серверном.

Уровень в выдуманной записи всегда test, а не настроенный в проекте: приложение, которое сверяет уровень со своим словом, так ничего и не откроет — на время проверки считайте этот уровень подходящим тоже. Настоящие покупки в это время не читаются, поэтому на боевом проекте режим не включают.

На путях «Собственный сервер» и «Приложение не подключаю» режим держит выключенным наш сервер, поэтому ветки «Проверить связку до первой оплаты» на вкладке нет. Ссылка «Показать остальные настройки» под кнопкой «Сохранить настройки» её откроет, но «Включить» там недоступна. Проверяйте тестовыми ключами Stripe.

Убедиться, что режим включился, можно двумя запросами: заведите покупателя со своего сервера (ключ годится и тестовый) и спросите его право.

curl -X POST "https://api.subster.ai/s2s/v1/users" \
  -H "Authorization: Bearer sk_test_…" \
  -H "Content-Type: application/json" \
  -d '{"externalId": "test-mode-check"}'
# { "guid": "<GUID>", "user_id": "<GUID>", "paywallUrl": null }

curl "https://api.subster.ai/public/entitlement?guid=<GUID>"
# { "guid": "<GUID>", "testMode": true, "grants": [{ "level": "test", "status": "active", … }] }

Тестовые ключи Stripe дают единственный полный прогон: владелец проекта переводит среду оплаты на тестовую, и воронку можно пройти насквозь тестовой картой. Деньги не списываются, а покупка, возврат в приложение и события происходят по-настоящему. Цены тестовой среды в боевой не работают — там их заводят заново.

«Отправить тестовое событие» лежит в строке получателя. Мы шлём событие типа webhook.test, и в журнале появляется ответ вашего сервера — так проверяется и обработчик, и подпись. Подписаться на этот тип нельзя, и на выключение адреса такие проверки не влияют.

Перед выходом в эфир

Пройдите список, когда проверка закончена и воронка вот-вот пойдёт в рекламу.

  1. Режим проверки выключен и сохранён. Ответ о праве доступа по идентификатору проекта приходит с testMode: false.
  2. Среда оплаты — боевая. Переключает владелец проекта: «Цены и приём оплаты», шаг 4. После этого тестовые платежи не принимаются.
  3. У каждой боевой цены есть уровень доступа. Цены тестовой среды в боевой не работают, а у заведённых заново — новые идентификаторы. Без пары «цена → уровень» приложение получит идентификатор цены вместо своего слова, а экран с кнопкой возврата в приложение не опубликуется. Пары задаются в кабинете — «Передача разработчику», шаг 5.
  4. На сервере боевой ключ sk_live_…, если вы списываете или возвращаете деньги. Тестовый ключ на этих операциях получает отказ test_key_not_allowed.
  5. Адрес получателя событий доступен из интернета. Адреса внутри вашей сети мы не вызываем. Нажмите «Отправить тестовое событие»: в журнале должен появиться ответ 2xx вашего сервера.
  6. Для Android вписан отпечаток ключа подписи, а не ключа загрузки. Перепутанный отпечаток ломает переход в приложение без видимой ошибки — «Возврат в приложение».

Дальше