Setting up payment via 1C-Bitrix link

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

Payment Link Setup for 1C-Bitrix

Payment by link — a manager generates a link and sends it to the buyer via messenger, email, or SMS. The buyer follows the link and pays without logging into the site. Popular for phone orders, B2B sales, and personal offers. Technically, it is a special URL with a token that leads to the payment page for a specific order or an arbitrary amount.

Two Payment Link Scenarios

Link to an existing order — the order has already been created in the system, the manager sends a payment link. The buyer does not see the checkout form, only the payment form.

Link for an arbitrary payment — the manager specifies the amount and description; the order in Bitrix is created at the moment the link is followed or payment is confirmed.

Generating a Payment Link

class PaymentLinkService
{
    public function generateForOrder(int $orderId, int $expiresInHours = 24): string
    {
        $order = \Bitrix\Sale\Order::load($orderId);
        if (!$order || $order->isPaid()) {
            throw new \InvalidArgumentException('Order not found or already paid');
        }

        // Generate a one-time token
        $token = bin2hex(random_bytes(32));
        $expires = (new \DateTime())->modify('+' . $expiresInHours . ' hours');

        PaymentLinkTable::add([
            'ORDER_ID'   => $orderId,
            'TOKEN'      => $token,
            'EXPIRES_AT' => \Bitrix\Main\Type\DateTime::createFromPhp($expires),
            'USED'       => 'N',
            'CREATED_BY' => $GLOBALS['USER']->GetID(),
        ]);

        return 'https://' . $_SERVER['HTTP_HOST'] . '/pay/' . $token . '/';
    }

    public function generateCustom(float $amount, string $description): string
    {
        $token = bin2hex(random_bytes(32));

        PaymentLinkTable::add([
            'ORDER_ID'    => null,
            'AMOUNT'      => $amount,
            'DESCRIPTION' => $description,
            'TOKEN'       => $token,
            'EXPIRES_AT'  => \Bitrix\Main\Type\DateTime::createFromPhp(
                (new \DateTime())->modify('+24 hours')
            ),
            'USED' => 'N',
        ]);

        return 'https://' . $_SERVER['HTTP_HOST'] . '/pay/' . $token . '/';
    }
}

Payment Link Page

// Component /local/components/my/payment.link/class.php
class PaymentLinkComponent extends \CBitrixComponent
{
    public function executeComponent(): void
    {
        $token = $this->arParams['TOKEN'] ?? $GLOBALS['APPLICATION']->GetCurDir();
        $token = trim($token, '/');

        // Find the link by token
        $link = PaymentLinkTable::getList([
            'filter' => [
                'TOKEN'  => $token,
                'USED'   => 'N',
                '>EXPIRES_AT' => new \Bitrix\Main\Type\DateTime(),
            ],
        ])->fetch();

        if (!$link) {
            $this->arResult['ERROR'] = 'Link is invalid or has expired';
            $this->includeComponentTemplate();
            return;
        }

        // If the link points to an order — show the payment form for the existing order
        if ($link['ORDER_ID']) {
            $order = \Bitrix\Sale\Order::load($link['ORDER_ID']);
            $this->arResult['ORDER']  = $order;
            $this->arResult['AMOUNT'] = $order->getPrice();
        } else {
            // Arbitrary payment — create an order on the fly
            $order = $this->createOrderFromLink($link);
            $this->arResult['ORDER']  = $order;
            $this->arResult['AMOUNT'] = $link['AMOUNT'];
        }

        // Initiate payment
        $paySystem = \Bitrix\Sale\PaySystem\Manager::getObjectById(DEFAULT_PAY_SYSTEM_ID);
        $payment   = $order->getPaymentCollection()->current();
        $response  = $paySystem->initiatePay($payment, null);

        if ($response->isSuccess()) {
            // Mark the link as used only after a successful payment
            // (via callback)
            $this->arResult['PAYMENT_URL'] = $response->getPaymentUrl();
        }

        $this->includeComponentTemplate();
    }
}

Managing Links from the Admin Panel

In the CRM or orders admin section, add a "Send Payment Link" button:

// On the order admin page — add a button
$adminPage->AddAdminContextMenuItem([
    'TEXT' => 'Create Payment Link',
    'LINK' => '/bitrix/admin/payment_link_create.php?ORDER_ID=' . $orderId,
    'ICON' => 'btn_pay',
]);

The page /bitrix/admin/payment_link_create.php displays the generated link, which the manager copies and sends to the buyer.

Limiting the Number of Uses

A link can be made single-use or multi-use:

// In the callback after successful payment
if ($payment->isPaid()) {
    PaymentLinkTable::update($linkId, [
        'USED' => 'Y',
        'PAID_AT' => new \Bitrix\Main\Type\DateTime(),
    ]);
}

Timeline

Task Duration
Token generation + link table 0.5 day
Payment link page 1 day
Admin panel creation interface 0.5 day