Setting Up Subscription Payments on 1C-Bitrix
Subscription monetization is business logic layered on top of recurring payments. Bitrix lacks a native subscription module—implementation is always custom. The complexity isn't in the charge itself (that’s solved in 1–2 days), but in lifecycle management: plan switching, trial periods, cancellation with access until period end, retry on failed payments.
We’ve been building such solutions for clients for over seven years. Along the way, we’ve encountered typical issues: payment gateway API incompatibility, duplicate charges, data loss during cron failures, and complexity in handling partial refunds. Our experience shows that without a well-thought-out architecture, subscription businesses lose up to 20% of revenue due to failed charges and customer churn. For example, churn from billing errors can lead to substantial revenue loss. A proper implementation prevents that.
Problems We Solve
Flexible Tariff Management
Off-the-shelf modules often don’t support complex scenarios like trials, freemium, or family plans. We create custom logic tailored to your business.
Reliable Billing
A scheduler with retry mechanism minimizes losses from failed payments. Each attempt is logged; when attempts are exhausted, the subscription is paused and the customer receives a notification.
Payment Gateway Integration
Correct setup of rebill_id via Tinkoff, Sber, or YooKassa APIs is required. We ensure seamless token transfer and hold handling.
How We Do It
Stack: PHP 8.1+, Bitrix (infoblocks v2.0, ORM), MariaDB, cron, REST API. We use separate tables for tariffs and subscriptions (see structure below). Tagged caching—cache clears when a subscription changes. All critical operations are wrapped in transactions.
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() ); Statuses: trialing → active → past_due → paused / cancelled / expired. Example billing scheduler code:
// /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()); } } How to Manage the Subscription Lifecycle?
After a successful charge, the period dates are updated and the retry counter is reset. If the charge fails, the status changes to past_due, triggering a retry chain. If a customer cancels, we keep access until the end of the paid period (cancel_at_period_end = TRUE). The scheduler will not create a new payment for such a subscription.
Example function to check active subscription:
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; } // In a protected template if (!userHasSubscription($USER->GetID(), 'premium')) { LocalRedirect('/subscribe/?redirect=' . urlencode($APPLICATION->GetCurPage())); } Why Custom Implementation Over Off-the-Shelf?
Pre-built modules from the Marketplace are often limited to standard scenarios: they don’t allow flexible retry, prorated billing, or integration with a unique CRM. A custom solution gives you full control—you decide when to charge, what notifications to send, and how to handle refunds. Moreover, we can integrate the subscription system with any acquirer: Tinkoff, Sber, YooKassa, ATOL.
| Feature | Off-the-Shelf Module | Custom Solution |
|---|---|---|
| Tariff flexibility | Limited | Full freedom |
| Retry logic | Basic | Configurable chain |
| CRM integration | None | Any CRM |
| Prorated billing | No | Yes |
Case: SaaS Platform Migration to Subscriptions
Our client—a B2B reporting automation service on Bitrix. Previously, they sold one-time licenses. Task: migrate clients to monthly subscriptions with automatic renewal. We implemented three tariff plans, billing via Tinkoff with rebill_id, a separate subscription management cabinet, email notifications 3 days before charge, and retry after 1/3/7 days. Integration with access system via userHasSubscription() check in every protected component. Result: client churn decreased by 15%, recurring revenue doubled.
What's Included
- Business requirements analysis and architecture design.
- Database creation and data model.
- API development for subscription management (create, change, cancel).
- Payment gateway integration (Tinkoff, Sber, YooKassa).
- Cron scheduler setup and retry logic.
- Testing of all scenarios and load testing.
- API and administration documentation.
- Training for your developers.
- 3-month warranty support.
Our Process
- Analysis — discuss tariff grid, subscription scenarios, payment options.
- Design — create database schema, API, documentation.
- Development — code in PHP, version control via Git.
- Testing — unit tests, integration scenarios, real data validation.
- Deployment — cron setup, migrations, load testing.
- Support — monitoring, refinements, 24/7 availability.
Estimated Timelines
| Task | Duration |
|---|---|
| Database structure and business model | 1–2 days |
| Plan selection and checkout pages | 2–3 days |
| Billing scheduler | 1–2 days |
| Subscription management cabinet | 1–2 days |
| Notifications and retry mechanism | 1 day |
Pricing is calculated individually, depending on integration complexity and customization scope. Contact us for a consultation on subscription architecture. Order a custom subscription module tailored to your needs—we'll prepare a commercial proposal with exact timelines.

