Files
alfa-bank-sdk/README.md

364 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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} коп.");
}
}
```