Как я, не будучи программистом, за несколько недель собрал медицинское приложение на связке GitHub Pages + Cloudflare Workers + Claude API — и что пошло не так.
Полгода назад я не написал ни строчки кода. Сегодня у меня есть работающее веб-приложение, которое анализирует результаты анализов крови с помощью ИИ, ведёт дневник давления, сахара и лекарств, принимает оплату подписки через Stripe и устанавливается на телефон как обычное приложение. Ниже — подробно о том, как это было устроено технически, какие ошибки я допустил по пути и что бы сделал иначе, если бы начинал сейчас.
С чего всё началось
Дома скопилась пачка результатов анализов за несколько лет — часть в бумажном виде, часть в PDF. Каждый раз одно и то же: смотришь на цифры, не понимаешь, что они значат в комплексе, гуглишь отдельные показатели, забываешь через неделю. Возникла простая мысль: а что, если отдать всю эту пачку ИИ и попросить объяснить по-человечески, что происходит. Я не программист. Но оказалось, что для реализации такой идеи сейчас это не обязательное условие — если понимать, из каких компонентов должна состоять система, и быть готовым разбираться с документацией методом проб и ошибок.
Архитектура: три бесплатных сервиса вместо сервера
Схема получилась такая:
- GitHub Pages — статический хостинг для самого интерфейса (HTML/CSS/JS). Бесплатно, надёжно, не требует сервера.
- Cloudflare Workers — serverless-прослойка между приложением и внешними API. Ключи хранятся в его переменных окружения на стороне Cloudflare, а браузер обращается только к самому Worker'у.
- Anthropic API (Claude) — обработка текста/фото/PDF с анализами и генерация понятного разбора.
- Stripe — приём платежей за подписку Premium.
Ничего из этого не требует классического сервера или базы данных — вся пользовательская история хранится в браузере пользователя (localStorage), а не на моей стороне. Это одновременно и плюс (простота, нет затрат на хранение, меньше ответственности за утечки), и ограничение (данные не синхронизируются между устройствами).
Первая серьёзная проблема: дыра в проверке оплаты
Когда я только запустил Stripe Payment Link, логика включения Premium была наивной: после успешной оплаты Stripe перенаправлял пользователя обратно на сайт с параметром ?premium=1 в ссылке, и код просто проверял его наличие:
Проблема очевидна, как только её произносишь вслух: любой человек мог вручную вписать ?premium=1 в адресную строку и получить Premium бесплатно, без всякой оплаты. Ссылка не проверяла, была ли оплата реальной.
Решение — добавить в Worker отдельный эндпоинт /verify-payment, который принимает session_id (Stripe присылает его при возврате после оплаты) и обращается напрямую к Stripe API, чтобы узнать реальный статус этой конкретной сессии:
Только если Stripe подтверждает paid: true для конкретного session_id — фронтенд включает Premium. Подделать это уже нельзя: session_id привязан к конкретной транзакции, и без реальной оплаты Stripe никогда не вернёт paid: true.
Урок: если платная функциональность в приложении включается по параметру в URL — это по умолчанию небезопасно, вне зависимости от того, насколько "неочевидным" кажется этот параметр. Любая проверка авторизации/оплаты должна происходить на сервере, обращающемся к первоисточнику данных, а не полагаться на то, что пришло в браузер.
PWA вместо магазинов приложений
Следующий вопрос — как дать пользователям что-то похожее на "установленное приложение", не проходя долгий путь модерации Google Play и App Store (тем более, что App Store особенно жёстко относится к приложениям, которые выглядят как обёртка над сайтом, и требует отдельной интеграции своей платёжной системы вместо Stripe для части стран).
Ответ — Progressive Web App. Это заняло на удивление немного работы:
— manifest.json с описанием приложения, иконками и цветовой темой
— sw.js — простой service worker, кэширующий статику для офлайн-доступа
— Пара строк в HTML для регистрации service worker и подключения манифеста
В результате при открытии сайта в Chrome на Android браузер сам предлагает «Установить приложение» —без всякого магазина. На iPhone то же самое доступно через Safari → «Поделиться» → «На экран Домой». После установки приложение открывается в полноэкранном режиме, без адресной строки — визуально неотличимо от нативного.
Для проекта на самой ранней стадии, без бюджета на разработку, это выглядит разумным промежуточным решением: закрывает главную пользу от "быть приложением", ничего не стоит и не требует ожидания модерации.
Первый выход к пользователям — и урок про модерацию соцсетей
Когда базовая функциональность и защита оплаты были готовы, я опубликовал пост о проекте на одной из крупных российских площадок с пользовательским контентом. В посте была история создания, описание функциональности и — в том числе — упоминание цены подписки.
Через несколько часов аккаунт был заблокирован навсегда за нарушение правил размещения рекламы. Формально пост действительно содержал признаки продвижения коммерческого продукта: прямую ссылку, упоминание цены, призыв попробовать.
Урок оказался полезным для дальнейших публикаций: на большинстве площадок с пользовательским контентом граница между "личной историей о своём проекте" и "рекламой" определяется формальными признаками — наличием цены, прямого CTA купить, ссылки без контекста. Если хочется избежать модерационных рисков:
- Не указывать конкретную стоимость в тексте
- Делать акцент на процессе и деталях реализации, а не на призыве попробовать продукт
- Заранее знакомиться с правилами конкретной площадки
Что дальше
Сейчас в работе:
- Английская версия интерфейса — для выхода за пределы русскоязычной аудитории
- Сборка через TWA для публикации в Google Play (после того, как появится первая группа реальных пользователей)
- Отдельная страница политики конфиденциальности — обязательный элемент доверия
для любого приложения, работающего с данными о здоровье.
Если интересно посмотреть, что получилось: https://aleksa09022019-dotcom.github.io/analyzes/MedScan.html. Оно бесплатно для базового использования, более глубокий ИИ-анализ трендов и совместимости лекарств доступен по платной подписке.
Отдельно подчеркну: это не медицинское устройство и не замена консультации врача — ИИ-разбор носит информационный характер, дисклеймер об этом есть прямо в интерфейсе.
Буду рад вопросам и критике в комментариях — особенно от тех, кто тоже собирал что-то на no-code инструментах и сталкивался с похожими граблями.