webmasterolegan / tbank-payments
PHP 8.5+ SDK для работы с API интернет-эквайринга T-Bank (Тинькофф)
Requires
- php: ^8.5
- ext-curl: *
- ext-json: *
- ext-mbstring: *
- ext-uri: *
- psr/http-client: ^1.0
- psr/http-factory: ^1.0
Requires (Dev)
- nyholm/psr7: ^1.8
- phpstan/phpstan: ^2.0
- phpunit/phpunit: ^11.0
Suggests
- guzzlehttp/guzzle: PSR-18 HTTP client implementation
- symfony/http-client: PSR-18 HTTP client implementation
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-09-17 11:58:38 UTC
README
PHP 8.5+ SDK для работы с API интернет-эквайринга T-Bank (бывший Tinkoff).
Возможности
| Группа | Методы |
|---|---|
| Платежи | Init, FinishAuthorize, Confirm, Charge |
| СБП | GetQr, GetQrState, GetQrBankList, QrMembersList, ChargeQr, AddAccountQr, GetAddAccountQrState |
| Статус | GetState, CheckOrder |
| Уведомления | Resend |
| Отмена / возврат | Cancel (полный и частичный) |
| Карты | AddCard, GetCardList, RemoveCard |
| Покупатели | AddCustomer, GetCustomer, RemoveCustomer |
| Чеки (ФФД 1.2) | SendClosingReceipt |
| Webhook | Валидация подписи, типизированное уведомление |
Установка
composer require webmasterolegan/tbank-payments
Требования: PHP ≥ 8.5, расширения curl, json, mbstring, uri.
Быстрый старт
use TBank\Payments\Enum\EnvironmentEnum; use TBank\Payments\TBankClient; use TBank\Payments\DTO\Request\InitPaymentRequestDto; use TBank\Payments\DTO\Shared\{ReceiptDto, ReceiptItemDto}; use TBank\Payments\Enum\Fiscal\{TaxationEnum, VatEnum}; $client = new TBankClient( terminalKey: 'YOUR_TERMINAL_KEY', password : 'YOUR_PASSWORD', environment: EnvironmentEnum::Production, );
1. Инициировать платёж
use TBank\Payments\Enum\{LanguageEnum, PayTypeEnum}; $request = new InitPaymentRequestDto( amount : 150000, orderId : 'order-2024-001', description: 'Заказ #2024-001', payType : PayTypeEnum::OneStep, language : LanguageEnum::Ru, successUrl : 'https://myshop.ru/success', failUrl : 'https://myshop.ru/fail', receipt : new ReceiptDto( taxation: TaxationEnum::UsnIncome, email : 'buyer@example.com', items : [ new ReceiptItemDto( name : 'Футболка синяя', price : 150000, quantity: 1.0, amount : 150000, tax : VatEnum::None, ), ], ), ); $response = $client->payment()->init($request); if ($response->hasPaymentUrl()) { header('Location: ' . $response->paymentUrl); exit; }
Для маркетплейса передайте Shops: сумма каждого магазина в копейках и комиссия (Fee), которая удерживается из возмещения партнёра. Если Fee не указан, банк возьмёт комиссию из настроек регистрации.
use TBank\Payments\DTO\Shared\ShopDto; $request = new InitPaymentRequestDto( amount : 150000, orderId: 'order-2024-001', shops : [ new ShopDto( shopCode: '10001', amount : 100000, name : 'Футболка синяя', fee : 2500, ), new ShopDto( shopCode: '10002', amount : 50000, name : 'Доставка', ), ], );
2. Подтвердить двухстадийное списание
use TBank\Payments\DTO\Request\ConfirmRequestDto; $confirm = $client->payment()->confirm( new ConfirmRequestDto(paymentId: '123456789') );
3. Отменить / вернуть платёж
use TBank\Payments\DTO\Request\CancelRequestDto; $cancel = $client->refund()->cancel( new CancelRequestDto(paymentId: '123456789') ); $cancel = $client->refund()->cancel( new CancelRequestDto(paymentId: '123456789', amount: 50000) );
4. Получить статус платежа
use TBank\Payments\Enum\PaymentStatusEnum; $state = $client->status()->getState('123456789'); if ($state->status === PaymentStatusEnum::Confirmed) { // Платёж подтверждён } if ($state->status->isSuccessful()) { // то же через метод enum }
5. Привязать карту
use TBank\Payments\DTO\Request\AddCardRequestDto; use TBank\Payments\Enum\CardCheckTypeEnum; $result = $client->card()->addCard( new AddCardRequestDto( customerKey: 'user-42', checkType : CardCheckTypeEnum::Hold, ) ); header('Location: ' . $result->paymentUrl);
6. Оплата по привязанной карте (рекуррент)
$request = new InitPaymentRequestDto( amount : 99900, orderId : 'sub-2024-05', customerKey: 'user-42', recurrent : true, ); $init = $client->payment()->init($request);
7. Обработка webhook
use TBank\Payments\Enum\{NotificationTypeEnum, PaymentStatusEnum}; use TBank\Payments\Exceptions\{InvalidWebhookPayloadException, InvalidWebhookSignatureException}; $handler = $client->webhookHandler(); try { $notification = $handler->handle(file_get_contents('php://input') ?: ''); if (!$notification->success) { http_response_code(200); echo $handler->acknowledge(); exit; } match ($notification->notificationType) { NotificationTypeEnum::Payment => handlePayment($notification), NotificationTypeEnum::LinkCard => handleLinkCard($notification), NotificationTypeEnum::Fiscalization => handleFiscalization($notification), default => null, }; match ($notification->status) { PaymentStatusEnum::Confirmed => handleConfirmed($notification), PaymentStatusEnum::Rejected => handleRejected($notification), PaymentStatusEnum::PartialRefunded => handlePartialRefund($notification), PaymentStatusEnum::Unknown => logUnknownStatus($notification), default => null, }; http_response_code(200); echo $handler->acknowledge(); } catch (InvalidWebhookSignatureException) { http_response_code(400); echo 'Bad signature'; } catch (InvalidWebhookPayloadException) { http_response_code(400); echo 'Bad payload'; }
8. Список и удаление карт
use TBank\Payments\DTO\Request\RemoveCardRequestDto; $cards = $client->card()->getCardList('user-42'); foreach ($cards->cards as $card) { echo "{$card->cardId}: {$card->pan}\n"; } $client->card()->removeCard( new RemoveCardRequestDto(customerKey: 'user-42', cardId: '123456'), );
9. Статус заказа (несколько платежей)
$order = $client->status()->checkOrder('order-2024-001'); foreach ($order->payments as $payment) { echo "{$payment->paymentId}: {$payment->status->value}\n"; }
10. Закрывающий чек
use TBank\Payments\DTO\Request\SendReceiptRequestDto; use TBank\Payments\Enum\Fiscal\{PaymentMethodEnum, PaymentObjectEnum}; $client->receipt()->sendClosingReceipt( new SendReceiptRequestDto( paymentId: '123456789', receipt : new ReceiptDto( taxation: TaxationEnum::UsnIncome, email : 'buyer@example.com', items : [ new ReceiptItemDto( name : 'Футболка синяя', price : 150000, quantity : 1.0, amount : 150000, tax : VatEnum::None, paymentObject: PaymentObjectEnum::Commodity, paymentMethod: PaymentMethodEnum::FullPayment, ), ], ), ), );
11. FinishAuthorize (3DS, своя форма)
use TBank\Payments\DTO\Request\FinishAuthorizeRequestDto; $response = $client->payment()->finishAuthorize( new FinishAuthorizeRequestDto( paymentId: '123456789', md : $_POST['MD'], paRes : $_POST['PaRes'], ), ); if ($response->requires3ds()) { // Редирект на ACS: $response->acsUrl }
12. Тестовая среда
$client = new TBankClient( terminalKey: 'YOUR_TERMINAL_KEY', password : 'YOUR_PASSWORD', environment: EnvironmentEnum::Test, );
13. Списание по RebillId (Charge)
use TBank\Payments\DTO\Request\ChargeRequestDto; $response = $client->payment()->charge( new ChargeRequestDto( paymentId: $init->paymentId, rebillId : $rebillIdFromWebhook, ), );
14. Оплата через СБП (GetQr)
use TBank\Payments\DTO\Request\GetQrRequestDto; use TBank\Payments\Enum\QrDataTypeEnum; $qr = $client->sbp()->getQr( new GetQrRequestDto( paymentId: $init->paymentId, dataType : QrDataTypeEnum::Payload, ), ); echo $qr->data; // payload или SVG при QrDataTypeEnum::Image
15. Покупатели
use TBank\Payments\DTO\Request\AddCustomerRequestDto; $client->customer()->add( new AddCustomerRequestDto( customerKey: 'user-42', email : 'user@example.com', phone : '+79001234567', ), ); $customer = $client->customer()->get('user-42'); $client->customer()->remove('user-42');
16. Статус СБП-платежа (GetQrState)
$state = $client->sbp()->getQrState($paymentId); if ($state->status === PaymentStatusEnum::Confirmed) { // СБП-платёж подтверждён }
17. Привязка счёта СБП (AddAccountQr)
use TBank\Payments\DTO\Request\AddAccountQrRequestDto; use TBank\Payments\Enum\{AccountQrStatusEnum, NotificationTypeEnum, QrDataTypeEnum}; $binding = $client->sbp()->addAccountQr( new AddAccountQrRequestDto( description: 'Привязка счёта для автоплатежей', dataType : QrDataTypeEnum::Payload, ), ); // Показать QR: $binding->data // Сохранить $binding->requestKey для проверки статуса $state = $client->sbp()->getAddAccountQrState($binding->requestKey); if ($state->status === AccountQrStatusEnum::Active) { // Счёт привязан; AccountToken придёт в webhook (NotificationType=QR) }
В webhook-уведомлении типа QR доступны поля accountToken и requestKey:
if ($notification->notificationType === NotificationTypeEnum::Qr) { saveAccountToken($notification->accountToken); }
18. Список банков СБП (GetQrBankList)
use TBank\Payments\DTO\Request\GetQrBankListRequestDto; use TBank\Payments\DTO\Shared\DeviceDto; use TBank\Payments\Enum\DeviceTypeEnum; $banks = $client->sbp()->getQrBankList( new GetQrBankListRequestDto( device: new DeviceDto(DeviceTypeEnum::Mobile, 'Android'), ), ); foreach ($banks->bankList as $bank) { echo "{$bank->bankName}: {$bank->nspkBankId}\n"; }
19. Автоплатёж СБП (ChargeQr)
use TBank\Payments\DTO\Request\ChargeQrRequestDto; $response = $client->sbp()->chargeQr( new ChargeQrRequestDto( paymentId : $init->paymentId, accountToken: $accountTokenFromWebhook, ), );
20. Повторная отправка уведомлений (Resend)
use TBank\Payments\DTO\Request\ResendRequestDto; use TBank\Payments\Enum\NotificationTypeEnum; $result = $client->notifications()->resend( new ResendRequestDto( paymentId : '123456789', notificationType: NotificationTypeEnum::Payment, ), ); echo $result->count; // сколько уведомлений отправлено повторно
21. Повтор при сетевых ошибках и переиспользование cURL
$client = new TBankClient( terminalKey : 'YOUR_TERMINAL_KEY', password : 'YOUR_PASSWORD', retryAttempts : 3, // до 3 попыток при NetworkException retryDelayMs : 200, // экспоненциальная задержка: 200, 400, 800… мс connectTimeout : 10, // таймаут установки соединения (сек) reuseConnection : true, // persistent cURL share (FrankenPHP, RoadRunner) );
22. PSR-18 HTTP-клиент с retry
По умолчанию SDK использует cURL. Для интеграции с Guzzle или Symfony HttpClient:
use TBank\Payments\Http\{Psr18HttpClient, RetryingHttpClient}; use TBank\Payments\Enum\EnvironmentEnum; $psr17 = new \Nyholm\Psr7\Factory\Psr17Factory(); $baseUrl = EnvironmentEnum::Production->baseUrl(); $client = new TBankClient( terminalKey: 'YOUR_TERMINAL_KEY', password : 'YOUR_PASSWORD', environment: EnvironmentEnum::Production, httpClient : new RetryingHttpClient( inner : new Psr18HttpClient( client : $psr18Client, requestFactory: $psr17, streamFactory : $psr17, baseUrl : $baseUrl, ), maxAttempts : 3, delayMs : 200, ), );
Альтернатива — встроенный retry через конструктор TBankClient (работает с любым HttpClientContract):
$client = new TBankClient( terminalKey : 'YOUR_TERMINAL_KEY', password : 'YOUR_PASSWORD', retryAttempts: 3, retryDelayMs : 200, );
PSR-18 без retry-обёртки:
use TBank\Payments\Http\Psr18HttpClient; use TBank\Payments\Enum\EnvironmentEnum; $psr17 = new \Nyholm\Psr7\Factory\Psr17Factory(); $client = new TBankClient( terminalKey: 'YOUR_TERMINAL_KEY', password : 'YOUR_PASSWORD', environment: EnvironmentEnum::Production, httpClient : new Psr18HttpClient( client : $psr18Client, // Guzzle или Symfony HttpClient requestFactory: $psr17, streamFactory : $psr17, baseUrl : EnvironmentEnum::Production->baseUrl(), ), );
Примеры (examples/)
В каталоге examples/ — готовые скрипты. Перед запуском задайте переменные окружения:
export TBANK_TERMINAL_KEY=your_terminal_key export TBANK_PASSWORD=your_password export TBANK_ENV=production # или test
| Скрипт | Описание |
|---|---|
01-init-payment.php |
Одностадийный платёж с чеком |
02-two-step-payment.php |
Двухстадийный платёж (холд + Confirm) |
03-finish-authorize-3ds.php |
Завершение 3DS (MD + PaRes) |
04-webhook-endpoint.php |
Обработчик NotificationURL |
05-cards.php |
Привязка, список, удаление карт |
06-refund.php |
Полный и частичный возврат |
07-receipt.php |
Закрывающий чек |
08-check-status.php |
Статус платежа и заказа |
09-recurrent-payment.php |
Рекуррентный платёж |
10-charge.php |
Списание по RebillId |
11-sbp-payment.php |
Init + GetQr (СБП) |
12-customer.php |
Регистрация и получение покупателя |
13-sbp-qr-state.php |
Статус СБП-платежа |
14-sbp-bank-list.php |
Список банков СБП |
15-resend.php |
Повторная отправка уведомлений |
16-sbp-account-binding.php |
Привязка счёта + автоплатёж СБП |
17-marketplace-shops.php |
Init с Shops и комиссией маркетплейса |
composer install php examples/01-init-payment.php php examples/05-cards.php list user-42 php examples/08-check-status.php payment 123456789
Архитектура пакета
Соглашения об именовании: DTO — суффикс Dto, enum — Enum, интерфейсы — Contract.
src/
├── TBankClient.php
├── TokenGenerator.php
├── WebhookHandler.php
├── Enum/
│ ├── PaymentStatusEnum.php
│ ├── PayTypeEnum.php
│ ├── LanguageEnum.php
│ ├── CardCheckTypeEnum.php
│ ├── EnvironmentEnum.php
│ ├── NotificationTypeEnum.php
│ ├── QrDataTypeEnum.php
│ ├── DeviceTypeEnum.php
│ ├── AccountQrStatusEnum.php
│ ├── Fiscal/ # TaxationEnum, VatEnum, PaymentObjectEnum, …
│ └── Card/ # CardStatusEnum, CardTypeEnum
├── Api/
├── DTO/
│ ├── Request/
│ ├── Response/
│ ├── Shared/
│ └── WebhookNotificationDto.php
├── Http/
│ ├── HttpClient.php
│ ├── HttpClientContract.php
│ ├── RetryingHttpClient.php
│ ├── Psr18HttpClient.php
│ └── JsonResponseParser.php
├── Support/ # ApiUrlBuilder, ApiValueParser
└── Exceptions/
├── TBankException.php
├── ApiException.php
├── NetworkException.php
├── InvalidWebhookSignatureException.php
└── InvalidWebhookPayloadException.php
Обработка ошибок
use TBank\Payments\Exceptions\{ApiException, NetworkException, TBankException}; try { $response = $client->payment()->init($request); } catch (ApiException $e) { echo $e->getMessage(); echo $e->getErrorCode(); } catch (NetworkException $e) { echo $e->getMessage(); } catch (TBankException $e) { // прочие ошибки пакета }
Запуск тестов
composer install composer ci # тесты + PHPStan composer test
Ссылки
Лицензия
MIT © Oleg Polyakov