Development of a subscription system on 1C-Bitrix

Our company is engaged in the development, support and maintenance of Bitrix and Bitrix24 solutions of any complexity. From simple one-page sites to complex online stores, CRM systems with 1C and telephony integration. The experience of developers is confirmed by certificates from the vendor.
Our competencies:
Development stages
Latest works
  • image_website-b2b-advance_0.png
    B2B ADVANCE company website development
    1181
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Website development for FIXPER company
    813
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Development based on Bitrix, Bitrix24, 1C for the company Development of an Online Appointment Booking Widget for a Medical Center
    564
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Development based on 1C Enterprise for MIRSANBEL
    747
  • image_crm_dolbimby_434_0.webp
    Website development on CRM Bitrix24 for DOLBIMBY
    655
  • image_crm_technotorgcomplex_453_0.webp
    Development based on Bitrix24 for the company TECHNOTORGKOMPLEKS
    976

Developing a Subscription System for 1C-Bitrix

A subscription system is a model where a client pays regularly (monthly, quarterly, annually) and gains access to a service or goods. 1C-Bitrix does not have a built-in recurring payment mechanism — it must be built on top of the sale, catalog modules and custom code.

Types of Subscriptions in Bitrix Context

Before designing, it is important to define the business model by subscription type:

Type Description Technical Implementation
Content Access Paid section, restricted materials User groups + access restrictions
Product Subscription Regular goods delivery Automatic order creation
Service Subscription SaaS, license, support Account status + auto-renewal

All three types share common features: periodic payment and access status management.

Data Storage Architecture

The core of the system is the subscriptions table. Created via the Bitrix ORM (inheritance from \Bitrix\Main\ORM\Data\DataManager):

class SubscriptionTable extends DataManager
{
    public static function getTableName(): string
    {
        return 'b_local_subscription';
    }

    public static function getMap(): array
    {
        return [
            new IntegerField('ID', ['primary' => true, 'autocomplete' => true]),
            new IntegerField('USER_ID', ['required' => true]),
            new IntegerField('PLAN_ID', ['required' => true]),
            new EnumField('STATUS', ['values' => ['TRIAL', 'ACTIVE', 'PAST_DUE', 'CANCELLED', 'EXPIRED']]),
            new DatetimeField('CURRENT_PERIOD_START'),
            new DatetimeField('CURRENT_PERIOD_END'),
            new DatetimeField('TRIAL_END'),
            new StringField('PAYMENT_TOKEN'),    // Recurring payment token
            new IntegerField('PAY_SYSTEM_ID'),
            new StringField('CANCEL_REASON'),
            new DatetimeField('CANCELLED_AT'),
            new DatetimeField('CREATED_AT'),
        ];
    }
}

Subscription plans table:

class SubscriptionPlanTable extends DataManager
{
    // ID, NAME, PRICE, CURRENCY, PERIOD_DAYS, TRIAL_DAYS,
    // IBLOCK_SECTION_IDS (available sections), FEATURES (JSON)
}

Access Management by Subscription

For "content" type subscriptions, access is realized through Bitrix user groups. Each plan corresponds to a group (b_group). Upon subscription activation — add the user to the group:

CUser::SetUserGroup($userId, array_merge(
    CUser::GetUserGroup($userId),
    [$plan->getGroupId()]
));

Upon expiration or cancellation — remove from the group. Access to sections is restricted by infoblock or component permissions.

Recurring Payments

This is the most complex part. Recurring payments require the payment system to store the payment method (card) and perform automatic charges on demand. Supported systems: YooKassa (Yandex.Kassa), CloudPayments, Robokassa (recurring), Stripe (for international clients).

Workflow:

  1. First payment: regular payment through the payment system API. In response — payment_method_id or saved method token.
  2. Save token: record in SubscriptionTable.PAYMENT_TOKEN.
  3. Auto-charging: when CURRENT_PERIOD_END is reached — call the payment system API with the token:
// Example for YooKassa
$payment = new \YooKassa\Client();
$payment->setAuth($shopId, $secretKey);
$response = $payment->createPayment([
    'amount' => ['value' => $plan->getPrice(), 'currency' => 'RUB'],
    'payment_method_id' => $subscription->getPaymentToken(),
    'capture' => true,
    'description' => 'Subscription ' . $plan->getName() . ' #' . $subscription->getId(),
]);
  1. Handle result: success → update CURRENT_PERIOD_START, CURRENT_PERIOD_END, save new status ACTIVE. Failure → status PAST_DUE, notify client, retry in 24/48 hours.

Trial Period

When creating a subscription with a trial:

$trialEnd = (new DateTime())->modify('+' . $plan->getTrialDays() . ' days');
SubscriptionTable::add([
    'USER_ID' => $userId,
    'PLAN_ID' => $planId,
    'STATUS' => 'TRIAL',
    'TRIAL_END' => $trialEnd,
    'CURRENT_PERIOD_END' => $trialEnd,
]);

An agent 1 day before trial end — notify the user. On the last day — attempt the first charge. If no card is attached — change status to EXPIRED.

Subscription Processing Agent

Daily cron script:

// Find subscriptions requiring renewal
$expiring = SubscriptionTable::getList([
    'filter' => [
        'STATUS' => ['ACTIVE', 'PAST_DUE'],
        '<=CURRENT_PERIOD_END' => new DateTime(),
    ]
])->fetchAll();

foreach ($expiring as $sub) {
    try {
        $result = chargeSubscription($sub);
        if ($result->isSuccess()) {
            SubscriptionTable::update($sub['ID'], [
                'STATUS' => 'ACTIVE',
                'CURRENT_PERIOD_START' => new DateTime(),
                'CURRENT_PERIOD_END' => (new DateTime())->modify('+' . $planPeriodDays . ' days'),
            ]);
        } else {
            handlePaymentFailure($sub);
        }
    } catch (\Exception $e) {
        logError($e, $sub);
    }
}

Subscriber Personal Account

Minimal functionality for the /personal/subscription/ page:

  • Current plan and status.
  • Next billing date and amount.
  • Payment history.
  • Cancel button (with optional reason prompt).
  • Plan changes (upgrade/downgrade).
  • Payment method update.

Upon cancellation: STATUS = 'CANCELLED', CANCELLED_AT = now(). Access is preserved until CURRENT_PERIOD_END — the client paid for the period and uses what they paid for.

Notifications

Required email events:

  • SUBSCRIPTION_CREATED — subscription confirmation.
  • SUBSCRIPTION_PAYMENT_SUCCESS — successful charge, receipt.
  • SUBSCRIPTION_PAYMENT_FAILED — charge error, update card.
  • SUBSCRIPTION_TRIAL_ENDING — 1-3 days before trial ends.
  • SUBSCRIPTION_CANCELLED — cancellation confirmation.
  • SUBSCRIPTION_EXPIRED — access closed.

Development Timeline

Option Scope Timeline
Without Recurring Subscription with manual payment, access management 5-7 days
With Recurring Payments Auto-charging via YooKassa or CloudPayments 10-14 days
Full Platform Multiple plans, trial, personal account, analytics 15-20 days