Налаштування передплатних платежів на 1С-Бітрікс

Наша компанія займається розробкою, підтримкою та обслуговуванням рішень на Бітрікс та Бітрікс24 будь-якої складності. Від простих односторінкових сайтів до складних інтернет-магазинів, CRM систем з інтеграцією 1С та телефонії. Досвід розробників підтверджено сертифікатами від вендора.
Пропоновані послуги
Показано 1 з 1 послугУсі 1626 послуг
Налаштування передплатних платежів на 1С-Бітрікс
Проста
~1 робочий день
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851
  • image_bitrix-bitrix-24-1c_development_of_an_online_appointment_booking_widget_for_a_medical_center_594_0.webp
    Розробка на базі Бітрікс, Бітрікс24, 1С для компанії Development of an Online
    585
  • image_bitrix-bitrix-24-1c_mirsanbel_458_0.webp
    Розробка на базі 1С Підприємство для компанії МИРСАНБЕЛ
    751
  • image_crm_dolbimby_434_0.webp
    Розробка сайту на CRM Бітрікс24 для компанії DOLBIMBY
    657
  • image_crm_technotorgcomplex_453_0.webp
    Розробка на базі Бітрікс24 для компанії ТЕХНОТОРГКОМПЛЕКС
    989

Налаштування підписочних платежів на 1С-Бітрікс

Підписочна модель оплати — monthly або annual billing з автоматичним продовженням — потребує окремої інфраструктури в 1С-Бітрікс: сутності «підписка», періодичних списань за збереженою карткою, управління станом (active, paused, cancelled) та роботи з пробними періодами. Стандартний модуль sale для цього не призначений, потрібна надбудова.

Модель даних підписки

// Структура таблиці підписок (ORM D7)
class SubscriptionTable extends \Bitrix\Main\Entity\DataManager
{
    public static function getTableName(): string
    {
        return 'b_subscription';
    }

    public static function getMap(): array
    {
        return [
            'ID'             => new IntegerField('ID', ['primary' => true, 'autocomplete' => true]),
            'USER_ID'        => new IntegerField('USER_ID', ['required' => true]),
            'PLAN_ID'        => new IntegerField('PLAN_ID', ['required' => true]),
            'STATUS'         => new StringField('STATUS'),  // ACTIVE, PAUSED, CANCELLED, TRIAL
            'PAYMENT_METHOD' => new StringField('PAYMENT_METHOD'),  // токен картки
            'AMOUNT'         => new FloatField('AMOUNT'),
            'CURRENCY'       => new StringField('CURRENCY', ['default_value' => 'RUB']),
            'BILLING_CYCLE'  => new StringField('BILLING_CYCLE'),  // MONTHLY, ANNUAL
            'NEXT_BILLING'   => new DatetimeField('NEXT_BILLING'),
            'TRIAL_END'      => new DatetimeField('TRIAL_END'),
            'CREATED_AT'     => new DatetimeField('CREATED_AT'),
            'CANCELLED_AT'   => new DatetimeField('CANCELLED_AT'),
        ];
    }
}

Створення підписки при оформленні

class SubscriptionService
{
    public function create(int $userId, int $planId, bool $trial = false): int
    {
        $plan = SubscriptionPlanTable::getById($planId)->fetch();

        $trialEnd  = $trial ? (new \DateTime())->modify('+' . $plan['TRIAL_DAYS'] . ' days') : null;
        $nextBill  = $trial
            ? $trialEnd
            : (new \DateTime())->modify('+1 ' . strtolower($plan['CYCLE']));

        $result = SubscriptionTable::add([
            'USER_ID'        => $userId,
            'PLAN_ID'        => $planId,
            'STATUS'         => $trial ? 'TRIAL' : 'ACTIVE',
            'AMOUNT'         => $plan['PRICE'],
            'BILLING_CYCLE'  => $plan['CYCLE'],
            'NEXT_BILLING'   => \Bitrix\Main\Type\DateTime::createFromPhp($nextBill),
            'TRIAL_END'      => $trialEnd
                ? \Bitrix\Main\Type\DateTime::createFromPhp($trialEnd)
                : null,
            'CREATED_AT'     => new \Bitrix\Main\Type\DateTime(),
        ]);

        return $result->getId();
    }

    // Прив'язка картки до підписки після першого платежу
    public function attachPaymentMethod(int $subscriptionId, string $paymentMethodId): void
    {
        SubscriptionTable::update($subscriptionId, [
            'PAYMENT_METHOD' => $paymentMethodId,
        ]);
    }
}

Білінг-агент: щоденний обхід підписок

class SubscriptionBillingAgent
{
    public static function run(): string
    {
        // Вибираємо всі підписки з NEXT_BILLING <= сьогодні
        $subscriptions = SubscriptionTable::getList([
            'filter' => [
                'STATUS'    => ['ACTIVE', 'TRIAL'],
                '<=NEXT_BILLING' => new \Bitrix\Main\Type\DateTime(),
            ],
        ]);

        $paymentService = new RecurringPaymentService();

        while ($sub = $subscriptions->fetch()) {
            if ($sub['STATUS'] === 'TRIAL') {
                // Пробний період завершився — перше списання
                $sub['STATUS'] = 'ACTIVE';
            }

            $charged = $paymentService->chargeRecurrent(
                $sub['USER_ID'],
                $sub['PAYMENT_METHOD'],
                $sub['AMOUNT'],
                'Підписка: продовження #' . $sub['ID']
            );

            if ($charged) {
                // Зсуваємо дату наступного білінгу
                $next = (new \DateTime())->modify(
                    $sub['BILLING_CYCLE'] === 'ANNUAL' ? '+1 year' : '+1 month'
                );
                SubscriptionTable::update($sub['ID'], [
                    'NEXT_BILLING' => \Bitrix\Main\Type\DateTime::createFromPhp($next),
                    'STATUS'       => 'ACTIVE',
                ]);
            } else {
                self::handleFailedPayment($sub);
            }
        }

        return '\MyModule\SubscriptionBillingAgent::run();';
    }

    private static function handleFailedPayment(array $sub): void
    {
        $attempts = (int)SubscriptionAttemptTable::countFailures($sub['ID'], '-7 days');

        if ($attempts >= 3) {
            // Скасовуємо підписку після 3 невдач за 7 днів
            SubscriptionTable::update($sub['ID'], [
                'STATUS'       => 'CANCELLED',
                'CANCELLED_AT' => new \Bitrix\Main\Type\DateTime(),
            ]);
            // Email покупцю
        } else {
            // Повтор через добу — зсуваємо NEXT_BILLING на 1 день
            SubscriptionTable::update($sub['ID'], [
                'NEXT_BILLING' => \Bitrix\Main\Type\DateTime::createFromPhp(
                    (new \DateTime())->modify('+1 day')
                ),
            ]);
        }
    }
}

Особистий кабінет покупця

Управління підпискою в ОК: пауза, скасування, зміна картки. Кнопки надсилають AJAX-запити до компонента:

// Пауза підписки (зупинити білінг, не скасовувати)
if ($action === 'pause') {
    SubscriptionTable::update($subscriptionId, ['STATUS' => 'PAUSED']);
}

// Відновлення
if ($action === 'resume') {
    $nextBilling = (new \DateTime())->modify('+1 month');
    SubscriptionTable::update($subscriptionId, [
        'STATUS'       => 'ACTIVE',
        'NEXT_BILLING' => \Bitrix\Main\Type\DateTime::createFromPhp($nextBilling),
    ]);
}

Терміни

Завдання Термін
Модель даних + сервіс підписок 2 дні
Білінг-агент + retry-логіка 1–2 дні
ОК покупця (пауза, скасування, зміна картки) 1–2 дні