1C-Bitrix Integration with Tinkoff Payment System
Tinkoff Oplata is not Tinkoff Credit and not Tinkoff Pay. Three different products, three different APIs — confusion between them is a standard mistake during initial setup. Tinkoff Oplata (also known as "Tinkoff Internet Acquiring") is a full-featured payment gateway for accepting card payments, operating via the API at securepay.tinkoff.ru/v2/. This article focuses specifically on that product.
Tinkoff Gateway Architecture
The Tinkoff API follows an Init → Pay → Confirm/Cancel flow. Key methods:
-
Init— initialize a payment, receivePaymentURLandPaymentId -
GetState— retrieve the current transaction status -
Confirm— confirm a pre-authorized payment -
Cancel— cancel a payment or issue a refund -
Charge— recurring charge against a saved card
All requests are signed with a token — a SHA-256 hash of the concatenated parameter values and password, with keys sorted alphabetically. An incorrect sort order when generating the token is the most common cause of INVALID_SIGNATURE errors on first launch.
Integration with the Bitrix sale Module
Tinkoff has an official Bitrix module on the Marketplace. Alternatively, a custom handler can be placed in /local/php_interface/include/sale_payment/tinkoff_acquiring/.
Handler structure:
handler.php — main class, extends ServiceHandler
.description.php — description and icon
.settings.php — fields: TerminalKey, Password, TestMode, TwoStagePayment
template/ — payment button template
The initiatePay method builds the request to Init:
$request = [
'TerminalKey' => $terminalKey,
'Amount' => $payment->getSum() * 100, // in kopecks
'OrderId' => $payment->getOrderId(),
'Description' => 'Order #' . $order->getField('ACCOUNT_NUMBER'),
'SuccessURL' => $returnUrl,
'FailURL' => $failUrl,
'NotificationURL' => $notifyUrl,
'Receipt' => $this->buildReceipt($order), // for 54-FZ
];
Notifications and Status Handling
Tinkoff sends a POST to NotificationURL on every status change. Statuses requiring action in Bitrix:
| Status | Meaning | Action |
|---|---|---|
AUTHORIZED |
Funds held | For two-stage — wait for Confirm |
CONFIRMED |
Payment confirmed | $payment->setPaid('Y') |
REJECTED |
Declined by the bank | Notify the customer |
REFUNDED |
Full refund | Update order status |
PARTIAL_REFUNDED |
Partial refund | Update refund amount |
REVERSED |
Authorization reversed | Cancel payment |
The notification includes a Token for signature verification — this check must always be implemented. After successful processing, return the string OK; otherwise Tinkoff will retry.
Receipt Data for 54-FZ
Tinkoff has a built-in online fiscal register. When a Receipt object is included in the Init request, the register generates a receipt automatically. Receipt structure:
{
"Email": "[email protected]",
"Phone": "+79001234567",
"Taxation": "osn",
"Items": [
{
"Name": "Product name",
"Price": 150000,
"Quantity": 2,
"Amount": 300000,
"Tax": "vat20",
"PaymentMethod": "full_payment",
"PaymentObject": "commodity"
}
]
}
Item data comes from the order basket: $order->getBasket()->getOrderableItems(). For each line item, the VAT rate from the catalog must be mapped to the Tinkoff API Tax code (vat0, vat10, vat20, none).
Recurring Payments
Tinkoff supports card binding during the first payment (the Recurrent: Y parameter in Init) and subsequent charges via Charge without customer involvement. In Bitrix this is used for subscriptions: when a subscription is created the RebillId from the notification is saved, and then a cron job calls Charge with the required Amount. See the recurring payments article for details.
Real-World Case: Timeout Under High Load
A fashion marketplace with around 500 orders per day. Customers periodically complained about landing on an error page after paying, even though the money had been debited. Diagnosis: during load spikes, the SuccessURL handler called GetState before Tinkoff had finished processing — the status came back as AUTHORIZED instead of CONFIRMED. The order status was not updated and the customer saw an error. Fix: display an intermediate "Payment is being processed" page on SuccessURL with JS polling of the status via a custom AJAX endpoint, and confirm payment exclusively on the notification callback.
Testing
A test terminal with a separate TerminalKey is created in the Tinkoff merchant dashboard (merchant.tinkoff.ru). Test cards for various scenarios (success, decline, 3DS) are in the API documentation. Before going live, verify: notification signature validation, correct amount in kopecks, Receipt generation, and duplicate callback handling.
Timelines: basic integration without fiscal compliance — 1–3 days. With 54-FZ and custom status logic — 3–6 working days.







