fix(formating)

This commit is contained in:
Anatoliy Poloz
2026-01-28 03:36:26 +03:00
parent 1723177949
commit b15d29f8e9
313 changed files with 1863 additions and 1533 deletions

View File

@@ -59,9 +59,8 @@ try {
echo "Beneficiary Request Sent (Mock).\n";
} catch (\Exception $e) {
echo "Error: " . $e->getMessage() . "\n";
echo 'Error: ' . $e->getMessage() . "\n";
if ($e instanceof \Apoloz\AlfaBank\Exceptions\AlfaBankException) {
echo "API Code: " . $e->getErrorCode() . "\n";
echo 'API Code: ' . $e->getErrorCode() . "\n";
}
}

View File

@@ -4,8 +4,8 @@ require_once __DIR__ . '/../vendor/autoload.php';
use Apoloz\AlfaBank\AlfaBank;
use Apoloz\AlfaBank\Enums\Environment;
use Apoloz\AlfaBank\Sbp\Requests\GetQrCodeRequest;
use Apoloz\AlfaBank\Sbp\Data\QrCodeRequestData;
use Apoloz\AlfaBank\Sbp\Requests\GetQrCodeRequest;
// 1. Initialize SBP
$sbp = AlfaBank::sbp(
@@ -30,8 +30,8 @@ try {
if ($response->successful()) {
$qrData = $response->dto();
echo "QR Payload: " . $qrData->payload . "\n";
echo "QR Image URL: " . $qrData->image . "\n";
echo 'QR Payload: ' . $qrData->payload . "\n";
echo 'QR Image URL: ' . $qrData->image . "\n";
} else {
echo "Failed to generate QR.\n";
}
@@ -41,5 +41,5 @@ try {
// foreach($paginator as $item) { ... }
} catch (\Exception $e) {
echo "Error: " . $e->getMessage() . "\n";
echo 'Error: ' . $e->getMessage() . "\n";
}

View File

@@ -35,7 +35,6 @@ $baas = AlfaBank::baas(
echo "BaaS Connector Initialized.\n";
// --- 2. SBP (Faster Payments) Initialization ---
$sbp = AlfaBank::sbp(
// SBP usually uses its own mTLS pair found in the sbp_payout/mtls folder
@@ -52,4 +51,3 @@ $sbp = AlfaBank::sbp(
echo "SBP Connector Initialized.\n";
// Now you can use $baas->login(...) or $sbp->send(...)

View File

@@ -3,10 +3,10 @@
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\Enums\Environment;
use Apoloz\AlfaBank\Requests\NominalAccounts\Beneficiaries\CreateBeneficiaryRequest;
use Apoloz\AlfaBank\Data\NominalAccounts\Beneficiaries\CreateBeneficiaryRequestData;
use Apoloz\AlfaBank\Data\NominalAccounts\Beneficiaries\AddressData;
use Spatie\LaravelData\DataCollection;
// 1. Initialize
@@ -29,7 +29,7 @@ $payload = new CreateBeneficiaryRequestData(
countryOfTaxResidence: 'RU',
name: 'Ivanov Ivan',
addresses: new DataCollection(AddressData::class, [
new AddressData('REGISTRATION_ADDRESS', 'Moscow', 'RU')
new AddressData('REGISTRATION_ADDRESS', 'Moscow', 'RU'),
])
// Note: digestSignatures is now optional!
);
@@ -40,12 +40,12 @@ $payload->sign($baas->getSigner());
// 5. Send
$response = $baas->send(new CreateBeneficiaryRequest($payload));
echo "Status: " . $response->status() . "\n";
echo "ID: " . $response->dto()->id . "\n";
echo 'Status: ' . $response->status() . "\n";
echo 'ID: ' . $response->dto()->id . "\n";
if ($response->successful()) {
$data = $response->dto(); // BeneficiaryResponseData
echo "Beneficiary creation accepted. ID: {$data->id}, Status: {$data->status}\n";
} else {
echo "Error: " . $response->body() . "\n";
echo 'Error: ' . $response->body() . "\n";
}

View File

@@ -3,10 +3,10 @@
require_once __DIR__ . '/../vendor/autoload.php';
use Apoloz\AlfaBank\AlfaBank;
use Apoloz\AlfaBank\Webhooks\WebhookFactory;
use Apoloz\AlfaBank\Requests\Webhooks\GetWebhooksRequest;
use Apoloz\AlfaBank\Requests\Webhooks\CreateWebhookRequest;
use Apoloz\AlfaBank\Data\Webhooks\Subscriptions\WebhookSubscriptionRequestData;
use Apoloz\AlfaBank\Requests\Webhooks\CreateWebhookRequest;
use Apoloz\AlfaBank\Requests\Webhooks\GetWebhooksRequest;
use Apoloz\AlfaBank\Webhooks\WebhookFactory;
/**
* 1. Managing Webhooks (BaaS API)
@@ -15,7 +15,7 @@ $baas = AlfaBank::baas('cert.pem', 'key.pem');
$baas->login('id', 'secret');
// List existing webhooks
$response = $baas->send(new GetWebhooksRequest());
$response = $baas->send(new GetWebhooksRequest);
$webhooks = $response->dto(); // DataCollection of WebhookSubscriptionData
foreach ($webhooks as $webhook) {
@@ -38,9 +38,9 @@ $rawPayload = [
'transactionId' => 'TX-12345',
'amount' => ['amount' => 100.50, 'currencyName' => 'RUR'],
'direction' => 'CREDIT',
'paymentPurpose' => 'Test payment'
]
]
'paymentPurpose' => 'Test payment',
],
],
];
// Use the factory to parse the payload into a DTO

View File

@@ -3,16 +3,15 @@
require_once __DIR__ . '/../vendor/autoload.php';
use Apoloz\AlfaBank\AlfaBank;
use Apoloz\AlfaBank\Requests\B2C\CreateB2CPaymentRequest;
use Apoloz\AlfaBank\Data\B2C\B2CPaymentRequestData;
use Apoloz\AlfaBank\Data\B2C\B2CClientData;
use Apoloz\AlfaBank\Data\B2C\B2CPaymentRecipientData;
use Apoloz\AlfaBank\Data\B2C\B2CPaymentRequestData;
use Apoloz\AlfaBank\Requests\B2C\CreateB2CPaymentRequest;
use Spatie\LaravelData\DataCollection;
/**
* Example: B2C Payout via SBP (Phone Number).
*/
$baas = AlfaBank::baas(
certificatePath: 'path/to/cert.pem',
privateKeyPath: 'path/to/key.pem'
@@ -29,8 +28,8 @@ $payload = new B2CPaymentRequestData(
'phone' => '79001112233',
'amount' => 100.00,
'nspkBankId' => '100000000001',
'sequence' => 'unique-payout-id-1'
]
'sequence' => 'unique-payout-id-1',
],
], DataCollection::class)
);
@@ -41,6 +40,5 @@ if ($response->successful()) {
$data = $response->dto(); // B2CPaymentResponseData
echo "Payout request sent. Status: {$data->status}\n";
} else {
echo "Error: " . $response->body() . "\n";
echo 'Error: ' . $response->body() . "\n";
}

View File

@@ -3,18 +3,17 @@
require_once __DIR__ . '/../vendor/autoload.php';
use Apoloz\AlfaBank\AlfaBank;
use Apoloz\AlfaBank\Utils\CmsSigner;
use Apoloz\AlfaBank\Requests\Registries\CreateRegistryRequest;
use Apoloz\AlfaBank\Data\Registries\RegistryRequestData;
use Apoloz\AlfaBank\Data\Registries\RegistryPaymentData;
use Apoloz\AlfaBank\Requests\Registries\SignRegistryRequest;
use Apoloz\AlfaBank\Data\Registries\RegistryRequestData;
use Apoloz\AlfaBank\Data\Registries\RegistrySignatureData;
use Apoloz\AlfaBank\Requests\Registries\CreateRegistryRequest;
use Apoloz\AlfaBank\Requests\Registries\SignRegistryRequest;
use Apoloz\AlfaBank\Utils\CmsSigner;
use Spatie\LaravelData\DataCollection;
/**
* Example: Creating and Signing a Payment Registry (Batch Payments).
*/
$baas = AlfaBank::baas(
certificatePath: 'path/to/cert.pem',
privateKeyPath: 'path/to/key.pem'
@@ -31,8 +30,8 @@ $payload = new RegistryRequestData(
'payeeInn' => '770...',
'payeeName' => 'Vendor 1',
'payerAccount' => '40702...',
'purpose' => 'Payment for goods'
]
'purpose' => 'Payment for goods',
],
], DataCollection::class)
);
@@ -55,11 +54,10 @@ $signatures = RegistrySignatureData::collect([
[
'base64Encoded' => $signature,
'signatureType' => 'RSA',
'certificateUuid' => 'YOUR_CERT_UUID'
]
'certificateUuid' => 'YOUR_CERT_UUID',
],
], DataCollection::class);
$baas->send(new SignRegistryRequest($registryId, $signatures));
echo "Registry Signed.\n";

View File

@@ -14,7 +14,6 @@ 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;
/**
@@ -50,7 +49,7 @@ try {
countryOfTaxResidence: 'RU',
name: 'Иванов Иван',
addresses: AddressData::collect([
new AddressData('REGISTRATION_ADDRESS', 'Moscow', 'RU')
new AddressData('REGISTRATION_ADDRESS', 'Moscow', 'RU'),
])
);
@@ -63,14 +62,13 @@ try {
// --- ЭТАП 2: Идентификация Входящего Платежа ---
// После создания банк должен проверить бенефициара
echo "2. Запуск верификации (скоринга)...";
echo '2. Запуск верификации (скоринга)...';
$baas->send(new ScoreBeneficiaryRequest($beneficiaryId));
echo "Скоринг инициирован.\n";
// --- ЭТАП 3: Выплата (Payoff) ---
// Когда бенефициар проверен, можно делать выплату
echo "3. Создание выплаты...";
echo '3. Создание выплаты...';
$payoffData = new PayoffRequestData(
type: 'PAYMENT_ORDER',
@@ -89,14 +87,14 @@ try {
digestSignatures: DigestSignatureData::collect([
[
'base64Encoded' => '...', // Здесь должна быть подпись тела выплаты
'signatureType' => 'cms'
]
'signatureType' => 'cms',
],
], DataCollection::class)
);
$payoffResponse = $baas->send(new CreatePayoffRequest($payoffData));
echo "Выплата создана. ID: " . $payoffResponse->dto()->payoffId . "\n";
echo 'Выплата создана. ID: ' . $payoffResponse->dto()->payoffId . "\n";
} catch (\Exception $e) {
echo "Ошибка: " . $e->getMessage() . "\n";
echo 'Ошибка: ' . $e->getMessage() . "\n";
}

View File

@@ -5,15 +5,14 @@ declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use Apoloz\AlfaBank\AlfaBank;
use Apoloz\AlfaBank\Data\SelfEmployed\CreateIncomeRequestData;
use Apoloz\AlfaBank\Enums\Environment;
use Apoloz\AlfaBank\Requests\SelfEmployed\RegisterIncomeRequest;
use Apoloz\AlfaBank\Data\SelfEmployed\CreateIncomeRequestData;
/**
* Пример регистрации дохода самозанятого.
* Позволяет автоматически задекларировать доход в ФНС от лица самозанятого.
*/
$baas = AlfaBank::baas(
certificatePath: __DIR__ . '/../tests/test_cert.pem',
privateKeyPath: __DIR__ . '/../tests/test_key.pem',
@@ -37,6 +36,5 @@ try {
echo "ID запроса: {$dto->requestId}\n";
echo "Текущий статус: {$dto->status}\n";
} catch (\Exception $e) {
echo "Ошибка: " . $e->getMessage() . "\n";
echo 'Ошибка: ' . $e->getMessage() . "\n";
}

View File

@@ -12,7 +12,6 @@ use Apoloz\AlfaBank\Sbp\Requests\Reversal\GetQrcReversalHistoryRequest;
* Пример получения истории возвратов по транзакции СБП.
* Полезно для отладки частичных возвратов или поиска зависших операций.
*/
$sbp = AlfaBank::sbp(
certificatePath: __DIR__ . '/../tests/test_cert.pem',
privateKeyPath: __DIR__ . '/../tests/test_key.pem',
@@ -27,7 +26,7 @@ try {
$response = $sbp->send(new GetQrcReversalHistoryRequest($payrrn, $termNo));
$dto = $response->dto();
echo "Найдено возвратов: " . ($dto->refunds ? $dto->refunds->count() : 0) . "\n";
echo 'Найдено возвратов: ' . ($dto->refunds ? $dto->refunds->count() : 0) . "\n";
if ($dto->refunds) {
foreach ($dto->refunds as $refund) {
@@ -38,6 +37,5 @@ try {
}
}
} catch (\Exception $e) {
echo "Ошибка: " . $e->getMessage() . "\n";
echo 'Ошибка: ' . $e->getMessage() . "\n";
}

View File

@@ -13,7 +13,6 @@ use Apoloz\AlfaBank\Requests\Files\UploadFileRequest;
* Сначала создается бенефициар, банк возвращает ссылку для загрузки паспорта.
* Затем SDK загружает файл по этой ссылке.
*/
$baas = AlfaBank::baas(
certificatePath: __DIR__ . '/../tests/test_cert.pem',
privateKeyPath: __DIR__ . '/../tests/test_key.pem',
@@ -34,13 +33,12 @@ try {
if ($response->status() === 200) {
echo "Файл успешно загружен в банк!\n";
} else {
echo "Ошибка загрузки: " . $response->body() . "\n";
echo 'Ошибка загрузки: ' . $response->body() . "\n";
}
} catch (\Exception $e) {
echo "Ошибка: " . $e->getMessage() . "\n";
echo 'Ошибка: ' . $e->getMessage() . "\n";
} finally {
if (is_resource($fileResource)) {
fclose($fileResource);
}
}

View File

@@ -34,7 +34,7 @@ $config = [
// BUSINESS DATA:
// From your Contract or Internet Bank. Not related to Auth directly.
'account_number' => '40702810...'
'account_number' => '40702810...',
];
// 2. Инициализация коннектора
@@ -56,7 +56,7 @@ try {
$tokenData = $baas->authWithCode($authCode, $config['redirect_uri']);
echo "Access Token получен!\n";
echo "Refresh Token: " . $tokenData->refresh_token . "\n";
echo 'Refresh Token: ' . $tokenData->refresh_token . "\n";
echo "(Сохраните refresh_token в базу данных)\n\n";
}
@@ -67,9 +67,9 @@ try {
echo "Обновляем сессию по Refresh Token...\n";
$tokenData = $baas->refreshToken($savedRefreshToken);
echo "Сессия обновлена. Новый Access Token действует " . $tokenData->expires_in . " сек.\n";
echo 'Сессия обновлена. Новый Access Token действует ' . $tokenData->expires_in . " сек.\n";
if ($tokenData->refresh_token) {
echo "Пришел новый Refresh Token (сохраните его): " . $tokenData->refresh_token . "\n";
echo 'Пришел новый Refresh Token (сохраните его): ' . $tokenData->refresh_token . "\n";
}
}
@@ -82,12 +82,11 @@ try {
type: 'FL_RESIDENT'
));
echo "Успешно! Найдено: " . count($response->dto()->beneficiaries) . "\n";
echo 'Успешно! Найдено: ' . count($response->dto()->beneficiaries) . "\n";
} catch (\Exception $e) {
echo "Ошибка: " . $e->getMessage() . "\n";
echo 'Ошибка: ' . $e->getMessage() . "\n";
if (method_exists($e, 'getResponse')) {
echo "Body: " . $e->getResponse()->body() . "\n";
echo 'Body: ' . $e->getResponse()->body() . "\n";
}
}

View File

@@ -34,7 +34,6 @@ $baas = AlfaBank::baas(
$link = $baas->getAuthorizationUrl(scopes: ['beneficiaries', 'payment']);
echo "1. Перейдите по ссылке для авторизации:\n$link\n\n";
// --- СЦЕНАРИЙ 2: Обмен кода на токен (в callback-скрипте) ---
// $code приходит в GET-параметре: /callback?code=...
$codeFromUrl = '...код из браузера...';
@@ -46,14 +45,14 @@ if ($codeFromUrl !== '...код из браузера...') {
// Saloon делает всё сам:
$authenticator = $baas->getAccessToken($codeFromUrl);
echo "Access Token: " . substr($authenticator->getAccessToken(), 0, 20) . "...\n";
echo "Refresh Token: " . $authenticator->getRefreshToken() . "\n";
echo "Expires At: " . $authenticator->getExpiresAt()->format('Y-m-d H:i:s') . "\n";
echo 'Access Token: ' . substr($authenticator->getAccessToken(), 0, 20) . "...\n";
echo 'Refresh Token: ' . $authenticator->getRefreshToken() . "\n";
echo 'Expires At: ' . $authenticator->getExpiresAt()->format('Y-m-d H:i:s') . "\n";
// TODO: Сохраните refresh_token в базу данных!
} catch (\Exception $e) {
echo "Ошибка обмена кода: " . $e->getMessage() . "\n";
echo 'Ошибка обмена кода: ' . $e->getMessage() . "\n";
}
}
@@ -78,7 +77,6 @@ if ($savedRefreshToken !== '...saved_refresh_token...') {
echo "Новый Refresh Token получен.\n";
}
} catch (\Exception $e) {
echo "Ошибка обновления токена: " . $e->getMessage() . "\n";
echo 'Ошибка обновления токена: ' . $e->getMessage() . "\n";
}
}

View File

@@ -5,8 +5,8 @@ declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use Apoloz\AlfaBank\AlfaBank;
use Apoloz\AlfaBank\Requests\Auth\RefreshClientSecretRequest;
use Apoloz\AlfaBank\Enums\Environment;
use Apoloz\AlfaBank\Requests\Auth\RefreshClientSecretRequest;
/**
* UTILITY SCRIPT: Generate a Client Secret using your TLS certificates.
@@ -45,16 +45,16 @@ try {
echo "\n-------------------------------------------------------\n";
echo "SUCCESS! New Client Secret Generated:\n";
echo "-------------------------------------------------------\n";
echo "Client ID: " . $data->clientId . "\n";
echo "Client Secret: " . $data->clientSecret . "\n";
echo 'Client ID: ' . $data->clientId . "\n";
echo 'Client Secret: ' . $data->clientSecret . "\n";
echo "-------------------------------------------------------\n";
echo "IMPORTANT: Save this to your .env file immediately.\n";
echo "It will not be shown again.\n";
} catch (\Exception $e) {
echo "\nERROR: Could not generate secret.\n";
echo "Message: " . $e->getMessage() . "\n";
echo 'Message: ' . $e->getMessage() . "\n";
if (method_exists($e, 'getResponse')) {
echo "Response: " . $e->getResponse()->body() . "\n";
echo 'Response: ' . $e->getResponse()->body() . "\n";
}
}

View File

@@ -31,7 +31,7 @@ $config = [
// BUSINESS DATA
'account_num' => '40702810...', // Nominal Account Number
'refresh_token' => 'saved_refresh_token_db' // Your stored token
'refresh_token' => 'saved_refresh_token_db', // Your stored token
];
try {
@@ -63,12 +63,12 @@ try {
// 4. Handle Result
$list = $response->dto();
echo "Found " . count($list->beneficiaries) . " beneficiaries.\n";
echo 'Found ' . count($list->beneficiaries) . " beneficiaries.\n";
foreach ($list->beneficiaries as $b) {
echo "- {$b->name} (ID: {$b->beneficiaryId})\n";
}
} catch (Exception $e) {
echo "Error: " . $e->getMessage() . "\n";
echo 'Error: ' . $e->getMessage() . "\n";
}

View File

@@ -5,9 +5,9 @@ declare(strict_types=1);
require_once __DIR__ . '/vendor/autoload.php';
use Apoloz\AlfaBank\AlfaBank;
use Apoloz\AlfaBank\Enums\Environment;
use Apoloz\AlfaBank\Requests\Auth\RefreshClientSecretRequest;
use Apoloz\AlfaBank\Requests\NominalAccounts\Beneficiaries\GetBeneficiariesRequest;
use Apoloz\AlfaBank\Enums\Environment;
/**
* FULLY AUTOMATED WORKFLOW
@@ -46,7 +46,7 @@ try {
$secretResponse = $baas->send(new RefreshClientSecretRequest($clientId));
$tempSecret = $secretResponse->dto()->clientSecret;
echo " Secret acquired: " . substr($tempSecret, 0, 5) . "...\n";
echo ' Secret acquired: ' . substr($tempSecret, 0, 5) . "...\n";
// 3. AUTHENTICATE OAUTH2
// Now we use the fresh secret to get an access token.
@@ -66,7 +66,7 @@ try {
$authenticator = $baasReady->refreshAccessToken($savedRefreshToken);
$baasReady->authenticate($authenticator);
echo " Session active. Access Token expires at " . $authenticator->getExpiresAt()->format('H:i:s') . "\n";
echo ' Session active. Access Token expires at ' . $authenticator->getExpiresAt()->format('H:i:s') . "\n";
// Update DB if refresh token rotated
if ($authenticator->getRefreshToken()) {
@@ -79,11 +79,11 @@ try {
$response = $baasReady->send(new GetBeneficiariesRequest($accNum, 'FL_RESIDENT'));
echo " Success! Found " . count($response->dto()->beneficiaries) . " records.\n";
echo ' Success! Found ' . count($response->dto()->beneficiaries) . " records.\n";
} catch (Exception $e) {
echo "CRITICAL ERROR: " . $e->getMessage() . "\n";
echo 'CRITICAL ERROR: ' . $e->getMessage() . "\n";
if (method_exists($e, 'getResponse')) {
echo "Body: " . $e->getResponse()->body() . "\n";
echo 'Body: ' . $e->getResponse()->body() . "\n";
}
}

View File

@@ -12,11 +12,11 @@
*/
use Apoloz\AlfaBank\Core\AlfaBankConnector;
use Apoloz\AlfaBank\Requests\NominalAccounts\Beneficiaries\ScoreBeneficiaryRequest;
use Apoloz\AlfaBank\Data\NominalAccounts\Beneficiaries\AddressData;
use Apoloz\AlfaBank\Data\NominalAccounts\Beneficiaries\CreateBeneficiaryRequestData;
use Apoloz\AlfaBank\Data\NominalAccounts\Beneficiaries\ScoreBeneficiaryRequestData;
use Apoloz\AlfaBank\Requests\NominalAccounts\Beneficiaries\CreateBeneficiaryRequest;
use Apoloz\AlfaBank\Data\NominalAccounts\Beneficiaries\CreateBeneficiaryRequestData;
use Apoloz\AlfaBank\Data\NominalAccounts\Beneficiaries\AddressData;
use Apoloz\AlfaBank\Requests\NominalAccounts\Beneficiaries\ScoreBeneficiaryRequest;
// 1. Get Configured Connector
// Laravel automatically injects config from config/alfa-bank.php
@@ -41,16 +41,18 @@ try {
$scoreResponse = $baas->send(new ScoreBeneficiaryRequest($scoreData));
$scoreResult = $scoreResponse->dto(); // ScoreBeneficiaryResponseData
echo "Scoring Result: " . $scoreResult->result . "\n";
echo "Description: " . $scoreResult->description . "\n";
echo 'Scoring Result: ' . $scoreResult->result . "\n";
echo 'Description: ' . $scoreResult->description . "\n";
if ($scoreResult->result !== 'SUCCESS') {
echo "Stopping: Scoring failed.\n";
return;
}
} catch (\Exception $e) {
echo "Scoring Error: " . $e->getMessage() . "\n";
echo 'Scoring Error: ' . $e->getMessage() . "\n";
return;
}
@@ -69,7 +71,7 @@ $createData = new CreateBeneficiaryRequestData(
type: 'REGISTRATION_ADDRESS',
address: '123456, г. Москва, ул. Тверская, д. 1',
country: 'RU'
)
),
])
);
@@ -83,14 +85,13 @@ try {
$beneficiary = $createResponse->dto(); // CreateBeneficiaryResponseData
echo "SUCCESS! Beneficiary Created.\n";
echo "ID: " . $beneficiary->id . "\n";
echo "Status: " . $beneficiary->status . "\n";
echo "Request ID: " . $beneficiary->requestId . "\n";
echo 'ID: ' . $beneficiary->id . "\n";
echo 'Status: ' . $beneficiary->status . "\n";
echo 'Request ID: ' . $beneficiary->requestId . "\n";
} catch (\Exception $e) {
echo "Creation Error: " . $e->getMessage() . "\n";
echo 'Creation Error: ' . $e->getMessage() . "\n";
if (method_exists($e, 'getResponse')) {
echo "Response Body: " . $e->getResponse()->body() . "\n";
echo 'Response Body: ' . $e->getResponse()->body() . "\n";
}
}

View File

@@ -13,12 +13,12 @@
*/
use Apoloz\AlfaBank\Core\AlfaBankConnector;
use Apoloz\AlfaBank\Requests\NominalAccounts\Beneficiaries\Requisites\CreateRequisiteRequest;
use Apoloz\AlfaBank\Data\NominalAccounts\Beneficiaries\Requisites\CreateRequisiteRequestData;
use Apoloz\AlfaBank\Requests\NominalAccounts\Payouts\PayoffRequest;
use Apoloz\AlfaBank\Data\NominalAccounts\Payouts\PayoffRequestData;
use Apoloz\AlfaBank\Data\Common\RecipientData;
use Apoloz\AlfaBank\Data\NominalAccounts\Beneficiaries\Requisites\CreateRequisiteRequestData;
use Apoloz\AlfaBank\Data\NominalAccounts\Payouts\PayoffRequestData;
use Apoloz\AlfaBank\Data\NominalAccounts\Payouts\PayoutRequisiteData;
use Apoloz\AlfaBank\Requests\NominalAccounts\Beneficiaries\Requisites\CreateRequisiteRequest;
use Apoloz\AlfaBank\Requests\NominalAccounts\Payouts\PayoffRequest;
use Apoloz\AlfaBank\Services\PhpseclibCardEncryptor;
// --- CONFIGURATION ---
@@ -34,8 +34,8 @@ $baas->login();
// ==========================================
// STEP 1: SECURE REGISTRATION (Tokenization)
// ==========================================
echo "--- Step 1: Registering Card Requisite ---
";
echo '--- Step 1: Registering Card Requisite ---
';
// A. Encrypt PAN for transport
$encryptor = new PhpseclibCardEncryptor(file_get_contents($cardPubKeyPath));
@@ -55,20 +55,20 @@ try {
$reqResponse = $baas->send(new CreateRequisiteRequest($reqData));
$requisiteId = $reqResponse->dto()->id; // E.g. "REQ-998877"
echo "SUCCESS! Card Registered.
";
echo 'SUCCESS! Card Registered.
';
echo "Requisite ID: $requisiteId (SAVE THIS TO DATABASE)
";
echo "Mask: " . substr($rawPan, 0, 6) . "******" . substr($rawPan, -4) . "
";
echo 'Mask: ' . substr($rawPan, 0, 6) . '******' . substr($rawPan, -4) . '
';
} catch (\Exception $e) {
echo "Registration Failed: " . $e->getMessage() . "
";
echo 'Registration Failed: ' . $e->getMessage() . '
';
return;
}
// ==========================================
// STEP 2: PAYOUT (Using Requisite ID)
// ==========================================
@@ -96,12 +96,12 @@ try {
$payResponse = $baas->send(new PayoffRequest($payoutData));
$result = $payResponse->dto();
echo "Payout Sent!
";
echo "ID: " . ($result->id ?? 'N/A') . "
";
echo 'Payout Sent!
';
echo 'ID: ' . ($result->id ?? 'N/A') . '
';
} catch (\Exception $e) {
echo "Payout Failed: " . $e->getMessage() . "
";
echo 'Payout Failed: ' . $e->getMessage() . '
';
}

View File

@@ -16,17 +16,17 @@ declare(strict_types=1);
require_once __DIR__ . '/../vendor/autoload.php';
use Apoloz\AlfaBank\AlfaBank;
use Apoloz\AlfaBank\Enums\Environment;
use Apoloz\AlfaBank\Sbp\Data\QrCodeRequestData;
use Apoloz\AlfaBank\Sbp\Requests\GetQrCodeRequest;
use Apoloz\AlfaBank\Core\AlfaConfig;
use Apoloz\AlfaBank\Sbp\SbpConnector;
use Apoloz\AlfaBank\Data\NominalAccounts\Identification\AmountDistributionData;
use Apoloz\AlfaBank\Data\NominalAccounts\Identification\IdentifyRequestData;
use Apoloz\AlfaBank\Data\NominalAccounts\Payouts\DigestSignatureData;
use Apoloz\AlfaBank\Enums\Environment;
use Apoloz\AlfaBank\Requests\NominalAccounts\Identification\GetIdentifyStatusRequest;
use Apoloz\AlfaBank\Requests\NominalAccounts\Identification\GetUnidentifiedTransactionsRequest;
use Apoloz\AlfaBank\Requests\NominalAccounts\Identification\IdentifyTransactionRequest;
use Apoloz\AlfaBank\Requests\NominalAccounts\Identification\GetIdentifyStatusRequest;
use Apoloz\AlfaBank\Data\NominalAccounts\Identification\IdentifyRequestData;
use Apoloz\AlfaBank\Data\NominalAccounts\Identification\AmountDistributionData;
use Apoloz\AlfaBank\Data\NominalAccounts\Payouts\DigestSignatureData;
use Apoloz\AlfaBank\Sbp\Data\QrCodeRequestData;
use Apoloz\AlfaBank\Sbp\Requests\GetQrCodeRequest;
use Apoloz\AlfaBank\Sbp\SbpConnector;
use Spatie\LaravelData\DataCollection;
echo "=== SBP QR → Номинальный счёт → Идентификация ===\n\n";
@@ -148,7 +148,7 @@ try {
$unidentifiedResponse = $baas->send($unidentifiedRequest);
$unidentifiedCollection = $unidentifiedResponse->dto();
echo "Найдено неидентифицированных пополнений: " . count($unidentifiedCollection->items) . "\n\n";
echo 'Найдено неидентифицированных пополнений: ' . count($unidentifiedCollection->items) . "\n\n";
if (count($unidentifiedCollection->items) === 0) {
echo "⚠️ Нет неидентифицированных пополнений. Возможные причины:\n";
@@ -175,7 +175,7 @@ try {
}
if ($targetTransaction === null) {
throw new \RuntimeException("Не найдена подходящая транзакция для идентификации");
throw new \RuntimeException('Не найдена подходящая транзакция для идентификации');
}
echo "✅ Выбрана транзакция для идентификации: {$targetTransaction->id}\n";
@@ -194,7 +194,7 @@ try {
new AmountDistributionData(
beneficiaryId: $beneficiaryId,
amount: $targetTransaction->amount
)
),
], AmountDistributionData::class);
// Подготавливаем данные для идентификации
@@ -207,7 +207,7 @@ try {
new DigestSignatureData(
base64Encoded: 'base64-encoded-signature-here', // Реальная подпись
signatureType: 'rsa'
)
),
], DigestSignatureData::class)
);

View File

@@ -12,16 +12,15 @@ declare(strict_types=1);
use Apoloz\AlfaBank\Core\AlfaBankConnector;
use Apoloz\AlfaBank\Core\AlfaConfig;
use Apoloz\AlfaBank\Data\NominalAccounts\Identification\AmountDistributionData;
use Apoloz\AlfaBank\Data\NominalAccounts\Identification\IdentifyRequestData;
use Apoloz\AlfaBank\Enums\Environment;
use Apoloz\AlfaBank\Sbp\SbpConnector;
use Apoloz\AlfaBank\Sbp\Data\QrCodeRequestData;
use Apoloz\AlfaBank\Sbp\Requests\GetQrCodeRequest;
use Apoloz\AlfaBank\Requests\NominalAccounts\Identification\GetIdentifyStatusRequest;
use Apoloz\AlfaBank\Requests\NominalAccounts\Identification\GetUnidentifiedTransactionsRequest;
use Apoloz\AlfaBank\Requests\NominalAccounts\Identification\IdentifyTransactionRequest;
use Apoloz\AlfaBank\Requests\NominalAccounts\Identification\GetIdentifyStatusRequest;
use Apoloz\AlfaBank\Data\NominalAccounts\Identification\IdentifyRequestData;
use Apoloz\AlfaBank\Data\NominalAccounts\Identification\AmountDistributionData;
use Apoloz\AlfaBank\Data\NominalAccounts\Payouts\DigestSignatureData;
use Apoloz\AlfaBank\Sbp\Data\QrCodeRequestData;
use Apoloz\AlfaBank\Sbp\Requests\GetQrCodeRequest;
use Apoloz\AlfaBank\Sbp\SbpConnector;
use Spatie\LaravelData\DataCollection;
echo "=== SBP QR → Номинальный счёт → Идентификация (Tinker) ===\n\n";
@@ -35,7 +34,7 @@ $beneficiaryId = 'your-beneficiary-id-here'; // ID бенефициара (по
$amount = '15000'; // 150.00 RUB в копейках
echo "Номинальный счёт: $nominalAccountNumber\n";
echo "Сумма: " . number_format($amount / 100, 2) . " RUB\n\n";
echo 'Сумма: ' . number_format($amount / 100, 2) . " RUB\n\n";
// =============================================================================
// ШАГ 1: Генерация SBP QR
@@ -74,6 +73,7 @@ try {
if ($qrData->ErrorCode !== 0) {
echo "❌ Ошибка генерации QR: {$qrData->message}\n";
return;
}
@@ -97,10 +97,11 @@ try {
);
$transactions = $unidentifiedResponse->dto()->items;
echo "Найдено транзакций: " . count($transactions) . "\n\n";
echo 'Найдено транзакций: ' . count($transactions) . "\n\n";
if (count($transactions) === 0) {
echo "⚠️ Нет неидентифицированных пополнений\n";
return;
}
@@ -126,6 +127,7 @@ try {
echo "⚠️ Укажите правильный beneficiaryId!\n";
echo " Получите список бенефициаров через:\n";
echo " >>> \$baas->send(new GetBeneficiariesRequest())->dto()\n";
return;
}
@@ -136,7 +138,7 @@ try {
new AmountDistributionData(
beneficiaryId: $beneficiaryId,
amount: $transaction->amount
)
),
], AmountDistributionData::class)
);
@@ -171,7 +173,7 @@ try {
} catch (\Exception $e) {
echo "\n❌ Ошибка: {$e->getMessage()}\n";
echo "Класс: " . get_class($e) . "\n";
echo 'Класс: ' . get_class($e) . "\n";
}
echo "\n=== Готово ===\n";

View File

@@ -9,9 +9,9 @@ namespace Apoloz\AlfaBank;
use Apoloz\AlfaBank\Core\AlfaBankConnector;
use Apoloz\AlfaBank\Core\AlfaConfig;
use Apoloz\AlfaBank\Enums\Environment;
use Apoloz\AlfaBank\Services\OpenSslCmsSigner;
use Apoloz\AlfaBank\Sbp\SbpConnector;
use Apoloz\AlfaBank\Requests\Accounts\GetAccountsRequest;
use Apoloz\AlfaBank\Sbp\SbpConnector;
use Apoloz\AlfaBank\Services\OpenSslCmsSigner;
class AlfaBank
{
@@ -29,7 +29,8 @@ class AlfaBank
): bool {
$baas = self::baas($certificatePath, $privateKeyPath, $environment, $caBundlePath);
$baas->login($clientId, $clientSecret);
$baas->send(new GetAccountsRequest());
$baas->send(new GetAccountsRequest);
return true;
}

View File

@@ -7,6 +7,7 @@ namespace Apoloz\AlfaBank\Core;
// <SECTION name="module_imports" intent="Import necessary contracts, services, and base framework components for the connector's operation.">
// <BELIEF_STATE "Saloon's plugin architecture requires importing traits for features like retries and timeouts. Local contracts (SignerInterface) are imported for dependency injection.">
use Apoloz\AlfaBank\Contracts\SignerInterface;
use Apoloz\AlfaBank\Enums\Environment;
use Apoloz\AlfaBank\Http\Middleware\RequestIdMiddleware;
use Apoloz\AlfaBank\Http\Pagination\AlfaOffsetPaginator;
use Apoloz\AlfaBank\Traits\HasAlfaErrorHandling;
@@ -15,19 +16,18 @@ use Saloon\Http\Connector;
use Saloon\Http\Request;
use Saloon\PaginationPlugin\Contracts\HasPagination;
use Saloon\PaginationPlugin\OffsetPaginator;
use Saloon\Traits\Plugins\AlwaysThrowOnErrors;
use Saloon\Traits\RequestProperties\HasTries;
use Saloon\Traits\Plugins\HasTimeout;
use Saloon\Traits\OAuth2\AuthorizationCodeGrant;
use Apoloz\AlfaBank\Enums\Environment;
use Saloon\Traits\Plugins\AlwaysThrowOnErrors;
use Saloon\Traits\Plugins\HasTimeout;
use Saloon\Traits\RequestProperties\HasTries;
class AlfaBankConnector extends Connector implements HasPagination
{
use AlwaysThrowOnErrors;
use AuthorizationCodeGrant;
use HasAlfaErrorHandling;
use HasTimeout;
use HasTries;
use AlwaysThrowOnErrors;
use HasAlfaErrorHandling;
use AuthorizationCodeGrant;
public function __construct(
protected AlfaConfig $alfaConfig,
@@ -42,6 +42,79 @@ class AlfaBankConnector extends Connector implements HasPagination
return $this->alfaConfig->baseUrl;
}
public function boot(\Saloon\Http\PendingRequest $pendingRequest): void
{
//
}
public function paginate(Request $request): OffsetPaginator
{
return new AlfaOffsetPaginator($this, $request);
}
// login() kept for ClientCredentials compatibility (legacy/simple flow)
public function login(?string $clientId = null, ?string $clientSecret = null, array $scopes = []): static
{
$oauthConfig = $this->oauthConfig();
// Allow overriding credentials via method arguments
if ($clientId) {
$oauthConfig->setClientId($clientId);
}
if ($clientSecret) {
$oauthConfig->setClientSecret($clientSecret);
}
if (empty($oauthConfig->getClientId()) || empty($oauthConfig->getClientSecret())) {
throw new \RuntimeException('Client ID/Secret not configured.');
}
$request = new \Saloon\Http\OAuth2\GetClientCredentialsTokenRequest($oauthConfig, $scopes);
$response = $this->send($request);
// Use the trait's helper to parse the token
$authenticator = $this->createOAuthAuthenticatorFromResponse($response);
$this->authenticate($authenticator);
return $this;
}
// </SECTION>
// </SECTION>
// <SECTION name="signer_accessor" intent="Provide access to the configured signing service for requests that require a digital signature.">
// <BELIEF_STATE "Requests should not instantiate their own signing logic. The connector acts as a service locator, providing the globally-configured Signer service. This ensures that all signatures are generated consistently.">
public function getSigner(): SignerInterface
{
if (! $this->signer) {
throw new \RuntimeException('No signer configured for this connector.');
}
return $this->signer;
}
// </SECTION>
// <SECTION name="logging_mutator" intent="Allow attaching a PSR-3 logger to the connector for debugging.">
// <BELIEF_STATE "Logging should be an optional, opt-in feature. This method provides a fluent interface to attach a logging middleware, which will automatically sanitize sensitive data before output.">
public function withLogging(\Psr\Log\LoggerInterface $logger): static
{
$this->middleware()->onRequest(new \Apoloz\AlfaBank\Http\Middleware\LoggingRequestMiddleware($logger));
$this->middleware()->onResponse(new \Apoloz\AlfaBank\Http\Middleware\LoggingResponseMiddleware($logger));
return $this;
}
/**
* Enable automated JWS response verification and decoding.
*/
public function withJwsVerification(string $publicKeyPem): static
{
$this->middleware()->onResponse(new \Apoloz\AlfaBank\Http\Middleware\VerifyJwsResponseMiddleware($publicKeyPem));
return $this;
}
protected function defaultOauthConfig(): OAuthConfig
{
$idUrl = $this->alfaConfig->baseUrl === Environment::PRODUCTION->value
@@ -83,80 +156,8 @@ class AlfaBankConnector extends Connector implements HasPagination
protected function defaultMiddleware(): array
{
return [
new RequestIdMiddleware(),
new RequestIdMiddleware,
];
}
public function boot(\Saloon\Http\PendingRequest $pendingRequest): void
{
//
}
public function paginate(Request $request): OffsetPaginator
{
return new AlfaOffsetPaginator($this, $request);
}
// login() kept for ClientCredentials compatibility (legacy/simple flow)
public function login(?string $clientId = null, ?string $clientSecret = null, array $scopes = []): static
{
$oauthConfig = $this->oauthConfig();
// Allow overriding credentials via method arguments
if ($clientId) {
$oauthConfig->setClientId($clientId);
}
if ($clientSecret) {
$oauthConfig->setClientSecret($clientSecret);
}
if (empty($oauthConfig->getClientId()) || empty($oauthConfig->getClientSecret())) {
throw new \RuntimeException("Client ID/Secret not configured.");
}
$request = new \Saloon\Http\OAuth2\GetClientCredentialsTokenRequest($oauthConfig, $scopes);
$response = $this->send($request);
// Use the trait's helper to parse the token
$authenticator = $this->createOAuthAuthenticatorFromResponse($response);
$this->authenticate($authenticator);
return $this;
}
// </SECTION>
// </SECTION>
// <SECTION name="signer_accessor" intent="Provide access to the configured signing service for requests that require a digital signature.">
// <BELIEF_STATE "Requests should not instantiate their own signing logic. The connector acts as a service locator, providing the globally-configured Signer service. This ensures that all signatures are generated consistently.">
public function getSigner(): SignerInterface
{
if (!$this->signer) {
throw new \RuntimeException("No signer configured for this connector.");
}
return $this->signer;
}
// </SECTION>
// <SECTION name="logging_mutator" intent="Allow attaching a PSR-3 logger to the connector for debugging.">
// <BELIEF_STATE "Logging should be an optional, opt-in feature. This method provides a fluent interface to attach a logging middleware, which will automatically sanitize sensitive data before output.">
public function withLogging(\Psr\Log\LoggerInterface $logger): static
{
$this->middleware()->onRequest(new \Apoloz\AlfaBank\Http\Middleware\LoggingRequestMiddleware($logger));
$this->middleware()->onResponse(new \Apoloz\AlfaBank\Http\Middleware\LoggingResponseMiddleware($logger));
return $this;
}
/**
* Enable automated JWS response verification and decoding.
*/
public function withJwsVerification(string $publicKeyPem): static
{
$this->middleware()->onResponse(new \Apoloz\AlfaBank\Http\Middleware\VerifyJwsResponseMiddleware($publicKeyPem));
return $this;
}
// </SECTION>
}

View File

@@ -14,6 +14,7 @@ namespace Apoloz\AlfaBank\Data\Auth;
// <SECTION name="module_imports" intent="Import the base Spatie Laravel Data class.">
// <BELIEF_STATE "Extending `Spatie\LaravelData\Data` enables automatic casting from JSON and provides other DTO benefits.">
use Spatie\LaravelData\Data;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for OAuth 2.0 token responses.">

View File

@@ -17,6 +17,7 @@ use Apoloz\AlfaBank\Data\PaginationData;
use Spatie\LaravelData\Attributes\DataCollectionOf;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\DataCollection;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for a collection of client consents.">

View File

@@ -14,9 +14,10 @@ namespace Apoloz\AlfaBank\Data\Auth\UserInfo;
// <SECTION name="module_imports" intent="Import necessary DTOs and Spatie Laravel Data components.">
// <BELIEF_STATE "This DTO represents a single consent entry. The `PaginationData` import here is likely due to prior usage or template, but not directly used within this single DTO.">
use Apoloz\AlfaBank\Data\PaginationData; // This import is likely extraneous for a single DTO, but kept for context consistency.
use Spatie\LaravelData\Attributes\DataCollectionOf; // This is also likely extraneous for a single DTO.
// This is also likely extraneous for a single DTO.
use Spatie\LaravelData\Data;
use Spatie\LaravelData\DataCollection; // This is also likely extraneous for a single DTO.
// This is also likely extraneous for a single DTO.
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for a single client consent.">

View File

@@ -14,6 +14,7 @@ namespace Apoloz\AlfaBank\Data\Auth\UserInfo;
// <SECTION name="module_imports" intent="Import the base Spatie Laravel Data class.">
// <BELIEF_STATE "Extending `Spatie\LaravelData\Data` enables automatic casting from JSON and provides other DTO benefits.">
use Spatie\LaravelData\Data;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for OpenID Connect UserInfo claims.">

View File

@@ -4,9 +4,7 @@ declare(strict_types=1);
namespace Apoloz\AlfaBank\Data\B2C;
use Spatie\LaravelData\Attributes\DataCollectionOf;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\DataCollection;
class B2CPaymentResponseData extends Data
{

View File

@@ -16,6 +16,7 @@ namespace Apoloz\AlfaBank\Data\FileExchange;
use Spatie\LaravelData\Attributes\DataCollectionOf;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\DataCollection;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for file download response data.">

View File

@@ -14,6 +14,7 @@ namespace Apoloz\AlfaBank\Data\FileExchange;
// <SECTION name="module_imports" intent="Import the base Spatie Laravel Data class.">
// <BELIEF_STATE "Extending `Spatie\LaravelData\Data` enables automatic casting from JSON and provides other DTO benefits.">
use Spatie\LaravelData\Data;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for file link details.">

View File

@@ -16,6 +16,7 @@ namespace Apoloz\AlfaBank\Data\FileExchange;
use Spatie\LaravelData\Attributes\DataCollectionOf;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\DataCollection;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for file upload response data.">

View File

@@ -14,6 +14,7 @@ namespace Apoloz\AlfaBank\Data\NominalAccounts\Beneficiaries;
// <SECTION name="module_imports" intent="Import the base Spatie Laravel Data class.">
// <BELIEF_STATE "Extending `Spatie\LaravelData\Data` enables automatic casting from JSON and provides other DTO benefits.">
use Spatie\LaravelData\Data;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for address details.">

View File

@@ -16,6 +16,7 @@ namespace Apoloz\AlfaBank\Data\NominalAccounts\Beneficiaries;
use Spatie\LaravelData\Attributes\DataCollectionOf;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\DataCollection;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for a beneficiary's balance details.">

View File

@@ -17,6 +17,7 @@ use Apoloz\AlfaBank\Data\PaginationData;
use Spatie\LaravelData\Attributes\DataCollectionOf;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\DataCollection;
// </SECTION>
// <SECTION name="class_definition" intent="Define the paginated collection for beneficiary transactions.">

View File

@@ -14,6 +14,7 @@ namespace Apoloz\AlfaBank\Data\NominalAccounts\Beneficiaries;
// <SECTION name="module_imports" intent="Import the base Spatie\LaravelData class.">
// <BELIEF_STATE "This class extends `Spatie\LaravelData\Data` to leverage its robust data mapping and type safety, ensuring consistent and predictable handling of individual transaction payloads.">
use Spatie\LaravelData\Data;
// </SECTION>
// <SECTION name="class_definition" intent="Define the beneficiary transaction data structure.">

View File

@@ -20,6 +20,7 @@ use Spatie\LaravelData\Attributes\Validation\Email;
use Spatie\LaravelData\Attributes\Validation\Numeric;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\DataCollection;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for creating a new beneficiary.">

View File

@@ -14,6 +14,7 @@ namespace Apoloz\AlfaBank\Data\NominalAccounts\Beneficiaries;
// <SECTION name="module_imports" intent="Import the base Spatie Laravel Data class.">
// <BELIEF_STATE "Extending `Spatie\LaravelData\Data` enables automatic casting from JSON and provides other DTO benefits.">
use Spatie\LaravelData\Data;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for the response of creating a beneficiary.">

View File

@@ -14,6 +14,7 @@ namespace Apoloz\AlfaBank\Data\NominalAccounts\Beneficiaries;
// <SECTION name="module_imports" intent="Import the base Spatie Laravel Data class.">
// <BELIEF_STATE "Extending `Spatie\LaravelData\Data` enables automatic casting from JSON and provides other DTO benefits.">
use Spatie\LaravelData\Data;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for an identification document.">

View File

@@ -14,6 +14,7 @@ namespace Apoloz\AlfaBank\Data\NominalAccounts\Beneficiaries;
// <SECTION name="module_imports" intent="Import the base Spatie Laravel Data class.">
// <BELIEF_STATE "Extending `Spatie\LaravelData\Data` enables automatic casting from JSON and provides other DTO benefits.">
use Spatie\LaravelData\Data;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for personal identification details.">

View File

@@ -18,6 +18,7 @@ use Apoloz\AlfaBank\Traits\SignableData;
use Spatie\LaravelData\Attributes\DataCollectionOf;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\DataCollection;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for creating a new requisite.">

View File

@@ -14,6 +14,7 @@ namespace Apoloz\AlfaBank\Data\NominalAccounts\Beneficiaries\Requisites;
// <SECTION name="module_imports" intent="Import the base Spatie Laravel Data class.">
// <BELIEF_STATE "Extending `Spatie\LaravelData\Data` enables automatic casting from JSON and provides other DTO benefits.">
use Spatie\LaravelData\Data;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for a single payment requisite.">

View File

@@ -16,9 +16,9 @@ namespace Apoloz\AlfaBank\Data\NominalAccounts\Beneficiaries\Requisites;
use Apoloz\AlfaBank\Data\NominalAccounts\Payouts\DigestSignatureData;
use Apoloz\AlfaBank\Traits\SignableData;
use Spatie\LaravelData\Attributes\DataCollectionOf;
use Spatie\LaravelData\Attributes\Validation\Numeric;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\DataCollection;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for updating an existing requisite.">

View File

@@ -20,6 +20,7 @@ use Spatie\LaravelData\Attributes\Validation\Email;
use Spatie\LaravelData\Attributes\Validation\Numeric;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\DataCollection;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for updating an existing beneficiary.">
@@ -39,3 +40,23 @@ class UpdateBeneficiaryRequestData extends Data
public ?DataCollection $addresses = null,
public ?string $accountNumber = null,
public ?bool $taxResidentUS = null,
public ?bool $foreignTaxResident = null,
public ?bool $financialActivities = null,
public ?string $name = null,
public ?string $registrationDate = null,
#[Numeric]
public ?string $inn = null,
#[Numeric]
public ?string $kpp = null,
#[Numeric]
public ?string $ogrn = null,
public ?PersonalInformationData $personalInformation = null,
public ?string $phoneNumber = null,
#[Email]
public ?string $email = null,
#[DataCollectionOf(DocumentData::class)]
public ?DataCollection $documents = null,
) {}
// </SECTION>
}
// </SECTION>

View File

@@ -14,6 +14,7 @@ namespace Apoloz\AlfaBank\Data\NominalAccounts\Beneficiaries;
// <SECTION name="module_imports" intent="Import the base Spatie Laravel Data class.">
// <BELIEF_STATE "Extending `Spatie\LaravelData\Data` enables automatic casting from JSON and provides other DTO benefits.">
use Spatie\LaravelData\Data;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for a specific wallet's balance.">

View File

@@ -17,6 +17,7 @@ use Apoloz\AlfaBank\Data\PaginationData;
use Spatie\LaravelData\Attributes\DataCollectionOf;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\DataCollection;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for a collection of beneficiaries.">

View File

@@ -16,7 +16,7 @@ namespace Apoloz\AlfaBank\Data\NominalAccounts;
use Apoloz\AlfaBank\Data\PaginationData; // This import might be misleading for a single DTO, but kept for context consistency.
use Spatie\LaravelData\Attributes\DataCollectionOf;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\DataCollection;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for a single beneficiary's details.">

View File

@@ -14,6 +14,7 @@ namespace Apoloz\AlfaBank\Data\NominalAccounts;
// <SECTION name="module_imports" intent="Import the base Spatie Laravel Data class.">
// <BELIEF_STATE "Extending `Spatie\LaravelData\Data` enables automatic casting from JSON and provides other DTO benefits.">
use Spatie\LaravelData\Data;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for the status of a counterparty request.">

View File

@@ -17,6 +17,7 @@ use Apoloz\AlfaBank\Data\PaginationData;
use Spatie\LaravelData\Attributes\DataCollectionOf;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\DataCollection;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for a collection of deals.">

View File

@@ -14,6 +14,7 @@ namespace Apoloz\AlfaBank\Data\NominalAccounts\Deals;
// <SECTION name="module_imports" intent="Import the base Spatie Laravel Data class.">
// <BELIEF_STATE "Extending `Spatie\LaravelData\Data` enables automatic casting from JSON and provides other DTO benefits.">
use Spatie\LaravelData\Data;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for a single deal's details.">

View File

@@ -14,6 +14,7 @@ namespace Apoloz\AlfaBank\Data\NominalAccounts\Deals;
// <SECTION name="module_imports" intent="Import the base Spatie Laravel Data class.">
// <BELIEF_STATE "Extending `Spatie\LaravelData\Data` enables automatic casting from JSON and provides other DTO benefits.">
use Spatie\LaravelData\Data;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for a payoff associated with a deal.">

View File

@@ -14,6 +14,7 @@ namespace Apoloz\AlfaBank\Data\NominalAccounts\Deals;
// <SECTION name="module_imports" intent="Import the base Spatie Laravel Data class.">
// <BELIEF_STATE "Extending `Spatie\LaravelData\Data` enables automatic casting from JSON and provides other DTO benefits.">
use Spatie\LaravelData\Data;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for the status of a deal request.">

View File

@@ -18,6 +18,7 @@ use Apoloz\AlfaBank\Traits\SignableData;
use Spatie\LaravelData\Attributes\DataCollectionOf;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\DataCollection;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for performing an action on a deal contractor.">

View File

@@ -14,6 +14,7 @@ namespace Apoloz\AlfaBank\Data\NominalAccounts\Deals\Steps;
// <SECTION name="module_imports" intent="Import the base Spatie Laravel Data class.">
// <BELIEF_STATE "Extending `Spatie\LaravelData\Data` enables automatic casting from JSON and provides other DTO benefits.">
use Spatie\LaravelData\Data;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for an amount allocated to a contractor's wallet.">

View File

@@ -18,6 +18,7 @@ use Apoloz\AlfaBank\Traits\SignableData;
use Spatie\LaravelData\Attributes\DataCollectionOf;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\DataCollection;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for performing an action on a deal step.">

View File

@@ -14,6 +14,7 @@ namespace Apoloz\AlfaBank\Data\NominalAccounts\Deals\Steps;
// <SECTION name="module_imports" intent="Import the base Spatie Laravel Data class.">
// <BELIEF_STATE "Extending `Spatie\LaravelData\Data` enables automatic casting from JSON and provides other DTO benefits.">
use Spatie\LaravelData\Data;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for the response of a step action request.">

View File

@@ -16,6 +16,7 @@ namespace Apoloz\AlfaBank\Data\NominalAccounts\Deals\Steps;
use Spatie\LaravelData\Attributes\DataCollectionOf;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\DataCollection;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for a contractor in a deal step.">

View File

@@ -14,6 +14,7 @@ namespace Apoloz\AlfaBank\Data\NominalAccounts\Deals\Steps;
// <SECTION name="module_imports" intent="Import the base Spatie Laravel Data class.">
// <BELIEF_STATE "Extending `Spatie\LaravelData\Data` enables automatic casting from JSON and provides other DTO benefits.">
use Spatie\LaravelData\Data;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for a single deal step.">

View File

@@ -14,6 +14,7 @@ namespace Apoloz\AlfaBank\Data\NominalAccounts\Deals;
// <SECTION name="module_imports" intent="Import the base Spatie Laravel Data class.">
// <BELIEF_STATE "Extending `Spatie\LaravelData\Data` enables automatic casting from JSON and provides other DTO benefits.">
use Spatie\LaravelData\Data;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for the response of a deal validation.">

View File

@@ -14,6 +14,7 @@ namespace Apoloz\AlfaBank\Data\NominalAccounts\Identification;
// <SECTION name="module_imports" intent="Import the base Spatie Laravel Data class.">
// <BELIEF_STATE "Extending `Spatie\LaravelData\Data` enables automatic casting from JSON and provides other DTO benefits.">
use Spatie\LaravelData\Data;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for distributing amounts to beneficiaries.">

View File

@@ -18,6 +18,7 @@ use Apoloz\AlfaBank\Traits\SignableData;
use Spatie\LaravelData\Attributes\DataCollectionOf;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\DataCollection;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for identifying a transaction.">

View File

@@ -14,6 +14,7 @@ namespace Apoloz\AlfaBank\Data\NominalAccounts\Identification;
// <SECTION name="module_imports" intent="Import the base Spatie Laravel Data class.">
// <BELIEF_STATE "Extending `Spatie\LaravelData\Data` enables automatic casting from JSON and provides other DTO benefits.">
use Spatie\LaravelData\Data;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for the response of an identification request.">

View File

@@ -14,6 +14,7 @@ namespace Apoloz\AlfaBank\Data\NominalAccounts\Identification;
// <SECTION name="module_imports" intent="Import the base Spatie Laravel Data class.">
// <BELIEF_STATE "Extending `Spatie\LaravelData\Data` enables automatic casting from JSON and provides other DTO benefits.">
use Spatie\LaravelData\Data;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for payer details.">

View File

@@ -18,6 +18,7 @@ use Apoloz\AlfaBank\Traits\SignableData;
use Spatie\LaravelData\Attributes\DataCollectionOf;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\DataCollection;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for refunding an unidentified transaction.">

View File

@@ -14,6 +14,7 @@ namespace Apoloz\AlfaBank\Data\NominalAccounts\Identification;
// <SECTION name="module_imports" intent="Import the base Spatie Laravel Data class.">
// <BELIEF_STATE "Extending `Spatie\LaravelData\Data` enables automatic casting from JSON and provides other DTO benefits.">
use Spatie\LaravelData\Data;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for the response of a refund request.">

View File

@@ -16,6 +16,7 @@ namespace Apoloz\AlfaBank\Data\NominalAccounts\Identification;
use Spatie\LaravelData\Attributes\DataCollectionOf;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\DataCollection;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for a collection of unidentified transactions.">

View File

@@ -14,6 +14,7 @@ namespace Apoloz\AlfaBank\Data\NominalAccounts\Identification;
// <SECTION name="module_imports" intent="Import the base Spatie Laravel Data class.">
// <BELIEF_STATE "Extending `Spatie\LaravelData\Data` enables automatic casting from JSON and provides other DTO benefits.">
use Spatie\LaravelData\Data;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for an unidentified transaction.">

View File

@@ -14,6 +14,7 @@ namespace Apoloz\AlfaBank\Data\NominalAccounts;
// <SECTION name="module_imports" intent="Import the base Spatie Laravel Data class.">
// <BELIEF_STATE "Extending `Spatie\LaravelData\Data` enables automatic casting from JSON and provides other DTO benefits.">
use Spatie\LaravelData\Data;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for a nominal account.">

View File

@@ -16,6 +16,7 @@ namespace Apoloz\AlfaBank\Data\NominalAccounts\Payouts;
use Apoloz\AlfaBank\Data\Shared\PayerInfoData;
use Apoloz\AlfaBank\Data\Shared\RecipientInfoData;
use Spatie\LaravelData\Data;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for detailed payoff records.">

View File

@@ -14,6 +14,7 @@ namespace Apoloz\AlfaBank\Data\NominalAccounts\Payouts;
// <SECTION name="module_imports" intent="Import the base Spatie Laravel Data class.">
// <BELIEF_STATE "Extending `Spatie\LaravelData\Data` enables automatic casting from JSON and provides other DTO benefits.">
use Spatie\LaravelData\Data;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for cryptographic digest signatures.">

View File

@@ -21,6 +21,7 @@ use Spatie\LaravelData\Attributes\DataCollectionOf;
use Spatie\LaravelData\Attributes\Validation\Numeric;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\DataCollection;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for creating a new payoff request.">

View File

@@ -14,6 +14,7 @@ namespace Apoloz\AlfaBank\Data\NominalAccounts\Payouts;
// <SECTION name="module_imports" intent="Import the base Spatie Laravel Data class.">
// <BELIEF_STATE "Extending `Spatie\LaravelData\Data` enables automatic casting from JSON and provides other DTO benefits.">
use Spatie\LaravelData\Data;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for the response of a payoff request.">

View File

@@ -14,6 +14,7 @@ namespace Apoloz\AlfaBank\Data\NominalAccounts\Payouts;
// <SECTION name="module_imports" intent="Import the base Spatie Laravel Data class.">
// <BELIEF_STATE "Extending `Spatie\LaravelData\Data` enables automatic casting from JSON and provides other DTO benefits.">
use Spatie\LaravelData\Data;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for payout requisite details.">

View File

@@ -14,6 +14,7 @@ namespace Apoloz\AlfaBank\Data\NominalAccounts\Payouts;
// <SECTION name="module_imports" intent="Import the base Spatie Laravel Data class.">
// <BELIEF_STATE "Extending `Spatie\LaravelData\Data` enables automatic casting from JSON and provides other DTO benefits.">
use Spatie\LaravelData\Data;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for recipient details.">

View File

@@ -14,10 +14,10 @@ namespace Apoloz\AlfaBank\Data\NominalAccounts\Payouts;
// <SECTION name="module_imports" intent="Import necessary classes for cryptographic signatures, data collection, and Laravel Data.">
// <BELIEF_STATE "These imports are essential for constructing a secure and structured tax refund request. `DigestSignatureData` ensures the integrity and authenticity of the request, `DataCollectionOf` and `DataCollection` facilitate handling multiple signatures, and `Data` provides the foundational capabilities for data object mapping and validation.">
use Apoloz\AlfaBank\Traits\SignableData;
use Apoloz\AlfaBank\Data\NominalAccounts\Payouts\DigestSignatureData;
use Spatie\LaravelData\Attributes\DataCollectionOf;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\DataCollection;
// </SECTION>
// <SECTION name="class_definition" intent="Define the tax refund request data structure.">

View File

@@ -14,6 +14,7 @@ namespace Apoloz\AlfaBank\Data\NominalAccounts\Payouts;
// <SECTION name="module_imports" intent="Import the base Spatie\LaravelData class.">
// <BELIEF_STATE "This class extends `Spatie\LaravelData\Data` to leverage its robust data mapping and type safety, ensuring consistent and predictable handling of tax refund response payloads.">
use Spatie\LaravelData\Data;
// </SECTION>
// <SECTION name="class_definition" intent="Define the tax refund response data structure.">

View File

@@ -16,9 +16,9 @@ namespace Apoloz\AlfaBank\Data\NominalAccounts\UniversalRecipients;
use Apoloz\AlfaBank\Data\NominalAccounts\Payouts\DigestSignatureData;
use Apoloz\AlfaBank\Traits\SignableData;
use Spatie\LaravelData\Attributes\DataCollectionOf;
use Spatie\LaravelData\Attributes\Validation\Numeric;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\DataCollection;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for creating a universal recipient.">

View File

@@ -14,6 +14,7 @@ namespace Apoloz\AlfaBank\Data\NominalAccounts\UniversalRecipients;
// <SECTION name="module_imports" intent="Import the base Spatie Laravel Data class.">
// <BELIEF_STATE "Extending `Spatie\LaravelData\Data` enables automatic casting from JSON and provides other DTO benefits.">
use Spatie\LaravelData\Data;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for a single universal recipient's details.">

View File

@@ -14,7 +14,8 @@ namespace Apoloz\AlfaBank\Data\NominalAccounts\UniversalRecipients;
// <SECTION name="module_imports" intent="Import the base response DTO.">
// <BELIEF_STATE "This DTO reuses `CreateBeneficiaryResponseData` as its base, indicating that the response structure for creating universal recipients is identical to that for beneficiaries. This promotes consistency and reduces code duplication.">
use Apoloz\AlfaBank\Data\NominalAccounts\Beneficiaries\CreateBeneficiaryResponseData;
use Spatie\LaravelData\Data; // Included for clarity, though inherited from base DTO.
// Included for clarity, though inherited from base DTO.
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for the response of creating a universal recipient.">

View File

@@ -14,6 +14,7 @@ namespace Apoloz\AlfaBank\Data;
// <SECTION name="module_imports" intent="Import the base Spatie Laravel Data class.">
// <BELIEF_STATE "Extending `Spatie\LaravelData\Data` enables automatic casting from JSON and provides other DTO benefits.">
use Spatie\LaravelData\Data;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for pagination metadata.">

View File

@@ -18,6 +18,7 @@ use Apoloz\AlfaBank\Traits\SignableData;
use Spatie\LaravelData\Attributes\DataCollectionOf;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\DataCollection;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for creating a sandbox RUB payment.">

View File

@@ -16,6 +16,7 @@ namespace Apoloz\AlfaBank\Data\Shared;
use Spatie\LaravelData\Attributes\Validation\Numeric;
use Spatie\LaravelData\Attributes\Validation\Size;
use Spatie\LaravelData\Data;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for payer information.">

View File

@@ -14,6 +14,7 @@ namespace Apoloz\AlfaBank\Data\Statement;
// <SECTION name="module_imports" intent="Import the base Spatie Laravel Data class.">
// <BELIEF_STATE "Extending `Spatie\LaravelData\Data` enables automatic casting from JSON and provides other DTO benefits.">
use Spatie\LaravelData\Data;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for monetary amount data.">

View File

@@ -14,6 +14,7 @@ namespace Apoloz\AlfaBank\Data\Statement;
// <SECTION name="module_imports" intent="Import the base Spatie Laravel Data class.">
// <BELIEF_STATE "Extending `Spatie\LaravelData\Data` enables automatic casting from JSON and provides other DTO benefits.">
use Spatie\LaravelData\Data;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for Russian Ruble transfer details.">

View File

@@ -17,6 +17,7 @@ use Apoloz\AlfaBank\Data\PaginationData;
use Spatie\LaravelData\Attributes\DataCollectionOf;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\DataCollection;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for a collection of account transactions.">

View File

@@ -14,6 +14,7 @@ namespace Apoloz\AlfaBank\Data\Statement;
// <SECTION name="module_imports" intent="Import necessary DTOs and Spatie Laravel Data components.">
// <BELIEF_STATE "This DTO includes nested `AmountData` for monetary values and `RurTransferData` for detailed transfer information.">
use Spatie\LaravelData\Data;
// </SECTION>
// <SECTION name="class_definition" intent="Define a DTO for a single account transaction.">

View File

@@ -2,8 +2,8 @@
namespace Apoloz\AlfaBank\Data;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\Attributes\DataCollectionOf;
use Spatie\LaravelData\Data;
use Spatie\LaravelData\DataCollection;
class WebhookCollectionData extends Data

View File

@@ -14,6 +14,7 @@ namespace Apoloz\AlfaBank\Exceptions;
// <SECTION name="module_imports" intent="Import the base Saloon Response class.">
// <BELIEF_STATE "The custom exception needs to hold the original Saloon `Response` object to give developers full access to the raw HTTP response for debugging purposes.">
use Saloon\Http\Response;
// </SECTION>
// <SECTION name="class_definition" intent="Define a custom exception that extends PHP's base Exception class.">

View File

@@ -1,4 +1,5 @@
<?php
/*
// MODULE_CONTRACT: Provides a Saloon middleware to standardize error handling for Alfa Bank API responses.
// RESPONSIBILITY: To intercept API responses, identify various error formats (BaaS, SBP, or generic HTTP), and throw a consistent `AlfaBankException`, thereby centralizing error reporting and simplifying error management in the SDK.
@@ -13,6 +14,7 @@ namespace Apoloz\AlfaBank\Http\Middleware;
// <BELIEF_STATE "These imports are crucial for the middleware's functionality. `AlfaBankException` provides a standardized way to signal API errors within the SDK, while `Response` from Saloon is the object that the middleware processes.">
use Apoloz\AlfaBank\Exceptions\AlfaBankException;
use Saloon\Http\Response;
// </SECTION>
// <SECTION name="class_definition" intent="Define the Alfa Bank API response error handling middleware.">

View File

@@ -45,6 +45,7 @@ class LoggingMiddleware implements RequestMiddleware, ResponseMiddleware
if (isset($headers['Authorization'])) {
$headers['Authorization'] = 'Bearer *****';
}
return $headers;
}
// </SECTION>
@@ -53,7 +54,9 @@ class LoggingMiddleware implements RequestMiddleware, ResponseMiddleware
// <BELIEF_STATE "This protected recursive helper method inspects the request or response body for predefined sensitive keys (e.g., `pan`, `client_secret`). If a sensitive key is found, its value is replaced with a mask. The method recursively processes nested arrays, ensuring that sensitive data is masked even within complex JSON structures, thereby enhancing log security.">
protected function maskBody(mixed $body): mixed
{
if (!is_array($body)) return $body;
if (! is_array($body)) {
return $body;
}
$sensitiveKeys = ['pan', 'cvc', 'digestSignatures', 'client_secret', 'refresh_token', 'access_token'];
@@ -64,6 +67,7 @@ class LoggingMiddleware implements RequestMiddleware, ResponseMiddleware
$body[$key] = $this->maskBody($value);
}
}
return $body;
}
// </SECTION>

View File

@@ -25,12 +25,15 @@ class LoggingRequestMiddleware implements RequestMiddleware
if (isset($headers['Authorization'])) {
$headers['Authorization'] = 'Bearer *****';
}
return $headers;
}
protected function maskBody(mixed $body): mixed
{
if (!is_array($body)) return $body;
if (! is_array($body)) {
return $body;
}
$sensitiveKeys = ['pan', 'cvc', 'digestSignatures', 'client_secret', 'refresh_token', 'access_token'];
@@ -41,6 +44,7 @@ class LoggingRequestMiddleware implements RequestMiddleware
$body[$key] = $this->maskBody($value);
}
}
return $body;
}
}

View File

@@ -21,7 +21,9 @@ class LoggingResponseMiddleware implements ResponseMiddleware
protected function maskBody(mixed $body): mixed
{
if (!is_array($body)) return $body;
if (! is_array($body)) {
return $body;
}
$sensitiveKeys = ['pan', 'cvc', 'digestSignatures', 'client_secret', 'refresh_token', 'access_token'];
@@ -32,6 +34,7 @@ class LoggingResponseMiddleware implements ResponseMiddleware
$body[$key] = $this->maskBody($value);
}
}
return $body;
}
}

View File

@@ -44,7 +44,7 @@ class AlfaOffsetPaginator extends OffsetPaginator
}
} catch (\Throwable $e) {
// Fallback to raw json if DTO fails
echo "DTO Error: " . $e->getMessage() . "\n";
echo 'DTO Error: ' . $e->getMessage() . "\n";
}
}
@@ -52,12 +52,20 @@ class AlfaOffsetPaginator extends OffsetPaginator
$json = $response->json();
// Common keys in Alfa API
if (isset($json['transactions'])) return $json['transactions'];
if (isset($json['payments'])) return $json['payments'];
if (isset($json['accounts'])) return $json['accounts'];
if (isset($json['transactions'])) {
return $json['transactions'];
}
if (isset($json['payments'])) {
return $json['payments'];
}
if (isset($json['accounts'])) {
return $json['accounts'];
}
// If it's a direct array (some endpoints)
if (array_is_list($json)) return $json;
if (array_is_list($json)) {
return $json;
}
return [];
}

View File

@@ -17,13 +17,14 @@ use Apoloz\AlfaBank\Contracts\SignerInterface;
use Apoloz\AlfaBank\Core\AlfaBankConnector;
use Apoloz\AlfaBank\Core\AlfaConfig;
use Apoloz\AlfaBank\Enums\Environment;
use Apoloz\AlfaBank\Laravel\Http\Controllers\AlfaWebhookController;
use Apoloz\AlfaBank\Laravel\Middleware\VerifyAlfaWebhook;
use Apoloz\AlfaBank\Sbp\SbpConnector;
use Apoloz\AlfaBank\Services\OpenSslCmsSigner;
use Apoloz\AlfaBank\Services\PhpseclibCardEncryptor;
use Apoloz\AlfaBank\Sbp\SbpConnector;
use Apoloz\AlfaBank\Laravel\Middleware\VerifyAlfaWebhook;
use Apoloz\AlfaBank\Laravel\Http\Controllers\AlfaWebhookController;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\ServiceProvider;
// </SECTION>
class AlfaBankServiceProvider extends ServiceProvider
@@ -37,6 +38,7 @@ class AlfaBankServiceProvider extends ServiceProvider
$this->app->singleton('alfa.baas.config', function ($app) {
$conf = $app['config']['alfa-bank.baas'];
return new AlfaConfig(
baseUrl: $conf['environment'] === 'production' ? Environment::PRODUCTION->value : Environment::SANDBOX->value,
certificatePath: $conf['certificate_path'],
@@ -61,6 +63,7 @@ class AlfaBankServiceProvider extends ServiceProvider
$this->app->bind(EncryptorInterface::class, function ($app) {
$key = $app['config']['alfa-bank.encryption.card_public_key'] ?? '';
return new PhpseclibCardEncryptor($key);
});
@@ -74,6 +77,7 @@ class AlfaBankServiceProvider extends ServiceProvider
$this->app->singleton('alfa.sbp.config', function ($app) {
$conf = $app['config']['alfa-bank.sbp'];
return new AlfaConfig(
baseUrl: $conf['environment'] === 'production' ? Environment::SBP_PRODUCTION->value : Environment::SBP_SANDBOX->value,
certificatePath: $conf['certificate_path'],
@@ -86,6 +90,7 @@ class AlfaBankServiceProvider extends ServiceProvider
$this->app->singleton(SbpConnector::class, function ($app) {
$config = $app['config']['alfa-bank.sbp'];
return new SbpConnector(
alfaConfig: $app->make('alfa.sbp.config'),
keyNameAlias: $config['key_name_alias'] ?? null

View File

@@ -16,6 +16,7 @@ namespace Apoloz\AlfaBank\Laravel\Commands;
use Apoloz\AlfaBank\Core\AlfaBankConnector;
use Apoloz\AlfaBank\Sbp\SbpConnector;
use Illuminate\Console\Command;
// </SECTION>
// <SECTION name="class_definition" intent="Define the Alfa Bank SDK connection check Artisan command.">
@@ -25,6 +26,7 @@ class CheckConnectionCommand extends Command
// <SECTION name="command_signature_and_description" intent="Define the command's name and description.">
// <BELIEF_STATE "The `$signature` property (`alfa:check`) makes the command executable via Artisan. The `$description` provides a clear summary of the command's purpose, aiding in user understanding when viewing `php artisan list`.">
protected $signature = 'alfa:check';
protected $description = 'Verify Alfa Bank SDK configuration and mTLS certificates';
// </SECTION>
@@ -52,16 +54,18 @@ class CheckConnectionCommand extends Command
// 1. Check Files
if (! file_exists($certPath)) {
$this->error("❌ Certificate not found at: {$certPath}");
return;
} else {
$this->comment("✅ Certificate found");
$this->comment('✅ Certificate found');
}
if (! file_exists($keyPath)) {
$this->error("❌ Private Key not found at: {$keyPath}");
return;
} else {
$this->comment("✅ Private Key found");
$this->comment('✅ Private Key found');
}
// 2. Validate Cert Expiry
@@ -70,20 +74,20 @@ class CheckConnectionCommand extends Command
if ($certInfo && isset($certInfo['validTo_time_t'])) {
$validTo = new \DateTimeImmutable('@' . $certInfo['validTo_time_t']);
$now = new \DateTimeImmutable();
$now = new \DateTimeImmutable;
if ($validTo < $now) {
$this->error("❌ Certificate EXPIRED on: " . $validTo->format('Y-m-d H:i:s'));
$this->error('❌ Certificate EXPIRED on: ' . $validTo->format('Y-m-d H:i:s'));
} else {
$diff = $now->diff($validTo);
$days = $diff->days;
$this->comment("✅ Certificate valid until: " . $validTo->format('Y-m-d') . " ({$days} days remaining)");
$this->comment('✅ Certificate valid until: ' . $validTo->format('Y-m-d') . " ({$days} days remaining)");
}
} else {
$this->warn("⚠️ Could not parse certificate date.");
$this->warn('⚠️ Could not parse certificate date.');
}
// 3. Attempt Connection (Ping)
$this->comment("Attempting TLS Handshake...");
$this->comment('Attempting TLS Handshake...');
try {
// We resolve the connector from container to use the config
@@ -93,18 +97,23 @@ class CheckConnectionCommand extends Command
// Simple GET to the base URL (or a health endpoint) just to test SSL handshake
// We don't need a valid token, we just want to see if we get a 401/403 instead of a connection error
$connector->send(new class extends \Saloon\Http\Request {
$connector->send(new class extends \Saloon\Http\Request
{
protected \Saloon\Enums\Method $method = \Saloon\Enums\Method::GET;
public function resolveEndpoint(): string { return '/'; }
public function resolveEndpoint(): string
{
return '/';
}
});
// If we are here, SSL worked!
$this->info("✅ Connection Successful! (Handshake completed)");
$this->info('✅ Connection Successful! (Handshake completed)');
} catch (\Exception $e) {
// Check if it's an SSL error
if (str_contains($e->getMessage(), 'certificate') || str_contains($e->getMessage(), 'SSL')) {
$this->error("❌ SSL/TLS Error: " . $e->getMessage());
$this->error('❌ SSL/TLS Error: ' . $e->getMessage());
} else {
// Other errors (like 404/401) mean SSL worked but endpoint failed, which is GOOD for this check
$this->info("✅ Connection Successful! (Server responded: {$e->getMessage()})");

View File

@@ -4,7 +4,6 @@ declare(strict_types=1);
namespace Apoloz\AlfaBank\Laravel\Facades;
use Apoloz\AlfaBank\Core\AlfaBankConnector;
use Illuminate\Support\Facades\Facade;
/**

View File

@@ -4,7 +4,6 @@ declare(strict_types=1);
namespace Apoloz\AlfaBank\Laravel\Facades;
use Apoloz\AlfaBank\Sbp\SbpConnector;
use Illuminate\Support\Facades\Facade;
/**

View File

@@ -31,13 +31,13 @@ class GetAccountsRequest extends Request implements Paginatable
return '/api/pp/v1/accounts';
}
protected function defaultQuery(): array
{
return $this->getPaginationQuery();
}
public function createDtoFromResponse(Response $response): DataCollection
{
return AccountData::collect($response->json('accounts'), DataCollection::class);
}
protected function defaultQuery(): array
{
return $this->getPaginationQuery();
}
}

View File

@@ -23,6 +23,11 @@ class CheckComplianceRequest extends Request
return '/api/aff/jp/v1/creditors/compliance';
}
public function createDtoFromResponse(Response $response): AffResponseData
{
return AffResponseData::from($response->json());
}
protected function defaultQuery(): array
{
return [
@@ -30,9 +35,4 @@ class CheckComplianceRequest extends Request
'name' => $this->name,
];
}
public function createDtoFromResponse(Response $response): AffResponseData
{
return AffResponseData::from($response->json());
}
}

View File

@@ -27,13 +27,13 @@ class CreateRegisterRequest extends Request implements HasBody
return '/api/aff/jp/v1/registers';
}
protected function defaultBody(): array
{
return $this->data->toArray();
}
public function createDtoFromResponse(Response $response): AffResponseData
{
return AffResponseData::from($response->json());
}
protected function defaultBody(): array
{
return $this->data->toArray();
}
}

View File

@@ -27,15 +27,15 @@ class SignRegisterRequest extends Request implements HasBody
return '/api/aff/jp/v1/registers/sign';
}
protected function defaultBody(): array
{
return $this->data->toArray();
}
public function createDtoFromResponse(Response $response): GeneralStatusData
{
return GeneralStatusData::from([
'success' => $response->successful(),
]);
}
protected function defaultBody(): array
{
return $this->data->toArray();
}
}

View File

@@ -19,6 +19,7 @@ use Saloon\Enums\Method;
use Saloon\Http\Request;
use Saloon\Http\Response;
use Saloon\Traits\Body\HasFormBody;
// </SECTION>
// <SECTION name="class_definition" intent="Define a class for the OAuth 2.0 token endpoint request.">
@@ -55,6 +56,14 @@ class GetTokenRequest extends Request implements HasBody
}
// </SECTION>
// <SECTION name="response_dto_caster" intent="Transform the raw JSON response into a structured TokenData DTO.">
// <BELIEF_STATE "Automatically casting the JSON token response into a `TokenData` DTO provides type safety and a clean, object-oriented way to access token properties like `access_token` and `expires_in`.">
public function createDtoFromResponse(Response $response): TokenData
{
return TokenData::from($response->json());
}
// </SECTION>
// <SECTION name="request_body_builder" intent="Construct the form-urlencoded body for the token request.">
// <BELIEF_STATE "This method builds the request body array. It correctly maps the class properties to the `snake_case` parameter names expected by the OAuth 2.0 standard.">
protected function defaultBody(): array
@@ -77,12 +86,4 @@ class GetTokenRequest extends Request implements HasBody
return $body;
}
// </SECTION>
// <SECTION name="response_dto_caster" intent="Transform the raw JSON response into a structured TokenData DTO.">
// <BELIEF_STATE "Automatically casting the JSON token response into a `TokenData` DTO provides type safety and a clean, object-oriented way to access token properties like `access_token` and `expires_in`.">
public function createDtoFromResponse(Response $response): TokenData
{
return TokenData::from($response->json());
}
// </SECTION>
}

Some files were not shown because too many files have changed in this diff Show More