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";
@@ -109,7 +109,7 @@ try {
echo "\n💡 Клиент должен отсканировать QR и оплатить {$qrData->payload}\n\n";
// Сохраняем QR изображение для показа клиенту
if (!empty($qrData->image)) {
if (! empty($qrData->image)) {
$qrImagePath = __DIR__ . '/qr_payment.png';
file_put_contents($qrImagePath, base64_decode($qrData->image));
echo " 📁 QR изображение сохранено: $qrImagePath\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

@@ -41,16 +41,16 @@ readonly class AlfaConfig
public ?string $signingPrivateKeyPath = null,
) {
// <BELIEF_STATE "This runtime check ensures that the provided certificate and key paths are not just valid strings, but point to actual, accessible files on the filesystem. This prevents configuration errors that would otherwise only surface during an HTTP request.">
if (!file_exists($this->certificatePath)) {
if (! file_exists($this->certificatePath)) {
throw new \InvalidArgumentException("Certificate not found: {$this->certificatePath}");
}
if (!file_exists($this->privateKeyPath)) {
if (! file_exists($this->privateKeyPath)) {
throw new \InvalidArgumentException("Private key not found: {$this->privateKeyPath}");
}
if ($this->signingCertificatePath && !file_exists($this->signingCertificatePath)) {
if ($this->signingCertificatePath && ! file_exists($this->signingCertificatePath)) {
throw new \InvalidArgumentException("Signing certificate not found: {$this->signingCertificatePath}");
}
if ($this->signingPrivateKeyPath && !file_exists($this->signingPrivateKeyPath)) {
if ($this->signingPrivateKeyPath && ! file_exists($this->signingPrivateKeyPath)) {
throw new \InvalidArgumentException("Signing private key not found: {$this->signingPrivateKeyPath}");
}
}

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.">
@@ -38,10 +40,10 @@ class AlfaResponseMiddleware
}
// 2. Handle SBP specific errors
if (isset($data['ErrorCode']) && (string)$data['ErrorCode'] !== '0') {
if (isset($data['ErrorCode']) && (string) $data['ErrorCode'] !== '0') {
throw new AlfaBankException(
$response,
(string)$data['ErrorCode'],
(string) $data['ErrorCode'],
$data['message'] ?? 'No message'
);
}

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

@@ -14,7 +14,7 @@ class RequestIdMiddleware implements RequestMiddleware
{
public function __invoke(PendingRequest $pendingRequest): void
{
if (!$pendingRequest->headers()->get('X-Request-ID')) {
if (! $pendingRequest->headers()->get('X-Request-ID')) {
$pendingRequest->headers()->add('X-Request-ID', Uuid::v4());
}
}

View File

@@ -17,8 +17,8 @@ class VerifyJwsResponseMiddleware
{
$contentType = $response->header('Content-Type');
if (str_contains((string)$contentType, 'application/jose')) {
if (!$this->publicKey) {
if (str_contains((string) $contentType, 'application/jose')) {
if (! $this->publicKey) {
return $response;
}

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>
@@ -50,18 +52,20 @@ class CheckConnectionCommand extends Command
$keyPath = (string) config("{$configKey}.private_key_path");
// 1. Check Files
if (!file_exists($certPath)) {
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)) {
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

@@ -35,7 +35,7 @@ class VerifyAlfaWebhook
$publicKey = file_get_contents($publicKeyPath);
$verified = \Apoloz\AlfaBank\Utils\JwsSigner::verify($signature, $publicKey);
if (!$verified) {
if (! $verified) {
return response()->json(['error' => 'Invalid Webhook Signature'], 401);
}

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();
}
}

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