How to Integrate QR Code Payments in 1C-Bitrix with SBP, Tinkoff, and YooKassa
When a customer places an order in an online store, they want to pay in two clicks — without entering card details.
A QR code on the payment page solves this: scan, confirm in the bank app, done. But under the hood, it's a mix of acquirer API, QR generation, status polling, and proper UX. We specialize in turnkey integrations for 1C-Bitrix and Bitrix24. With over 10 years of experience, we immediately spot bottlenecks. For example, on a project with an inventory of 150,000 SKUs, we implemented QR SBP via Tinkoff. The problem was that during peak loads of 100 concurrent orders, polling couldn't keep up with status updates, causing some payments to hang. We optimized by adding Bitrix agent queues and increasing the polling interval to 5 seconds with exponential backoff. As a result, 99.9% of payments completed correctly. A typical integration costs from $500–1.5k, but pays for itself within 3 months through fee savings.
Types of QR Codes for Payment
QR SBP via the Faster Payments System
Works with any participating bank. According to Bank of Russia Regulation No. 580-P, transfers are credited within seconds. Fees are 0.4–0.7% of the amount — 3–5 times lower than card processing. With a monthly turnover of $9k–13k, fee savings reach $360–520. This is the best option due to low fees and broad acceptance.
Bank QR — a proprietary format
Specific to a bank (SberPay QR, Tinkoff QR). Works only with that bank's app. Suitable for a single-brand audience.
QR as a link image
A regular link to the payment page encoded in a QR. Opens a mobile browser; the customer chooses a payment method on the site. Simpler to implement but adds an extra step.
QR SBP — the Optimal Choice for an Online Store
QR SBP wins on key parameters: fees 2–3 times lower than cards, instant money transfer, and easy refunds. For the customer, it's a familiar scenario: scan a QR in any banking app. According to statistics, 70% of users prefer SBP when available. Fee savings can reach 80% compared to cards. For example, with a monthly turnover of $4.5k–6.5k, savings amount to about $180–260; at $18k–26k, it's $720–1k.
How We Set Up QR Code Payment in 1C-Bitrix
Step-by-step integration overview
The setup process involves several steps: 1. Select an acquirer and register a terminal (Tinkoff, YooKassa, etc.). 2. Generate a QR code for each order via the acquirer API. 3. Implement status polling with idempotency and transaction locks to avoid duplicate payments. 4. Integrate into standard 1C-Bitrix components using asynchronous agent queues. 5. Test all scenarios (success, cancel, timeout) with SHA-256 signature validation of callbacks.How to Connect QR SBP via Tinkoff
First, create a payment in the acquirer via Init, get the PaymentId. Then request the QR code via GetQr. PHP code:
// Create a payment with QR type $params = [ 'TerminalKey' => TINKOFF_TERMINAL, 'Amount' => (int)($order->getPrice() * 100), 'OrderId' => $order->getAccountNumber(), 'Description' => 'Order #' . $order->getAccountNumber(), 'NotificationURL' => '/local/tools/sale_ps_result.php', 'PayType' => 'O', ]; $params['Token'] = tinkoffSign($params, TINKOFF_SECRET); $initResult = tinkoffPost('/v2/Init', $params); $paymentId = $initResult['PaymentId']; // Request QR for SBP $qrParams = [ 'TerminalKey' => TINKOFF_TERMINAL, 'PaymentId' => $paymentId, 'DataType' => 'IMAGE', // or 'PAYLOAD' to get a link string ]; $qrParams['Token'] = tinkoffSign($qrParams, TINKOFF_SECRET); $qrResult = tinkoffPost('/v2/GetQr', $qrParams); // $qrResult['Data'] — base64-encoded PNG with QR code (when DataType=IMAGE) // or SBP link string (when DataType=PAYLOAD) It's important to handle errors: if the payment isn't created, return a clear message to the user. We always add a fallback in case the acquirer API is unavailable, such as reserving the order and retrying with exponential backoff.
How to Implement QR SBP via YooKassa
YooKassa automatically generates a QR SBP when creating a payment with the sbp method:
$payment = $client->createPayment([ 'amount' => ['value' => '1500.00', 'currency' => 'USD'], 'payment_method_data' => ['type' => 'sbp'], 'confirmation' => ['type' => 'qr'], 'description' => 'Order #' . $orderId, ], uniqid('', true)); $qrUrl = $payment->getConfirmation()->getConfirmationData(); // qrUrl — string like https://qr.nspk.ru/... — encode to QR on the client To render the QR from a URL on the client, use the JS library qrcodejs or qr-code-styling. This is faster and doesn't load the server.
Displaying QR on the Page and Status Polling
// Show QR and poll payment status async function showQRPayment(orderId) { const resp = await fetch('/api/get-payment-qr.php', { method: 'POST', body: JSON.stringify({ orderId }), }); const data = await resp.json(); document.getElementById('qr-image').src = 'data:image/png;base64,' + data.qrBase64; document.getElementById('qr-block').style.display = 'block'; // Polling: check status every 3 seconds const pollInterval = setInterval(async () => { const status = await checkPaymentStatus(orderId); if (status === 'paid') { clearInterval(pollInterval); window.location.href = '/payment/success/'; } if (status === 'expired') { clearInterval(pollInterval); showExpiredMessage(); } }, 3000); // Stop polling after 15 minutes setTimeout(() => clearInterval(pollInterval), 15 * 60 * 1000); } Server-side status check script with idempotency and transactional integrity:
// local/api/check-payment-status.php $orderId = (int)($_POST['orderId'] ?? 0); $paymentId = getExternalPaymentId($orderId); // saved PaymentId from Tinkoff $params = [ 'TerminalKey' => TINKOFF_TERMINAL, 'PaymentId' => $paymentId, ]; $params['Token'] = tinkoffSign($params, TINKOFF_SECRET); $status = tinkoffPost('/v2/GetState', $params); $map = [ 'CONFIRMED' => 'paid', 'CANCELED' => 'cancelled', 'DEADLINE_EXPIRED' => 'expired', ]; echo json_encode([ 'status' => $map[$status['Status']] ?? 'pending', ]); Polling must be resilient: on network errors, retry with exponential backoff, and never block the UI. Additionally, implement a MySQL transaction lock when processing callback notifications to prevent duplicate payments.
Common Mistakes When Setting Up QR Payment
One frequent issue is a timeout when generating the QR via the acquirer API. If the acquirer doesn't respond within 10 seconds, you need a fallback: show an alternative payment method or retry the request with a 1-2 second interval. We set a timeout of at least 30 seconds for the Tinkoff API and check payment status in the background via agents.
Another common problem is lack of handling duplicate notifications. When the acquirer sends multiple callbacks, you must check the payment status in the database with a transactional lock before updating. Otherwise, an order could be paid twice. This is easily solved with a SELECT ... FOR UPDATE in MySQL.
What's Included in Turnkey QR Payment Setup
| Component | Description |
|---|---|
| Acquirer selection and connection | Tinkoff, YooKassa, or another bank with SBP |
| QR code generation on the order page | Base64 image or URL + client-side rendering |
| Payment status polling with idempotency | Server script + client interval with timeout handling |
| Integration with 1C-Bitrix | Payment system, notification handlers, logging |
| Documentation and testing | Admin instructions, all scenarios tested |
| Post-launch support | 1-month warranty, consultation for modifications |
Estimated Timelines
| Stage | Time |
|---|---|
| QR SBP via Tinkoff/YooKassa | 1–2 days |
| Status polling + UX update with exponential backoff | 0.5–1 day |
| Integration into checkout page | 0.5–1 day |
| Total (including testing) | from 2 to 4 days |
Contact us for a project assessment and optimal solution. With over 10 years of integration experience, we guarantee stable payment processing. Get your project evaluated — leave a request. Additional resources: Faster Payments System, QR code.







