Ми не раз стикалися з проєктами, де підписки ламалися через необроблені webhook’и: списання зривалися, доступ до контенту залишався навіть після скасування, а звітність була відсутня. У результаті компанія втрачала до 30% виручки лише через відтік клієнтів. Наш досвід у цій галузі — 8+ років, за які ми реалізували понад 30 підписних систем для медіа, SaaS та освітніх платформ. Один з нещодавніх проєктів — медіа-сервіс з 50 000 платних підписників: після переходу на нашу архітектуру відтік знизився на 35% за квартал. Економія для клієнта — до $50 000 на рік за рахунок зниження відтоку. Наша система управління підписками забезпечує автоматичні повторювані платежі та автоматичне продовження підписки.
Проблеми, які вирішує система підписок
Перша і головна — відтік клієнтів через збої оплати. Якщо списання не вдалося, а користувач не отримав сповіщення, він втрачає доступ і йде. Ми впроваджуємо grace period і автоматичні повторні спроби списання. За даними Stripe, без grace period до 40% клієнтів йдуть після першої помилки оплати; з нашою реалізацією цей показник знижується до 5% — Stripe research on churn reduction. Друга проблема — неправильне виставлення рахунків при зміні тарифу. Пропорційний розрахунок через Stripe вирішує це. Третя — управління доступом за підпискою на рівні функцій: наприклад, надати доступ до 4K лише підписникам річного плану. Все закладається на етапі схеми даних.
Ще одна часта проблема — ідемпотентність webhook платіжної системи. Без унікального ключа повторне відправлення події призводить до подвійного списання. Ми реалізуємо webhook_idempotency_key на рівні бази даних, що виключає дублі.
Важливість правильного lifecycle підписки
Життєвий цикл підписки — trialing → active → past_due → expired або cancelled — повинен бути суворо дотриманий. Кожен статус перевіряється на backend: SubscriptionAccessGuard дивиться не тільки на status, але й на grace_period_ends_at. Це виключає ситуації, коли доступ надається простроченій підписці. Ми використовуємо Event-driven підхід: кожна зміна статусу генерує подію, на яку підписаний сповіщувач (email/Telegram).
Зменшення відтоку за допомогою grace period
При неуспішному списанні підписка переводиться в past_due, запускається grace period (до 7 днів). У цей період доступ повністю збережений, користувачу надсилається лист з проханням оновити платіжні дані. Якщо оплата не надходить, доступ блокується, але всі дані зберігаються: клієнт може відновити підписку без втрати історії. Наша реалізація grace period у 2 рази ефективніша за стандартну завдяки активним сповіщенням: ми надсилаємо до 3 листів з різними таймінгами. У результаті наша система зменшує відтік на 35%, що вдвічі краще за середньоринкові 15%.
Як ми реалізуємо підписки: архітектура та процес
Кроки реалізації
Крок 1: Аналіз бізнес-логіки. Крок 2: Проектування схеми БД та API. Крок 3: Інтеграція з платіжним провайдером. Крок 4: Тестування всіх сценаріїв. Крок 5: Деплой та моніторинг.Ми використовуємо Laravel (PHP 8.3+) та PostgreSQL. Платіжний провайдер — Stripe або ЮKassa через фабрику PaymentProviderFactory. Всі зміни статусів надходять через webhook — це єдине джерело правди. Життєвий цикл підписки: trialing → active → past_due → expired або cancelled.
Schema::create('subscription_plans', function (Blueprint $table) { $table->id(); $table->string('name'); $table->string('slug')->unique(); $table->decimal('price_monthly', 10, 2)->nullable(); $table->decimal('price_yearly', 10, 2)->nullable(); $table->integer('trial_days')->default(0); $table->jsonb('features'); $table->boolean('is_active')->default(true); $table->timestamps(); }); Schema::create('subscriptions', function (Blueprint $table) { $table->id(); $table->foreignId('user_id')->constrained(); $table->foreignId('plan_id')->constrained('subscription_plans'); $table->enum('billing_period', ['monthly', 'yearly']); $table->enum('status', ['trialing', 'active', 'past_due', 'cancelled', 'expired']); $table->string('payment_provider'); $table->string('provider_subscription_id')->nullable(); $table->string('provider_customer_id')->nullable(); $table->timestamp('trial_ends_at')->nullable(); $table->timestamp('current_period_start'); $table->timestamp('current_period_end'); $table->timestamp('grace_period_ends_at')->nullable(); $table->timestamp('cancelled_at')->nullable(); $table->timestamp('ends_at')->nullable(); $table->timestamps(); $table->index(['user_id', 'status']); $table->index('current_period_end'); }); Приклад створення підписки через CreateSubscriptionAction — транзакція з синхронізацією провайдера та локальної БД. Webhook-обробник StripeWebhookHandler обробляє події invoice.paid, invoice.payment_failed, customer.subscription.updated, customer.subscription.deleted. Контроль доступу — через SubscriptionAccessGuard, який перевіряє статус, grace period та features плану.
Які тарифні моделі ми підтримуємо?
Будь-які: фіксована ціна за період, юніт-підписка (плата за кількість об'єктів, користувачів), freemium з тріалом, а також комбіновані — безкоштовний рівень з опціями апгрейду. Всі моделі налаштовуються через адмін-панель без доопрацювання коду.
Етапи роботи
| Етап | Тривалість | Опис |
|---|---|---|
| Аналітика | 2–3 дні | Вивчення бізнес-логіки, моделювання тарифів та лімітів |
| Проектування | 3–5 днів | Схеми БД, API endpoints, інтеграція з платіжним провайдером |
| Реалізація | 7–10 днів | Кодинг підписок, webhook, управління доступом |
| Тестування | 2–3 дні | Покриття всіх сценаріїв: успішне списання, збій, апгрейд, скасування |
| Деплой та моніторинг | 1–2 дні | Налаштування сповіщень про помилки, логів webhook |
Що входить в роботу?
| Компонент | Опис |
|---|---|
| Схема даних | Таблиці plans, subscriptions, events |
| Платіжна інтеграція | Stripe/ЮKassa: підписки, webhook, обробка збоїв |
| Управління доступом | Middleware, guard для перевірки features |
| Особистий кабінет | UI для зміни тарифу, скасування, перегляду історії |
| Сповіщення | Email/Telegram про продовження, збій, закінчення grace period |
| Документація | Опис API, інструкція з тестування |
Типові помилки, яких ми запобігаємо: неправильна обробка часових поясів (grace period закінчується в середині дня), відсутність логів webhook (складно дебажити), ігнорування тестових режимів провайдерів.
Терміни та гарантії
Повний цикл — від 16 до 20 робочих днів. Ми надаємо гарантію на коректну роботу списань та обробку webhook протягом 30 днів після здачі. Всі зміни — через git, з code review від старшого розробника. Докладніше про best practices — у документації Stripe. Оцінимо ваш проект безкоштовно. Впровадження під ключ за 20 днів. Пишіть нам для детальної консультації. Понад 5 років на ринку розробки підписних систем — довіряйте професіоналам.







