Настройка подписочных платежей на 1С-Битрикс
Подписочная монетизация — бизнес-логика поверх рекуррентных платежей. В Битрикс нет нативного модуля подписок, реализация всегда кастомная. Сложность не в самом списании (оно решается за 1–2 дня), а в управлении жизненным циклом: смена тарифа, триальный период, отмена с сохранением доступа до конца периода, retry при неудачном платеже.
Компоненты системы
| Слой | Что делает |
|---|---|
| Тарифные планы | Хранит условия: цена, период, возможности |
| Подписки пользователей | user → plan, даты периода, статус |
| Токены карт | rebill_id от эквайера |
| Биллинг-планировщик | Cron для создания платежей по расписанию |
| Управление доступом | Проверка активной подписки при запросе |
| Уведомления | Email при успехе, ошибке, окончании |
Структура данных
CREATE TABLE b_subscription_plans (
id SERIAL PRIMARY KEY,
code VARCHAR(32) UNIQUE NOT NULL,
name VARCHAR(128),
price DECIMAL(10,2),
currency CHAR(3) DEFAULT 'RUB',
period_days INT NOT NULL,
trial_days INT DEFAULT 0,
is_active BOOLEAN DEFAULT TRUE
);
CREATE TABLE b_user_subscriptions (
id SERIAL PRIMARY KEY,
user_id INT NOT NULL,
plan_id INT REFERENCES b_subscription_plans(id),
rebill_id VARCHAR(128),
status VARCHAR(16) DEFAULT 'trialing',
trial_ends_at TIMESTAMP,
period_start TIMESTAMP,
period_end TIMESTAMP,
cancel_at_period_end BOOLEAN DEFAULT FALSE,
retry_count INT DEFAULT 0,
last_payment_at TIMESTAMP,
created_at TIMESTAMP DEFAULT NOW()
);
Статусы: trialing → active → past_due → paused / cancelled / expired.
Биллинг-планировщик
// /local/cron/subscription_billing.php
// Cron: 0 9 * * * php /var/www/shop/local/cron/subscription_billing.php
define('NO_KEEP_STATISTIC', true);
require $_SERVER['DOCUMENT_ROOT'] . '/bitrix/modules/main/include/prolog_before.php';
$db = Bitrix\Main\Application::getConnection();
$due = $db->query("
SELECT s.id, s.user_id, s.rebill_id, p.price, p.currency, p.period_days, u.EMAIL
FROM b_user_subscriptions s
JOIN b_subscription_plans p ON p.id = s.plan_id
JOIN b_users u ON u.ID = s.user_id
WHERE s.status = 'active'
AND s.cancel_at_period_end = FALSE
AND DATE(s.period_end) = CURRENT_DATE
");
while ($row = $due->fetch()) {
try {
$success = chargeRebill($row['rebill_id'], $row['price'], $row['currency']);
if ($success) {
$db->query("UPDATE b_user_subscriptions SET
period_start = period_end,
period_end = period_end + INTERVAL '" . (int)$row['period_days'] . " days',
retry_count = 0,
last_payment_at = NOW()
WHERE id = " . (int)$row['id']);
createBitrixOrderForSubscription($row);
} else {
$db->query("UPDATE b_user_subscriptions
SET status = 'past_due', retry_count = retry_count + 1
WHERE id = " . (int)$row['id']);
sendPaymentFailedNotification($row['EMAIL']);
}
} catch (\Exception $e) {
logError('billing', $row['id'], $e->getMessage());
}
}
Проверка активной подписки в компонентах
function userHasSubscription(int $userId, string $planCode = null): bool
{
$db = Bitrix\Main\Application::getConnection();
$sql = "SELECT COUNT(1) FROM b_user_subscriptions s
JOIN b_subscription_plans p ON p.id = s.plan_id
WHERE s.user_id = " . (int)$userId . "
AND s.status IN ('active', 'trialing')
AND s.period_end > NOW()";
if ($planCode) {
$sql .= " AND p.code = '" . $db->getSqlHelper()->forSql($planCode) . "'";
}
return (int)$db->queryScalar($sql) > 0;
}
// В шаблоне закрытого раздела
if (!userHasSubscription($USER->GetID(), 'premium')) {
LocalRedirect('/subscribe/?redirect=' . urlencode($APPLICATION->GetCurPage()));
}
Кейс: SaaS-платформа, переход на подписки
Клиент — B2B-сервис автоматизации отчётности на Битрикс. До этого — единовременные лицензии. Задача: перевести клиентов на ежемесячную подписку с автоматическим продлением.
Сделали: три тарифных плана, биллинг через Тинькофф с rebill_id, отдельный ЛК управления подпиской, email-уведомления за 3 дня до списания, retry через 1/3/7 дней. Интеграция с системой доступов — через проверку userHasSubscription() в каждом защищённом компоненте.
Сроки
| Задача | Срок |
|---|---|
| Структура БД и бизнес-модели | 1–2 дня |
| Страница выбора плана и оформления | 2–3 дня |
| Биллинг-планировщик | 1–2 дня |
| ЛК управления подпиской | 1–2 дня |
| Уведомления и retry | 1 день |







