# 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