Диагностика
Вы пройдёте путь от оплаты до доступа в приложении по двум журналам кабинета и одному запросу — и найдёте место, где он оборвался.
Перед началом
Оба журнала живут в кабинете владельца проекта, в разделе «Подключение приложения».
| Что смотреть | Где это | Кому видно |
|---|---|---|
| Логи 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-email | 404 без кода: отказ по тарифу там неотличим от любого другого |
| журнал доставок | события просто перестают приходить, в журнале — строка решения 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, и в журнале появляется ответ вашего сервера — так проверяется и обработчик, и подпись. Подписаться на этот тип нельзя, и на выключение адреса такие проверки не влияют.
Перед выходом в эфир
Пройдите список, когда проверка закончена и воронка вот-вот пойдёт в рекламу.
- Режим проверки выключен и сохранён. Ответ о праве доступа по идентификатору проекта приходит с
testMode: false. - Среда оплаты — боевая. Переключает владелец проекта: «Цены и приём оплаты», шаг 4. После этого тестовые платежи не принимаются.
- У каждой боевой цены есть уровень доступа. Цены тестовой среды в боевой не работают, а у заведённых заново — новые идентификаторы. Без пары «цена → уровень» приложение получит идентификатор цены вместо своего слова, а экран с кнопкой возврата в приложение не опубликуется. Пары задаются в кабинете — «Передача разработчику», шаг 5.
- На сервере боевой ключ
sk_live_…, если вы списываете или возвращаете деньги. Тестовый ключ на этих операциях получает отказtest_key_not_allowed. - Адрес получателя событий доступен из интернета. Адреса внутри вашей сети мы не вызываем. Нажмите «Отправить тестовое событие»: в журнале должен появиться ответ
2xxвашего сервера. - Для Android вписан отпечаток ключа подписи, а не ключа загрузки. Перепутанный отпечаток ломает переход в приложение без видимой ошибки — «Возврат в приложение».
Дальше
- Проверить право доступа — что означает каждое поле ответа и почему пустой список приходит на незнакомый идентификатор.
- Вебхуки — как устроены события, их подпись и повторы.
- Возврат в приложение — настройка перехода, если покупатель не доезжает до приложения вовсе.
- MCP-сервер — подключить наш конспект по интеграции к своему ИИ-помощнику, чтобы он разбирал жалобы по нашим текстам, а не по памяти.