Тарифи та білінг
Метадані
Section titled “Метадані”| Поле | Значення |
|---|---|
| Статус | 🚧 в розробці (мікс: підписки Free/Pro/Max — backend-only, нічого не gate-ять; tutor-tokens — 🟢 prod) |
| Поверхні | web (підписки — лише backend; tutor-tokens — web UI + bot заплановано) |
| Доступність | tutor-tokens — потрібен акаунт для покупки (каталог пакетів публічний); підписки Free/Pro/Max — недоступні користувачу (нема UI, плани не засіяні) |
| Власник | TODO: |
| Останнє підтвердження | 2026-06-14 · curl (api.abitly.org/tutor/tokens/packages — 3 живі пакети; /subscriptions/plans → []; abitly.org/uk/tutor — жива сторінка) + код:main (abitly-api-v2, abitly-frontend-v2) |
Призначення
Section titled “Призначення”Описує дві паралельні монетизаційні моделі платформи та їхній реальний стан:
- Підписки Free / Pro / Max — багаторівневі тарифи з помісячним білінгом і per-plan фічами. Повністю реалізовані в api-v2 (модуль
subscriptions), але на момент перевірки нічого не обмежують: фронтенд не має ні сторінки тарифів, ні API-клієнта, плани в проді не засіяні (/subscriptions/plans→[]), аFeatureResolverServiceне викликається жодним продуктовим модулем. - AI-tutor токени — разові покупки пакетів токенів, якими користувач оплачує звернення до AI-тьютора. Це жива prod-монетизація: публічний каталог пакетів, оплата через Monobank, баланс гейтить надсилання повідомлень тьютору.
Окремо від тарифів існує модель premium one-off (разові покупки навчальних матеріалів) — інша поверхня оплати, описана у своїй картці.
User flow
Section titled “User flow”tutor-tokens (🟢 prod)
Section titled “tutor-tokens (🟢 prod)”Точка входу: https://abitly.org/uk/tutor (роут /tutor, без локалізованого перейменування в PATHNAMES).
- Лендинг тьютора показує блок пакетів (
PricingSection/LandingPackageCard) — дані з публічногоGET /tutor/tokens/packages(не потребує логіну). - Купівля пакета: гість → Google-логін; залогінений →
POST /tutor/tokens/purchase{ packageSlug, returnUrl }→ редирект на Monobank. - Повернення на
/tutor/purchase/success— пулінгGET /tutor/tokens/order-status?orderId=…доwalletCreditedAt(токени зараховано в гаманець). - У чаті тьютора баланс показує
BalancePill; при нестачі токенів —InsufficientTokensModal(стрім тьютора повертаєbalance_exhausted).
Платіжний редирект — той самий рейл, що й матеріали: data-flows — оплата Monobank. Особливість: після оплати webhook не просто помічає ордер як paid, а кредитує зовнішній tutor-wallet (TutorWalletClientService), із lease-таймстампом і звіркою через wallet-reconciler.cron.
Підписки Free/Pro/Max (🚧 backend-only)
Section titled “Підписки Free/Pro/Max (🚧 backend-only)”Користувацького flow немає — нема сторінки тарифів і API-клієнта на фронтенді. Спроєктований backend-flow (для довідки, не доступний у проді):
GET /subscriptions/plans— список активних планів із фічами (сторінка тарифів). У проді зараз повертає[].POST /subscriptions/subscribe{ planSlug }— Free → одразуACTIVE; платний →PENDING_PAYMENT+ Monobank-інвойс.POST /subscriptions/change-plan— апгрейд (одразу, з пропорційним донарахуванням) / даунгрейд (з кінця періоду).POST /subscriptions/cancel— скасування в кінці періоду.- Помісячне поновлення — щоденний крон
08:00 UTCстворює новий Monobank-інвойс (нативного recurring у Monobank немає). Деталі станів і крону — у домен-доці api-v2.
Дані та API
Section titled “Дані та API”| Джерело | Ендпоінт / entity | Призначення |
|---|---|---|
| api-v2 | GET /tutor/tokens/packages (public) | каталог пакетів токенів — 3 живі: small_250 (49₴/250), best_1000 (149₴/1000), power_2000 (249₴/2000) |
| api-v2 | POST /tutor/tokens/purchase · GET /tutor/tokens/order-status (JWT) + Monobank webhook | покупка токенів; entity TokenOrder (таблиця nmt_tests.tutor_token_orders) |
| api-v2 | GET /subscriptions/plans (public) · /subscriptions/me · /subscribe · /change-plan · /cancel · /features[/:key] (JWT) | модуль підписок — робочі ендпоінти, але без споживачів на фронтенді; /plans у проді → [] |
| api-v2 | entities Plan, PlanFeature, UserFeatureOverride, Subscription, SubscriptionInvoice | тарифи, key-value фічі плану, per-user override, підписка, інвойси (shared Postgres) |
| api-v2 | admin POST/PATCH/DELETE /admin/subscriptions/plans[…] | CRUD планів/фіч, guard AdminApiKeyGuard (X-Admin-Key) — плани засіваються лише адміном |
Per-feature першоджерело — домен-доки api-v2: subscriptions/overview, plans-and-features.
Зв’язки з іншими фічами
Section titled “Зв’язки з іншими фічами”- Checkout Monobank — спільний платіжний рейл для покупки токенів і (потенційно) підписок; той самий merchant, різні webhook-URL і
reference. - Навчальні матеріали — окрема монетизація (premium one-off), не тариф; разом із підписками шарить
MonobankServiceізBillingModule. - Автентифікація — покупка токенів і підписка вимагають JWT; pending-purchase resume після Google-логіну.
- AI-тьютор (
/tutor) — споживач токенів; картки тьютора ще немає (див. індекс).
| Репо | Шлях | Примітка |
|---|---|---|
abitly-api-v2 | src/api/subscriptions/** | модуль Free/Pro/Max: контролери, feature-resolver.service.ts, subscription-billing.{service,cron}.ts, guards/admin-api-key.guard.ts |
abitly-api-v2 | src/api/tutor-billing/** · src/database/entities/tokenOrder.ts | покупка токенів: контролер, webhook, tutor-wallet-client.service.ts, wallet-reconciler.cron.ts |
abitly-api-v2 | src/api/tutor/** · src/api/billing/** | AI-тьютор (/tutor/sessions, guard TutorDevAuthGuard → у проді = JWT); MonobankService спільний у BillingModule |
abitly-frontend-v2 | src/app/[locale]/(main)/tutor/** · src/components/pages/ai-tutor/** | лендинг тьютора з PricingSection, /tutor/purchase/success |
abitly-frontend-v2 | src/api/tokens/** · src/components/pages/shared/Tutor/components/Tokens/** | API-клієнт токенів; BalancePill, InsufficientTokensModal |
abitly-frontend-v2 | — | немає клієнта /subscriptions/* і сторінки тарифів Free/Pro/Max (підтверджено пошуком по src/) |
Обмеження та блокери
Section titled “Обмеження та блокери”- Підписки нічого не gate-ять.
FeatureResolverService(override → план → free-fallback) існує і покритий тестами, алеgetFeatureValue/getAllFeaturesвикликаються виключно всередині модуляsubscriptions(дляGET /subscriptions/features) і в тестах. Жоден продуктовий модуль (offers, materials, nmt-test, vstuplysty…) не імпортуєSubscriptionsModuleі не звертається до резолвера → ліміти планів (напр.max_saved_offers,has_analytics) не діють. - Підписки не мають UI і не засіяні в проді. Фронтенд не має сторінки тарифів чи API-клієнта;
GET /subscriptions/plansу проді →[],GET /subscriptions/features/max_saved_offers(anon) →{"value":null}. Засів планів — лише через адмін-ендпоінти (AdminApiKeyGuard). - tutor-tokens — жива, але вузька монетизація: покриває лише AI-тьютора, не дає доступу до інших платних фіч. Каталог пакетів публічний; покупка і баланс — за JWT.
- Token credit — багатокроковий: webhook не лише помічає ордер
paid, а кредитує зовнішній wallet черезTutorWalletClientService; невдалий кредит підхоплюєwallet-reconciler.cron(walletCreditAttemptedAtlease).cost_units—bigint, не приводити доNumber(). ⚠️ Суми пакетів продубльовані whitelist-ом у tutor-сервісі — міняти лише «трійкою», інакше кредити мовчки 400-яться: див. інцидент 2026-07-16. - Power-вартість vs. ціна: ціни/розміри пакетів і
priceCoinsприходять із backend (/tutor/tokens/packages); не хардкодити на фронтенді. - Власник фічі —
TODO:.
Історія змін
Section titled “Історія змін”| Дата | Подія | Джерело |
|---|---|---|
TODO: | реліз модуля subscriptions (Free/Pro/Max + billing) у main — без споживачів-гейтів | abitly-api-v2 |
TODO: | реліз tutor-tokens (AddTutorTokenOrders) і прод-лендингу /tutor | abitly-api-v2 / abitly-frontend-v2 |
| 2026-06-14 | створено картку; розмежовано prod (tutor-tokens) vs. dormant backend (Free/Pro/Max); гейтинг звірено пошуком call-site + live API | PR Wave 1 |
| 2026-07-16 | інцидент: 45 днів покупки токенів не кредитували гаманець (дрейф whitelist у tutor); виправлено + бекфіл — пост-мортем | abitly-AI-tutor@cdf904c |