doc(exmaples): for nominatl

This commit is contained in:
2026-01-19 00:04:48 +03:00
parent ade610f75b
commit a8f84de46f
2 changed files with 281 additions and 158 deletions

339
README.md
View File

@@ -1,194 +1,217 @@
# Alfa Bank SDK (Unofficial)
This is an unofficial PHP SDK for the [Alfa Bank API](https://developers.alfabank.ru/), built using [Saloon](https://docs.saloon.dev) and [Spatie Laravel Data](https://spatie.be/docs/laravel-data).
[![Tests](https://github.com/apoloz/alfa-bank-sdk/actions/workflows/tests.yml/badge.svg)](https://github.com/apoloz/alfa-bank-sdk/actions)
[![Static Analysis](https://img.shields.io/badge/phpstan-level%205-brightgreen.svg)](https://phpstan.org/)
## Requirements
Профессиональная PHP-библиотека для интеграции с API Альфа-Банка (BaaS и СБП). Построена на базе [Saloon v3](https://docs.saloon.dev) и [Spatie Laravel Data](https://spatie.be/docs/laravel-data).
## Требования
- PHP 8.2+
- OpenSSL (for certificate generation/handling)
- Расширение OpenSSL
- [Saloon](https://docs.saloon.dev) v3.0+
- [Spatie Laravel Data](https://spatie.be/docs/laravel-data)
- [phpseclib](https://phpseclib.org/) (for Card Encryption)
## Installation
## Установка
```bash
composer require apoloz/alfa-bank-sdk
```
## Architecture & Data Flow
---
The SDK abstracts complex banking protocols into a clean, object-oriented interface.
## Архитектура и потоки данных
### 1. Unified Request Architecture
Every request follows a strict "Input DTO -> Request -> Connector -> Output DTO" pipeline.
```mermaid
graph LR
User[Client App] --> DTO_IN[Request DTO]
DTO_IN --> REQ[Request Class]
REQ --> CON[Connector]
CON -->|mTLS + Signature| API((Alfa Bank))
API -->|Raw JSON| CON
CON --> REQ
REQ -->|Data Mapping| DTO_OUT[Response DTO/Collection]
DTO_OUT --> User
style CON fill:#f96,stroke:#333,stroke-width:2px
style DTO_IN fill:#bbf,stroke:#333
style DTO_OUT fill:#9f6,stroke:#333
```
### 2. Authentication & Security Flow (BaaS)
The BaaS API requires a two-step handshake: mTLS for the tunnel and OAuth 2.0 for authorization.
Библиотека обеспечивает строгую типизацию на всех этапах. Вы работаете только с объектами (DTO), а SDK берет на себя всю сложность протоколов банка.
### 1. Жизненный цикл запроса (BaaS)
```mermaid
sequenceDiagram
participant App as Client Application
participant SDK as AlfaBank SDK
participant API as Alfa Bank Gateway
participant App as Приложение
participant DTO as Request DTO
participant SDK as Connector
participant API as Alfa Bank
Note over App, API: 1. Connection (mTLS)
SDK->>API: Client Hello (X.509 Certificate)
API-->>SDK: Server Hello & Encrypted Tunnel established
Note over App, API: 2. Authorization (OAuth 2.0)
App->>SDK: login(id, secret)
SDK->>API: POST /token (Client Credentials)
API-->>SDK: Access Token (JWT)
SDK->>SDK: Store Token in Authenticator
Note over App, API: 3. Business Requests
App->>SDK: send(CreatePaymentRequest)
SDK->>API: POST /payments (Header: Bearer Token)
API-->>SDK: 201 Created
App->>DTO: 1. Создание данных
App->>SDK: 2. send(Request)
SDK->>SDK: 3. Маппинг полей (PascalCase)
SDK->>SDK: 4. Подпись/Шифрование
SDK->>API: 5. HTTPS + mTLS
API-->>SDK: 6. JSON Ответ
SDK->>SDK: 7. Валидация ошибок
SDK->>App: 8. Возврат Response DTO
```
### 3. SBP Security (JCS & Body Signing)
Unlike BaaS, SBP requires signing the actual JSON body using a recursive key-sorting algorithm (JCS).
### 2. Безопасность СБП (Body Signing)
Для СБП реализована автоматическая каноникализация JSON (JCS) с рекурсивной сортировкой ключей перед подписью RSA-SHA256.
```mermaid
flowchart TD
A["Raw Data (Array)"] -->|Recursive ksort| B("Canonical Array")
B -->|JSON_UNESCAPED_UNICODE| C("JCS JSON String")
C -->|RSA-SHA256 Sign| D("Binary Signature")
D -->|Base64 Encode| E["'Authorization' Header"]
E -->|HTTPS + mTLS| F((Alfa Bank SBP Gateway))
subgraph SDK Automatics
B
C
D
end
```
---
### 4. Webhook Pipeline (Laravel Integration)
The SDK acts as a security firewall for incoming bank notifications.
## Настройка и Безопасность
```mermaid
sequenceDiagram
participant Bank as Alfa Bank
participant Mid as VerifyAlfaWebhook (Middleware)
participant Fact as WebhookFactory
participant Event as AlfaWebhookReceived (Event)
participant Listener as Your Listener
Альфа-Банк требует **два набора ключей**:
1. **Транспортные (mTLS):** Для установки защищенного соединения.
2. **Прикладные (Signing):** Для цифровой подписи реестров и платежей.
Bank->>Mid: POST /webhooks/alfa
Mid->>Mid: 1. Validate Credentials
alt Authorized
Mid->>Fact: 2. Parse Raw JSON
Fact->>Fact: 3. Detect Type (BaaS/SBP)
Fact->>Event: 4. Dispatch(Typed DTO)
Event->>Listener: 5. Handle Business Logic
Mid-->>Bank: 200 OK
else Unauthorized
Mid-->>Bank: 401 Unauthorized
end
```
### Быстрый старт (Laravel)
## Security & Setup (Crucial)
Alfa Bank uses a comprehensive security model requiring **two distinct sets of keys**:
1. **Transport (mTLS):** To establish the SSL connection.
2. **Signing (JWS/CMS):** To digitally sign the request body.
### Where to get keys?
- **Sandbox:** Provided by the bank support or downloaded from the Developer Portal.
- **Production:**
- **Transport (mTLS):** Generate a private key and CSR using OpenSSL. Send the CSR to `alfa_api@alfabank.ru`.
- **Signing:** Generated by you (the client). Register the public certificate in "Alfa Business Online".
### 1. Converting PFX to PEM
If the bank provided a `.pfx` file for signing, convert it to PEM:
1. Опубликуйте конфиг:
```bash
# Extract Private Key
openssl pkcs12 -in signing.pfx -nocerts -out signing_key.pem -nodes
# Extract Public Certificate
openssl pkcs12 -in signing.pfx -clcerts -nokeys -out signing_cert.pem
php artisan vendor:publish --tag=alfa-bank-config
```
### 2. Initialization
```php
use Apoloz\AlfaBank\AlfaBank;
use Apoloz\AlfaBank\Enums\Environment;
$baas = AlfaBank::baas(
certificatePath: '/path/to/transport_cert.pem',
privateKeyPath: '/path/to/transport_key.pem',
signingCertificatePath: '/path/to/signing_cert.pem',
signingPrivateKeyPath: '/path/to/signing_key.pem',
environment: Environment::SANDBOX
);
2. Настройте `.env`:
```env
ALFA_BAAS_CERT_PATH=/path/to/transport.crt
ALFA_BAAS_KEY_PATH=/path/to/transport.key
ALFA_BAAS_CLIENT_ID=your_id
ALFA_BAAS_CLIENT_SECRET=your_secret
```
## Core Usage
### Making Requests
#### SBP: Generate QR Code
```php
use Apoloz\AlfaBank\Sbp\Requests\GetQrCodeRequest;
use Apoloz\AlfaBank\Sbp\Data\QrCodeRequestData;
$response = $sbp->send(new GetQrCodeRequest(
new QrCodeRequestData(TermNo: '...', amount: '100')
));
$link = $response->dto()->payload;
```
#### Standard Payments
```php
use Apoloz\AlfaBank\Requests\Payments\CreatePaymentRequest;
use Apoloz\AlfaBank\Data\Payments\PaymentData;
$payload = new PaymentData(amount: 100.50, ...);
$response = $baas->send(new CreatePaymentRequest($payload));
echo $response->dto()->id;
```
### Advanced Features
#### Pagination
The SDK supports automatic pagination for all list endpoints:
```php
use Apoloz\AlfaBank\Requests\Accounts\GetAccountsRequest;
$paginator = $baas->paginate(new GetAccountsRequest());
foreach ($paginator->items() as $account) {
echo $account->number; // Typed AccountData object
}
```
#### Diagnostics
Run the built-in Artisan command to verify your configuration:
3. Проверьте соединение:
```bash
php artisan alfa:check
```
## License
---
MIT
## Примеры использования
### 1. Авторизация и получение счетов
```php
use Apoloz\AlfaBank\Laravel\Facades\AlfaBank;
use Apoloz\AlfaBank\Requests\Accounts\GetAccountsRequest;
// SDK сам получит токен и применит его
$accounts = AlfaBank::send(new GetAccountsRequest());
foreach ($accounts as $account) {
echo $account->number . ": " . $account->openDate;
}
```
### 2. Стандартный платеж (BaaS)
```php
use Apoloz\AlfaBank\Requests\Payments\CreatePaymentRequest;
use Apoloz\AlfaBank\Data\Payments\PaymentData;
$data = new PaymentData(
amount: 1500.00,
date: date('Y-m-d'),
externalId: 'INV-2026-001',
operationCode: '01',
payeeAccount: '40702...',
payeeBankBic: '044525593',
payeeName: 'ООО Партнер',
payerAccount: '40702...',
payerBankBic: '044525593',
payerBankCorrAccount: '30101...',
payerInn: '7700000000',
payerName: 'Моя Компания',
priority: '5',
purpose: 'Оплата по счету №1'
);
$response = AlfaBank::send(new CreatePaymentRequest($data));
echo "Платеж создан: " . $response->id;
```
### 3. СБП: Генерация QR-кода
```php
use Apoloz\AlfaBank\Laravel\Facades\Sbp;
use Apoloz\AlfaBank\Sbp\Requests\GetQrCodeRequest;
use Apoloz\AlfaBank\Sbp\Data\QrCodeRequestData;
$payload = new QrCodeRequestData(
termNo: '90080567',
amount: '10000', // 100.00 руб
paymentPurpose: 'Заказ №5'
);
$response = Sbp::send(new GetQrCodeRequest($payload));
echo "Отсканируйте код: " . $response->payload;
```
### 4. Работа с выписками (Пагинация)
```php
use Apoloz\AlfaBank\Requests\Statement\GetTransactionsRequest;
// Автоматическая итерация через все страницы
$paginator = AlfaBank::paginate(new GetTransactionsRequest(
accountNumber: '40702...',
executeDateFrom: '2026-01-01',
executeDateTo: '2026-01-18'
));
foreach ($paginator->items() as $transaction) {
echo "Транзакция {" . $transaction->uuid . ": " . $transaction->amount['amount'] . " RUB\n";
}
```
### 5. Самозанятые (Справочники и выплаты)
```php
use Apoloz\AlfaBank\Requests\SelfEmployed\GetActivitiesRequest;
// Получение списка разрешенных видов деятельности
$activities = AlfaBank::send(new GetActivitiesRequest());
foreach ($activities as $activity) {
echo "{$activity->code}: {$activity->name}\n";
}
```
### 6. Номинальные счета (Полный цикл)
Сценарий создания бенефициара и выплаты с подписью:
```php
use Apoloz\AlfaBank\Requests\NominalAccounts\Beneficiaries\CreateBeneficiaryRequest;
use Apoloz\AlfaBank\Requests\NominalAccounts\Payouts\CreatePayoffRequest;
// 1. Создание бенефициара с CMS подписью
$response = $baas->send(new CreateBeneficiaryRequest($beneficiaryData));
// 2. Инициирование выплаты
$payoff = $baas->send(new CreatePayoffRequest($payoffData));
echo "Статус выплаты: " . $payoff->dto()->status;
```
---
## Продвинутые возможности
### Диагностика без Laravel
```php
use Apoloz\AlfaBank\AlfaBank;
$ok = AlfaBank::check(
certificatePath: 'cert.pem',
privateKeyPath: 'key.pem',
clientId: '...',
clientSecret: '...'
);
```
### Логирование с маскировкой данных
SDK автоматически скрывает номера карт, токены и подписи в логах.
```php
$connector = AlfaBank::getFacadeRoot();
$connector->withLogging(Log::channel('daily')->getLogger());
```
### Обработка Webhooks (Laravel)
```php
// routes/api.php
Route::alfaWebhooks('/webhooks/alfa');
// Listener
public function handle(AlfaWebhookReceived $event) {
$data = $event->webhook->data;
// $data будет объектом TransactionWebhookData или SbpPaymentWebhookData
}
```
---
## Лицензия
MIT. Автор: Anatoliy Poloz.

View File

@@ -0,0 +1,100 @@
<?php
declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use Apoloz\AlfaBank\AlfaBank;
use Apoloz\AlfaBank\Data\NominalAccounts\Beneficiaries\AddressData;
use Apoloz\AlfaBank\Data\NominalAccounts\Beneficiaries\CreateBeneficiaryRequestData;
use Apoloz\AlfaBank\Data\NominalAccounts\Payouts\DigestSignatureData;
use Apoloz\AlfaBank\Data\NominalAccounts\Payouts\PayoffRequestData;
use Apoloz\AlfaBank\Data\NominalAccounts\Payouts\PayoutRequisiteData;
use Apoloz\AlfaBank\Data\NominalAccounts\Payouts\RecipientData;
use Apoloz\AlfaBank\Requests\NominalAccounts\Beneficiaries\CreateBeneficiaryRequest;
use Apoloz\AlfaBank\Requests\NominalAccounts\Beneficiaries\ScoreBeneficiaryRequest;
use Apoloz\AlfaBank\Requests\NominalAccounts\Payouts\CreatePayoffRequest;
use Apoloz\AlfaBank\Utils\CmsSigner;
use Spatie\LaravelData\DataCollection;
/**
* ПОЛНЫЙ ЖИЗНЕННЫЙ ЦИКЛ НОМИНАЛЬНОГО СЧЕТА
* Сценарий: Регистрация контрагента -> Верификация -> Выплата
*/
// 1. Инициализация SDK
$baas = AlfaBank::baas(
certificatePath: 'transport.crt',
privateKeyPath: 'transport.key'
);
// Данные для подписи (в реальном приложении загружаются из защищенного хранилища)
$signCert = file_get_contents('signing.crt');
$signKey = file_get_contents('signing.key');
try {
$baas->login('client_id', 'client_secret');
// --- ЭТАП 1: Создание Бенефициара (Контрагента) ---
echo "1. Регистрация бенефициара...\n";
$beneficiaryData = new CreateBeneficiaryRequestData(
type: 'FL_RESIDENT',
countryOfTaxResidence: 'RU',
addresses: AddressData::collect([
['type' => 'REGISTRATION_ADDRESS', 'address' => 'г. Москва, ул. Ленина 1', 'country' => 'RU']
], DataCollection::class),
digestSignatures: DigestSignatureData::collect([
[
'base64Encoded' => CmsSigner::sign('Data to sign', $signCert, $signKey),
'signatureType' => 'cms'
]
], DataCollection::class),
name: 'Иванов Иван Иванович',
inn: '771234567890'
);
$response = $baas->send(new CreateBeneficiaryRequest($beneficiaryData));
$beneficiaryId = $response->dto()->requestId; // В некоторых версиях API возвращается UUID
echo "Бенефициар создан. ID запроса: $beneficiaryId\n";
// --- ЭТАП 2: Верификация (Скоринг) ---
// После создания банк должен проверить бенефициара
echo "2. Запуск верификации (скоринга)...";
$baas->send(new ScoreBeneficiaryRequest($beneficiaryId));
echo "Скоринг инициирован.\n";
// --- ЭТАП 3: Выплата (Payoff) ---
// Когда бенефициар проверен, можно делать выплату
echo "3. Создание выплаты...";
$payoffData = new PayoffRequestData(
type: 'PAYMENT_ORDER',
accountNumber: '40702810...', // Ваш номинальный счет
recipient: new RecipientData(
name: 'Иванов Иван Иванович',
inn: '771234567890'
),
payoutRequisite: new PayoutRequisiteData(
type: 'ACCOUNT',
accountNumber: '40817810...', // Личный счет Иванова
bik: '044525593'
),
amount: '1000.50',
purpose: 'Выплата по договору комиссии',
digestSignatures: DigestSignatureData::collect([
[
'base64Encoded' => '...', // Здесь должна быть подпись тела выплаты
'signatureType' => 'cms'
]
], DataCollection::class)
);
$payoffResponse = $baas->send(new CreatePayoffRequest($payoffData));
echo "Выплата создана. ID: " . $payoffResponse->dto()->payoffId . "\n";
} catch (\Exception $e) {
echo "Ошибка: " . $e->getMessage() . "\n";
}