# Alfa Bank SDK (Unofficial) Профессиональная PHP-библиотека для интеграции с API Альфа-Банка (BaaS и СБП). Построена на базе [Saloon v3](https://docs.saloon.dev) и [Spatie Laravel Data](https://spatie.be/docs/laravel-data). ## Требования - PHP 8.2+ - [Saloon](https://docs.saloon.dev) v3.0+ - [Spatie Laravel Data](https://spatie.be/docs/laravel-data) ## Установка ```bash composer require apoloz/alfa-bank-sdk ``` ## Подготовка сертификатов (mTLS) Взаимодействие с API Альфа-Банка требует двухстороннего TLS-соединения. Банк не выдает закрытый ключ, вы должны сгенерировать его самостоятельно. **1. Генерация закрытого ключа и запроса (CSR):** Используйте OpenSSL для создания ключа и файла запроса. Создайте файл `openssl.cnf`: ```ini [req] distinguished_name = req_distinguished_name prompt = no [req_distinguished_name] C = RU L = Moscow O = Название Вашей Компании CN = ФИО Ответственного emailAddress = your_email@company.com 1.2.643.100.4 = 1234567890 (Ваш ИНН) ``` Выполните команды в терминале: ```bash # Генерация закрытого ключа (храните его в секрете!) openssl genrsa -out private.key 2048 # Генерация запроса на сертификат openssl req -new -key private.key -out request.csr -config openssl.cnf -utf8 ``` **2. Отправка в Банк:** Отправьте файл `request.csr` на почту `alfa_api@alfabank.ru`. В ответ банк пришлет публичный сертификат (`client.cer` или `client.crt`). **3. Использование:** Поместите полученный сертификат и ваш закрытый ключ в надежное место и укажите пути к ним в `.env` (переменные `ALFA_BAAS_CERT_PATH` и `ALFA_BAAS_KEY_PATH`). ## Конфигурация (Laravel) Опубликуйте файл конфигурации: ```bash php artisan vendor:publish --tag=alfa-bank-config ``` ### Настройка `.env` Добавьте следующие переменные в ваш файл `.env`. Вам понадобятся сертификаты (PEM) и ключи. ```dotenv # --- BaaS (Номинальные счета, Выплаты) --- ALFA_BAAS_ENVIRONMENT=sandbox # или production ALFA_BAAS_CLIENT_ID=your-client-id ALFA_BAAS_CLIENT_SECRET=your-client-secret # URL в вашем приложении, на который вернется пользователь после входа (должен совпадать с настройками в Портале) ALFA_BAAS_REDIRECT_URI=https://your-app.com/alfa/callback ALFA_BAAS_CERT_PATH=storage/certs/baas_cert.pem ALFA_BAAS_KEY_PATH=storage/certs/baas_key.pem # Путь к корневым сертификатам (или 'false' для отключения проверки SSL в sandbox) ALFA_BAAS_CA_BUNDLE=false # Опционально: Ключи для CMS-подписи (если отличаются от транспортных) # ALFA_BAAS_SIGNING_CERT_PATH=storage/certs/sign_cert.pem # ALFA_BAAS_SIGNING_KEY_PATH=storage/certs/sign_key.pem # --- SBP (СБП, QR-коды) --- ALFA_SBP_ENVIRONMENT=sandbox # или production ALFA_SBP_ALIAS=your-key-name-alias ALFA_SBP_CERT_PATH=storage/certs/sbp_cert.pem ALFA_SBP_KEY_PATH=storage/certs/sbp_key.pem ALFA_SBP_CA_BUNDLE=false # --- Webhooks --- ALFA_WEBHOOK_USER=alfa ALFA_WEBHOOK_PASSWORD=secret # Для проверки JWS-подписи вебхуков (опционально) ALFA_WEBHOOK_PUBLIC_KEY=storage/certs/alfa_public_key.pem ``` ### Файл конфигурации (`config/alfa-bank.php`) Вот как выглядит структура конфигурационного файла по умолчанию. Вы можете использовать его для тонкой настройки, например, чтобы задать пути динамически. ```php return [ 'baas' => [ 'environment' => env('ALFA_BAAS_ENVIRONMENT', 'sandbox'), 'client_id' => env('ALFA_BAAS_CLIENT_ID'), 'client_secret' => env('ALFA_BAAS_CLIENT_SECRET'), 'redirect_uri' => env('ALFA_BAAS_REDIRECT_URI'), 'certificate_path' => env('ALFA_BAAS_CERT_PATH'), 'private_key_path' => env('ALFA_BAAS_KEY_PATH'), 'ca_bundle' => env('ALFA_BAAS_CA_BUNDLE'), // ... ], 'sbp' => [ 'environment' => env('ALFA_SBP_ENVIRONMENT', 'sandbox'), 'key_name_alias' => env('ALFA_SBP_ALIAS'), 'certificate_path' => env('ALFA_SBP_CERT_PATH'), 'private_key_path' => env('ALFA_SBP_KEY_PATH'), 'ca_bundle' => env('ALFA_SBP_CA_BUNDLE'), // ... ], 'webhook' => [ 'user' => env('ALFA_WEBHOOK_USER'), 'password' => env('ALFA_WEBHOOK_PASSWORD'), 'alfa_public_key' => env('ALFA_WEBHOOK_PUBLIC_KEY'), ], ]; ``` ## Подключение и Безопасность SDK поддерживает две разные модели безопасности, используемые в API Альфа-Банка. ### 1. Подключение к СБП (Pay-in) **Шаги для получения доступа:** 1. Генерируете пару ключей: `openssl req -x509 -newkey rsa:2048 -keyout client.key -out client.crt -nodes -days 3650`. 2. Отправляете `client.crt` менеджеру банка (acquiring@alfabank.ru). 3. Получаете от банка `alias` (key-name) и `TermNo`. **Поток данных и подпись:** * **Transport**: Обязательный **MTLS**. Вы должны передать `client.crt` и `client.key` в конфигурацию Guzzle/Saloon. * **Payload**: Тело запроса канонизируется по алгоритму **JCS**. * **Signature**: SHA256withRSA подпись JCS-строки помещается в заголовок `Authorization`. **SDK делает это автоматически**: ```php // В config/alfa-bank.php 'sbp' => [ 'alias' => 'my-company-alias', 'private_key' => storage_path('certs/client.key'), 'cert' => storage_path('certs/client.crt'), ], ``` --- ### 2. Подключение к BaaS (Номинальные счета) **Шаги для получения доступа:** 1. Регистрация в личном кабинете партнера. 2. Получение `client_id` и `client_secret` для OAuth2. 3. Регистрация публичного ключа для проверки подписей выплат. **Поток данных и подпись:** * **Auth**: Стандартный OAuth2 Bearer Token. * **Payload Signature**: Для критических операций (выплаты, идентификация) требуется `digestSignatures`. Это массив CMS-подписей (PKCS7) в формате Base64, которыми подписываются финансовые поля. **Пример формирования CMS в SDK:** ```php use Apoloz\AlfaBank\Utils\CmsSigner; $payload = json_encode($dataToSign); $signature = CmsSigner::sign($payload, $privateKey, $certificate); // $signature помещается в массив digestSignatures ``` ### 3. Авторизация Authorization Code Flow (H2H) SDK использует нативный функционал Saloon для OAuth2. **1. Инициализация:** ```php $baas = AlfaBank::baas( clientId: '...', clientSecret: '...', redirectUri: '...', certificatePath: '...', privateKeyPath: '...' ); ``` **2. Получение ссылки на вход:** ```php $url = $baas->getAuthorizationUrl(['beneficiaries']); // Перенаправьте пользователя на $url ``` **3. Обмен кода на токен:** ```php $authenticator = $baas->getAccessToken($code); $baas->authenticate($authenticator); // Применить токен // Сохраните $authenticator->getRefreshToken() в БД ``` **4. Обновление токена:** ```php $newAuth = $baas->refreshAccessToken($savedRefreshToken); $baas->authenticate($newAuth); ``` **Пример обработки callback в Laravel:** ```php // routes/web.php Route::get('/alfa/callback', function (Request $request, AlfaBankConnector $baas) { $code = $request->query('code'); // Обмениваем временный код на постоянные токены $authenticator = $baas->getAccessToken($code); // Сохраняем refresh_token в БД для будущих обновлений сессии $refreshToken = $authenticator->getRefreshToken(); auth()->user()->update(['alfa_refresh_token' => $refreshToken]); return "Авторизация в Альфа-Банке прошла успешно!"; }); ``` Полный пример: [examples/13_h2h_auth_service.php](examples/13_h2h_auth_service.php) ### 4. Генерация Client Secret (Утилита) Если банк выдал только сертификаты, вы можете сгенерировать `client_secret` самостоятельно: ```php // См. examples/14_generate_client_secret.php $baas->send(new RefreshClientSecretRequest($clientId)); ``` > **Важно для продакшена:** > Запрос нового секрета **инвалидирует** старый. Если у вас запущено несколько экземпляров приложения (или worker-ов), не запрашивайте секрет при каждом старте. > **Решение:** Получите секрет один раз, сохраните в общий кеш (Redis/DB) с TTL (например, 29 дней) и запрашивайте новый только при истечении срока или отсутствии в кеше. > Полный пример автоматизации: [examples/16_automated_startup.php](examples/16_automated_startup.php) --- ## Архитектура и Потоки данных ### Гибридный поток: СБП -> Номинальный счет ```mermaid sequenceDiagram participant Client as Покупатель participant SBP as SDK (SBP Connector) participant API as Alfa Bank API participant BaaS as SDK (BaaS Connector) Note over SBP, API: MTLS + JCS Signature SBP->>API: 1. GetQrCodeRequest(NominalAcc) API-->>SBP: 2. QR Payload (URL) Client->>API: 3. Оплата в приложении (СБП) Note over API, BaaS: Webhook with Signature API->>BaaS: 4. Webhook (Входящий платеж) Note over BaaS, API: OAuth2 + CMS Signature BaaS->>API: 5. IdentifyTransactionRequest(beneficiaryId) API-->>BaaS: 6. 200 OK (Зачислено) ``` --- ## Примеры использования ### 1. СБП: Прием платежа на номинальный счет ```php use Apoloz\AlfaBank\Laravel\Facades\Sbp; use Apoloz\AlfaBank\Sbp\Requests\GetQrCodeRequest; use Apoloz\AlfaBank\Sbp\Data\QrCodeRequestData; $response = Sbp::send(new GetQrCodeRequest( new QrCodeRequestData( termNo: '90080567', amount: '10000', // 100.00 RUB paymentPurpose: 'Пополнение кошелька #123' ) )); echo "QR URL: " . $response->dto()->payload; ``` ### 2. Идентификация платежа (BaaS) Когда платеж пришел на номинальный счет, его нужно привязать к бенефициару: ```php use Apoloz\AlfaBank\Requests\NominalAccounts\Identification\IdentifyTransactionRequest; use Apoloz\AlfaBank\Data\NominalAccounts\Identification\IdentifyRequestData; use Apoloz\AlfaBank\Data\NominalAccounts\Identification\AmountDistributionData; $data = new IdentifyRequestData( transactionId: '1220112MOCO#DS4001415', accountNumber: '40702810...', amountDistribution: AmountDistributionData::collect([ ['beneficiaryId' => 'BEN-001', 'amount' => '100.00'] ], DataCollection::class) ); // Автоматическая подпись данных (использует сертификаты из конфигурации) $data->sign($baas->getSigner()); AlfaBank::send(new IdentifyTransactionRequest($data)); ``` ### 3. Работа с номинальными счетами (Бенефициары) ... (existing content) ... ### 4. Самозанятые (Регистрация доходов) ```php use Apoloz\AlfaBank\Requests\SelfEmployed\RegisterIncomeRequest; use Apoloz\AlfaBank\Data\SelfEmployed\CreateIncomeRequestData; $data = new CreateIncomeRequestData( vatin: '1234567890', name: 'Консультационные услуги', amount: '5000.00', date: now()->format('Y-m-d') ); $response = AlfaBank::send(new RegisterIncomeRequest($data)); ``` ### 5. Файловый обмен (KYC) Загрузка сканов документов бенефициаров через pre-signed ссылки: ```php use Apoloz\AlfaBank\Requests\Files\UploadFileRequest; $file = fopen('document.pdf', 'r'); $uploadUrl = 'https://baas.alfabank.ru/filebox/...'; // Получено из метода создания бенефициара AlfaBank::send(new UploadFileRequest($uploadUrl, $file)); ``` ## Сценарии для Laravel Tinker (Copy-Paste) Эти примеры разработаны специально для быстрого запуска и тестирования в консоли `php artisan tinker`. Они содержат полные цепочки бизнес-логики. * [17_laravel_tinker_scoring_creation.php](examples/17_laravel_tinker_scoring_creation.php) — **Скоринг и Создание Бенефициара**: Полный цикл проверки и регистрации контрагента с автоматической подписью данных. * [18_laravel_tinker_identification_payout.php](examples/18_laravel_tinker_identification_payout.php) — **Идентификация и Выплата**: Привязка входящих денег к бенефициару и последующий вывод на карту (с шифрованием PAN). * [19_laravel_tinker_card_registration_payout.php](examples/19_laravel_tinker_card_registration_payout.php) — **Безопасная регистрация карт**: Токенизация карты (создание Реквизита) и выплата по ID (PCI DSS Friendly). --- ## Продвинутые возможности ### Управление возвратами СБП Просмотр всей истории попыток возврата по транзакции: ```php $response = Sbp::send(new GetQrcReversalHistoryRequest($payrrn, $termNo)); foreach ($response->dto()->refunds as $refund) { echo "Статус возврата: " . $refund->status; } ``` ### Webhook Handling (Laravel) ```php // Обработка СБП уведомлений в Listener public function handle(AlfaWebhookReceived $event) { if ($event->webhook->isSbp()) { $data = Sbp::parsePaymentNotification($event->webhook->payload); Log::info("СБП Оплата: {$data->amount} коп."); } } ```