refactor(core): enhance SDK structure and documentation

Co-authored-by: Qwen-Coder <qwen-coder@alibabacloud.com>
This commit is contained in:
2026-01-18 21:48:25 +03:00
parent 3da69c03e1
commit d214eecebe
378 changed files with 10514 additions and 496 deletions

View File

@@ -0,0 +1,31 @@
<?php
use Apoloz\AlfaBank\Core\AlfaBankConnector;
use Apoloz\AlfaBank\Core\AlfaConfig;
use Apoloz\AlfaBank\Requests\Accounts\GetAccountsRequest;
use Saloon\Http\Faking\MockResponse;
test('can get accounts list', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
GetAccountsRequest::class => MockResponse::make([
'accounts' => [
[
'number' => '40702810...',
'dateCreated' => '2023-01-01',
'type' => 'Checking',
'balance' => ['amount' => 1000]
]
]
], 200, ['Content-Type' => 'application/json']),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$response = $connector->send(new GetAccountsRequest());
expect($response->status())->toBe(200)
->and($response->dto()->count())->toBe(1)
->and($response->dto()->first()->number)->toBe('40702810...');
});

View File

@@ -0,0 +1,49 @@
<?php
use Apoloz\AlfaBank\Core\AlfaBankConnector;
use Apoloz\AlfaBank\Requests\B2B_1C\CheckMerchantRequest;
use Apoloz\AlfaBank\Requests\Claims\ResolveClaimRequest;
use Apoloz\AlfaBank\Requests\Logistics\ExecuteOrderActionRequest;
use Saloon\Http\Faking\MockResponse;
test('can check 1c merchant', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
CheckMerchantRequest::class => MockResponse::make([], 200),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$response = $connector->send(new CheckMerchantRequest('MF123', 'Basic ...'));
expect($response->status())->toBe(200);
});
test('can resolve claim', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
ResolveClaimRequest::class => MockResponse::make([], 200),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$response = $connector->send(new ResolveClaimRequest('CLAIM-1', new \Apoloz\AlfaBank\Data\Claims\ClaimResolvedData('RESOLVED')));
expect($response->status())->toBe(200);
});
test('can execute logistics action', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
ExecuteOrderActionRequest::class => MockResponse::make([], 200),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$response = $connector->send(new ExecuteOrderActionRequest(1, 2, 'ORDER-1', 'deliver', new \Apoloz\AlfaBank\Data\Logistics\LogisticsActionData()));
expect($response->status())->toBe(200);
});

View File

@@ -0,0 +1,34 @@
<?php
use Apoloz\AlfaBank\Core\AlfaBankConnector;
use Apoloz\AlfaBank\Requests\Aff\CheckComplianceRequest;
use Apoloz\AlfaBank\Requests\Aff\CreateRegisterRequest;
use Saloon\Http\Faking\MockResponse;
test('can check aff compliance', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
CheckComplianceRequest::class => MockResponse::make(['status' => 'APPROVED'], 200),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$response = $connector->send(new CheckComplianceRequest('7700000000', 'Test LLC'));
expect($response->status())->toBe(200);
});
test('can create aff register', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
CreateRegisterRequest::class => MockResponse::make(['id' => 'AFF-123'], 200),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$response = $connector->send(new CreateRegisterRequest(new \Apoloz\AlfaBank\Data\Aff\AffRegisterData([])));
expect($response->status())->toBe(200);
});

View File

@@ -1,6 +1,7 @@
<?php
use Apoloz\AlfaBank\AlfaBankConnector;
use Apoloz\AlfaBank\Core\AlfaBankConnector;
use Apoloz\AlfaBank\Core\AlfaConfig;
use Apoloz\AlfaBank\Requests\Auth\GetTokenRequest;
use Saloon\Http\Faking\MockResponse;
@@ -14,12 +15,40 @@ test('can authenticate and get token', function () {
], 200),
]);
$connector = new AlfaBankConnector('cert.pem', 'key.pem');
$config = new AlfaConfig(
baseUrl: 'https://test.com',
certificatePath: __DIR__ . '/../../test_key.pem', // Use any existing file
privateKeyPath: __DIR__ . '/../../test_key.pem'
);
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$request = new GetTokenRequest('client-id', 'client-secret', 'code', 'redirect-uri');
$request = new GetTokenRequest('client-id', 'client-secret', null, null, 'client_credentials');
$response = $connector->send($request);
expect($response->status())->toBe(200)
->and($response->dto()->access_token)->toBe('test-access-token');
});
test('can login and set token', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
GetTokenRequest::class => MockResponse::make([
'access_token' => 'test-access-token',
'refresh_token' => 'test-refresh-token',
'expires_in' => 3600,
'token_type' => 'Bearer',
], 200),
]);
$config = new AlfaConfig(
baseUrl: 'https://test.com',
certificatePath: __DIR__ . '/../../test_key.pem',
privateKeyPath: __DIR__ . '/../../test_key.pem'
);
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$connector->login('client-id', 'client-secret');
expect($connector->getAuthenticator())->not->toBeNull();
});

View File

@@ -0,0 +1,28 @@
<?php
use Apoloz\AlfaBank\Core\AlfaBankConnector;
use Apoloz\AlfaBank\Core\AlfaConfig;
use Apoloz\AlfaBank\Requests\Auth\UserInfo\GetUserInfoRequest;
use Saloon\Http\Faking\MockResponse;
test('can get user info from JWT', function () {
// Create a dummy JWT
$header = base64_encode(json_encode(['alg' => 'none']));
$payload = base64_encode(json_encode(['sub' => 'user-123', 'name' => 'John Doe']));
$signature = '';
$jwt = "$header.$payload.$signature";
$mockClient = new \Saloon\Http\Faking\MockClient([
GetUserInfoRequest::class => MockResponse::make($jwt, 200, ['Content-Type' => 'application/jwt']),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$response = $connector->send(new GetUserInfoRequest());
expect($response->status())->toBe(200)
->and($response->dto()->sub)->toBe('user-123')
->and($response->dto()->name)->toBe('John Doe');
});

View File

@@ -0,0 +1,59 @@
<?php
use Apoloz\AlfaBank\Core\AlfaBankConnector;
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 Apoloz\AlfaBank\Requests\B2C\GetB2CPaymentReportRequest;
use Saloon\Http\Faking\MockResponse;
use Spatie\LaravelData\DataCollection;
test('can create b2c sbp payout', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
CreateB2CPaymentRequest::class => MockResponse::make([
'status' => 'SUCCESS'
], 200, ['Content-Type' => 'application/json']),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$payload = new B2CPaymentRequestData(
client: new B2CClientData(b2cClientId: 'uuid-1', partnerId: 123),
inn: '7700000000',
account: '40702...',
total: 100.53,
signedBody: 'SIGNATURE',
payments: B2CPaymentRecipientData::collect([
[
'phone' => '79001112233',
'amount' => 100.53,
'nspkBankId' => '100000000001',
'sequence' => 'seq-1',
'name' => ['first' => 'Ivan', 'last' => 'Ivanov']
]
], DataCollection::class)
);
$response = $connector->send(new CreateB2CPaymentRequest($payload));
expect($response->status())->toBe(200);
});
test('can get b2c payment report', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
GetB2CPaymentReportRequest::class => MockResponse::make([
'payments' => []
], 200, ['Content-Type' => 'application/json']),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$response = $connector->send(new GetB2CPaymentReportRequest(partnerId: 123));
expect($response->status())->toBe(200);
});

View File

@@ -0,0 +1,32 @@
<?php
use Apoloz\AlfaBank\Core\AlfaBankConnector;
use Apoloz\AlfaBank\Core\AlfaConfig;
use Apoloz\AlfaBank\Requests\Cards\GetCardsRequest;
use Saloon\Http\Faking\MockResponse;
test('can get cards list', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
GetCardsRequest::class => MockResponse::make([
'cards' => [
[
'cardId' => 'card-1',
'maskedNumber' => '5555****4444',
'status' => 'ACTIVE',
'expirationDate' => '12/25',
'paymentSystem' => 'MasterCard'
]
]
], 200, ['Content-Type' => 'application/json']),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$response = $connector->send(new GetCardsRequest());
expect($response->status())->toBe(200)
->and($response->dto()->count())->toBe(1)
->and($response->dto()->first()->cardId)->toBe('card-1');
});

View File

@@ -0,0 +1,64 @@
<?php
use Apoloz\AlfaBank\Core\AlfaBankConnector;
use Apoloz\AlfaBank\Requests\CloudSignature\GetDnRequest;
use Apoloz\AlfaBank\Requests\CloudSignature\IssueRsaCertificateRequest;
use Apoloz\AlfaBank\Requests\CloudSignature\SubmitSigningRequest;
use Apoloz\AlfaBank\Requests\CloudSignature\ConfirmSigningWithSmsRequest;
use Saloon\Http\Faking\MockResponse;
test('can get user dn', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
GetDnRequest::class => MockResponse::make(['dn' => 'CN=Test'], 200),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$response = $connector->send(new GetDnRequest());
expect($response->status())->toBe(200);
});
test('can request rsa certificate issue', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
IssueRsaCertificateRequest::class => MockResponse::make(['id' => 'ISSUE-1'], 200),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$response = $connector->send(new IssueRsaCertificateRequest(tokenSerialNumber: '123'));
expect($response->status())->toBe(200);
});
test('can submit signing request', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
SubmitSigningRequest::class => MockResponse::make(['operationId' => 'OP-1'], 200),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$response = $connector->send(new SubmitSigningRequest('REQ-1'));
expect($response->status())->toBe(200);
});
test('can confirm signing with sms', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
ConfirmSigningWithSmsRequest::class => MockResponse::make([], 200),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$response = $connector->send(new ConfirmSigningWithSmsRequest('OP-1', '123456'));
expect($response->status())->toBe(200);
});

View File

@@ -0,0 +1,28 @@
<?php
use Apoloz\AlfaBank\Core\AlfaBankConnector;
use Apoloz\AlfaBank\Requests\CustomerInfo\GetCustomerInfoRequest;
use Saloon\Http\Faking\MockResponse;
test('can get customer info', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
GetCustomerInfoRequest::class => MockResponse::make([
'organizationId' => 'org-123',
'fullName' => 'OOO Romashka',
'shortName' => 'Romashka',
'inn' => '1234567890',
'kpps' => ['123456789'],
'ogrn' => '1234567890123'
], 200),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$response = $connector->send(new GetCustomerInfoRequest());
expect($response->status())->toBe(200)
->and($response->dto()->organizationId)->toBe('org-123')
->and($response->dto()->fullName)->toBe('OOO Romashka');
});

View File

@@ -0,0 +1,40 @@
<?php
use Apoloz\AlfaBank\Core\AlfaBankConnector;
use Apoloz\AlfaBank\Core\AlfaConfig;
use Apoloz\AlfaBank\Exceptions\AlfaBankException;
use Apoloz\AlfaBank\Requests\Auth\GetTokenRequest;
use Saloon\Http\Faking\MockResponse;
test('it throws AlfaBankException on BaaS error', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
GetTokenRequest::class => MockResponse::make([
'error' => 'invalid_client',
'error_description' => 'Bad client secret'
], 401, ['Content-Type' => 'application/json']),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$connector->login('id', 'secret');
})->throws(AlfaBankException::class, 'Alfa Bank API Error: [invalid_client] Bad client secret');
test('it throws AlfaBankException on SBP error', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
'*' => MockResponse::make([
'ErrorCode' => 'RQ05014',
'message' => 'Invalid amount'
], 200, ['Content-Type' => 'application/json']), // SBP often returns 200 even on errors
]);
$config = createTestConfig();
$connector = new \Apoloz\AlfaBank\Sbp\SbpConnector($config, 'alias');
$connector->withMockClient($mockClient);
$connector->send(new class extends \Saloon\Http\Request {
protected \Saloon\Enums\Method $method = \Saloon\Enums\Method::GET;
public function resolveEndpoint(): string { return '/'; }
});
})->throws(AlfaBankException::class, 'Alfa Bank API Error: [RQ05014] Invalid amount');

View File

@@ -0,0 +1,40 @@
<?php
use Apoloz\AlfaBank\Core\AlfaBankConnector;
use Apoloz\AlfaBank\Requests\Escrow\GetBuildProjectsRequest;
use Apoloz\AlfaBank\Requests\Escrow\GetEscrowOrdersRequest;
use Saloon\Http\Faking\MockResponse;
test('can get build projects', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
GetBuildProjectsRequest::class => MockResponse::make([
['projectNumber' => '123', 'commercialName' => 'ЖК Тест']
], 200),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$response = $connector->send(new GetBuildProjectsRequest());
expect($response->status())->toBe(200)
->and($response->json())->toBeArray()
->and($response->json('0.projectNumber'))->toBe('123');
});
test('can get escrow orders', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
GetEscrowOrdersRequest::class => MockResponse::make([
'orders' => []
], 200),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$response = $connector->send(new GetEscrowOrdersRequest('2023-01-01', '2023-12-31'));
expect($response->status())->toBe(200);
});

View File

@@ -0,0 +1,51 @@
<?php
use Apoloz\AlfaBank\Core\AlfaBankConnector;
use Apoloz\AlfaBank\Core\AlfaConfig;
use Apoloz\AlfaBank\Requests\FileExchange\UploadFileRequest;
use Apoloz\AlfaBank\Requests\FileExchange\DownloadFileRequest;
use Saloon\Http\Faking\MockResponse;
test('can upload a file', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
'*' => MockResponse::make([], 200),
]);
$config = new AlfaConfig(
baseUrl: 'https://test.com',
certificatePath: __DIR__ . '/../test_key.pem',
privateKeyPath: __DIR__ . '/../test_key.pem'
);
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$stream = fopen('php://memory', 'r+');
fwrite($stream, 'test content');
rewind($stream);
$request = new UploadFileRequest('https://upload.url', $stream, 12);
$response = $connector->send($request);
expect($response->status())->toBe(200);
fclose($stream);
});
test('can download a file', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
'*' => MockResponse::make('file content', 200),
]);
$config = new AlfaConfig(
baseUrl: 'https://test.com',
certificatePath: __DIR__ . '/../test_key.pem',
privateKeyPath: __DIR__ . '/../test_key.pem'
);
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$request = new DownloadFileRequest('https://download.url');
$response = $connector->send($request);
expect($response->status())->toBe(200)
->and($response->body())->toBe('file content');
});

View File

@@ -0,0 +1,19 @@
<?php
use Apoloz\AlfaBank\Core\AlfaBankConnector;
use Apoloz\AlfaBank\Requests\Logistics\GetDeliveriesRequest;
use Saloon\Http\Faking\MockResponse;
test('can get deliveries', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
GetDeliveriesRequest::class => MockResponse::make(['deliveries' => []], 200),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$response = $connector->send(new GetDeliveriesRequest('2023-01-01T00:00:00.000Z'));
expect($response->status())->toBe(200);
});

View File

@@ -0,0 +1,34 @@
<?php
use Apoloz\AlfaBank\Core\AlfaBankConnector;
use Apoloz\AlfaBank\Requests\Loyalty\GetLoyaltyAccountsRequest;
use Apoloz\AlfaBank\Requests\Loyalty\GetLoyaltyTransactionsRequest;
use Saloon\Http\Faking\MockResponse;
test('can get loyalty accounts', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
GetLoyaltyAccountsRequest::class => MockResponse::make(['accounts' => []], 200),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$response = $connector->send(new GetLoyaltyAccountsRequest());
expect($response->status())->toBe(200);
});
test('can get loyalty transactions', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
GetLoyaltyTransactionsRequest::class => MockResponse::make(['transactions' => []], 200),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$response = $connector->send(new GetLoyaltyTransactionsRequest(new \Apoloz\AlfaBank\Data\Loyalty\LoyaltyFilterData('2023-01-01', '2023-01-31')));
expect($response->status())->toBe(200);
});

View File

@@ -0,0 +1,49 @@
<?php
use Apoloz\AlfaBank\Core\AlfaBankConnector;
use Apoloz\AlfaBank\Requests\Bonus\GetBonusNumbersRequest;
use Apoloz\AlfaBank\Requests\Loans\GetCreditOffersRequest;
use Apoloz\AlfaBank\Requests\Registries\CreateSelfEncashmentRegistryRequest;
use Saloon\Http\Faking\MockResponse;
test('can get bonus numbers', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
GetBonusNumbersRequest::class => MockResponse::make(['bonusNumbers' => []], 200),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$response = $connector->send(new GetBonusNumbersRequest());
expect($response->status())->toBe(200);
});
test('can get credit offers', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
GetCreditOffersRequest::class => MockResponse::make(['offers' => []], 200),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$response = $connector->send(new GetCreditOffersRequest());
expect($response->status())->toBe(200);
});
test('can create self-encashment registry', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
CreateSelfEncashmentRegistryRequest::class => MockResponse::make([], 201),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$response = $connector->send(new CreateSelfEncashmentRegistryRequest(new \Apoloz\AlfaBank\Data\Registries\SelfEncashmentData([])));
expect($response->status())->toBe(201);
});

View File

@@ -1,10 +1,12 @@
<?php
use Apoloz\AlfaBank\AlfaBankConnector;
use Apoloz\AlfaBank\Core\AlfaBankConnector;
use Apoloz\AlfaBank\Core\AlfaConfig;
use Apoloz\AlfaBank\Requests\NominalAccounts\Beneficiaries\CreateBeneficiaryRequest;
use Apoloz\AlfaBank\Data\NominalAccounts\Beneficiaries\CreateBeneficiaryRequestData;
use Apoloz\AlfaBank\Data\NominalAccounts\Beneficiaries\AddressData;
use Apoloz\AlfaBank\Data\NominalAccounts\Payouts\DigestSignatureData;
use Spatie\LaravelData\DataCollection;
use Saloon\Http\Faking\MockResponse;
test('can create a beneficiary', function () {
@@ -15,16 +17,22 @@ test('can create a beneficiary', function () {
], 202),
]);
$connector = new AlfaBankConnector('cert.pem', 'key.pem');
$config = new AlfaConfig(
baseUrl: 'https://test.com',
certificatePath: __DIR__ . '/../../test_key.pem',
privateKeyPath: __DIR__ . '/../../test_key.pem'
);
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$payload = new CreateBeneficiaryRequestData(
type: 'FL_RESIDENT',
countryOfTaxResidence: 'RU',
addresses: AddressData::collect([
addresses: new DataCollection(AddressData::class, [
new AddressData('REGISTRATION_ADDRESS', 'Moscow', 'RU')
]),
digestSignatures: DigestSignatureData::collect([
digestSignatures: new DataCollection(DigestSignatureData::class, [
new DigestSignatureData('base64sig', 'rsa')
])
);

View File

@@ -0,0 +1,40 @@
<?php
use Apoloz\AlfaBank\Core\AlfaBankConnector;
use Apoloz\AlfaBank\Core\AlfaConfig;
use Apoloz\AlfaBank\Requests\NominalAccounts\Deals\Steps\ManageStepRequest;
use Apoloz\AlfaBank\Data\NominalAccounts\Deals\Steps\StepActionRequestData;
use Apoloz\AlfaBank\Data\NominalAccounts\Payouts\DigestSignatureData;
use Spatie\LaravelData\DataCollection;
use Saloon\Http\Faking\MockResponse;
test('can manage deal step', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
ManageStepRequest::class => MockResponse::make([
'dealId' => 'deal-123',
'stepId' => 'step-456',
'stepNumber' => 1
], 200),
]);
$config = new AlfaConfig(
baseUrl: 'https://test.com',
certificatePath: __DIR__ . '/../../test_key.pem',
privateKeyPath: __DIR__ . '/../../test_key.pem'
);
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$payload = new StepActionRequestData(
dealId: 'deal-123',
action: 'CREATE',
digestSignatures: new DataCollection(DigestSignatureData::class, [
new DigestSignatureData('sig', 'rsa')
])
);
$response = $connector->send(new ManageStepRequest($payload));
expect($response->status())->toBe(200)
->and($response->dto()->stepId)->toBe('step-456');
});

View File

@@ -1,6 +1,7 @@
<?php
use Apoloz\AlfaBank\AlfaBankConnector;
use Apoloz\AlfaBank\Core\AlfaBankConnector;
use Apoloz\AlfaBank\Core\AlfaConfig;
use Apoloz\AlfaBank\Requests\NominalAccounts\GetNominalAccountsRequest;
use Saloon\Http\Faking\MockResponse;
@@ -9,12 +10,17 @@ test('can list nominal accounts', function () {
GetNominalAccountsRequest::class => MockResponse::make([
[
'accountNumber' => '40817810306050000000',
'isActive' => true
'isActive' => true,
]
], 200),
]);
$connector = new AlfaBankConnector('cert.pem', 'key.pem');
$config = new AlfaConfig(
baseUrl: 'https://test.com',
certificatePath: __DIR__ . '/../../test_key.pem',
privateKeyPath: __DIR__ . '/../../test_key.pem'
);
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$response = $connector->send(new GetNominalAccountsRequest());

View File

@@ -0,0 +1,34 @@
<?php
use Apoloz\AlfaBank\Core\AlfaBankConnector;
use Apoloz\AlfaBank\Requests\Crypto\GetCertificatesRequest;
use Apoloz\AlfaBank\Requests\Partner\GetPartnerApplicationsRequest;
use Saloon\Http\Faking\MockResponse;
test('can get crypto certificates', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
GetCertificatesRequest::class => MockResponse::make(['certificates' => []], 200),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$response = $connector->send(new GetCertificatesRequest());
expect($response->status())->toBe(200);
});
test('can get partner applications', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
GetPartnerApplicationsRequest::class => MockResponse::make(['items' => []], 200),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$response = $connector->send(new GetPartnerApplicationsRequest());
expect($response->status())->toBe(200);
});

View File

@@ -0,0 +1,57 @@
<?php
use Apoloz\AlfaBank\Core\AlfaBankConnector;
use Apoloz\AlfaBank\Core\AlfaConfig;
use Apoloz\AlfaBank\Requests\Payments\CreatePaymentRequest;
use Apoloz\AlfaBank\Requests\Payments\GetPaymentStateRequest;
use Apoloz\AlfaBank\Data\Payments\PaymentData;
use Saloon\Http\Faking\MockResponse;
test('can create payment order', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
CreatePaymentRequest::class => MockResponse::make('', 201),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$payload = new PaymentData(
amount: 100.00,
date: '2023-10-10',
externalId: 'ext-pay-1',
operationCode: '01',
payeeAccount: '40702...',
payeeBankBic: '044...',
payeeName: 'OOO ROMASHKA',
payerAccount: '40702...',
payerBankBic: '044...',
payerBankCorrAccount: '301...',
payerInn: '770...',
payerName: 'MY COMPANY',
priority: '5',
purpose: 'Payment for services'
);
$response = $connector->send(new CreatePaymentRequest($payload));
expect($response->status())->toBe(201);
});
test('can get payment state', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
GetPaymentStateRequest::class => MockResponse::make([
'bankStatus' => 'ACCEPTED',
'bankComment' => 'Processing'
], 200, ['Content-Type' => 'application/json']),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$response = $connector->send(new GetPaymentStateRequest('ext-pay-1'));
expect($response->status())->toBe(200)
->and($response->dto()->bankStatus)->toBe('ACCEPTED');
});

View File

@@ -0,0 +1,36 @@
<?php
use Apoloz\AlfaBank\Core\AlfaBankConnector;
use Apoloz\AlfaBank\Requests\Poa\CreatePoaRequest;
use Apoloz\AlfaBank\Requests\Poa\GetPoaRequestStatusRequest;
use Saloon\Http\Faking\MockResponse;
test('can create poa request', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
CreatePoaRequest::class => MockResponse::make(['requestUuid' => 'uuid-123'], 200),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$response = $connector->send(new CreatePoaRequest('poa-1', '1234567890'));
expect($response->status())->toBe(200)
->and($response->json('requestUuid'))->toBe('uuid-123');
});
test('can get poa request status', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
GetPoaRequestStatusRequest::class => MockResponse::make(['status' => 'COMPLETED'], 200),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$response = $connector->send(new GetPoaRequestStatusRequest('uuid-123'));
expect($response->status())->toBe(200)
->and($response->json('status'))->toBe('COMPLETED');
});

View File

@@ -0,0 +1,65 @@
<?php
use Apoloz\AlfaBank\Core\AlfaBankConnector;
use Apoloz\AlfaBank\Data\Registries\RegistryPaymentData;
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 Saloon\Http\Faking\MockResponse;
use Spatie\LaravelData\DataCollection;
test('can create payment registry', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
CreateRegistryRequest::class => MockResponse::make([
'id' => 'REG-123',
'status' => 'CREATED'
], 201),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$payload = new RegistryRequestData(
payments: RegistryPaymentData::collect([
[
'amount' => 100.0,
'date' => '2023-10-10',
'payeeAccount' => '40702...',
'payeeBankBic' => '044...',
'payeeInn' => '770...',
'payeeName' => 'Recipient LLC',
'payerAccount' => '40702...',
'purpose' => 'Payment'
]
], DataCollection::class)
);
$response = $connector->send(new CreateRegistryRequest($payload));
expect($response->status())->toBe(201)
->and($response->json('id'))->toBe('REG-123');
});
test('can sign payment registry', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
SignRegistryRequest::class => MockResponse::make([], 200),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$signatures = RegistrySignatureData::collect([
[
'base64Encoded' => 'SIG...',
'signatureType' => 'RSA',
'certificateUuid' => 'CERT-123'
]
], DataCollection::class);
$response = $connector->send(new SignRegistryRequest('REG-123', $signatures));
expect($response->status())->toBe(200);
});

View File

@@ -0,0 +1,23 @@
<?php
use Apoloz\AlfaBank\Core\AlfaBankConnector;
use Apoloz\AlfaBank\Requests\Reports\CreateAccountBalanceReportRequest;
use Saloon\Http\Faking\MockResponse;
test('can create account balance report', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
CreateAccountBalanceReportRequest::class => MockResponse::make([
'id' => 'report-123',
'status' => 'PENDING'
], 201),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$response = $connector->send(new CreateAccountBalanceReportRequest(new \Apoloz\AlfaBank\Data\Reports\AccountBalanceReportData(['40702...'])));
expect($response->status())->toBe(201)
->and($response->json('id'))->toBe('report-123');
});

View File

@@ -0,0 +1,85 @@
<?php
use Apoloz\AlfaBank\Core\AlfaBankConnector;
use Apoloz\AlfaBank\Data\Salary\SalaryAccountApplicationData;
use Apoloz\AlfaBank\Data\Salary\SalaryEmployeeData;
use Apoloz\AlfaBank\Data\Salary\SalaryOrganizationData;
use Apoloz\AlfaBank\Data\Salary\SalaryPayrollApplicationData;
use Apoloz\AlfaBank\Requests\Salary\CreateAccountApplicationRequest;
use Apoloz\AlfaBank\Requests\Salary\CreatePayrollApplicationRequest;
use Apoloz\AlfaBank\Requests\Salary\GetAccountApplicationStatusRequest;
use Saloon\Http\Faking\MockResponse;
use Spatie\LaravelData\DataCollection;
test('can create salary account application', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
CreateAccountApplicationRequest::class => MockResponse::make(['id' => 'ACC-123'], 201),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$payload = new SalaryAccountApplicationData(
applicationId: 'app-1',
applicationCreatedAt: '2023-10-10T10:00:00Z',
registryNumber: 'reg-1',
registryCreatedAt: '2023-10-10T10:00:00Z',
contractNumber: 'cnt-1',
bic: '044...',
organization: new SalaryOrganizationData('Org LLC', '407...', '770...'),
rowsCount: 1,
employees: SalaryEmployeeData::collect([
['firstName' => 'Ivan', 'lastName' => 'Ivanov']
], DataCollection::class)
);
$response = $connector->send(new CreateAccountApplicationRequest($payload));
expect($response->status())->toBe(201);
});
test('can create salary payroll application', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
CreatePayrollApplicationRequest::class => MockResponse::make(['id' => 'PAY-123'], 201),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$payload = new SalaryPayrollApplicationData(
applicationId: 'app-2',
applicationCreatedAt: '2023-10-10T10:00:00Z',
totalAmount: 1000.0,
payrollId: 'pay-1',
admissionValue: '1',
registryNumber: 'reg-2',
registryCreatedAt: '2023-10-10',
contractNumber: 'cnt-1',
bic: '044...',
organization: new SalaryOrganizationData('Org LLC', '407...', '770...'),
rowsCount: 1,
employees: SalaryEmployeeData::collect([
['firstName' => 'Ivan', 'lastName' => 'Ivanov', 'amount' => 1000.0]
], DataCollection::class)
);
$response = $connector->send(new CreatePayrollApplicationRequest($payload));
expect($response->status())->toBe(201);
});
test('can get salary application status', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
GetAccountApplicationStatusRequest::class => MockResponse::make(['status' => 'PROCESSED'], 200),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$response = $connector->send(new GetAccountApplicationStatusRequest('ACC-123'));
expect($response->status())->toBe(200);
});

View File

@@ -0,0 +1,30 @@
<?php
use Apoloz\AlfaBank\Sbp\SbpConnector;
use Apoloz\AlfaBank\Sbp\Requests\GetQrCodeRequest;
use Apoloz\AlfaBank\Sbp\Data\QrCodeRequestData;
use Apoloz\AlfaBank\Core\AlfaConfig;
use Saloon\Http\Faking\MockResponse;
test('sbp connector uses injected config', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
GetQrCodeRequest::class => MockResponse::make(['payload' => 'https://test.qr'], 200),
]);
$config = new AlfaConfig(
baseUrl: 'https://test.alfa',
certificatePath: __DIR__ . '/../../test_key.pem', // valid path
privateKeyPath: __DIR__ . '/../../test_key.pem', // valid path
);
$connector = new SbpConnector($config, 'alias');
$connector->withMockClient($mockClient);
expect($connector->resolveBaseUrl())->toBe('https://test.alfa');
$response = $connector->send(new GetQrCodeRequest(
new QrCodeRequestData('term', '01', '100', 'purpose')
));
expect($response->status())->toBe(200);
});

View File

@@ -1,6 +1,7 @@
<?php
use Apoloz\AlfaBank\Sbp\SbpConnector;
use Apoloz\AlfaBank\Core\AlfaConfig;
use Apoloz\AlfaBank\Sbp\Requests\GetQrCodeRequest;
use Apoloz\AlfaBank\Sbp\Data\QrCodeRequestData;
use Saloon\Http\Faking\MockResponse;
@@ -15,11 +16,16 @@ test('can generate static qr code', function () {
], 200),
]);
$connector = new SbpConnector('cert.pem', 'key.pem', 'key-alias');
$config = new AlfaConfig(
baseUrl: 'https://test.com',
certificatePath: __DIR__ . '/../../test_key.pem',
privateKeyPath: __DIR__ . '/../../test_key.pem'
);
$connector = new SbpConnector($config, 'key-alias');
$connector->withMockClient($mockClient);
$payload = new QrCodeRequestData(
TermNo: 'test-term',
termNo: 'test-term',
amount: '100'
);

View File

@@ -0,0 +1,37 @@
<?php
use Apoloz\AlfaBank\Sbp\SbpConnector;
use Apoloz\AlfaBank\Core\AlfaConfig;
use Apoloz\AlfaBank\Sbp\Requests\Reversal\GetQrcReversalDataRequest;
use Apoloz\AlfaBank\Sbp\Data\Reversal\GetQrcReversalDataRequestData;
use Saloon\Http\Faking\MockResponse;
test('can check reversal data', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
GetQrcReversalDataRequest::class => MockResponse::make([
'ErrorCode' => 0,
'message' => 'OK',
'qrcId' => 'test-id',
'messageID' => 'guid-123',
'payerBnkName' => 'Alfa'
], 200),
]);
$config = new AlfaConfig(
baseUrl: 'https://test.com',
certificatePath: __DIR__ . '/../../test_key.pem',
privateKeyPath: __DIR__ . '/../../test_key.pem'
);
$connector = new SbpConnector($config, 'alias');
$connector->withMockClient($mockClient);
$payload = new GetQrcReversalDataRequestData(
TermNo: 'term-1',
qrcId: 'test-id'
);
$response = $connector->send(new GetQrcReversalDataRequest($payload));
expect($response->status())->toBe(200)
->and($response->dto()->messageID)->toBe('guid-123');
});

View File

@@ -0,0 +1,35 @@
<?php
use Apoloz\AlfaBank\Core\AlfaBankConnector;
use Apoloz\AlfaBank\Requests\Scoring\GetComplianceScoreRequest;
use Apoloz\AlfaBank\Requests\Security\GetSubscriptionsRequest;
use Saloon\Http\Faking\MockResponse;
test('can get compliance score', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
GetComplianceScoreRequest::class => MockResponse::make(['score' => 'LOW_RISK'], 200),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$response = $connector->send(new GetComplianceScoreRequest('7700000000'));
expect($response->status())->toBe(200)
->and($response->json('score'))->toBe('LOW_RISK');
});
test('can get security subscriptions', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
GetSubscriptionsRequest::class => MockResponse::make(['subscriptions' => []], 200),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$response = $connector->send(new GetSubscriptionsRequest());
expect($response->status())->toBe(200);
});

View File

@@ -0,0 +1,30 @@
<?php
use Apoloz\AlfaBank\Core\AlfaBankConnector;
use Apoloz\AlfaBank\Core\AlfaConfig;
use Apoloz\AlfaBank\Requests\SelfEmployed\CreateAgreementLinkRequest;
use Apoloz\AlfaBank\Data\SelfEmployed\AgreementPageRequestData;
use Saloon\Http\Faking\MockResponse;
test('can create agreement link', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
CreateAgreementLinkRequest::class => MockResponse::make([
'agreementPageId' => 'uuid-page-123',
'agreementPageLink' => 'https://link.alfa/...'
], 200, ['Content-Type' => 'application/json']),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$payload = new AgreementPageRequestData(
phone: '79001234567',
returnUrl: 'https://my-site.com/return'
);
$response = $connector->send(new CreateAgreementLinkRequest($payload));
expect($response->status())->toBe(200)
->and($response->json('agreementPageLink'))->toBe('https://link.alfa/...');
});

View File

@@ -0,0 +1,25 @@
<?php
use Apoloz\AlfaBank\Core\AlfaBankConnector;
use Apoloz\AlfaBank\Core\AlfaConfig;
use Apoloz\AlfaBank\Requests\SelfEmployed\GetActivitiesRequest;
use Saloon\Http\Faking\MockResponse;
test('can get activities dictionary', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
GetActivitiesRequest::class => MockResponse::make([
['id' => '1', 'name' => 'IT Services'],
['id' => '2', 'name' => 'Design'],
], 200, ['Content-Type' => 'application/json']),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$response = $connector->send(new GetActivitiesRequest());
expect($response->status())->toBe(200)
->and($response->dto()->count())->toBe(2)
->and($response->dto()->first()->name)->toBe('IT Services');
});

View File

@@ -0,0 +1,33 @@
<?php
use Apoloz\AlfaBank\Core\AlfaBankConnector;
use Apoloz\AlfaBank\Core\AlfaConfig;
use Apoloz\AlfaBank\Requests\SelfEmployed\CreatePayoutRequest;
use Apoloz\AlfaBank\Data\SelfEmployed\PayoutRequestData;
use Saloon\Http\Faking\MockResponse;
test('can create a payout for self-employed', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
CreatePayoutRequest::class => MockResponse::make([
'requestId' => 'payout-req-123',
'state' => 'PROCESSED'
], 200, ['Content-Type' => 'application/json']),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$payload = new PayoutRequestData(
externalId: 'ext-id-456',
company: ['vatin' => '7700000000'],
requisite: [
['amount' => 1000, 'vatin' => '112233445566']
]
);
$response = $connector->send(new CreatePayoutRequest($payload));
expect($response->status())->toBe(200)
->and($response->json('requestId'))->toBe('payout-req-123');
});

View File

@@ -0,0 +1,38 @@
<?php
use Apoloz\AlfaBank\Core\AlfaBankConnector;
use Apoloz\AlfaBank\Core\AlfaConfig;
use Apoloz\AlfaBank\Requests\SelfEmployed\GetTaxpayerStatusRequest;
use Apoloz\AlfaBank\Data\SelfEmployed\TaxpayerStatusRequestData;
use Saloon\Http\Faking\MockResponse;
test('can get self-employed status from FTS', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
GetTaxpayerStatusRequest::class => MockResponse::make([
'requestId' => '00000000-0000-0000-0000-000000000001',
'externalId' => '00000000-0000-0000-0000-000000000002',
'taxpayerStatusList' => [
[
'vatin' => '112233445566',
'status' => 'ACTIVE',
'message' => 'Is self-employed'
]
]
], 200, ['Content-Type' => 'application/json']),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$payload = new TaxpayerStatusRequestData(
externalId: '00000000-0000-0000-0000-000000000002',
selfemployedVatinList: ['112233445566']
);
$response = $connector->send(new GetTaxpayerStatusRequest($payload));
expect($response->status())->toBe(200)
->and($response->dto()->requestId)->toBe('00000000-0000-0000-0000-000000000001')
->and($response->dto()->taxpayerStatusList[0]['status'])->toBe('ACTIVE');
});

View File

@@ -0,0 +1,42 @@
<?php
use Apoloz\AlfaBank\Core\AlfaBankConnector;
use Apoloz\AlfaBank\Core\AlfaConfig;
use Apoloz\AlfaBank\Requests\Statement\GetTransactionsRequest;
use Saloon\Http\Faking\MockResponse;
test('can get transactions statement', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
GetTransactionsRequest::class => MockResponse::make([
'pagination' => [
'totalCount' => 1,
'limit' => 10,
'offset' => 0
],
'transactions' => [
[
'uuid' => 'uuid-123',
'transactionId' => 'tx-123',
'operationDate' => '2023-01-01',
'number' => '1',
'direction' => 'CREDIT',
'amount' => ['amount' => 100.00, 'currencyName' => 'RUB'],
'amountRub' => ['amount' => 100.00, 'currencyName' => 'RUB'],
'paymentPurpose' => 'Test',
'correspondingAccount' => '408...'
]
]
], 200, ['Content-Type' => 'application/json']),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$request = new GetTransactionsRequest('408...', '2023-01-01', '2023-01-02');
$response = $connector->send($request);
expect($response->status())->toBe(200)
->and($response->dto()->transactions[0]->transactionId)->toBe('tx-123')
->and($response->dto()->transactions[0]->amount->amount)->toBe(100.00);
});

View File

@@ -0,0 +1,36 @@
<?php
use Apoloz\AlfaBank\Utils\CardEncryptor;
use Apoloz\AlfaBank\Utils\CmsSigner;
test('can encrypt card pan', function () {
// Generate a temporary RSA key for testing
$res = openssl_pkey_new([
"private_key_bits" => 2048,
"private_key_type" => OPENSSL_KEYTYPE_RSA,
]);
openssl_pkey_export($res, $privateKey);
$details = openssl_pkey_get_details($res);
$publicKey = $details['key'];
$pan = '4779642610000010';
$encrypted = CardEncryptor::encrypt($pan, $publicKey);
expect($encrypted)->toBeString()
->and(base64_decode($encrypted))->not->toBeFalse();
});
test('can canonicalize json', function () {
$data = [
'z' => 1,
'a' => [
'c' => 3,
'b' => 2
]
];
$canonical = CmsSigner::canonicalize($data);
// Keys should be sorted: a (then b, c), z
expect($canonical)->toBe('{"a":{"b":2,"c":3},"z":1}');
});

View File

@@ -0,0 +1,37 @@
<?php
use Apoloz\AlfaBank\Utils\Uuid;
use Apoloz\AlfaBank\Http\Middleware\RequestIdMiddleware;
use Saloon\Http\PendingRequest;
use Saloon\Http\Request;
use Saloon\Enums\Method;
test('it generates valid uuid v4', function () {
$uuid = Uuid::v4();
expect($uuid)->toBeString()
->and(strlen($uuid))->toBe(36)
->and(preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/', $uuid))->toBe(1);
});
test('middleware adds uuid to request', function () {
$middleware = new RequestIdMiddleware();
$request = new class extends Request {
protected Method $method = Method::GET;
public function resolveEndpoint(): string { return '/'; }
};
$connector = new class extends \Saloon\Http\Connector {
public function resolveBaseUrl(): string { return 'https://test'; }
};
$pendingRequest = new PendingRequest($connector, $request);
$middleware($pendingRequest);
$header = $pendingRequest->headers()->get('X-Request-ID');
expect($header)->toBeString()
->and(preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/', $header))->toBe(1);
});

View File

@@ -0,0 +1,51 @@
<?php
use Apoloz\AlfaBank\Core\AlfaBankConnector;
use Apoloz\AlfaBank\Requests\Webhooks\CreateWebhookRequest;
use Apoloz\AlfaBank\Requests\Webhooks\GetWebhooksRequest;
use Apoloz\AlfaBank\Requests\Webhooks\DeleteWebhookRequest;
use Apoloz\AlfaBank\Data\Webhooks\Subscriptions\WebhookSubscriptionRequestData;
use Saloon\Http\Faking\MockResponse;
test('can list webhooks', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
GetWebhooksRequest::class => MockResponse::make([['id' => 'WH-1']], 200),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$response = $connector->send(new GetWebhooksRequest());
expect($response->status())->toBe(200)
->and($response->json('0.id'))->toBe('WH-1');
});
test('can create webhook', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
CreateWebhookRequest::class => MockResponse::make(['id' => 'WH-2'], 201),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$response = $connector->send(new CreateWebhookRequest(new WebhookSubscriptionRequestData('https://test.io', ['transactions'])));
expect($response->status())->toBe(201);
});
test('can delete webhook', function () {
$mockClient = new \Saloon\Http\Faking\MockClient([
DeleteWebhookRequest::class => MockResponse::make([], 204),
]);
$config = createTestConfig();
$connector = new AlfaBankConnector($config);
$connector->withMockClient($mockClient);
$response = $connector->send(new DeleteWebhookRequest('WH-1'));
expect($response->status())->toBe(204);
});

View File

@@ -0,0 +1,36 @@
<?php
use Apoloz\AlfaBank\Webhooks\WebhookFactory;
use Apoloz\AlfaBank\Data\Webhooks\Payloads\BaasWebhookData;
use Apoloz\AlfaBank\Data\Webhooks\Payloads\SbpWebhookData;
test('can detect baas webhook', function () {
$payload = [
[
'object' => 'ul_transaction_default',
'actionType' => 'create',
'eventTime' => '2022-01-12T00:00:00',
'data' => []
]
];
expect(WebhookFactory::isBaas($payload))->toBeTrue();
$webhook = WebhookFactory::create($payload);
expect($webhook)->toBeInstanceOf(BaasWebhookData::class)
->and($webhook->object)->toBe('ul_transaction_default');
});
test('can detect sbp webhook', function () {
$payload = [
'command' => 'qrc/notification',
'status' => 'ACWP',
'qrcId' => 'test-id'
];
expect(WebhookFactory::isSbp($payload))->toBeTrue();
$webhook = WebhookFactory::create($payload);
expect($webhook)->toBeInstanceOf(SbpWebhookData::class)
->and($webhook->command)->toBe('qrc/notification');
});

128
tests/Pest.php Normal file
View File

@@ -0,0 +1,128 @@
<?php
use Apoloz\AlfaBank\Core\AlfaConfig;
/**
* Define a mock app() helper for tests running outside of Laravel.
*/
if (!function_exists('app')) {
function app($abstract = null, array $parameters = []) {
static $container = [];
if ($abstract === null) {
return new class {
public function make($abstract) { return app($abstract); }
};
}
if (isset($container[$abstract])) {
return $container[$abstract];
}
// Minimal implementations for laravel-data
if ($abstract === \Spatie\LaravelData\Support\DataConfig::class) {
if (isset($container[$abstract])) return $container[$abstract];
return $container[$abstract] = \Spatie\LaravelData\Support\DataConfig::createFromConfig(config('data'));
}
if ($abstract === \Spatie\LaravelData\Support\DataContainer::class) {
return \Spatie\LaravelData\Support\DataContainer::get();
}
// Auto-wiring via Reflection
try {
$reflector = new ReflectionClass($abstract);
if (! $reflector->isInstantiable()) {
return null;
}
$constructor = $reflector->getConstructor();
if (is_null($constructor)) {
return $container[$abstract] = new $abstract();
}
$dependencies = [];
foreach ($constructor->getParameters() as $parameter) {
$type = $parameter->getType();
if ($type instanceof ReflectionNamedType && ! $type->isBuiltin()) {
$dependencies[] = app($type->getName());
} else {
if ($parameter->isDefaultValueAvailable()) {
$dependencies[] = $parameter->getDefaultValue();
} else {
$dependencies[] = null;
}
}
}
return $container[$abstract] = $reflector->newInstanceArgs($dependencies);
} catch (\Throwable $e) {
return null;
}
}
}
/**
* Define a mock config() helper for tests running outside of Laravel.
*/
if (!function_exists('config')) {
function config($key = null, $default = null) {
$dataConfig = [
'validation_strategy' => 'disabled',
'transformers' => [],
'casters' => [],
'rule_inferrers' => [],
'normalizers' => [
\Spatie\LaravelData\Normalizers\ModelNormalizer::class,
\Spatie\LaravelData\Normalizers\FormRequestNormalizer::class,
\Spatie\LaravelData\Normalizers\ArrayableNormalizer::class,
\Spatie\LaravelData\Normalizers\ObjectNormalizer::class,
\Spatie\LaravelData\Normalizers\ArrayNormalizer::class,
\Spatie\LaravelData\Normalizers\JsonNormalizer::class,
],
'features' => [
'cast_and_transform_iterables' => true,
],
'max_transformation_depth' => null,
'throw_when_max_transformation_depth_reached' => true,
'wrap_execution_type' => 'disabled',
'var_dumper_caster_mode' => 'narrow',
];
if ($key === 'data') {
return $dataConfig;
}
if (is_string($key) && str_starts_with($key, 'data.')) {
$subKey = substr($key, 5);
// Handle simple dot notation for one level deep
if (array_key_exists($subKey, $dataConfig)) {
return $dataConfig[$subKey];
}
// Handle features.key
if (str_starts_with($subKey, 'features.')) {
$featureKey = substr($subKey, 9);
return $dataConfig['features'][$featureKey] ?? $default;
}
}
return $default;
}
}
/**
* Helper to create a test config.
*/
function createTestConfig(): AlfaConfig {
// Create temp files to satisfy file_exists checks
$cert = tempnam(sys_get_temp_dir(), 'cert');
$key = tempnam(sys_get_temp_dir(), 'key');
return new AlfaConfig(
baseUrl: 'https://test.alfabank.ru',
certificatePath: $cert,
privateKeyPath: $key
);
}