Files
alfa-bank-sdk/README.md

557 lines
14 KiB
Markdown

# 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).
## Requirements
- PHP 8.2+
- OpenSSL (for certificate generation/handling)
- [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
```
## 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 (usually as `.pfx` or `.zip`).
- **Production:**
- **Transport (mTLS):** Generate a private key and CSR using OpenSSL (`openssl genrsa`, `openssl req`). Send the CSR to `alfa_api@alfabank.ru`. The bank will reply with your Certificate.
- **Signing:** Generated by you (the client). The public certificate must be registered in "Alfa Business Online" or sent to your onboarding manager.
### 1. Converting PFX to PEM
If the bank provided a `.pfx` file for signing, you must convert it to PEM format:
```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
```
### 2. Initialization
You can now initialize the SDK with both key pairs:
```php
use Apoloz\AlfaBank\AlfaBank;
use Apoloz\AlfaBank\Enums\Environment;
$baas = AlfaBank::baas(
// Transport Keys (mTLS)
certificatePath: '/path/to/transport_cert.pem',
privateKeyPath: '/path/to/transport_key.pem',
// Signing Keys (Business Logic) - Optional, defaults to transport keys if not set
signingCertificatePath: '/path/to/signing_cert.pem',
signingPrivateKeyPath: '/path/to/signing_key.pem',
environment: Environment::SANDBOX,
caBundlePath: '/path/to/root_ca_bundle.crt'
);
```
## Core Usage
### Authentication (BaaS)
Use the `login` method to perform OAuth Client Credentials flow:
```php
$baas->login(
clientId: 'your-client-id',
clientSecret: 'your-client-secret'
);
```
### Making Requests
#### SBP: Generate QR Code
```php
use Apoloz\AlfaBank\Sbp\Requests\GetQrCodeRequest;
use Apoloz\AlfaBank\Sbp\Data\QrCodeRequestData;
$sbp = AlfaBank::sbp(
certificatePath: '...',
privateKeyPath: '...',
keyNameAlias: '9728109955', // Terminal ID
signingPrivateKeyPath: '/path/to/signing_key.pem' // Specific signing key for SBP body
);
$response = $sbp->send(new GetQrCodeRequest(
new QrCodeRequestData(TermNo: '...', amount: '100')
));
$link = $response->dto()->payload;
```
#### Nominal Accounts
```php
$response = $baas->send(new CreateBeneficiaryRequest($payload));
```
### Self-Employed (Payouts & Status)
The SDK supports managing self-employed beneficiaries, checking their FTS status, and processing payouts.
#### Check Taxpayer Status
```php
use Apoloz\AlfaBank\Requests\SelfEmployed\GetTaxpayerStatusRequest;
use Apoloz\AlfaBank\Data\SelfEmployed\TaxpayerStatusRequestData;
$payload = new TaxpayerStatusRequestData(
externalId: 'unique-id',
selfemployedVatinList: ['123456789012']
);
$response = $baas->send(new GetTaxpayerStatusRequest($payload));
$status = $response->dto()->taxpayerStatusList[0];
```
#### Process Payout
```php
use Apoloz\AlfaBank\Requests\SelfEmployed\CreatePayoutRequest;
use Apoloz\AlfaBank\Data\SelfEmployed\PayoutRequestData;
$payload = new PayoutRequestData(
externalId: 'ext-id-456',
company: ['vatin' => '7700000000'],
requisite: [
['amount' => 1000, 'vatin' => '112233445566']
]
);
$response = $baas->send(new CreatePayoutRequest($payload));
```
### File Exchange
Upload and download files securely.
```php
use Apoloz\AlfaBank\Requests\FileExchange\UploadFileRequest;
use Apoloz\AlfaBank\Requests\FileExchange\DownloadFileRequest;
// Upload
$stream = fopen('path/to/file', 'r');
$baas->send(new UploadFileRequest($uploadUrl, $stream, $fileSize));
// Download
$response = $baas->send(new DownloadFileRequest($downloadUrl));
$content = $response->body();
```
### Payments (RUR Payment Orders)
Create standard payment orders and check their status.
```php
use Apoloz\AlfaBank\Requests\Payments\CreatePaymentRequest;
use Apoloz\AlfaBank\Data\Payments\PaymentData;
$payload = new PaymentData(
amount: 100.50,
date: '2023-10-25',
externalId: 'order-12345',
operationCode: '01',
payeeAccount: '40702...',
payeeBankBic: '044525225',
payeeName: 'Partner LLC',
payerAccount: '40702...',
payerBankBic: '044525593',
payerBankCorrAccount: '30101...',
payerInn: '7700000000',
payerName: 'My Company',
priority: '5',
purpose: 'Payment for services'
);
// Create Payment
$baas->send(new CreatePaymentRequest($payload));
// Check State
use Apoloz\AlfaBank\Requests\Payments\GetPaymentStateRequest;
$state = $baas->send(new GetPaymentStateRequest('order-12345'))->dto();
echo $state->bankStatus; // e.g. ACCEPTED
```
### Cards
Retrieve a list of client cards.
```php
use Apoloz\AlfaBank\Requests\Cards\GetCardsRequest;
$response = $baas->send(new GetCardsRequest());
foreach ($response->dto() as $card) {
echo $card->maskedNumber . ' - ' . $card->status;
}
```
### B2C Payouts (SBP via Phone)
Send money to individuals using their phone number through SBP.
```php
use Apoloz\AlfaBank\Requests\B2C\CreateB2CPaymentRequest;
use Apoloz\AlfaBank\Data\B2C\B2CPaymentRequestData;
use Apoloz\AlfaBank\Data\B2C\B2CClientData;
use Apoloz\AlfaBank\Data\B2C\B2CPaymentRecipientData;
$payload = new B2CPaymentRequestData(
client: new B2CClientData(b2cClientId: '...', partnerId: 123),
inn: '7700000000',
account: '40702...',
total: 100.53,
signedBody: '...base64_signature...',
payments: B2CPaymentRecipientData::collect([
[
'phone' => '79001112233',
'amount' => 100.53,
'nspkBankId' => '100000000001',
'sequence' => 'unique-seq-1',
'name' => ['first' => 'Ivan', 'last' => 'Ivanov']
]
], DataCollection::class)
);
$baas->send(new CreateB2CPaymentRequest($payload));
```
### Payment Registries (Batch Ruble Payments)
Create a registry for multiple ruble payments and sign it.
```php
use Apoloz\AlfaBank\Requests\Registries\CreateRegistryRequest;
use Apoloz\AlfaBank\Data\Registries\RegistryRequestData;
use Apoloz\AlfaBank\Data\Registries\RegistryPaymentData;
$payload = new RegistryRequestData(
payments: RegistryPaymentData::collect([
[
'amount' => 1000.0,
'date' => '2023-11-01',
'payeeAccount' => '40702...',
'payeeBankBic' => '044525225',
'payeeInn' => '7700000000',
'payeeName' => 'Vendor LLC',
'payerAccount' => '40702...',
'purpose' => 'Batch payment part 1'
]
], DataCollection::class),
description: 'Weekly payroll'
);
$response = $baas->send(new CreateRegistryRequest($payload));
$registryId = $response->json('id');
// Sign Registry
use Apoloz\AlfaBank\Requests\Registries\SignRegistryRequest;
use Apoloz\AlfaBank\Data\Registries\RegistrySignatureData;
use Spatie\LaravelData\DataCollection;
$signatures = RegistrySignatureData::collect([
[
'base64Encoded' => '...', // Base64 signature
'signatureType' => 'RSA',
'certificateUuid' => '...'
]
], DataCollection::class);
$baas->send(new SignRegistryRequest($registryId, $signatures));
```
### Customer Info
Get organization profile.
```php
use Apoloz\AlfaBank\Requests\CustomerInfo\GetCustomerInfoRequest;
$info = $baas->send(new GetCustomerInfoRequest())->dto();
echo $info->fullName;
```
### Limits
Get client's operation limits.
```php
use Apoloz\AlfaBank\Requests\Limits\GetGeneralLimitsRequest;
$response = $baas->send(new GetGeneralLimitsRequest());
$limits = $response->json();
```
### Debit Cards
Apply for a new debit card.
```php
use Apoloz\AlfaBank\Requests\DebitCards\CreateDebitCardApplicationRequest;
use Apoloz\AlfaBank\Data\DebitCards\DebitCardApplicationData;
$payload = new DebitCardApplicationData(
embossingName: 'IVAN IVANOV',
firstName: 'Иван',
lastName: 'Иванов',
middleName: 'Иванович',
mobilePhone: '79001112233',
passportBirthDate: '1990-01-01',
sex: 'm',
sopdConfirmed: true
);
$baas->send(new CreateDebitCardApplicationRequest($payload));
```
### User Compliance
Check user compliance for loans.
```php
use Apoloz\AlfaBank\Requests\Compliance\GetUserComplianceRequest;
$baas->send(new GetUserComplianceRequest());
```
### DFA (Digital Financial Assets)
List initial placements.
```php
use Apoloz\AlfaBank\Requests\DFA\GetDfaInitialPlacementListRequest;
$baas->send(new GetDfaInitialPlacementListRequest(limit: 5));
```
### Currency Rates
Get currency rates by office.
```php
use Apoloz\AlfaBank\Requests\Rates\GetOfficeRatesRequest;
$baas->send(new GetOfficeRatesRequest());
```
### Escrow Accounts
Manage construction projects and escrow orders.
```php
use Apoloz\AlfaBank\Requests\Escrow\GetBuildProjectsRequest;
$projects = $baas->send(new GetBuildProjectsRequest())->json();
```
### Power of Attorney (Poa)
Request and check status of digital POAs.
```php
use Apoloz\AlfaBank\Requests\Poa\CreatePoaRequest;
$response = $baas->send(new CreatePoaRequest('poa-123', '7700000000'));
$uuid = $response->json('requestUuid');
```
### Loyalty
Access bonus accounts and transaction history.
```php
use Apoloz\AlfaBank\Requests\Loyalty\GetLoyaltyAccountsRequest;
$accounts = $baas->send(new GetLoyaltyAccountsRequest())->json();
```
### Reports
Order certificates and reports.
```php
use Apoloz\AlfaBank\Requests\Reports\CreateAccountBalanceReportRequest;
$baas->send(new CreateAccountBalanceReportRequest(['40702...'], 'eng'));
```
### Crypto Service
Manage encryption and signing certificates.
```php
use Apoloz\AlfaBank\Requests\Crypto\GetCertificatesRequest;
$certificates = $baas->send(new GetCertificatesRequest())->json();
```
### Partner (MMB)
Access partner program data and applications.
```php
use Apoloz\AlfaBank\Requests\Partner\GetPartnerApplicationsRequest;
$apps = $baas->send(new GetPartnerApplicationsRequest())->json();
```
### Miscellaneous
- **Bonus Numbers**: `$baas->send(new GetBonusNumbersRequest());`
- **Credit Offers**: `$baas->send(new GetCreditOffersRequest());`
- **Self-encashment**: `$baas->send(new CreateSelfEncashmentRegistryRequest($data));`
- **Business Lounge**: `$baas->send(new GetLoungeBalanceRequest());`
- **Scoring**: `$baas->send(new GetComplianceScoreRequest('7700000000'));`
- **Security**: `$baas->send(new GetSubscriptionsRequest());`
- **Logistics**: `$baas->send(new GetDeliveriesRequest('...'));`
- **Webhooks**: `$baas->send(new GetWebhooksRequest());`
- **1C Integration**: `$baas->send(new CheckMerchantRequest('MF123', 'Basic ...'));`
- **Claims**: `$baas->send(new ResolveClaimRequest('ID', $data));`
- **Orders (Travel/Afisha)**: `$baas->send(new CreateTravelOrderRequest($data));`
- **Salary Project**: `$baas->send(new CreatePayrollApplicationRequest($payload));`
- **Cloud Signature**: `$baas->send(new SubmitSigningRequest('REQ-ID'));`
- **Aff Integration**: `$baas->send(new CheckComplianceRequest('INN', 'Name'));`
- **Digital Ruble**: `$sbp->send(new CreateUniQrRequest($uniQrData));`
### Signing Data (CMS)
For requests requiring a digital signature (e.g., Payments), you must canonicalize and sign the data manually.
```php
use Apoloz\AlfaBank\Utils\CmsSigner;
use Apoloz\AlfaBank\Data\NominalAccounts\Payouts\DigestSignatureData;
use Spatie\LaravelData\DataCollection;
// 1. Prepare data
$fields = [
'amount' => 100.50,
// ... other fields
];
// 2. Canonicalize & Sign
$json = CmsSigner::canonicalize($fields);
$cert = file_get_contents('/path/to/signing_cert.pem');
$key = file_get_contents('/path/to/signing_key.pem');
$signature = CmsSigner::sign($json, $cert, $key);
// 3. Attach to DTO
$paymentData->digestSignatures = DigestSignatureData::collect([
[
'base64Encoded' => $signature,
'signatureType' => 'CMS'
]
], DataCollection::class);
```
## Advanced Features
### Pagination
The SDK supports automatic pagination for list endpoints:
```php
use Apoloz\AlfaBank\Requests\Accounts\GetAccountsRequest;
$paginator = $baas->paginate(new GetAccountsRequest());
foreach ($paginator as $account) {
// Each item is processed automatically
}
```
## Laravel Integration
The package supports auto-discovery. Publish the config file:
```bash
php artisan vendor:publish --tag=alfa-bank-config
```
Configure your `.env`:
```env
ALFA_BAAS_CERT_PATH=/path/to/transport_cert.pem
# ... other settings
ALFA_WEBHOOK_USER=your_webhook_user
ALFA_WEBHOOK_PASSWORD=your_webhook_password
```
### Webhook Handling (Laravel)
The SDK provides a built-in controller and route macro to handle incoming webhooks and dispatch Laravel events.
1. **Register the route** in `routes/api.php`:
```php
Route::alfaWebhooks('/webhooks/alfa');
```
2. **Listen for the event** in `app/Providers/EventServiceProvider.php`:
```php
use Apoloz\AlfaBank\Laravel\Events\AlfaWebhookReceived;
protected $listen = [
AlfaWebhookReceived::class => [
\App\Listeners\HandleAlfaWebhook::class,
],
];
```
3. **Handle the DTO** in your listener:
```php
public function handle(AlfaWebhookReceived $event)
{
$webhook = $event->webhook; // BaseWebhookData DTO
if ($webhook->object === 'ul_transaction_default') {
$transaction = $webhook->data;
// ...
}
}
```
## Error Handling
The SDK throws `Apoloz\AlfaBank\Exceptions\AlfaBankException` when the API returns an error.
```php
try {
$baas->send($request);
} catch (\Apoloz\AlfaBank\Exceptions\AlfaBankException $e) {
echo $e->getErrorCode(); // e.g. "invalid_token" or "RQ05014"
echo $e->getMessage();
}
```
## Utilities
- **Card PAN Encryption**: `CardEncryptor::encrypt($pan, $pubKey)`
- **CMS Signing**: `CmsSigner::sign($data, $cert, $key)`
- **JWS/JWE**: `JwsSigner` and `JweEncryptor` for transport security.
## Diagnostics
### Health Check (Laravel)
Run the built-in Artisan command to verify your mTLS certificates and connection:
```bash
php artisan alfa:check
```
### Logging
Attach a PSR-3 logger to debug requests (sensitive data redacted):
```php
$baas->withLogging(Log::getLogger());
```
## License
MIT