Setting up recurring payments in 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
    1175
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Website development for FIXPER company
    811
  • 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

Setting Up Recurring Payments on 1C-Bitrix

A recurring payment is an automatic charge to the buyer's card without their repeated involvement. The card is linked once, and the store then initiates charges independently. Used for: automatic subscription renewals, regular deliveries, scheduled payments. Technically, this is not standard functionality of the 1C-Bitrix sale module — it requires additional implementation.

How It Works Technically

Recurring payments rely on card tokenization. On the first payment:

  1. The buyer enters their card; the payment system processes the transaction
  2. The system returns a card token (RebillId, RecurringId — each acquiring service uses its own term)
  3. The store saves the token, linking it to the buyer
  4. For subsequent charges, the store sends a request with the token — no need to re-enter the card

Saving a Card Token (YooKassa)

// On the first payment, request saving the card
$payment = $yookassaClient->createPayment([
    'amount'              => ['value' => $amount, 'currency' => 'RUB'],
    'payment_method_data' => ['type' => 'bank_card'],
    'confirmation'        => ['type' => 'redirect', 'return_url' => $returnUrl],
    'save_payment_method' => true,  // key parameter
    'capture'             => true,
    'description'         => 'First payment, card binding',
], uniqid('', true));

// After successful payment in the callback:
$completedPayment = $yookassaClient->getPaymentInfo($paymentId);
$paymentMethodId  = $completedPayment->getPaymentMethod()->getId();

// Save $paymentMethodId in the buyer's profile
UserCardTable::add([
    'USER_ID'          => $userId,
    'PAYMENT_METHOD_ID'=> $paymentMethodId,
    'LAST4'            => $completedPayment->getPaymentMethod()->getLast4(),
    'CARD_TYPE'        => $completedPayment->getPaymentMethod()->getCardType(),
    'ACTIVE'           => 'Y',
]);

Subsequent Charge via Token

public function chargeRecurrent(int $userId, float $amount, string $description): bool
{
    $card = UserCardTable::getActiveCard($userId);
    if (!$card) {
        return false;
    }

    try {
        $payment = $yookassaClient->createPayment([
            'amount'            => ['value' => number_format($amount, 2, '.', ''), 'currency' => 'RUB'],
            'payment_method_id' => $card['PAYMENT_METHOD_ID'],  // saved card token
            'capture'           => true,
            'description'       => $description,
        ], uniqid('', true));

        // Recurring payments immediately go to succeeded or pending status
        if ($payment->getStatus() === 'succeeded') {
            return true;
        }

        // Handle via webhook
        return false;
    } catch (\Exception $e) {
        // Log and notify the user
        return false;
    }
}

1C-Bitrix Task Scheduler

For regular charges, use 1C-Bitrix agents:

// Register an agent when a subscription is created
\CAgent::AddAgent(
    '\MyModule\RecurringAgent::run(' . $subscriptionId . ');',
    'my_module',
    'N',
    86400,  // interval in seconds (1 day)
    date('d.m.Y H:i:s', strtotime('+1 month')),  // first run date
    'Y',
    date('d.m.Y H:i:s', strtotime('+1 month'))
);

// Agent class
class RecurringAgent
{
    public static function run(int $subscriptionId): string
    {
        $subscription = SubscriptionTable::getById($subscriptionId)->fetch();
        if (!$subscription || $subscription['STATUS'] !== 'ACTIVE') {
            return ''; // empty string = delete the agent
        }

        $charged = (new RecurringPaymentService())->chargeRecurrent(
            $subscription['USER_ID'],
            $subscription['AMOUNT'],
            'Subscription renewal #' . $subscriptionId
        );

        if (!$charged) {
            // Mark subscription as problematic and notify
        }

        // Return the method name for the next run
        return '\MyModule\RecurringAgent::run(' . $subscriptionId . ');';
    }
}

Charge Error Handling

The card may be blocked, the limit exceeded, or funds may be insufficient. Standard retry strategy:

  • Day 0: first attempt — failure
  • Day 1: retry
  • Day 3: retry + email to buyer
  • Day 7: final attempt + cancellation notification

Timeline

Task Duration
Saving card token on first payment 1 day
Recurring charge mechanism + agents 1–2 days
Error handling and notifications 1 day