diff --git a/.gitea/ISSUE_TEMPLATE/bug_report.md b/.gitea/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000..b8471ae --- /dev/null +++ b/.gitea/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,31 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: bug +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Initialize SDK with '...' +2. Call method '...' +3. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Environment (please complete the following information):** + - PHP Version: [e.g. 8.2] + - SDK Version [e.g. 1.0.0] + - OS: [e.g. Ubuntu 22.04] + +**Additional context** +Add any other context about the problem here. diff --git a/.gitignore b/.gitignore index 7012a8d..d2eb7ca 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,11 @@ .env phpunit.xml composer.lock +/build/ +*.pem +*.key +*.crt +*.pfx +.DS_Store +.idea/ +.vscode/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..fe9bfac --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,65 @@ +# Contributing to Alfa Bank SDK + +Thank you for your interest in contributing to the unofficial Alfa Bank SDK! We welcome pull requests, bug reports, and feature requests. + +## Getting Started + +1. **Fork the repository** on GitHub. +2. **Clone your fork** locally: + ```bash + git clone https://github.com/your-username/alfa-bank-sdk.git + cd alfa-bank-sdk + ``` +3. **Install dependencies**: + ```bash + composer install + ``` + +## Development Standards (Strict) + +To maintain the "Enterprise Grade" quality of this SDK, all contributions must adhere to the following architectural rules: + +### 1. Requests & Responses +- **DTOs Only:** Never pass raw arrays to a Request constructor. Always create a dedicated DTO in `src/Data/...`. +- **Strict Typing:** All Request classes must implement the `createDtoFromResponse(Response $response)` method. Returning raw JSON is forbidden. +- **Saloon v3:** Follow [Saloon PHP](https://docs.saloon.dev) best practices. + +### 2. Pagination +- If an endpoint returns a list (e.g. transactions, accounts), the Request class **MUST**: + 1. Implement `Saloon\PaginationPlugin\Contracts\Paginatable`. + 2. Use the `Apoloz\AlfaBank\Traits\HasAlfaPagination` trait. + 3. Return a `Spatie\LaravelData\DataCollection` in `createDtoFromResponse`. + +### 3. Security +- **No Secrets:** Never commit real keys (`.pem`, `.key`, `.pfx`) or credentials. +- **Crypto:** Use the `CmsSigner` and `JwsSigner` utilities for cryptographic operations. Do not roll your own crypto logic. + +## Running Quality Checks + +We use **Pest** for testing and **PHPStan** for static analysis. + +```bash +# Run Unit & Feature Tests +vendor/bin/pest + +# Run Static Analysis (Level 5) +vendor/bin/phpstan analyse +``` + +**PRs with failing tests or PHPStan errors will not be merged.** + +## Submitting a Pull Request + +1. Create a new branch: `git checkout -b feature/my-new-feature`. +2. Write tests for your new feature (we aim for high coverage). +3. Commit changes with clear, descriptive messages. +4. Push to your fork and open a Pull Request against `main`. + +## Reporting Bugs + +Please open an issue on GitHub with: +- A clear description of the bug. +- Minimal reproducible code example. +- SDK version and PHP version. + +Thank you for helping improve this SDK! \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..5c1df74 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Anatoliy Poloz + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index a478885..6ebcee9 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ This is an unofficial PHP SDK for the [Alfa Bank API](https://developers.alfaban - PHP 8.2+ - OpenSSL (for certificate generation/handling) -- [Saloon](https://docs.saloon.dev) +- [Saloon](https://docs.saloon.dev) v3.0+ - [Spatie Laravel Data](https://spatie.be/docs/laravel-data) - [phpseclib](https://phpseclib.org/) (for Card Encryption) @@ -16,6 +16,457 @@ This is an unofficial PHP SDK for the [Alfa Bank API](https://developers.alfaban composer require apoloz/alfa-bank-sdk ``` +## Security & Setup (Crucial) + +Alfa Bank uses a comprehensive security model requiring **two distinct sets of keys**: +1. **Transport (mTLS):** To establish the SSL connection. +2. **Signing (JWS/CMS):** To digitally sign the request body. + +### Where to get keys? +- **Sandbox:** Provided by the bank support or downloaded from the Developer Portal (usually as `.pfx` or `.zip`). +- **Production:** + - **Transport (mTLS):** Generate a private key and CSR using OpenSSL (`openssl genrsa`, `openssl req`). Send the CSR to `alfa_api@alfabank.ru`. The bank will reply with your Certificate. + - **Signing:** Generated by you (the client). The public certificate must be registered in "Alfa Business Online" or sent to your onboarding manager. + +### 1. Converting PFX to PEM +If the bank provided a `.pfx` file for signing, you must convert it to PEM format: +```bash +# Extract Private Key +openssl pkcs12 -in signing.pfx -nocerts -out signing_key.pem -nodes + +# Extract Public Certificate +openssl pkcs12 -in signing.pfx -clcerts -nokeys -out signing_cert.pem +``` + +### 2. Initialization +You can now initialize the SDK with both key pairs: + +```php +use Apoloz\AlfaBank\AlfaBank; +use Apoloz\AlfaBank\Enums\Environment; + +$baas = AlfaBank::baas( + // Transport Keys (mTLS) + certificatePath: '/path/to/transport_cert.pem', + privateKeyPath: '/path/to/transport_key.pem', + + // Signing Keys (Business Logic) - Optional, defaults to transport keys if not set + signingCertificatePath: '/path/to/signing_cert.pem', + signingPrivateKeyPath: '/path/to/signing_key.pem', + + environment: Environment::SANDBOX, + caBundlePath: '/path/to/root_ca_bundle.crt' +); +``` + +## Core Usage + +### Authentication (BaaS) + +Use the `login` method to perform OAuth Client Credentials flow: + +```php +$baas->login( + clientId: 'your-client-id', + clientSecret: 'your-client-secret' +); +``` + +### Making Requests + +#### SBP: Generate QR Code +```php +use Apoloz\AlfaBank\Sbp\Requests\GetQrCodeRequest; +use Apoloz\AlfaBank\Sbp\Data\QrCodeRequestData; + +$sbp = AlfaBank::sbp( + certificatePath: '...', + privateKeyPath: '...', + keyNameAlias: '9728109955', // Terminal ID + signingPrivateKeyPath: '/path/to/signing_key.pem' // Specific signing key for SBP body +); + +$response = $sbp->send(new GetQrCodeRequest( + new QrCodeRequestData(TermNo: '...', amount: '100') +)); + +$link = $response->dto()->payload; +``` + +#### Nominal Accounts +```php +$response = $baas->send(new CreateBeneficiaryRequest($payload)); +``` + +### Self-Employed (Payouts & Status) + +The SDK supports managing self-employed beneficiaries, checking their FTS status, and processing payouts. + +#### Check Taxpayer Status +```php +use Apoloz\AlfaBank\Requests\SelfEmployed\GetTaxpayerStatusRequest; +use Apoloz\AlfaBank\Data\SelfEmployed\TaxpayerStatusRequestData; + +$payload = new TaxpayerStatusRequestData( + externalId: 'unique-id', + selfemployedVatinList: ['123456789012'] +); + +$response = $baas->send(new GetTaxpayerStatusRequest($payload)); +$status = $response->dto()->taxpayerStatusList[0]; +``` + +#### Process Payout +```php +use Apoloz\AlfaBank\Requests\SelfEmployed\CreatePayoutRequest; +use Apoloz\AlfaBank\Data\SelfEmployed\PayoutRequestData; + +$payload = new PayoutRequestData( + externalId: 'ext-id-456', + company: ['vatin' => '7700000000'], + requisite: [ + ['amount' => 1000, 'vatin' => '112233445566'] + ] +); +$response = $baas->send(new CreatePayoutRequest($payload)); +``` + +### File Exchange + +Upload and download files securely. + +```php +use Apoloz\AlfaBank\Requests\FileExchange\UploadFileRequest; +use Apoloz\AlfaBank\Requests\FileExchange\DownloadFileRequest; + +// Upload +$stream = fopen('path/to/file', 'r'); +$baas->send(new UploadFileRequest($uploadUrl, $stream, $fileSize)); + +// Download +$response = $baas->send(new DownloadFileRequest($downloadUrl)); +$content = $response->body(); +``` + +### Payments (RUR Payment Orders) + +Create standard payment orders and check their status. + +```php +use Apoloz\AlfaBank\Requests\Payments\CreatePaymentRequest; +use Apoloz\AlfaBank\Data\Payments\PaymentData; + +$payload = new PaymentData( + amount: 100.50, + date: '2023-10-25', + externalId: 'order-12345', + operationCode: '01', + payeeAccount: '40702...', + payeeBankBic: '044525225', + payeeName: 'Partner LLC', + payerAccount: '40702...', + payerBankBic: '044525593', + payerBankCorrAccount: '30101...', + payerInn: '7700000000', + payerName: 'My Company', + priority: '5', + purpose: 'Payment for services' +); + +// Create Payment +$baas->send(new CreatePaymentRequest($payload)); + +// Check State +use Apoloz\AlfaBank\Requests\Payments\GetPaymentStateRequest; +$state = $baas->send(new GetPaymentStateRequest('order-12345'))->dto(); +echo $state->bankStatus; // e.g. ACCEPTED +``` + +### Cards + +Retrieve a list of client cards. + +```php +use Apoloz\AlfaBank\Requests\Cards\GetCardsRequest; + +$response = $baas->send(new GetCardsRequest()); +foreach ($response->dto() as $card) { + echo $card->maskedNumber . ' - ' . $card->status; +} +``` + +### B2C Payouts (SBP via Phone) + +Send money to individuals using their phone number through SBP. + +```php +use Apoloz\AlfaBank\Requests\B2C\CreateB2CPaymentRequest; +use Apoloz\AlfaBank\Data\B2C\B2CPaymentRequestData; +use Apoloz\AlfaBank\Data\B2C\B2CClientData; +use Apoloz\AlfaBank\Data\B2C\B2CPaymentRecipientData; + +$payload = new B2CPaymentRequestData( + client: new B2CClientData(b2cClientId: '...', partnerId: 123), + inn: '7700000000', + account: '40702...', + total: 100.53, + signedBody: '...base64_signature...', + payments: B2CPaymentRecipientData::collect([ + [ + 'phone' => '79001112233', + 'amount' => 100.53, + 'nspkBankId' => '100000000001', + 'sequence' => 'unique-seq-1', + 'name' => ['first' => 'Ivan', 'last' => 'Ivanov'] + ] + ], DataCollection::class) +); + +$baas->send(new CreateB2CPaymentRequest($payload)); +``` + +### Payment Registries (Batch Ruble Payments) + +Create a registry for multiple ruble payments and sign it. + +```php +use Apoloz\AlfaBank\Requests\Registries\CreateRegistryRequest; +use Apoloz\AlfaBank\Data\Registries\RegistryRequestData; +use Apoloz\AlfaBank\Data\Registries\RegistryPaymentData; + +$payload = new RegistryRequestData( + payments: RegistryPaymentData::collect([ + [ + 'amount' => 1000.0, + 'date' => '2023-11-01', + 'payeeAccount' => '40702...', + 'payeeBankBic' => '044525225', + 'payeeInn' => '7700000000', + 'payeeName' => 'Vendor LLC', + 'payerAccount' => '40702...', + 'purpose' => 'Batch payment part 1' + ] + ], DataCollection::class), + description: 'Weekly payroll' +); + +$response = $baas->send(new CreateRegistryRequest($payload)); +$registryId = $response->json('id'); + +// Sign Registry +use Apoloz\AlfaBank\Requests\Registries\SignRegistryRequest; +use Apoloz\AlfaBank\Data\Registries\RegistrySignatureData; +use Spatie\LaravelData\DataCollection; + +$signatures = RegistrySignatureData::collect([ + [ + 'base64Encoded' => '...', // Base64 signature + 'signatureType' => 'RSA', + 'certificateUuid' => '...' + ] +], DataCollection::class); + +$baas->send(new SignRegistryRequest($registryId, $signatures)); +``` + +### Customer Info + +Get organization profile. + +```php +use Apoloz\AlfaBank\Requests\CustomerInfo\GetCustomerInfoRequest; + +$info = $baas->send(new GetCustomerInfoRequest())->dto(); +echo $info->fullName; +``` + +### Limits + +Get client's operation limits. + +```php +use Apoloz\AlfaBank\Requests\Limits\GetGeneralLimitsRequest; + +$response = $baas->send(new GetGeneralLimitsRequest()); +$limits = $response->json(); +``` + +### Debit Cards + +Apply for a new debit card. + +```php +use Apoloz\AlfaBank\Requests\DebitCards\CreateDebitCardApplicationRequest; +use Apoloz\AlfaBank\Data\DebitCards\DebitCardApplicationData; + +$payload = new DebitCardApplicationData( + embossingName: 'IVAN IVANOV', + firstName: 'Иван', + lastName: 'Иванов', + middleName: 'Иванович', + mobilePhone: '79001112233', + passportBirthDate: '1990-01-01', + sex: 'm', + sopdConfirmed: true +); + +$baas->send(new CreateDebitCardApplicationRequest($payload)); +``` + +### User Compliance + +Check user compliance for loans. + +```php +use Apoloz\AlfaBank\Requests\Compliance\GetUserComplianceRequest; + +$baas->send(new GetUserComplianceRequest()); +``` + +### DFA (Digital Financial Assets) + +List initial placements. + +```php +use Apoloz\AlfaBank\Requests\DFA\GetDfaInitialPlacementListRequest; + +$baas->send(new GetDfaInitialPlacementListRequest(limit: 5)); +``` + +### Currency Rates + +Get currency rates by office. + +```php +use Apoloz\AlfaBank\Requests\Rates\GetOfficeRatesRequest; + +$baas->send(new GetOfficeRatesRequest()); +``` + +### Escrow Accounts + +Manage construction projects and escrow orders. + +```php +use Apoloz\AlfaBank\Requests\Escrow\GetBuildProjectsRequest; + +$projects = $baas->send(new GetBuildProjectsRequest())->json(); +``` + +### Power of Attorney (Poa) + +Request and check status of digital POAs. + +```php +use Apoloz\AlfaBank\Requests\Poa\CreatePoaRequest; + +$response = $baas->send(new CreatePoaRequest('poa-123', '7700000000')); +$uuid = $response->json('requestUuid'); +``` + +### Loyalty + +Access bonus accounts and transaction history. + +```php +use Apoloz\AlfaBank\Requests\Loyalty\GetLoyaltyAccountsRequest; + +$accounts = $baas->send(new GetLoyaltyAccountsRequest())->json(); +``` + +### Reports + +Order certificates and reports. + +```php +use Apoloz\AlfaBank\Requests\Reports\CreateAccountBalanceReportRequest; + +$baas->send(new CreateAccountBalanceReportRequest(['40702...'], 'eng')); +``` + +### Crypto Service + +Manage encryption and signing certificates. + +```php +use Apoloz\AlfaBank\Requests\Crypto\GetCertificatesRequest; + +$certificates = $baas->send(new GetCertificatesRequest())->json(); +``` + +### Partner (MMB) + +Access partner program data and applications. + +```php +use Apoloz\AlfaBank\Requests\Partner\GetPartnerApplicationsRequest; + +$apps = $baas->send(new GetPartnerApplicationsRequest())->json(); +``` + +### Miscellaneous + +- **Bonus Numbers**: `$baas->send(new GetBonusNumbersRequest());` +- **Credit Offers**: `$baas->send(new GetCreditOffersRequest());` +- **Self-encashment**: `$baas->send(new CreateSelfEncashmentRegistryRequest($data));` +- **Business Lounge**: `$baas->send(new GetLoungeBalanceRequest());` +- **Scoring**: `$baas->send(new GetComplianceScoreRequest('7700000000'));` +- **Security**: `$baas->send(new GetSubscriptionsRequest());` +- **Logistics**: `$baas->send(new GetDeliveriesRequest('...'));` +- **Webhooks**: `$baas->send(new GetWebhooksRequest());` +- **1C Integration**: `$baas->send(new CheckMerchantRequest('MF123', 'Basic ...'));` +- **Claims**: `$baas->send(new ResolveClaimRequest('ID', $data));` +- **Orders (Travel/Afisha)**: `$baas->send(new CreateTravelOrderRequest($data));` +- **Salary Project**: `$baas->send(new CreatePayrollApplicationRequest($payload));` +- **Cloud Signature**: `$baas->send(new SubmitSigningRequest('REQ-ID'));` +- **Aff Integration**: `$baas->send(new CheckComplianceRequest('INN', 'Name'));` +- **Digital Ruble**: `$sbp->send(new CreateUniQrRequest($uniQrData));` + +### Signing Data (CMS) + +For requests requiring a digital signature (e.g., Payments), you must canonicalize and sign the data manually. + +```php +use Apoloz\AlfaBank\Utils\CmsSigner; +use Apoloz\AlfaBank\Data\NominalAccounts\Payouts\DigestSignatureData; +use Spatie\LaravelData\DataCollection; + +// 1. Prepare data +$fields = [ + 'amount' => 100.50, + // ... other fields +]; + +// 2. Canonicalize & Sign +$json = CmsSigner::canonicalize($fields); +$cert = file_get_contents('/path/to/signing_cert.pem'); +$key = file_get_contents('/path/to/signing_key.pem'); +$signature = CmsSigner::sign($json, $cert, $key); + +// 3. Attach to DTO +$paymentData->digestSignatures = DigestSignatureData::collect([ + [ + 'base64Encoded' => $signature, + 'signatureType' => 'CMS' + ] +], DataCollection::class); +``` + +## Advanced Features + +### Pagination +The SDK supports automatic pagination for list endpoints: + +```php +use Apoloz\AlfaBank\Requests\Accounts\GetAccountsRequest; + +$paginator = $baas->paginate(new GetAccountsRequest()); + +foreach ($paginator as $account) { + // Each item is processed automatically +} +``` + ## Laravel Integration The package supports auto-discovery. Publish the config file: @@ -26,120 +477,80 @@ php artisan vendor:publish --tag=alfa-bank-config Configure your `.env`: ```env -ALFA_BAAS_CERT_PATH=/path/to/cert.cer -ALFA_BAAS_KEY_PATH=/path/to/key.key -ALFA_BAAS_ENV=sandbox - -ALFA_SBP_CERT_PATH=/path/to/cert.cer -ALFA_SBP_KEY_PATH=/path/to/key.key -ALFA_SBP_KEY_ALIAS=your-alias -ALFA_SBP_ENV=sandbox +ALFA_BAAS_CERT_PATH=/path/to/transport_cert.pem +# ... other settings +ALFA_WEBHOOK_USER=your_webhook_user +ALFA_WEBHOOK_PASSWORD=your_webhook_password ``` -Now you can inject the connectors: -```php -use Apoloz\AlfaBank\AlfaBankConnector; +### Webhook Handling (Laravel) -public function index(AlfaBankConnector $connector) { - $connector->withToken('...')->send(new GetNominalAccountsRequest()); +The SDK provides a built-in controller and route macro to handle incoming webhooks and dispatch Laravel events. + +1. **Register the route** in `routes/api.php`: +```php +Route::alfaWebhooks('/webhooks/alfa'); +``` + +2. **Listen for the event** in `app/Providers/EventServiceProvider.php`: +```php +use Apoloz\AlfaBank\Laravel\Events\AlfaWebhookReceived; + +protected $listen = [ + AlfaWebhookReceived::class => [ + \App\Listeners\HandleAlfaWebhook::class, + ], +]; +``` + +3. **Handle the DTO** in your listener: +```php +public function handle(AlfaWebhookReceived $event) +{ + $webhook = $event->webhook; // BaseWebhookData DTO + + if ($webhook->object === 'ul_transaction_default') { + $transaction = $webhook->data; + // ... + } } ``` -## Core Usage (Generic PHP) +## Error Handling -### 1. Initialize the Connector +The SDK throws `Apoloz\AlfaBank\Exceptions\AlfaBankException` when the API returns an error. ```php -use Apoloz\AlfaBank\AlfaBankConnector; -use Apoloz\AlfaBank\Enums\Environment; - -$connector = new AlfaBankConnector( - certificatePath: '/path/to/client.cer', - privateKeyPath: '/path/to/private.key', - environment: Environment::SANDBOX -); +try { + $baas->send($request); +} catch (\Apoloz\AlfaBank\Exceptions\AlfaBankException $e) { + echo $e->getErrorCode(); // e.g. "invalid_token" or "RQ05014" + echo $e->getMessage(); +} ``` -### 2. Authentication +## Utilities + +- **Card PAN Encryption**: `CardEncryptor::encrypt($pan, $pubKey)` +- **CMS Signing**: `CmsSigner::sign($data, $cert, $key)` +- **JWS/JWE**: `JwsSigner` and `JweEncryptor` for transport security. + +## Diagnostics + +### Health Check (Laravel) +Run the built-in Artisan command to verify your mTLS certificates and connection: + +```bash +php artisan alfa:check +``` + +### Logging +Attach a PSR-3 logger to debug requests (sensitive data redacted): ```php -use Apoloz\AlfaBank\Requests\Auth\GetTokenRequest; - -$request = new GetTokenRequest( - clientId: 'your-client-id', - clientSecret: 'your-client-secret', - code: 'auth-code', - redirectUri: 'https://your-app.com/callback' -); - -$response = $connector->send($request); -$tokenData = $response->dto(); - -$connector->withToken($tokenData->access_token); +$baas->withLogging(Log::getLogger()); ``` -### 3. SBP / QR Payouts (C2B) - -```php -use Apoloz\AlfaBank\Sbp\SbpConnector; - -$sbpConnector = new SbpConnector( - certificatePath: '/path/to/client.cer', - privateKeyPath: '/path/to/private.key', - keyNameAlias: 'your-key-alias', - environment: Environment::SANDBOX -); - -$response = $sbpConnector->send(new GetQrCodeRequest($payload)); -``` - -### 4. Utilities - -#### Card PAN Encryption -Required for card payouts in Nominal Accounts. - -```php -use Apoloz\AlfaBank\Utils\CardEncryptor; - -$encryptedPan = CardEncryptor::encrypt( - pan: '4779642610000010', - publicKeyPem: file_get_contents('alfa-card-key.pub') -); -``` - -#### JSON Web Signature (JWS) -Used for signing some BaaS requests. - -```php -use Apoloz\AlfaBank\Utils\JwsSigner; - -$jws = JwsSigner::sign( - payload: ['foo' => 'bar'], - privateKeyPem: file_get_contents('my-key.pem'), - certificateSerialNumber: '610003...' -); -``` - -#### JSON Web Encryption (JWE) -Used for encrypting sensitive BaaS requests. - -```php -use Apoloz\AlfaBank\Utils\JweEncryptor; - -$jwe = JweEncryptor::encrypt( - jws: $jws, - publicKeyPem: file_get_contents('alfa-cert.cer'), - certificateSerialNumber: '610003...' -); -``` - -## Features - -- **Mutual TLS**: Automated certificate handling. -- **Laravel Ready**: Service Provider and Config included. -- **Typed DTOs**: Comprehensive response parsing. -- **Full Product Coverage**: BaaS (Nominal Accounts, Escrow), SBP (QR, Subscriptions), Digital Ruble. - ## License MIT diff --git a/composer.json b/composer.json index a71e3ac..dca68bb 100644 --- a/composer.json +++ b/composer.json @@ -1,41 +1,49 @@ { - "name": "apoloz/alfa-bank-sdk", - "description": "Unofficial Alfa Bank API SDK for PHP using Saloon and Laravel Data", - "type": "library", - "license": "MIT", - "authors": [ - { - "name": "A. Poloz", - "email": "t0ly.od@yandex.ru" + "name": "apoloz/alfa-bank-sdk", + "description": "Unofficial Alfa Bank API SDK for PHP using Saloon and Laravel Data", + "type": "library", + "license": "MIT", + "authors": [ + { + "name": "apoloz", + "email": "anatoliy.poloz@gmail.com" + } + ], + "require": { + "php": "^8.2", + "saloonphp/saloon": "^3.0", + "saloonphp/laravel-plugin": "^3.0", + "saloonphp/pagination-plugin": "^2.0", + "spatie/laravel-data": "^4.0", + "guzzlehttp/guzzle": "^7.0", + "phpseclib/phpseclib": "^3.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.0", + "pestphp/pest": "^2.0", + "phpstan/phpstan": "^2.1" + }, + "autoload": { + "psr-4": { + "Apoloz\\AlfaBank\\": "src/" + } + }, + "extra": { + "laravel": { + "providers": [ + "Apoloz\\AlfaBank\\Laravel\\AlfaBankServiceProvider" + ] + } + }, + "minimum-stability": "stable", + "prefer-stable": true, + "scripts": { + "test": "vendor/bin/pest", + "test-coverage": "vendor/bin/pest --coverage" + }, + "config": { + "allow-plugins": { + "pestphp/pest-plugin": true + } } - ], - "require": { - "php": "^8.2", - "saloonphp/saloon": "^3.0", - "spatie/laravel-data": "^4.0", - "guzzlehttp/guzzle": "^7.0", - "phpseclib/phpseclib": "^3.0" - }, - "require-dev": { - "phpunit/phpunit": "^10.0", - "pestphp/pest": "^2.0" - }, - "autoload": { - "psr-4": { - "Apoloz\\AlfaBank\\": "src/" - } - }, - "extra": { - "laravel": { - "providers": [ - "Apoloz\\AlfaBank\\Laravel\\AlfaBankServiceProvider" - ] - } - }, - "minimum-stability": "stable", - "prefer-stable": true, - "scripts": { - "test": "vendor/bin/pest", - "test-coverage": "vendor/bin/pest --coverage" - } } diff --git a/examples/01_baas_flow.php b/examples/01_baas_flow.php new file mode 100644 index 0000000..ecf884b --- /dev/null +++ b/examples/01_baas_flow.php @@ -0,0 +1,40 @@ +login( + clientId: 'your-client-id', + clientSecret: 'your-client-secret' + ); + echo "Authentication Successful!\n"; + + // 2. Create a Beneficiary (Counterparty) + echo "Creating Beneficiary...\n"; + + // In a real app, use the DTOs + // $payload = new CreateBeneficiaryRequestData(...); + // $baas->send(new CreateBeneficiaryRequest($payload)); + + echo "Beneficiary Request Sent (Mock).\n"; + +} catch (\Exception $e) { + echo "Error: " . $e->getMessage() . "\n"; + if ($e instanceof \Apoloz\AlfaBank\Exceptions\AlfaBankException) { + echo "API Code: " . $e->getErrorCode() . "\n"; + } +} + diff --git a/examples/02_sbp_qr.php b/examples/02_sbp_qr.php new file mode 100644 index 0000000..3f5d099 --- /dev/null +++ b/examples/02_sbp_qr.php @@ -0,0 +1,45 @@ +send($request); + + if ($response->successful()) { + $qrData = $response->dto(); + echo "QR Payload: " . $qrData->payload . "\n"; + echo "QR Image URL: " . $qrData->image . "\n"; + } else { + echo "Failed to generate QR.\n"; + } + + // Example of Pagination (if supported by SBP endpoints) + // $paginator = $sbp->paginate(new SomeSbpListRequest()); + // foreach($paginator as $item) { ... } + +} catch (\Exception $e) { + echo "Error: " . $e->getMessage() . "\n"; +} diff --git a/examples/03_sandbox_init.php b/examples/03_sandbox_init.php new file mode 100644 index 0000000..b14004b --- /dev/null +++ b/examples/03_sandbox_init.php @@ -0,0 +1,55 @@ +login(...) or $sbp->send(...) + diff --git a/examples/04_nominal_accounts.php b/examples/04_nominal_accounts.php new file mode 100644 index 0000000..01d28a1 --- /dev/null +++ b/examples/04_nominal_accounts.php @@ -0,0 +1,54 @@ + 'REGISTRATION_ADDRESS', + 'address' => 'Moscow, Lenina st, 10', + 'country' => 'RU' + ] + ], DataCollection::class), + digestSignatures: DigestSignatureData::collect([ + [ + 'base64Encoded' => 'MOCK_SIGNATURE', // In real life, use CmsSigner::sign() + 'signatureType' => 'cms' + ] + ], DataCollection::class) +); + +// 2. Login and send request +$baas->login('client-id', 'client-secret'); +$response = $baas->send(new CreateBeneficiaryRequest($payload)); + +if ($response->successful()) { + $data = $response->dto(); // BeneficiaryResponseData + echo "Beneficiary creation accepted. ID: {$data->id}, Status: {$data->status}\n"; +} else { + echo "Error: " . $response->body() . "\n"; +} diff --git a/examples/05_webhooks.php b/examples/05_webhooks.php new file mode 100644 index 0000000..fdbbc5e --- /dev/null +++ b/examples/05_webhooks.php @@ -0,0 +1,49 @@ +login('id', 'secret'); + +// List existing webhooks +$response = $baas->send(new GetWebhooksRequest()); +$webhooks = $response->dto(); // DataCollection of WebhookSubscriptionData + +foreach ($webhooks as $webhook) { + echo "Webhook ID: {$webhook->id}, URL: {$webhook->callbackUri}\n"; +} + +// Create new webhook +$data = new WebhookSubscriptionRequestData('https://your-app.com/webhooks/alfa', ['transactions']); +$baas->send(new CreateWebhookRequest($data)); + +/** + * 2. Parsing incoming webhook payload (Webhook Controller logic) + */ +$rawPayload = [ + [ + 'actionType' => 'create', + 'eventTime' => '2023-10-27T10:00:00Z', + 'object' => 'ul_transaction_default', + 'data' => [ + 'transactionId' => 'TX-12345', + 'amount' => ['amount' => 100.50, 'currencyName' => 'RUR'], + 'direction' => 'CREDIT', + 'paymentPurpose' => 'Test payment' + ] + ] +]; + +// Use the factory to parse the payload into a DTO +$webhookDto = WebhookFactory::create($rawPayload); + +echo "Received Webhook: {$webhookDto->object} ({$webhookDto->actionType})\n"; diff --git a/examples/06_sbp_b2c_payout.php b/examples/06_sbp_b2c_payout.php new file mode 100644 index 0000000..8069264 --- /dev/null +++ b/examples/06_sbp_b2c_payout.php @@ -0,0 +1,46 @@ + '79001112233', + 'amount' => 100.00, + 'nspkBankId' => '100000000001', + 'sequence' => 'unique-payout-id-1' + ] + ], DataCollection::class) +); + +$baas->login('client-id', 'client-secret'); +$response = $baas->send(new CreateB2CPaymentRequest($payload)); + +if ($response->successful()) { + $data = $response->dto(); // B2CPaymentResponseData + echo "Payout request sent. Status: {$data->status}\n"; +} else { + echo "Error: " . $response->body() . "\n"; +} + diff --git a/examples/07_registry_signing.php b/examples/07_registry_signing.php new file mode 100644 index 0000000..9a135a0 --- /dev/null +++ b/examples/07_registry_signing.php @@ -0,0 +1,65 @@ + 500.0, + 'date' => date('Y-m-d'), + 'payeeAccount' => '40702...', + 'payeeBankBic' => '044...', + 'payeeInn' => '770...', + 'payeeName' => 'Vendor 1', + 'payerAccount' => '40702...', + 'purpose' => 'Payment for goods' + ] + ], DataCollection::class) +); + +$baas->login('client-id', 'client-secret'); +$response = $baas->send(new CreateRegistryRequest($payload)); +$registryId = $response->dto()->id; + +echo "Registry Created: $registryId\n"; + +// 2. Sign Registry +// In real life, you might need to sign the digest provided by the bank +// or the registry content itself. This depends on the specific bank requirement. +$signature = CmsSigner::sign( + $registryId, // Example data + file_get_contents('path/to/sign_cert.pem'), + file_get_contents('path/to/sign_key.pem') +); + +$signatures = RegistrySignatureData::collect([ + [ + 'base64Encoded' => $signature, + 'signatureType' => 'RSA', + 'certificateUuid' => 'YOUR_CERT_UUID' + ] +], DataCollection::class); + +$baas->send(new SignRegistryRequest($registryId, $signatures)); + +echo "Registry Signed.\n"; + diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..416924d --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,9 @@ +parameters: + level: 5 + paths: + - src + excludePaths: + - src/Laravel + - src/Http/Pagination + - src/Traits/HasAlfaPagination.php + tmpDir: build/phpstan diff --git a/src/AlfaBank.php b/src/AlfaBank.php new file mode 100644 index 0000000..df7bb5e --- /dev/null +++ b/src/AlfaBank.php @@ -0,0 +1,80 @@ + +// +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; +// + +//
+// +class AlfaBank +{ + //
+ // + public static function baas( + string $certificatePath, + string $privateKeyPath, + Environment $environment = Environment::SANDBOX, + ?string $caBundlePath = null, + ?string $signingCertificatePath = null, + ?string $signingPrivateKeyPath = null + ): AlfaBankConnector { + $config = new AlfaConfig( + baseUrl: $environment->value, + certificatePath: $certificatePath, + privateKeyPath: $privateKeyPath, + caBundlePath: $caBundlePath, + signingCertificatePath: $signingCertificatePath, + signingPrivateKeyPath: $signingPrivateKeyPath + ); + + $signerCert = $signingCertificatePath ?: $certificatePath; + $signerKey = $signingPrivateKeyPath ?: $privateKeyPath; + + $signer = new OpenSslCmsSigner($signerCert, $signerKey); + + return new AlfaBankConnector($config, $signer); + } + //
+ + //
+ // + public static function sbp( + string $certificatePath, + string $privateKeyPath, + string $keyNameAlias, + Environment $environment = Environment::SANDBOX, + ?string $caBundlePath = null, + ?string $signingCertificatePath = null, + ?string $signingPrivateKeyPath = null + ): SbpConnector { + $baseUrl = $environment === Environment::PRODUCTION + ? Environment::SBP_PRODUCTION->value + : Environment::SBP_SANDBOX->value; + + $config = new AlfaConfig( + baseUrl: $baseUrl, + certificatePath: $certificatePath, + privateKeyPath: $privateKeyPath, + caBundlePath: $caBundlePath, + signingCertificatePath: $signingCertificatePath, + signingPrivateKeyPath: $signingPrivateKeyPath + ); + + return new SbpConnector($config, $keyNameAlias); + } + //
+} \ No newline at end of file diff --git a/src/Contracts/EncryptorInterface.php b/src/Contracts/EncryptorInterface.php new file mode 100644 index 0000000..1d9f54d --- /dev/null +++ b/src/Contracts/EncryptorInterface.php @@ -0,0 +1,8 @@ + +// +interface SignerInterface +{ + public function sign(string $data): string; +} +//
+ +//
+// +interface EncryptorInterface +{ + public function encrypt(string $data): string; +} +//
diff --git a/src/Contracts/SignerInterface.php b/src/Contracts/SignerInterface.php new file mode 100644 index 0000000..444ea18 --- /dev/null +++ b/src/Contracts/SignerInterface.php @@ -0,0 +1,8 @@ + +// +use Apoloz\AlfaBank\Contracts\SignerInterface; +use Apoloz\AlfaBank\Http\Middleware\AlfaResponseMiddleware; +use Apoloz\AlfaBank\Http\Middleware\RequestIdMiddleware; +use Apoloz\AlfaBank\Http\Pagination\AlfaOffsetPaginator; +use Apoloz\AlfaBank\Requests\Auth\GetTokenRequest; +use Saloon\Http\Connector; +use Saloon\Http\Request; +use Saloon\PaginationPlugin\Contracts\HasPagination; +use Saloon\PaginationPlugin\OffsetPaginator; +use Saloon\Traits\RequestProperties\HasTries; +use Saloon\Traits\Plugins\HasTimeout; + +class AlfaBankConnector extends Connector implements HasPagination +{ + use HasTimeout; + use HasTries; + + public function __construct( + protected AlfaConfig $alfaConfig, + protected ?SignerInterface $signer = null + ) { + $this->tries = 3; + $this->retryInterval = 500; + } + + public function resolveBaseUrl(): string + { + return $this->alfaConfig->baseUrl; + } + + protected function defaultConfig(): array + { + $config = [ + 'cert' => $this->alfaConfig->certificatePath, + 'ssl_key' => $this->alfaConfig->privateKeyPath, + 'verify' => true, + ]; + + if ($this->alfaConfig->caBundlePath) { + $config['verify'] = $this->alfaConfig->caBundlePath; + } + + return $config; + } + + protected function defaultMiddleware(): array + { + return []; + } + + public function boot(\Saloon\Http\PendingRequest $pendingRequest): void + { + $pendingRequest->middleware()->onResponse(new AlfaResponseMiddleware()); + $pendingRequest->middleware()->onRequest(new RequestIdMiddleware()); + } + + public function paginate(Request $request): OffsetPaginator + { + return new AlfaOffsetPaginator($this, $request); + } + + public function login(?string $clientId = null, ?string $clientSecret = null): static + { + // Allow overriding config via method arguments + $clientId = $clientId ?? $this->alfaConfig->clientId; + $clientSecret = $clientSecret ?? $this->alfaConfig->clientSecret; + + if (!$clientId || !$clientSecret) { + throw new \RuntimeException("Client ID/Secret not configured."); + } + + $request = new GetTokenRequest( + clientId: $clientId, + clientSecret: $clientSecret, + grantType: 'client_credentials' + ); + + // Send request (using self to reuse cert/ssl config) + // We temporarily disable auth middleware if we had any, + // but here we are raw, so it's fine. + $response = $this->send($request); + $data = $response->dto(); + + $this->authenticate(new \Saloon\Http\Auth\TokenAuthenticator($data->access_token)); + + return $this; + } + // + + //
+ // + public function getSigner(): SignerInterface + { + if (!$this->signer) { + throw new \RuntimeException("No signer configured for this connector."); + } + return $this->signer; + } + //
+ + //
+ // + public function withLogging(\Psr\Log\LoggerInterface $logger): static + { + $middleware = new \Apoloz\AlfaBank\Http\Middleware\LoggingMiddleware($logger); + + $this->middleware()->onRequest([$middleware, 'onRequest']); + $this->middleware()->onResponse([$middleware, 'onResponse']); + + return $this; + } + //
+} \ No newline at end of file diff --git a/src/Core/AlfaConfig.php b/src/Core/AlfaConfig.php new file mode 100644 index 0000000..460264a --- /dev/null +++ b/src/Core/AlfaConfig.php @@ -0,0 +1,54 @@ + +// +readonly class AlfaConfig +{ + //
+ // + public function __construct( + // + public string $baseUrl, + // + public string $certificatePath, + // + public string $privateKeyPath, + // + public ?string $caBundlePath = null, + // + public ?string $clientId = null, + // + public ?string $clientSecret = null, + // + public ?string $signingCertificatePath = null, + // + public ?string $signingPrivateKeyPath = null, + ) { + // + if (!file_exists($this->certificatePath)) { + throw new \InvalidArgumentException("Certificate not found: {$this->certificatePath}"); + } + if (!file_exists($this->privateKeyPath)) { + throw new \InvalidArgumentException("Private key not found: {$this->privateKeyPath}"); + } + if ($this->signingCertificatePath && !file_exists($this->signingCertificatePath)) { + throw new \InvalidArgumentException("Signing certificate not found: {$this->signingCertificatePath}"); + } + if ($this->signingPrivateKeyPath && !file_exists($this->signingPrivateKeyPath)) { + throw new \InvalidArgumentException("Signing private key not found: {$this->signingPrivateKeyPath}"); + } + } + //
+} diff --git a/src/Data/Accounts/AccountData.php b/src/Data/Accounts/AccountData.php new file mode 100644 index 0000000..85c4f8c --- /dev/null +++ b/src/Data/Accounts/AccountData.php @@ -0,0 +1,16 @@ + +// use Spatie\LaravelData\Data; +// +//
+// class TokenData extends Data { + //
+ // public function __construct( public string $access_token, public string $refresh_token, - public ?string $id_token, + public ?string $id_token, // Specific to OpenID Connect public int $expires_in, public string $token_type ) {} + //
} diff --git a/src/Data/Auth/UserInfo/ClientConsentCollectionData.php b/src/Data/Auth/UserInfo/ClientConsentCollectionData.php index a0d3ceb..f5af245 100644 --- a/src/Data/Auth/UserInfo/ClientConsentCollectionData.php +++ b/src/Data/Auth/UserInfo/ClientConsentCollectionData.php @@ -1,17 +1,32 @@ +// use Apoloz\AlfaBank\Data\PaginationData; use Spatie\LaravelData\Attributes\DataCollectionOf; use Spatie\LaravelData\Data; use Spatie\LaravelData\DataCollection; +//
+//
+// class ClientConsentCollectionData extends Data { + //
+ // public function __construct( public PaginationData $pagination, #[DataCollectionOf(ClientConsentData::class)] public DataCollection $items ) {} + //
} diff --git a/src/Data/Auth/UserInfo/ClientConsentData.php b/src/Data/Auth/UserInfo/ClientConsentData.php index 3ec8311..fc7366b 100644 --- a/src/Data/Auth/UserInfo/ClientConsentData.php +++ b/src/Data/Auth/UserInfo/ClientConsentData.php @@ -1,14 +1,28 @@ +// +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. use Spatie\LaravelData\Data; -use Spatie\LaravelData\DataCollection; +use Spatie\LaravelData\DataCollection; // This is also likely extraneous for a single DTO. +//
+//
+// class ClientConsentData extends Data { + //
+ // public function __construct( public string $createDateTime, public string $sub, @@ -17,4 +31,5 @@ class ClientConsentData extends Data public string $expireDate, public ?string $organizationIdHash = null, ) {} + //
} diff --git a/src/Data/Auth/UserInfo/UserInfoData.php b/src/Data/Auth/UserInfo/UserInfoData.php index 2ca8a65..ea326fc 100644 --- a/src/Data/Auth/UserInfo/UserInfoData.php +++ b/src/Data/Auth/UserInfo/UserInfoData.php @@ -1,15 +1,29 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class UserInfoData extends Data { + //
+ // public function __construct( public string $sub, - public ?string $iss = null, - public ?string $aud = null, + public ?string $iss = null, // Issuer + public ?string $aud = null, // Audience public ?string $name = null, public ?string $given_name = null, public ?string $family_name = null, @@ -18,7 +32,8 @@ class UserInfoData extends Data public ?string $gender = null, public ?string $birthdate = null, public ?string $phone_number = null, - public ?string $inn = null, + public ?string $inn = null, // Individual Taxpayer Number (Russia) // Add more claims as needed based on the response ) {} + //
} diff --git a/src/Data/B2B_1C/MerchantData.php b/src/Data/B2B_1C/MerchantData.php new file mode 100644 index 0000000..59c748b --- /dev/null +++ b/src/Data/B2B_1C/MerchantData.php @@ -0,0 +1,14 @@ + '...', 'last' => '...', 'middle' => '...'] + public ?string $paymentDescription = null, + ) {} +} diff --git a/src/Data/B2C/B2CPaymentRequestData.php b/src/Data/B2C/B2CPaymentRequestData.php new file mode 100644 index 0000000..4884efb --- /dev/null +++ b/src/Data/B2C/B2CPaymentRequestData.php @@ -0,0 +1,21 @@ + +// use Spatie\LaravelData\Attributes\DataCollectionOf; use Spatie\LaravelData\Data; use Spatie\LaravelData\DataCollection; +//
+//
+// class DownloadFileResponseData extends Data { + //
+ // public function __construct( #[DataCollectionOf(FileLinkData::class)] public DataCollection $downloadFileLinks, ) {} + //
} diff --git a/src/Data/FileExchange/FileLinkData.php b/src/Data/FileExchange/FileLinkData.php index b47a74e..125d465 100644 --- a/src/Data/FileExchange/FileLinkData.php +++ b/src/Data/FileExchange/FileLinkData.php @@ -1,14 +1,29 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class FileLinkData extends Data { + //
+ // public function __construct( public string $url, public string $method, - public ?FileLinkData $detachedSignatureLink = null, + public ?FileLinkData $detachedSignatureLink = null, // Nested link for signatures ) {} + //
} diff --git a/src/Data/FileExchange/UploadFileResponseData.php b/src/Data/FileExchange/UploadFileResponseData.php index 9068cf8..b293586 100644 --- a/src/Data/FileExchange/UploadFileResponseData.php +++ b/src/Data/FileExchange/UploadFileResponseData.php @@ -1,15 +1,30 @@ +// use Spatie\LaravelData\Attributes\DataCollectionOf; use Spatie\LaravelData\Data; use Spatie\LaravelData\DataCollection; +//
+//
+// class UploadFileResponseData extends Data { + //
+ // public function __construct( #[DataCollectionOf(FileLinkData::class)] public DataCollection $uploadFileLinks, ) {} + //
} diff --git a/src/Data/Limits/LimitData.php b/src/Data/Limits/LimitData.php new file mode 100644 index 0000000..cae56a4 --- /dev/null +++ b/src/Data/Limits/LimitData.php @@ -0,0 +1,17 @@ + +// use Spatie\LaravelData\Data; +//
+//
+// class AddressData extends Data { + //
+ // public function __construct( public string $type, public string $address, public ?string $country = null, ) {} + //
} diff --git a/src/Data/NominalAccounts/Beneficiaries/BeneficiaryBalanceData.php b/src/Data/NominalAccounts/Beneficiaries/BeneficiaryBalanceData.php index b63b444..f2e2dcb 100644 --- a/src/Data/NominalAccounts/Beneficiaries/BeneficiaryBalanceData.php +++ b/src/Data/NominalAccounts/Beneficiaries/BeneficiaryBalanceData.php @@ -1,13 +1,27 @@ +// use Spatie\LaravelData\Attributes\DataCollectionOf; use Spatie\LaravelData\Data; use Spatie\LaravelData\DataCollection; +//
+//
+// class BeneficiaryBalanceData extends Data { + //
+ // public function __construct( public string $beneficiaryId, public string $accountNumber, @@ -15,4 +29,5 @@ class BeneficiaryBalanceData extends Data #[DataCollectionOf(WalletBalanceData::class)] public ?DataCollection $balances = null, ) {} + //
} diff --git a/src/Data/NominalAccounts/Beneficiaries/BeneficiaryTransactionCollectionData.php b/src/Data/NominalAccounts/Beneficiaries/BeneficiaryTransactionCollectionData.php index a719207..be80f27 100644 --- a/src/Data/NominalAccounts/Beneficiaries/BeneficiaryTransactionCollectionData.php +++ b/src/Data/NominalAccounts/Beneficiaries/BeneficiaryTransactionCollectionData.php @@ -1,17 +1,33 @@ +// use Apoloz\AlfaBank\Data\PaginationData; use Spatie\LaravelData\Attributes\DataCollectionOf; use Spatie\LaravelData\Data; use Spatie\LaravelData\DataCollection; +//
+//
+// class BeneficiaryTransactionCollectionData extends Data { + //
+ // public function __construct( public ?PaginationData $pagination = null, #[DataCollectionOf(BeneficiaryTransactionData::class)] public ?DataCollection $transactionsList = null, ) {} + //
} +//
diff --git a/src/Data/NominalAccounts/Beneficiaries/BeneficiaryTransactionData.php b/src/Data/NominalAccounts/Beneficiaries/BeneficiaryTransactionData.php index fddcb50..6cb4ca7 100644 --- a/src/Data/NominalAccounts/Beneficiaries/BeneficiaryTransactionData.php +++ b/src/Data/NominalAccounts/Beneficiaries/BeneficiaryTransactionData.php @@ -1,14 +1,30 @@ +// use Spatie\LaravelData\Data; +// +//
+// class BeneficiaryTransactionData extends Data { + //
+ // public function __construct( public string $type, public string $amount, public ?string $comment = null, ) {} + //
} +//
diff --git a/src/Data/NominalAccounts/Beneficiaries/CreateBeneficiaryRequestData.php b/src/Data/NominalAccounts/Beneficiaries/CreateBeneficiaryRequestData.php index 54d4979..570fb0e 100644 --- a/src/Data/NominalAccounts/Beneficiaries/CreateBeneficiaryRequestData.php +++ b/src/Data/NominalAccounts/Beneficiaries/CreateBeneficiaryRequestData.php @@ -1,14 +1,28 @@ +// use Apoloz\AlfaBank\Data\NominalAccounts\Payouts\DigestSignatureData; use Spatie\LaravelData\Attributes\DataCollectionOf; use Spatie\LaravelData\Data; use Spatie\LaravelData\DataCollection; +// +//
+// class CreateBeneficiaryRequestData extends Data { + //
+ // public function __construct( public string $type, public string $countryOfTaxResidence, @@ -31,4 +45,5 @@ class CreateBeneficiaryRequestData extends Data #[DataCollectionOf(DocumentData::class)] public ?DataCollection $documents = null, ) {} + //
} diff --git a/src/Data/NominalAccounts/Beneficiaries/CreateBeneficiaryResponseData.php b/src/Data/NominalAccounts/Beneficiaries/CreateBeneficiaryResponseData.php index 693e2f6..c08ca2d 100644 --- a/src/Data/NominalAccounts/Beneficiaries/CreateBeneficiaryResponseData.php +++ b/src/Data/NominalAccounts/Beneficiaries/CreateBeneficiaryResponseData.php @@ -1,13 +1,28 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class CreateBeneficiaryResponseData extends Data { + //
+ // public function __construct( public string $requestId, public string $status, ) {} + //
} diff --git a/src/Data/NominalAccounts/Beneficiaries/DocumentData.php b/src/Data/NominalAccounts/Beneficiaries/DocumentData.php index 7977a71..d95ec68 100644 --- a/src/Data/NominalAccounts/Beneficiaries/DocumentData.php +++ b/src/Data/NominalAccounts/Beneficiaries/DocumentData.php @@ -1,11 +1,25 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class DocumentData extends Data { + //
+ // public function __construct( public string $type, public string $number, @@ -15,4 +29,5 @@ class DocumentData extends Data public ?string $expireDate = null, public ?string $division = null, ) {} + //
} diff --git a/src/Data/NominalAccounts/Beneficiaries/PersonalInformationData.php b/src/Data/NominalAccounts/Beneficiaries/PersonalInformationData.php index 5e6fe14..c05cf28 100644 --- a/src/Data/NominalAccounts/Beneficiaries/PersonalInformationData.php +++ b/src/Data/NominalAccounts/Beneficiaries/PersonalInformationData.php @@ -1,11 +1,25 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class PersonalInformationData extends Data { + //
+ // public function __construct( public ?string $inn = null, public ?string $firstName = null, @@ -16,4 +30,5 @@ class PersonalInformationData extends Data public ?string $birthPlace = null, public ?string $snils = null, ) {} + //
} diff --git a/src/Data/NominalAccounts/Beneficiaries/Requisites/CreateRequisiteRequestData.php b/src/Data/NominalAccounts/Beneficiaries/Requisites/CreateRequisiteRequestData.php index 3a7a88b..a184e53 100644 --- a/src/Data/NominalAccounts/Beneficiaries/Requisites/CreateRequisiteRequestData.php +++ b/src/Data/NominalAccounts/Beneficiaries/Requisites/CreateRequisiteRequestData.php @@ -1,14 +1,28 @@ +// use Apoloz\AlfaBank\Data\NominalAccounts\Payouts\DigestSignatureData; use Spatie\LaravelData\Attributes\DataCollectionOf; use Spatie\LaravelData\Data; use Spatie\LaravelData\DataCollection; +//
+//
+// class CreateRequisiteRequestData extends Data { + //
+ // public function __construct( public string $type, #[DataCollectionOf(DigestSignatureData::class)] @@ -24,4 +38,5 @@ class CreateRequisiteRequestData extends Data public ?string $kpp = null, public ?bool $isMain = null, ) {} + //
} diff --git a/src/Data/NominalAccounts/Beneficiaries/Requisites/RequisiteData.php b/src/Data/NominalAccounts/Beneficiaries/Requisites/RequisiteData.php index 4172d43..790383a 100644 --- a/src/Data/NominalAccounts/Beneficiaries/Requisites/RequisiteData.php +++ b/src/Data/NominalAccounts/Beneficiaries/Requisites/RequisiteData.php @@ -1,16 +1,30 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class RequisiteData extends Data { + //
+ // public function __construct( - public ?string $id = null, public string $type, public bool $isMain, public bool $isActive, + public ?string $id = null, // Requisite ID might be generated on creation public ?string $pan = null, public ?string $phoneNumber = null, public ?string $bankId = null, @@ -20,4 +34,5 @@ class RequisiteData extends Data public ?string $inn = null, public ?string $kpp = null, ) {} + //
} diff --git a/src/Data/NominalAccounts/Beneficiaries/Requisites/UpdateRequisiteRequestData.php b/src/Data/NominalAccounts/Beneficiaries/Requisites/UpdateRequisiteRequestData.php index 3bd0801..62d7814 100644 --- a/src/Data/NominalAccounts/Beneficiaries/Requisites/UpdateRequisiteRequestData.php +++ b/src/Data/NominalAccounts/Beneficiaries/Requisites/UpdateRequisiteRequestData.php @@ -1,19 +1,33 @@ +// use Apoloz\AlfaBank\Data\NominalAccounts\Payouts\DigestSignatureData; use Spatie\LaravelData\Attributes\DataCollectionOf; use Spatie\LaravelData\Data; use Spatie\LaravelData\DataCollection; +//
+//
+// class UpdateRequisiteRequestData extends Data { + //
+ // public function __construct( #[DataCollectionOf(DigestSignatureData::class)] public DataCollection $digestSignatures, public ?string $pan = null, - public ?string $keyId = null, + public ?string $keyId = null, // Specific identifier for some requisite types public ?string $phoneNumber = null, public ?string $bankId = null, public ?string $accountNumber = null, @@ -21,4 +35,5 @@ class UpdateRequisiteRequestData extends Data public ?bool $isMain = null, public ?bool $isActive = null, ) {} + //
} diff --git a/src/Data/NominalAccounts/Beneficiaries/UpdateBeneficiaryRequestData.php b/src/Data/NominalAccounts/Beneficiaries/UpdateBeneficiaryRequestData.php index e2055f7..8f8f4e0 100644 --- a/src/Data/NominalAccounts/Beneficiaries/UpdateBeneficiaryRequestData.php +++ b/src/Data/NominalAccounts/Beneficiaries/UpdateBeneficiaryRequestData.php @@ -1,14 +1,28 @@ +// use Apoloz\AlfaBank\Data\NominalAccounts\Payouts\DigestSignatureData; use Spatie\LaravelData\Attributes\DataCollectionOf; use Spatie\LaravelData\Data; use Spatie\LaravelData\DataCollection; +//
+//
+// class UpdateBeneficiaryRequestData extends Data { + //
+ // public function __construct( public string $type, #[DataCollectionOf(DigestSignatureData::class)] @@ -24,4 +38,5 @@ class UpdateBeneficiaryRequestData extends Data public ?DataCollection $documents = null, public ?bool $isActive = null, ) {} + //
} diff --git a/src/Data/NominalAccounts/Beneficiaries/WalletBalanceData.php b/src/Data/NominalAccounts/Beneficiaries/WalletBalanceData.php index a8b36cf..ed7ff3c 100644 --- a/src/Data/NominalAccounts/Beneficiaries/WalletBalanceData.php +++ b/src/Data/NominalAccounts/Beneficiaries/WalletBalanceData.php @@ -1,14 +1,29 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class WalletBalanceData extends Data { + //
+ // public function __construct( public string $walletType, public string $freeBalance, public string $heldBalance, ) {} + //
} diff --git a/src/Data/NominalAccounts/BeneficiaryCollectionData.php b/src/Data/NominalAccounts/BeneficiaryCollectionData.php index 9f8c697..358439e 100644 --- a/src/Data/NominalAccounts/BeneficiaryCollectionData.php +++ b/src/Data/NominalAccounts/BeneficiaryCollectionData.php @@ -1,18 +1,33 @@ +// use Apoloz\AlfaBank\Data\PaginationData; -use Spatie\LaravelData\Data; use Spatie\LaravelData\Attributes\DataCollectionOf; +use Spatie\LaravelData\Data; use Spatie\LaravelData\DataCollection; +//
+//
+// class BeneficiaryCollectionData extends Data { + //
+ // public function __construct( public PaginationData $pagination, public string $accountNumber, #[DataCollectionOf(BeneficiaryData::class)] public DataCollection $beneficiaries ) {} + //
} diff --git a/src/Data/NominalAccounts/BeneficiaryData.php b/src/Data/NominalAccounts/BeneficiaryData.php index eeea621..7062129 100644 --- a/src/Data/NominalAccounts/BeneficiaryData.php +++ b/src/Data/NominalAccounts/BeneficiaryData.php @@ -1,18 +1,32 @@ +// +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; +//
+//
+// class BeneficiaryData extends Data { + //
+ // public function __construct( public string $beneficiaryId, public string $type, - public ?array $personalInformation = null, + public ?array $personalInformation = null, // Could be PersonalInformationData DTO public ?string $inn = null, public ?string $firstName = null, public ?string $lastName = null, @@ -21,11 +35,12 @@ class BeneficiaryData extends Data public ?string $countryOfTaxResidence = null, public ?string $phoneNumber = null, public ?string $email = null, - public ?array $addresses = null, - public ?array $documents = null, + public ?array $addresses = null, // Could be DataCollectionOf(AddressData::class) + public ?array $documents = null, // Could be DataCollectionOf(DocumentData::class) public ?bool $isActive = null, - public ?array $balances = null, + public ?array $balances = null, // Could be DataCollectionOf(WalletBalanceData::class) public ?bool $isBeneficiary = null, public ?bool $isVpr = null, ) {} + //
} \ No newline at end of file diff --git a/src/Data/NominalAccounts/CounterpartyRequestStatusData.php b/src/Data/NominalAccounts/CounterpartyRequestStatusData.php index 599c54d..e8216bd 100644 --- a/src/Data/NominalAccounts/CounterpartyRequestStatusData.php +++ b/src/Data/NominalAccounts/CounterpartyRequestStatusData.php @@ -1,11 +1,25 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class CounterpartyRequestStatusData extends Data { + //
+ // public function __construct( public string $requestId, public string $status, @@ -14,4 +28,5 @@ class CounterpartyRequestStatusData extends Data public ?string $description = null, public ?string $resultData = null, ) {} + //
} diff --git a/src/Data/NominalAccounts/Deals/DealCollectionData.php b/src/Data/NominalAccounts/Deals/DealCollectionData.php index 8b29ea3..5b640ce 100644 --- a/src/Data/NominalAccounts/Deals/DealCollectionData.php +++ b/src/Data/NominalAccounts/Deals/DealCollectionData.php @@ -1,17 +1,32 @@ +// use Apoloz\AlfaBank\Data\PaginationData; use Spatie\LaravelData\Attributes\DataCollectionOf; use Spatie\LaravelData\Data; use Spatie\LaravelData\DataCollection; +//
+//
+// class DealCollectionData extends Data { + //
+ // public function __construct( public PaginationData $pagination, #[DataCollectionOf(DealData::class)] public DataCollection $deals ) {} + //
} diff --git a/src/Data/NominalAccounts/Deals/DealData.php b/src/Data/NominalAccounts/Deals/DealData.php index 688af91..e0b9139 100644 --- a/src/Data/NominalAccounts/Deals/DealData.php +++ b/src/Data/NominalAccounts/Deals/DealData.php @@ -1,15 +1,30 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class DealData extends Data { + //
+ // public function __construct( public string $dealId, public string $status, public string $expireDate, public bool $isValid, ) {} + //
} diff --git a/src/Data/NominalAccounts/Deals/DealPayoffData.php b/src/Data/NominalAccounts/Deals/DealPayoffData.php index b25b224..728c1c5 100644 --- a/src/Data/NominalAccounts/Deals/DealPayoffData.php +++ b/src/Data/NominalAccounts/Deals/DealPayoffData.php @@ -1,11 +1,25 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class DealPayoffData extends Data { + //
+ // public function __construct( public string $stepId, public string $payoffId, @@ -15,4 +29,5 @@ class DealPayoffData extends Data public string $amount, public string $status, ) {} + //
} diff --git a/src/Data/NominalAccounts/Deals/DealRequestStatusData.php b/src/Data/NominalAccounts/Deals/DealRequestStatusData.php index aa394cf..8329036 100644 --- a/src/Data/NominalAccounts/Deals/DealRequestStatusData.php +++ b/src/Data/NominalAccounts/Deals/DealRequestStatusData.php @@ -1,14 +1,29 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class DealRequestStatusData extends Data { + //
+ // public function __construct( public string $requestId, public string $status, public ?string $description = null, ) {} + //
} diff --git a/src/Data/NominalAccounts/Deals/Steps/ContractorActionRequestData.php b/src/Data/NominalAccounts/Deals/Steps/ContractorActionRequestData.php index ef82631..9ba5b00 100644 --- a/src/Data/NominalAccounts/Deals/Steps/ContractorActionRequestData.php +++ b/src/Data/NominalAccounts/Deals/Steps/ContractorActionRequestData.php @@ -1,16 +1,30 @@ +// use Apoloz\AlfaBank\Data\NominalAccounts\Payouts\DigestSignatureData; use Spatie\LaravelData\Attributes\DataCollectionOf; use Spatie\LaravelData\Data; use Spatie\LaravelData\DataCollection; +//
+//
+// class ContractorActionRequestData extends Data { + //
+ // public function __construct( - public string $action, + public string $action, // e.g., ADD_AMOUNT, REMOVE_AMOUNT, SET_REQUISITE public string $dealId, public string $stepId, public string $beneficiaryId, @@ -19,8 +33,9 @@ class ContractorActionRequestData extends Data public ?string $amount = null, public ?string $purpose = null, #[DataCollectionOf(ContractorAmountData::class)] - public ?DataCollection $amounts = null, + public ?DataCollection $amounts = null, // For detailed amount distribution public ?bool $keepOnNominalAccount = null, public ?string $requisiteId = null, ) {} + //
} diff --git a/src/Data/NominalAccounts/Deals/Steps/ContractorAmountData.php b/src/Data/NominalAccounts/Deals/Steps/ContractorAmountData.php index 7312183..7a1d549 100644 --- a/src/Data/NominalAccounts/Deals/Steps/ContractorAmountData.php +++ b/src/Data/NominalAccounts/Deals/Steps/ContractorAmountData.php @@ -1,13 +1,28 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class ContractorAmountData extends Data { + //
+ // public function __construct( public ?string $walletType = null, public ?string $amount = null, ) {} + //
} diff --git a/src/Data/NominalAccounts/Deals/Steps/StepActionRequestData.php b/src/Data/NominalAccounts/Deals/Steps/StepActionRequestData.php index 154d2ec..176b480 100644 --- a/src/Data/NominalAccounts/Deals/Steps/StepActionRequestData.php +++ b/src/Data/NominalAccounts/Deals/Steps/StepActionRequestData.php @@ -1,20 +1,35 @@ +// use Apoloz\AlfaBank\Data\NominalAccounts\Payouts\DigestSignatureData; use Spatie\LaravelData\Attributes\DataCollectionOf; use Spatie\LaravelData\Data; use Spatie\LaravelData\DataCollection; +//
+//
+// class StepActionRequestData extends Data { + //
+ // public function __construct( public string $dealId, - public string $action, + public string $action, // e.g., COMPLETE, CANCEL #[DataCollectionOf(DigestSignatureData::class)] public DataCollection $digestSignatures, public ?string $stepId = null, public ?string $description = null, ) {} + //
} diff --git a/src/Data/NominalAccounts/Deals/Steps/StepActionResponseData.php b/src/Data/NominalAccounts/Deals/Steps/StepActionResponseData.php index 77ef9df..37ec448 100644 --- a/src/Data/NominalAccounts/Deals/Steps/StepActionResponseData.php +++ b/src/Data/NominalAccounts/Deals/Steps/StepActionResponseData.php @@ -1,11 +1,25 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class StepActionResponseData extends Data { + //
+ // public function __construct( public ?string $dealId = null, public ?string $stepId = null, @@ -14,4 +28,5 @@ class StepActionResponseData extends Data public ?string $status = null, public ?string $requestId = null, ) {} + //
} diff --git a/src/Data/NominalAccounts/Deals/Steps/StepContractorData.php b/src/Data/NominalAccounts/Deals/Steps/StepContractorData.php index ee8a73b..7071739 100644 --- a/src/Data/NominalAccounts/Deals/Steps/StepContractorData.php +++ b/src/Data/NominalAccounts/Deals/Steps/StepContractorData.php @@ -1,13 +1,27 @@ +// use Spatie\LaravelData\Attributes\DataCollectionOf; use Spatie\LaravelData\Data; use Spatie\LaravelData\DataCollection; +//
+//
+// class StepContractorData extends Data { + //
+ // public function __construct( public ?string $beneficiaryId = null, public ?string $role = null, @@ -18,4 +32,5 @@ class StepContractorData extends Data #[DataCollectionOf(ContractorAmountData::class)] public ?DataCollection $amounts = null, ) {} + //
} diff --git a/src/Data/NominalAccounts/Deals/Steps/StepData.php b/src/Data/NominalAccounts/Deals/Steps/StepData.php index 0ad7169..86185d1 100644 --- a/src/Data/NominalAccounts/Deals/Steps/StepData.php +++ b/src/Data/NominalAccounts/Deals/Steps/StepData.php @@ -1,11 +1,25 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class StepData extends Data { + //
+ // public function __construct( public string $stepId, public int $stepNumber, @@ -13,4 +27,5 @@ class StepData extends Data public string $status, public bool $isValid, ) {} + //
} diff --git a/src/Data/NominalAccounts/Deals/ValidateDealResponseData.php b/src/Data/NominalAccounts/Deals/ValidateDealResponseData.php index 7598ec9..4c0f160 100644 --- a/src/Data/NominalAccounts/Deals/ValidateDealResponseData.php +++ b/src/Data/NominalAccounts/Deals/ValidateDealResponseData.php @@ -1,14 +1,29 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class ValidateDealResponseData extends Data { + //
+ // public function __construct( public string $dealId, public bool $isValid, - public ?array $description = null, + public ?array $description = null, // Can be a string or array of error messages ) {} + //
} diff --git a/src/Data/NominalAccounts/Identification/AmountDistributionData.php b/src/Data/NominalAccounts/Identification/AmountDistributionData.php index 2af629b..87e5aa2 100644 --- a/src/Data/NominalAccounts/Identification/AmountDistributionData.php +++ b/src/Data/NominalAccounts/Identification/AmountDistributionData.php @@ -1,13 +1,28 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class AmountDistributionData extends Data { + //
+ // public function __construct( public string $beneficiaryId, public string $amount, ) {} + //
} diff --git a/src/Data/NominalAccounts/Identification/IdentifyRequestData.php b/src/Data/NominalAccounts/Identification/IdentifyRequestData.php index 031012e..410d12f 100644 --- a/src/Data/NominalAccounts/Identification/IdentifyRequestData.php +++ b/src/Data/NominalAccounts/Identification/IdentifyRequestData.php @@ -1,14 +1,28 @@ +// use Apoloz\AlfaBank\Data\NominalAccounts\Payouts\DigestSignatureData; use Spatie\LaravelData\Attributes\DataCollectionOf; use Spatie\LaravelData\Data; use Spatie\LaravelData\DataCollection; +//
+//
+// class IdentifyRequestData extends Data { + //
+ // public function __construct( public string $transactionId, public string $accountNumber, @@ -17,4 +31,5 @@ class IdentifyRequestData extends Data #[DataCollectionOf(DigestSignatureData::class)] public DataCollection $digestSignatures, ) {} + //
} diff --git a/src/Data/NominalAccounts/Identification/IdentifyResponseData.php b/src/Data/NominalAccounts/Identification/IdentifyResponseData.php index 5674c9e..21324bc 100644 --- a/src/Data/NominalAccounts/Identification/IdentifyResponseData.php +++ b/src/Data/NominalAccounts/Identification/IdentifyResponseData.php @@ -1,13 +1,28 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class IdentifyResponseData extends Data { + //
+ // public function __construct( public string $identifyId, public string $status, ) {} + //
} diff --git a/src/Data/NominalAccounts/Identification/PayerData.php b/src/Data/NominalAccounts/Identification/PayerData.php index d2ff841..5f95533 100644 --- a/src/Data/NominalAccounts/Identification/PayerData.php +++ b/src/Data/NominalAccounts/Identification/PayerData.php @@ -1,11 +1,25 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class PayerData extends Data { + //
+ // public function __construct( public ?string $inn = null, public ?string $kpp = null, @@ -15,4 +29,5 @@ class PayerData extends Data public ?string $bankBik = null, public ?string $bankName = null, ) {} + //
} diff --git a/src/Data/NominalAccounts/Identification/RefundUnidentifiedTransactionRequestData.php b/src/Data/NominalAccounts/Identification/RefundUnidentifiedTransactionRequestData.php index e0f2896..64401b7 100644 --- a/src/Data/NominalAccounts/Identification/RefundUnidentifiedTransactionRequestData.php +++ b/src/Data/NominalAccounts/Identification/RefundUnidentifiedTransactionRequestData.php @@ -1,18 +1,33 @@ +// use Apoloz\AlfaBank\Data\NominalAccounts\Payouts\DigestSignatureData; use Spatie\LaravelData\Attributes\DataCollectionOf; use Spatie\LaravelData\Data; use Spatie\LaravelData\DataCollection; +//
+//
+// class RefundUnidentifiedTransactionRequestData extends Data { + //
+ // public function __construct( public string $accountNumber, public string $id, #[DataCollectionOf(DigestSignatureData::class)] public DataCollection $digestSignatures, ) {} + //
} diff --git a/src/Data/NominalAccounts/Identification/RefundUnidentifiedTransactionResponseData.php b/src/Data/NominalAccounts/Identification/RefundUnidentifiedTransactionResponseData.php index ff02014..ab26b96 100644 --- a/src/Data/NominalAccounts/Identification/RefundUnidentifiedTransactionResponseData.php +++ b/src/Data/NominalAccounts/Identification/RefundUnidentifiedTransactionResponseData.php @@ -1,13 +1,28 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class RefundUnidentifiedTransactionResponseData extends Data { + //
+ // public function __construct( public string $refundId, public string $status, ) {} + //
} diff --git a/src/Data/NominalAccounts/Identification/UnidentifiedTransactionCollectionData.php b/src/Data/NominalAccounts/Identification/UnidentifiedTransactionCollectionData.php index 7eacf08..4e83a8f 100644 --- a/src/Data/NominalAccounts/Identification/UnidentifiedTransactionCollectionData.php +++ b/src/Data/NominalAccounts/Identification/UnidentifiedTransactionCollectionData.php @@ -1,13 +1,27 @@ +// use Spatie\LaravelData\Attributes\DataCollectionOf; use Spatie\LaravelData\Data; use Spatie\LaravelData\DataCollection; +//
+//
+// class UnidentifiedTransactionCollectionData extends Data { + //
+ // public function __construct( public int $offset, public int $limit, @@ -15,4 +29,5 @@ class UnidentifiedTransactionCollectionData extends Data #[DataCollectionOf(UnidentifiedTransactionData::class)] public ?DataCollection $unidentifiedTransactions = null, ) {} + //
} diff --git a/src/Data/NominalAccounts/Identification/UnidentifiedTransactionData.php b/src/Data/NominalAccounts/Identification/UnidentifiedTransactionData.php index 138af10..224fa9a 100644 --- a/src/Data/NominalAccounts/Identification/UnidentifiedTransactionData.php +++ b/src/Data/NominalAccounts/Identification/UnidentifiedTransactionData.php @@ -1,17 +1,32 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class UnidentifiedTransactionData extends Data { + //
+ // public function __construct( public string $id, public string $amount, public ?string $documentNumber = null, public ?string $date = null, public ?string $purpose = null, - public ?PayerData $payer = null, + public ?PayerData $payer = null, // Nested DTO for payer details ) {} + //
} diff --git a/src/Data/NominalAccounts/NominalAccountData.php b/src/Data/NominalAccounts/NominalAccountData.php index c8f35e1..b1d38fe 100644 --- a/src/Data/NominalAccounts/NominalAccountData.php +++ b/src/Data/NominalAccounts/NominalAccountData.php @@ -1,13 +1,28 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class NominalAccountData extends Data { + //
+ // public function __construct( public string $accountNumber, public bool $isActive, ) {} + //
} diff --git a/src/Data/NominalAccounts/Payouts/DetailedPayoffData.php b/src/Data/NominalAccounts/Payouts/DetailedPayoffData.php index bda2158..6234d03 100644 --- a/src/Data/NominalAccounts/Payouts/DetailedPayoffData.php +++ b/src/Data/NominalAccounts/Payouts/DetailedPayoffData.php @@ -1,20 +1,37 @@ +// +use Apoloz\AlfaBank\Data\Shared\PayerInfoData; +use Apoloz\AlfaBank\Data\Shared\RecipientInfoData; use Spatie\LaravelData\Data; +//
+//
+// class DetailedPayoffData extends Data { + //
+ // public function __construct( public ?string $dealId = null, public ?string $stepId = null, public ?string $paymentType = null, public ?string $requisiteType = null, - public ?array $payer = null, - public ?array $recipient = null, + public ?PayerInfoData $payer = null, + public ?RecipientInfoData $recipient = null, public ?string $amount = null, public ?string $purpose = null, public ?string $status = null, ) {} + //
} diff --git a/src/Data/NominalAccounts/Payouts/DigestSignatureData.php b/src/Data/NominalAccounts/Payouts/DigestSignatureData.php index a61ba1a..a8ecce1 100644 --- a/src/Data/NominalAccounts/Payouts/DigestSignatureData.php +++ b/src/Data/NominalAccounts/Payouts/DigestSignatureData.php @@ -1,13 +1,28 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class DigestSignatureData extends Data { + //
+ // public function __construct( public string $base64Encoded, public string $signatureType, ) {} + //
} diff --git a/src/Data/NominalAccounts/Payouts/PayoffRequestData.php b/src/Data/NominalAccounts/Payouts/PayoffRequestData.php index 8d5e638..09b71ba 100644 --- a/src/Data/NominalAccounts/Payouts/PayoffRequestData.php +++ b/src/Data/NominalAccounts/Payouts/PayoffRequestData.php @@ -1,13 +1,27 @@ +// use Spatie\LaravelData\Attributes\DataCollectionOf; use Spatie\LaravelData\Data; use Spatie\LaravelData\DataCollection; +//
+//
+// class PayoffRequestData extends Data { + //
+ // public function __construct( public string $type, public string $accountNumber, @@ -18,7 +32,8 @@ class PayoffRequestData extends Data #[DataCollectionOf(DigestSignatureData::class)] public DataCollection $digestSignatures, public ?string $deponent = null, - public ?array $requisites = null, - public ?array $tax = null, + public ?array $requisites = null, // Can be nested DTOs if defined + public ?array $tax = null, // Can be nested DTOs if defined ) {} + //
} diff --git a/src/Data/NominalAccounts/Payouts/PayoffResponseData.php b/src/Data/NominalAccounts/Payouts/PayoffResponseData.php index 8af7818..b37c6af 100644 --- a/src/Data/NominalAccounts/Payouts/PayoffResponseData.php +++ b/src/Data/NominalAccounts/Payouts/PayoffResponseData.php @@ -1,14 +1,29 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class PayoffResponseData extends Data { + //
+ // public function __construct( public string $payoffId, public string $status, public ?string $description = null, ) {} + //
} \ No newline at end of file diff --git a/src/Data/NominalAccounts/Payouts/PayoutRequisiteData.php b/src/Data/NominalAccounts/Payouts/PayoutRequisiteData.php index c417241..fe6a541 100644 --- a/src/Data/NominalAccounts/Payouts/PayoutRequisiteData.php +++ b/src/Data/NominalAccounts/Payouts/PayoutRequisiteData.php @@ -1,11 +1,25 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class PayoutRequisiteData extends Data { + //
+ // public function __construct( public string $type, public ?string $requisiteId = null, @@ -15,4 +29,5 @@ class PayoutRequisiteData extends Data public ?string $accountNumber = null, public ?string $bik = null, ) {} + //
} diff --git a/src/Data/NominalAccounts/Payouts/RecipientData.php b/src/Data/NominalAccounts/Payouts/RecipientData.php index 7d70475..968e6f4 100644 --- a/src/Data/NominalAccounts/Payouts/RecipientData.php +++ b/src/Data/NominalAccounts/Payouts/RecipientData.php @@ -1,11 +1,25 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class RecipientData extends Data { + //
+ // public function __construct( public string $type, public ?string $name = null, @@ -13,4 +27,5 @@ class RecipientData extends Data public ?string $kpp = null, public ?string $beneficiaryId = null, ) {} + //
} diff --git a/src/Data/NominalAccounts/Payouts/TaxRefundRequestData.php b/src/Data/NominalAccounts/Payouts/TaxRefundRequestData.php index b6ec26d..2df5c69 100644 --- a/src/Data/NominalAccounts/Payouts/TaxRefundRequestData.php +++ b/src/Data/NominalAccounts/Payouts/TaxRefundRequestData.php @@ -1,14 +1,28 @@ +// use Apoloz\AlfaBank\Data\NominalAccounts\Payouts\DigestSignatureData; use Spatie\LaravelData\Attributes\DataCollectionOf; use Spatie\LaravelData\Data; use Spatie\LaravelData\DataCollection; +//
+//
+// class TaxRefundRequestData extends Data { + //
+ // public function __construct( public string $beneficiaryId, public string $amount, @@ -18,4 +32,6 @@ class TaxRefundRequestData extends Data public DataCollection $digestSignatures, public ?string $requisiteId = null, ) {} + //
} +//
diff --git a/src/Data/NominalAccounts/Payouts/TaxRefundResponseData.php b/src/Data/NominalAccounts/Payouts/TaxRefundResponseData.php index 376218c..f5c6b8b 100644 --- a/src/Data/NominalAccounts/Payouts/TaxRefundResponseData.php +++ b/src/Data/NominalAccounts/Payouts/TaxRefundResponseData.php @@ -1,14 +1,30 @@ +// use Spatie\LaravelData\Data; +// +//
+// class TaxRefundResponseData extends Data { + //
+ // public function __construct( public string $taxRefundId, public string $status, public ?string $description = null, ) {} + //
} +//
diff --git a/src/Data/NominalAccounts/UniversalRecipients/CreateUniversalRecipientRequestData.php b/src/Data/NominalAccounts/UniversalRecipients/CreateUniversalRecipientRequestData.php index 5627f4f..a0d2962 100644 --- a/src/Data/NominalAccounts/UniversalRecipients/CreateUniversalRecipientRequestData.php +++ b/src/Data/NominalAccounts/UniversalRecipients/CreateUniversalRecipientRequestData.php @@ -1,18 +1,33 @@ +// use Apoloz\AlfaBank\Data\NominalAccounts\Payouts\DigestSignatureData; use Spatie\LaravelData\Attributes\DataCollectionOf; use Spatie\LaravelData\Data; use Spatie\LaravelData\DataCollection; +// +//
+// class CreateUniversalRecipientRequestData extends Data { + //
+ // public function __construct( public string $accountNumber, public string $type, #[DataCollectionOf(DigestSignatureData::class)] public DataCollection $digestSignatures, ) {} + //
} diff --git a/src/Data/NominalAccounts/UniversalRecipients/UniversalRecipientData.php b/src/Data/NominalAccounts/UniversalRecipients/UniversalRecipientData.php index 6fff41c..fbe54b8 100644 --- a/src/Data/NominalAccounts/UniversalRecipients/UniversalRecipientData.php +++ b/src/Data/NominalAccounts/UniversalRecipients/UniversalRecipientData.php @@ -1,15 +1,30 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class UniversalRecipientData extends Data { + //
+ // public function __construct( public string $universalRecipientId, public bool $isBeneficiary, public string $type, public bool $isActive, ) {} + //
} diff --git a/src/Data/NominalAccounts/UniversalRecipients/UniversalRecipientResponseData.php b/src/Data/NominalAccounts/UniversalRecipients/UniversalRecipientResponseData.php index e269134..e87b09b 100644 --- a/src/Data/NominalAccounts/UniversalRecipients/UniversalRecipientResponseData.php +++ b/src/Data/NominalAccounts/UniversalRecipients/UniversalRecipientResponseData.php @@ -1,9 +1,20 @@ +// use Apoloz\AlfaBank\Data\NominalAccounts\Beneficiaries\CreateBeneficiaryResponseData; -use Spatie\LaravelData\Data; +use Spatie\LaravelData\Data; // Included for clarity, though inherited from base DTO. +//
-// Reuse response structure if identical (requestId, status) +//
+// class UniversalRecipientResponseData extends CreateBeneficiaryResponseData {} diff --git a/src/Data/Orders/OrderRequestData.php b/src/Data/Orders/OrderRequestData.php new file mode 100644 index 0000000..7aaaea3 --- /dev/null +++ b/src/Data/Orders/OrderRequestData.php @@ -0,0 +1,12 @@ + +// use Spatie\LaravelData\Data; +//
+//
+// class PaginationData extends Data { + //
+ // public function __construct( public int $offset, public int $limit, public int $totalCount ) {} + //
} diff --git a/src/Data/Partner/PartnerApplicationData.php b/src/Data/Partner/PartnerApplicationData.php new file mode 100644 index 0000000..7793070 --- /dev/null +++ b/src/Data/Partner/PartnerApplicationData.php @@ -0,0 +1,14 @@ + +// use Apoloz\AlfaBank\Data\NominalAccounts\Payouts\DigestSignatureData; use Spatie\LaravelData\Attributes\DataCollectionOf; use Spatie\LaravelData\Data; use Spatie\LaravelData\DataCollection; +//
+//
+// class CreateRubPaymentRequestData extends Data { + //
+ // public function __construct( public string $externalId, public float $amount, @@ -25,7 +39,7 @@ class CreateRubPaymentRequestData extends Data public string $payerName, public string $purpose, public string $priority = '5', - public string $deliveryKind = 'электронно', + public string $deliveryKind = 'электронно', // "electronically" public string $urgencyCode = 'NORMAL', public ?string $payerKpp = null, public ?string $payeeKpp = null, @@ -33,7 +47,8 @@ class CreateRubPaymentRequestData extends Data public ?string $payeeBankCorrAccount = null, #[DataCollectionOf(DigestSignatureData::class)] public ?DataCollection $digestSignatures = null, - public ?array $departmentalInfo = null, - public ?array $vat = null, + public ?array $departmentalInfo = null, // Can be a nested DTO + public ?array $vat = null, // Can be a nested DTO ) {} + //
} diff --git a/src/Data/Sandbox/SandboxTestResponseData.php b/src/Data/Sandbox/SandboxTestResponseData.php new file mode 100644 index 0000000..2b009c1 --- /dev/null +++ b/src/Data/Sandbox/SandboxTestResponseData.php @@ -0,0 +1,12 @@ + +// +use Spatie\LaravelData\Data; +//
+ +//
+// +class PayerInfoData extends Data +{ + //
+ // + public function __construct( + public string $accountNumber, + public string $name, + public string $inn, + public string $kpp, + ) {} + //
+} diff --git a/src/Data/Shared/RecipientInfoData.php b/src/Data/Shared/RecipientInfoData.php new file mode 100644 index 0000000..88ab288 --- /dev/null +++ b/src/Data/Shared/RecipientInfoData.php @@ -0,0 +1,31 @@ + +// +use Spatie\LaravelData\Data; +//
+ +//
+// +class RecipientInfoData extends Data +{ + //
+ // + public function __construct( + public ?string $beneficiaryId = null, + public ?string $accountNumber = null, + public ?string $bik = null, + public ?string $name = null, + public ?string $inn = null, + ) {} + //
+} diff --git a/src/Data/Statement/AmountData.php b/src/Data/Statement/AmountData.php new file mode 100644 index 0000000..6c3f2f4 --- /dev/null +++ b/src/Data/Statement/AmountData.php @@ -0,0 +1,28 @@ + +// +use Spatie\LaravelData\Data; +//
+ +//
+// +class AmountData extends Data +{ + //
+ // + public function __construct( + public float $amount, + public string $currencyName, + ) {} + //
+} diff --git a/src/Data/Statement/RurTransferData.php b/src/Data/Statement/RurTransferData.php new file mode 100644 index 0000000..5e8a99c --- /dev/null +++ b/src/Data/Statement/RurTransferData.php @@ -0,0 +1,44 @@ + +// +use Spatie\LaravelData\Data; +//
+ +//
+// +class RurTransferData extends Data +{ + //
+ // + public function __construct( + public ?string $payeeAccount = null, + public ?string $payeeBankBic = null, + public ?string $payeeBankCorrAccount = null, + public ?string $payeeBankName = null, + public ?string $payeeInn = null, + public ?string $payeeKpp = null, + public ?string $payeeName = null, + public ?string $payerAccount = null, + public ?string $payerBankBic = null, + public ?string $payerBankCorrAccount = null, + public ?string $payerBankName = null, + public ?string $payerInn = null, + public ?string $payerKpp = null, + public ?string $payerName = null, + public ?string $paymentKind110 = null, + public ?string $priority = null, + public ?string $receiptDate = null, + public ?string $valueDate = null, + ) {} + //
+} diff --git a/src/Data/Statement/TransactionCollectionData.php b/src/Data/Statement/TransactionCollectionData.php new file mode 100644 index 0000000..4875239 --- /dev/null +++ b/src/Data/Statement/TransactionCollectionData.php @@ -0,0 +1,32 @@ + +// +use Apoloz\AlfaBank\Data\PaginationData; +use Spatie\LaravelData\Attributes\DataCollectionOf; +use Spatie\LaravelData\Data; +use Spatie\LaravelData\DataCollection; +//
+ +//
+// +class TransactionCollectionData extends Data +{ + //
+ // + public function __construct( + public PaginationData $pagination, + #[DataCollectionOf(TransactionData::class)] + public DataCollection $transactions + ) {} + //
+} diff --git a/src/Data/Statement/TransactionData.php b/src/Data/Statement/TransactionData.php new file mode 100644 index 0000000..1f59d2f --- /dev/null +++ b/src/Data/Statement/TransactionData.php @@ -0,0 +1,36 @@ + +// +use Spatie\LaravelData\Data; +//
+ +//
+// +class TransactionData extends Data +{ + //
+ // + public function __construct( + public string $uuid, + public string $transactionId, + public string $operationDate, + public string $number, + public string $direction, // DEBIT, CREDIT + public AmountData $amount, // Nested DTO for amount + public AmountData $amountRub, // Nested DTO for amount in RUB + public ?string $paymentPurpose = null, + public ?RurTransferData $rurTransfer = null, // Nested DTO for RUR specific transfer details + public ?string $correspondingAccount = null, + ) {} + //
+} diff --git a/src/Data/Webhooks/Payloads/BaasWebhookData.php b/src/Data/Webhooks/Payloads/BaasWebhookData.php new file mode 100644 index 0000000..0f5cf3e --- /dev/null +++ b/src/Data/Webhooks/Payloads/BaasWebhookData.php @@ -0,0 +1,23 @@ + +// enum Environment: string { + //
+ // case PRODUCTION = 'https://baas.alfabank.ru'; case SANDBOX = 'https://sandbox.alfabank.ru'; + //
+ + //
+ // + case SBP_PRODUCTION = 'https://pay.alfabank.ru/fsCryptoProxy'; + case SBP_SANDBOX = 'https://217.12.103.132:2443/fsCryptoProxy'; + //
} +//
diff --git a/src/Exceptions/AlfaBankException.php b/src/Exceptions/AlfaBankException.php new file mode 100644 index 0000000..c353692 --- /dev/null +++ b/src/Exceptions/AlfaBankException.php @@ -0,0 +1,50 @@ + +// +use Saloon\Http\Response; +// + +//
+// +class AlfaBankException extends \Exception +{ + //
+ // + public function __construct( + protected Response $response, + protected ?string $errorCode = null, + protected ?string $errorDescription = null + ) { + $message = "Alfa Bank API Error: [{$errorCode}] {$errorDescription}"; + parent::__construct($message, $response->status()); + } + //
+ + //
+ // + public function getResponse(): Response + { + return $this->response; + } + + public function getErrorCode(): ?string + { + return $this->errorCode; + } + + public function getErrorDescription(): ?string + { + return $this->errorDescription; + } + //
+} diff --git a/src/Http/Middleware/AlfaResponseMiddleware.php b/src/Http/Middleware/AlfaResponseMiddleware.php new file mode 100644 index 0000000..391b74a --- /dev/null +++ b/src/Http/Middleware/AlfaResponseMiddleware.php @@ -0,0 +1,61 @@ + +// +use Apoloz\AlfaBank\Exceptions\AlfaBankException; +use Saloon\Http\Response; +//
+ +//
+// +class AlfaResponseMiddleware +{ + //
+ // + public function __invoke(Response $response): void + { + $contentType = $response->header('Content-Type') ?? ''; + + if (str_contains($contentType, 'application/json')) { + $data = $response->json(); + + // 1. Handle standard BaaS errors + if (isset($data['error'])) { + throw new AlfaBankException( + $response, + $data['error'], + $data['error_description'] ?? 'No description' + ); + } + + // 2. Handle SBP specific errors + if (isset($data['ErrorCode']) && (string)$data['ErrorCode'] !== '0') { + throw new AlfaBankException( + $response, + (string)$data['ErrorCode'], + $data['message'] ?? 'No message' + ); + } + } + + // 3. Handle generic HTTP errors without body or non-json body + if ($response->failed()) { + throw new AlfaBankException( + $response, + 'HTTP_' . $response->status(), + 'Request failed with status ' . $response->status() + ); + } + } + //
+} +//
diff --git a/src/Http/Middleware/LoggingMiddleware.php b/src/Http/Middleware/LoggingMiddleware.php new file mode 100644 index 0000000..91b7206 --- /dev/null +++ b/src/Http/Middleware/LoggingMiddleware.php @@ -0,0 +1,79 @@ + +// +use Psr\Log\LoggerInterface; +use Saloon\Http\PendingRequest; +use Saloon\Http\Response; +// + +//
+// +class LoggingMiddleware +{ + //
+ // + public function __construct(protected LoggerInterface $logger) {} + //
+ + //
+ // + public function onRequest(PendingRequest $request): void + { + $this->logger->info("AlfaBank Request: {$request->getMethod()->value} {$request->getRequest()->resolveEndpoint()}", [ + 'headers' => $this->maskHeaders($request->headers()->all()), + 'body' => $this->maskBody($request->body()?->all()), + ]); + } + //
+ + //
+ // + public function onResponse(Response $response): void + { + $this->logger->info("AlfaBank Response: {$response->status()}", [ + 'body' => $this->maskBody($response->json()), + ]); + } + //
+ + //
+ // + protected function maskHeaders(array $headers): array + { + if (isset($headers['Authorization'])) { + $headers['Authorization'] = 'Bearer *****'; + } + return $headers; + } + //
+ + //
+ // + protected function maskBody(mixed $body): mixed + { + if (!is_array($body)) return $body; + + $sensitiveKeys = ['pan', 'cvc', 'digestSignatures', 'client_secret', 'refresh_token', 'access_token']; + + foreach ($body as $key => $value) { + if (in_array($key, $sensitiveKeys, true)) { + $body[$key] = '*****'; + } elseif (is_array($value)) { + $body[$key] = $this->maskBody($value); + } + } + return $body; + } + //
+} +//
diff --git a/src/Http/Middleware/RequestIdMiddleware.php b/src/Http/Middleware/RequestIdMiddleware.php new file mode 100644 index 0000000..26d5729 --- /dev/null +++ b/src/Http/Middleware/RequestIdMiddleware.php @@ -0,0 +1,32 @@ + +// +use Apoloz\AlfaBank\Utils\Uuid; +use Saloon\Http\PendingRequest; +// + +//
+// +class RequestIdMiddleware +{ + //
+ // + public function __invoke(PendingRequest $pendingRequest): void + { + if (!$pendingRequest->headers()->get('X-Request-ID')) { + $pendingRequest->headers()->add('X-Request-ID', Uuid::v4()); + } + } + //
+} +//
diff --git a/src/Http/Pagination/AlfaOffsetPaginator.php b/src/Http/Pagination/AlfaOffsetPaginator.php new file mode 100644 index 0000000..9edfdd4 --- /dev/null +++ b/src/Http/Pagination/AlfaOffsetPaginator.php @@ -0,0 +1,62 @@ +getLimit(); + if (is_int($limit)) { + $this->perPageLimit = $limit; + } + } + } + + protected function isLastPage(Response $response): bool + { + $items = $this->getPageItems($response, $this->request); + + return count($items) < $this->perPageLimit; + } + + protected function getPageItems(Response $response, Request $request): array + { + // 1. Try to extract items from DTO if available + if (method_exists($request, 'createDtoFromResponse')) { + try { + $dto = $response->dto(); + if ($dto instanceof DataCollection) { + return $dto->toCollection()->all(); + } + } catch (\Throwable $e) { + // Fallback to raw json if DTO fails + echo "DTO Error: " . $e->getMessage() . "\n"; + } + } + + // 2. Fallback to raw JSON strategies + $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 it's a direct array (some endpoints) + if (array_is_list($json)) return $json; + + return []; + } +} \ No newline at end of file diff --git a/src/Laravel/AlfaBankServiceProvider.php b/src/Laravel/AlfaBankServiceProvider.php index 8e9b9c4..693346a 100644 --- a/src/Laravel/AlfaBankServiceProvider.php +++ b/src/Laravel/AlfaBankServiceProvider.php @@ -1,46 +1,120 @@ +use Apoloz\AlfaBank\Contracts\EncryptorInterface; +use Apoloz\AlfaBank\Contracts\SignerInterface; +use Apoloz\AlfaBank\Core\AlfaBankConnector; +use Apoloz\AlfaBank\Core\AlfaConfig; use Apoloz\AlfaBank\Enums\Environment; +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; +// class AlfaBankServiceProvider extends ServiceProvider { + /** + * Register any application services. + */ public function register(): void { $this->mergeConfigFrom(__DIR__ . '/Config/alfa-bank.php', 'alfa-bank'); - $this->app->singleton(AlfaBankConnector::class, function ($app) { - $config = $app['config']['alfa-bank.baas']; + $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'], + privateKeyPath: $conf['private_key_path'], + caBundlePath: $conf['ca_bundle'] ?? null, + clientId: $conf['client_id'], + clientSecret: $conf['client_secret'], + signingCertificatePath: $conf['signing_certificate_path'] ?? null, + signingPrivateKeyPath: $conf['signing_private_key_path'] ?? null, + ); + }); + + $this->app->bind(SignerInterface::class, function ($app) { + /** @var AlfaConfig $config */ + $config = $app->make('alfa.baas.config'); + $cert = $config->signingCertificatePath ?: $config->certificatePath; + $key = $config->signingPrivateKeyPath ?: $config->privateKeyPath; + return new OpenSslCmsSigner($cert, $key); + }); + + $this->app->bind(EncryptorInterface::class, function ($app) { + $key = $app['config']['alfa-bank.encryption.card_public_key'] ?? ''; + return new PhpseclibCardEncryptor($key); + }); + + $this->app->singleton(AlfaBankConnector::class, function ($app) { return new AlfaBankConnector( - certificatePath: $config['certificate_path'], - privateKeyPath: $config['private_key_path'], - environment: $config['environment'] === 'production' ? Environment::PRODUCTION : Environment::SANDBOX + alfaConfig: $app->make('alfa.baas.config'), + signer: $app->make(SignerInterface::class) + ); + }); + $this->app->alias(AlfaBankConnector::class, 'alfa.baas'); + + $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'], + privateKeyPath: $conf['private_key_path'], + caBundlePath: $conf['ca_bundle'] ?? null, + signingCertificatePath: $conf['signing_certificate_path'] ?? null, + signingPrivateKeyPath: $conf['signing_private_key_path'] ?? null, ); }); $this->app->singleton(SbpConnector::class, function ($app) { $config = $app['config']['alfa-bank.sbp']; - return new SbpConnector( - certificatePath: $config['certificate_path'], - privateKeyPath: $config['private_key_path'], - keyNameAlias: $config['key_name_alias'], - environment: $config['environment'] === 'production' ? Environment::PRODUCTION : Environment::SANDBOX + alfaConfig: $app->make('alfa.sbp.config'), + keyNameAlias: $config['key_name_alias'] ); }); + $this->app->alias(SbpConnector::class, 'alfa.sbp'); } + /** + * Bootstrap any application services. + */ public function boot(): void { if ($this->app->runningInConsole()) { $this->publishes([ __DIR__ . '/Config/alfa-bank.php' => config_path('alfa-bank.php'), ], 'alfa-bank-config'); + + $this->commands([ + \Apoloz\AlfaBank\Laravel\Commands\CheckConnectionCommand::class, + ]); + } + + if ($this->app->bound('router')) { + $router = $this->app->make('router'); + $router->aliasMiddleware('alfa.webhook', VerifyAlfaWebhook::class); + + Route::macro('alfaWebhooks', function (string $url) { + return Route::post($url, AlfaWebhookController::class) + ->name('alfa.webhooks') + ->middleware('alfa.webhook'); + }); } } } diff --git a/src/Laravel/Commands/CheckConnectionCommand.php b/src/Laravel/Commands/CheckConnectionCommand.php new file mode 100644 index 0000000..e63586b --- /dev/null +++ b/src/Laravel/Commands/CheckConnectionCommand.php @@ -0,0 +1,114 @@ + +// +use Apoloz\AlfaBank\Core\AlfaBankConnector; +use Apoloz\AlfaBank\Sbp\SbpConnector; +use Illuminate\Console\Command; +// + +//
+// +class CheckConnectionCommand extends Command +{ + //
+ // + protected $signature = 'alfa:check'; + protected $description = 'Verify Alfa Bank SDK configuration and mTLS certificates'; + //
+ + //
+ // + public function handle(): void + { + $this->info('Starting Alfa Bank SDK Diagnostics...'); + + $this->checkConnector('BaaS (Nominal Accounts)', 'alfa-bank.baas'); + $this->checkConnector('SBP (Faster Payments)', 'alfa-bank.sbp'); + } + //
+ + //
+ // + private function checkConnector(string $name, string $configKey): void + { + $this->newLine(); + $this->info("Checking {$name} configuration..."); + + $certPath = (string) config("{$configKey}.certificate_path"); + $keyPath = (string) config("{$configKey}.private_key_path"); + + // 1. Check Files + if (!file_exists($certPath)) { + $this->error("❌ Certificate not found at: {$certPath}"); + return; + } else { + $this->comment("✅ Certificate found"); + } + + if (!file_exists($keyPath)) { + $this->error("❌ Private Key not found at: {$keyPath}"); + return; + } else { + $this->comment("✅ Private Key found"); + } + + // 2. Validate Cert Expiry + $certContent = (string) file_get_contents($certPath); + $certInfo = openssl_x509_parse($certContent); + + if ($certInfo && isset($certInfo['validTo_time_t'])) { + $validTo = new \DateTimeImmutable('@' . $certInfo['validTo_time_t']); + $now = new \DateTimeImmutable(); + if ($validTo < $now) { + $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)"); + } + } else { + $this->warn("⚠️ Could not parse certificate date."); + } + + // 3. Attempt Connection (Ping) + $this->comment("Attempting TLS Handshake..."); + + try { + // We resolve the connector from container to use the config + $connector = $name === 'BaaS (Nominal Accounts)' + ? app(AlfaBankConnector::class) + : app(SbpConnector::class); + + // 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 { + protected \Saloon\Enums\Method $method = \Saloon\Enums\Method::GET; + public function resolveEndpoint(): string { return '/'; } + }); + + // If we are here, SSL worked! + $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()); + } 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()})"); + } + } + } + //
+} +//
diff --git a/src/Laravel/Config/alfa-bank.php b/src/Laravel/Config/alfa-bank.php index 8391391..7ac7163 100644 --- a/src/Laravel/Config/alfa-bank.php +++ b/src/Laravel/Config/alfa-bank.php @@ -1,15 +1,62 @@ +// return [ + //
+ // 'baas' => [ 'certificate_path' => env('ALFA_BAAS_CERT_PATH'), 'private_key_path' => env('ALFA_BAAS_KEY_PATH'), + 'signing_certificate_path' => env('ALFA_BAAS_SIGNING_CERT_PATH'), + 'signing_private_key_path' => env('ALFA_BAAS_SIGNING_KEY_PATH'), + 'client_id' => env('ALFA_BAAS_CLIENT_ID'), + 'client_secret' => env('ALFA_BAAS_CLIENT_SECRET'), 'environment' => env('ALFA_BAAS_ENV', 'sandbox'), // sandbox or production ], + //
+ + //
+ // 'sbp' => [ 'certificate_path' => env('ALFA_SBP_CERT_PATH'), 'private_key_path' => env('ALFA_SBP_KEY_PATH'), + 'signing_certificate_path' => env('ALFA_SBP_SIGNING_CERT_PATH'), + 'signing_private_key_path' => env('ALFA_SBP_SIGNING_KEY_PATH'), 'key_name_alias' => env('ALFA_SBP_KEY_ALIAS'), 'environment' => env('ALFA_SBP_ENV', 'sandbox'), ], + + /* + |-------------------------------------------------------------------------- + | Webhook Verification + |-------------------------------------------------------------------------- + | + | Configure credentials for incoming webhooks if you use Basic Auth. + | + */ + 'webhook' => [ + 'user' => env('ALFA_WEBHOOK_USER'), + 'password' => env('ALFA_WEBHOOK_PASSWORD'), + ], + + /* + |-------------------------------------------------------------------------- + | Encryption Keys + |-------------------------------------------------------------------------- + | + | Public key for Card PAN encryption (Nominal Accounts). + | + */ + 'encryption' => [ + 'card_public_key' => env('ALFA_CARD_PUBLIC_KEY'), + ], ]; +//
diff --git a/src/Laravel/Events/AlfaWebhookReceived.php b/src/Laravel/Events/AlfaWebhookReceived.php new file mode 100644 index 0000000..1678e31 --- /dev/null +++ b/src/Laravel/Events/AlfaWebhookReceived.php @@ -0,0 +1,16 @@ +json()->all(); + + // BaaS often sends an array of events + if (array_is_list($payload)) { + foreach ($payload as $event) { + $this->processEvent($event); + } + } else { + $this->processEvent($payload); + } + + return response()->json(['status' => 'ok']); + } + + protected function processEvent(array $payload): void + { + $webhookDto = WebhookFactory::create($payload); + + AlfaWebhookReceived::dispatch($webhookDto); + } +} diff --git a/src/Laravel/Middleware/VerifyAlfaWebhook.php b/src/Laravel/Middleware/VerifyAlfaWebhook.php new file mode 100644 index 0000000..dc06ef9 --- /dev/null +++ b/src/Laravel/Middleware/VerifyAlfaWebhook.php @@ -0,0 +1,27 @@ +getUser() !== $user || $request->getPassword() !== $password) { + return response()->json(['error' => 'Unauthorized Webhook'], 401); + } + } + + return $next($request); + } +} diff --git a/src/Requests/Accounts/GetAccountsRequest.php b/src/Requests/Accounts/GetAccountsRequest.php new file mode 100644 index 0000000..7bef1a4 --- /dev/null +++ b/src/Requests/Accounts/GetAccountsRequest.php @@ -0,0 +1,41 @@ +limit = $limit; + $this->offset = $offset; + } + + public function resolveEndpoint(): string + { + 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); + } +} \ No newline at end of file diff --git a/src/Requests/Aff/CheckComplianceRequest.php b/src/Requests/Aff/CheckComplianceRequest.php new file mode 100644 index 0000000..473149b --- /dev/null +++ b/src/Requests/Aff/CheckComplianceRequest.php @@ -0,0 +1,36 @@ + $this->inn, + 'name' => $this->name, + ]; + } + + public function createDtoFromResponse(Response $response): AffResponseData + { + return AffResponseData::from($response->json()); + } +} diff --git a/src/Requests/Aff/CreateRegisterRequest.php b/src/Requests/Aff/CreateRegisterRequest.php new file mode 100644 index 0000000..5342481 --- /dev/null +++ b/src/Requests/Aff/CreateRegisterRequest.php @@ -0,0 +1,37 @@ +data->toArray(); + } + + public function createDtoFromResponse(Response $response): AffResponseData + { + return AffResponseData::from($response->json()); + } +} \ No newline at end of file diff --git a/src/Requests/Aff/DownloadRegisterDocumentsRequest.php b/src/Requests/Aff/DownloadRegisterDocumentsRequest.php new file mode 100644 index 0000000..2619ff5 --- /dev/null +++ b/src/Requests/Aff/DownloadRegisterDocumentsRequest.php @@ -0,0 +1,30 @@ +registerId}/documents"; + } + + public function createDtoFromResponse(Response $response): DownloadedFileData + { + return DownloadedFileData::from([ + 'content' => $response->body(), + 'size' => strlen($response->body()), + ]); + } +} \ No newline at end of file diff --git a/src/Requests/Aff/GetPaymentStatusRequest.php b/src/Requests/Aff/GetPaymentStatusRequest.php new file mode 100644 index 0000000..388092e --- /dev/null +++ b/src/Requests/Aff/GetPaymentStatusRequest.php @@ -0,0 +1,27 @@ +paymentId}"; + } + + public function createDtoFromResponse(Response $response): AffResponseData + { + return AffResponseData::from($response->json()); + } +} \ No newline at end of file diff --git a/src/Requests/Aff/SignRegisterRequest.php b/src/Requests/Aff/SignRegisterRequest.php new file mode 100644 index 0000000..73c2054 --- /dev/null +++ b/src/Requests/Aff/SignRegisterRequest.php @@ -0,0 +1,39 @@ +data->toArray(); + } + + public function createDtoFromResponse(Response $response): GeneralStatusData + { + return GeneralStatusData::from([ + 'success' => $response->successful(), + ]); + } +} \ No newline at end of file diff --git a/src/Requests/Auth/GetTokenRequest.php b/src/Requests/Auth/GetTokenRequest.php index 1fc59ca..e4827e9 100644 --- a/src/Requests/Auth/GetTokenRequest.php +++ b/src/Requests/Auth/GetTokenRequest.php @@ -1,46 +1,81 @@ +// use Apoloz\AlfaBank\Data\Auth\TokenData; +use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasFormBody; -use Saloon\Contracts\Body\HasBody; +// +//
+// class GetTokenRequest extends Request implements HasBody { + //
+ // use HasFormBody; + //
+ //
+ // protected Method $method = Method::POST; + //
+ //
+ // public function __construct( protected string $clientId, protected string $clientSecret, - protected string $code, - protected string $redirectUri, + protected ?string $code = null, + protected ?string $redirectUri = null, protected string $grantType = 'authorization_code' ) {} + //
+ //
+ // public function resolveEndpoint(): string { return '/oidc/token'; } + //
+ //
+ // protected function defaultBody(): array { - return [ + $body = [ 'grant_type' => $this->grantType, 'client_id' => $this->clientId, 'client_secret' => $this->clientSecret, - 'code' => $this->code, - 'redirect_uri' => $this->redirectUri, ]; - } + + if($this->grantType === 'authorization_code') { + $body['code'] = $this->code; + $body['redirect_uri'] = $this->redirectUri; + } + return $body; + } + //
+ + //
+ // public function createDtoFromResponse(Response $response): TokenData { return TokenData::from($response->json()); } + //
} diff --git a/src/Requests/Auth/RefreshClientSecretRequest.php b/src/Requests/Auth/RefreshClientSecretRequest.php index d8bee6c..d69bcd7 100644 --- a/src/Requests/Auth/RefreshClientSecretRequest.php +++ b/src/Requests/Auth/RefreshClientSecretRequest.php @@ -1,26 +1,50 @@ +// +use Apoloz\AlfaBank\Data\Auth\ClientSecretData; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; +//
+//
+// class RefreshClientSecretRequest extends Request { + //
+ // protected Method $method = Method::POST; + //
+ //
+ // public function __construct( protected string $clientId ) {} + //
+ //
+ // public function resolveEndpoint(): string { return "/oidc/clients/{$this->clientId}/client-secret"; } + //
- public function createDtoFromResponse(Response $response): array + //
+ // + public function createDtoFromResponse(Response $response): ClientSecretData { - return $response->json(); + return ClientSecretData::from($response->json()); } + //
} diff --git a/src/Requests/Auth/RevokeTokenRequest.php b/src/Requests/Auth/RevokeTokenRequest.php index ae80a04..6fa6fd8 100644 --- a/src/Requests/Auth/RevokeTokenRequest.php +++ b/src/Requests/Auth/RevokeTokenRequest.php @@ -1,30 +1,57 @@ +// +use Apoloz\AlfaBank\Data\Shared\GeneralStatusData; use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; use Saloon\Traits\Body\HasFormBody; +//
+//
+// class RevokeTokenRequest extends Request implements HasBody { + //
+ // use HasFormBody; + //
+ //
+ // protected Method $method = Method::POST; + //
+ //
+ // public function __construct( protected string $clientId, protected string $clientSecret, protected string $token, protected ?string $tokenTypeHint = null ) {} + //
+ //
+ // public function resolveEndpoint(): string { return '/oidc/revoke'; } + //
+ //
+ // protected function defaultBody(): array { return array_filter([ @@ -34,4 +61,10 @@ class RevokeTokenRequest extends Request implements HasBody 'token_type_hint' => $this->tokenTypeHint, ]); } + //
+ + public function createDtoFromResponse(Response $response): GeneralStatusData + { + return GeneralStatusData::from(['success' => $response->successful()]); + } } diff --git a/src/Requests/Auth/UserInfo/GetClientConsentsRequest.php b/src/Requests/Auth/UserInfo/GetClientConsentsRequest.php index 9dc7df2..95f164a 100644 --- a/src/Requests/Auth/UserInfo/GetClientConsentsRequest.php +++ b/src/Requests/Auth/UserInfo/GetClientConsentsRequest.php @@ -1,16 +1,32 @@ +// use Apoloz\AlfaBank\Data\Auth\UserInfo\ClientConsentCollectionData; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; +//
+//
+// class GetClientConsentsRequest extends Request { + //
+ // protected Method $method = Method::GET; + //
+ //
+ // public function __construct( protected ?string $clientId = null, protected ?string $sub = null, @@ -19,12 +35,18 @@ class GetClientConsentsRequest extends Request protected ?int $limit = 1000, protected ?int $offset = 0, ) {} + //
+ //
+ // public function resolveEndpoint(): string { return '/oidc/client-consents'; } + //
+ //
+ // protected function defaultQuery(): array { return array_filter([ @@ -36,9 +58,13 @@ class GetClientConsentsRequest extends Request 'offset' => $this->offset, ], fn($value) => $value !== null); } + //
+ //
+ // public function createDtoFromResponse(Response $response): ClientConsentCollectionData { return ClientConsentCollectionData::from($response->json()); } + //
} diff --git a/src/Requests/Auth/UserInfo/GetUserInfoRequest.php b/src/Requests/Auth/UserInfo/GetUserInfoRequest.php index 90b270d..4e5bb12 100644 --- a/src/Requests/Auth/UserInfo/GetUserInfoRequest.php +++ b/src/Requests/Auth/UserInfo/GetUserInfoRequest.php @@ -1,21 +1,40 @@ +// use Apoloz\AlfaBank\Data\Auth\UserInfo\UserInfoData; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; +//
+//
+// class GetUserInfoRequest extends Request { + //
+ // protected Method $method = Method::GET; + //
+ //
+ // public function resolveEndpoint(): string { return '/oidc/userinfo'; } + //
+ //
+ // protected function defaultHeaders(): array { return [ @@ -23,13 +42,8 @@ class GetUserInfoRequest extends Request ]; } - /** - * The response is a JWT. We need to decode it to get the claims. - * Note: This implementation assumes the user will handle JWT verification/decoding - * or we can do a simple base64 decode of the payload if trust is established via mTLS/OIDC. - * - * For now, we will return the raw JWT string in a specific DTO or try to parse it if simple. - */ + //
+ // public function createDtoFromResponse(Response $response): UserInfoData { $jwt = $response->body(); @@ -46,4 +60,5 @@ class GetUserInfoRequest extends Request throw new \Exception("Invalid UserInfo response format. Expected JWT."); } + //
} diff --git a/src/Requests/B2B_1C/CheckMerchantRequest.php b/src/Requests/B2B_1C/CheckMerchantRequest.php new file mode 100644 index 0000000..5e37dc2 --- /dev/null +++ b/src/Requests/B2B_1C/CheckMerchantRequest.php @@ -0,0 +1,35 @@ +merchantId}"; + } + + protected function defaultHeaders(): array + { + return [ + 'Authorization-1C' => $this->auth1C, + ]; + } + + public function createDtoFromResponse(Response $response): MerchantData + { + return MerchantData::from($response->json()); + } +} diff --git a/src/Requests/B2B_1C/GetPaymentLinkRequest.php b/src/Requests/B2B_1C/GetPaymentLinkRequest.php new file mode 100644 index 0000000..1d1bde8 --- /dev/null +++ b/src/Requests/B2B_1C/GetPaymentLinkRequest.php @@ -0,0 +1,35 @@ +qrId}"; + } + + protected function defaultHeaders(): array + { + return [ + 'Authorization-1C' => $this->auth1C, + ]; + } + + public function createDtoFromResponse(Response $response): PaymentLinkData + { + return PaymentLinkData::from($response->json()); + } +} diff --git a/src/Requests/B2B_1C/SearchPaymentsRequest.php b/src/Requests/B2B_1C/SearchPaymentsRequest.php new file mode 100644 index 0000000..ee2fb3e --- /dev/null +++ b/src/Requests/B2B_1C/SearchPaymentsRequest.php @@ -0,0 +1,45 @@ + $this->auth1C, + ]; + } + + protected function defaultBody(): array + { + return $this->data; + } + + public function createDtoFromResponse(Response $response): DataCollection + { + return Payment1CData::collect($response->json(), DataCollection::class); + } +} diff --git a/src/Requests/B2C/CreateB2CPaymentRequest.php b/src/Requests/B2C/CreateB2CPaymentRequest.php new file mode 100644 index 0000000..67e1d61 --- /dev/null +++ b/src/Requests/B2C/CreateB2CPaymentRequest.php @@ -0,0 +1,37 @@ +data->toArray(); + } + + public function createDtoFromResponse(Response $response): B2CPaymentResponseData + { + return B2CPaymentResponseData::from($response->json()); + } +} diff --git a/src/Requests/B2C/GetB2CPaymentReportRequest.php b/src/Requests/B2C/GetB2CPaymentReportRequest.php new file mode 100644 index 0000000..92e4829 --- /dev/null +++ b/src/Requests/B2C/GetB2CPaymentReportRequest.php @@ -0,0 +1,42 @@ + $this->b2cClientId, + 'partnerId' => $this->partnerId, + 'startDate' => $this->startDate, + 'finishDate' => $this->finishDate, + 'transactionId' => $this->transactionId, + ]); + } + + public function createDtoFromResponse(Response $response): B2CPaymentReportData + { + return B2CPaymentReportData::from($response->json()); + } +} diff --git a/src/Requests/Bonus/GetBonusNumbersRequest.php b/src/Requests/Bonus/GetBonusNumbersRequest.php new file mode 100644 index 0000000..9c5b55e --- /dev/null +++ b/src/Requests/Bonus/GetBonusNumbersRequest.php @@ -0,0 +1,24 @@ +json('bonusNumbers') ?? [], DataCollection::class); + } +} diff --git a/src/Requests/Cards/GetCardsRequest.php b/src/Requests/Cards/GetCardsRequest.php new file mode 100644 index 0000000..1803f95 --- /dev/null +++ b/src/Requests/Cards/GetCardsRequest.php @@ -0,0 +1,24 @@ +json('cards'), DataCollection::class); + } +} diff --git a/src/Requests/Claims/ResolveClaimRequest.php b/src/Requests/Claims/ResolveClaimRequest.php new file mode 100644 index 0000000..e8bf3be --- /dev/null +++ b/src/Requests/Claims/ResolveClaimRequest.php @@ -0,0 +1,38 @@ +claimId}"; + } + + protected function defaultBody(): array + { + return $this->data->toArray(); + } + + public function createDtoFromResponse(Response $response): GeneralStatusData + { + return GeneralStatusData::from(['success' => $response->successful()]); + } +} \ No newline at end of file diff --git a/src/Requests/CloudSignature/ConfirmSigningWithSmsRequest.php b/src/Requests/CloudSignature/ConfirmSigningWithSmsRequest.php new file mode 100644 index 0000000..170725a --- /dev/null +++ b/src/Requests/CloudSignature/ConfirmSigningWithSmsRequest.php @@ -0,0 +1,39 @@ +operationId}"; + } + + protected function defaultBody(): array + { + return [ + 'smsCode' => $this->smsCode, + ]; + } + + public function createDtoFromResponse(Response $response): GeneralStatusData + { + return GeneralStatusData::from(['success' => $response->successful()]); + } +} diff --git a/src/Requests/CloudSignature/GetDnFileRequest.php b/src/Requests/CloudSignature/GetDnFileRequest.php new file mode 100644 index 0000000..49729d6 --- /dev/null +++ b/src/Requests/CloudSignature/GetDnFileRequest.php @@ -0,0 +1,26 @@ + $response->body(), + 'size' => strlen($response->body()), + ]); + } +} diff --git a/src/Requests/CloudSignature/GetDnRequest.php b/src/Requests/CloudSignature/GetDnRequest.php new file mode 100644 index 0000000..0b92cbf --- /dev/null +++ b/src/Requests/CloudSignature/GetDnRequest.php @@ -0,0 +1,23 @@ +json()); + } +} diff --git a/src/Requests/CloudSignature/IssueRsaCertificateRequest.php b/src/Requests/CloudSignature/IssueRsaCertificateRequest.php new file mode 100644 index 0000000..5f479f6 --- /dev/null +++ b/src/Requests/CloudSignature/IssueRsaCertificateRequest.php @@ -0,0 +1,40 @@ + $this->tokenSerialNumber, + 'pkcs10Content' => $this->pkcs10Content, + ]); + } + + public function createDtoFromResponse(Response $response): CloudSignatureResponseData + { + return CloudSignatureResponseData::from($response->json()); + } +} diff --git a/src/Requests/CloudSignature/SubmitSigningRequest.php b/src/Requests/CloudSignature/SubmitSigningRequest.php new file mode 100644 index 0000000..9b7fbe4 --- /dev/null +++ b/src/Requests/CloudSignature/SubmitSigningRequest.php @@ -0,0 +1,27 @@ +id}/operations"; + } + + public function createDtoFromResponse(Response $response): CloudSignatureResponseData + { + return CloudSignatureResponseData::from($response->json()); + } +} diff --git a/src/Requests/Compliance/GetUserComplianceRequest.php b/src/Requests/Compliance/GetUserComplianceRequest.php new file mode 100644 index 0000000..2ebd91d --- /dev/null +++ b/src/Requests/Compliance/GetUserComplianceRequest.php @@ -0,0 +1,23 @@ +json()); + } +} diff --git a/src/Requests/Crypto/GetCertificatesRequest.php b/src/Requests/Crypto/GetCertificatesRequest.php index 2dd5f6b..6c736a6 100644 --- a/src/Requests/Crypto/GetCertificatesRequest.php +++ b/src/Requests/Crypto/GetCertificatesRequest.php @@ -19,6 +19,6 @@ class GetCertificatesRequest extends Request public function createDtoFromResponse(Response $response): DataCollection { - return CertificateData::collect($response->json(), DataCollection::class); + return CertificateData::collect($response->json('certificates') ?? [], DataCollection::class); } -} +} \ No newline at end of file diff --git a/src/Requests/Crypto/UpdateCertificateRequest.php b/src/Requests/Crypto/UpdateCertificateRequest.php index b0119b0..ae5b721 100644 --- a/src/Requests/Crypto/UpdateCertificateRequest.php +++ b/src/Requests/Crypto/UpdateCertificateRequest.php @@ -1,32 +1,54 @@ +// use Apoloz\AlfaBank\Data\Crypto\CertificateData; use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; +//
+//
+// class UpdateCertificateRequest extends Request implements HasBody { use HasJsonBody; + //
+ // protected Method $method = Method::PATCH; + //
+ //
+ // public function __construct( protected string $id, protected string $status, // ACTIVE or REVOKED protected string $op = 'replace', protected string $path = '/status' ) {} + //
+ //
+ // public function resolveEndpoint(): string { return "/api/gd/v1/crypto-service/certificates/{$this->id}"; } + //
+ //
+ // protected function defaultBody(): array { return [ @@ -35,9 +57,13 @@ class UpdateCertificateRequest extends Request implements HasBody 'value' => $this->status, ]; } + //
+ //
+ // public function createDtoFromResponse(Response $response): CertificateData { return CertificateData::from($response->json()); } + //
} diff --git a/src/Requests/Crypto/UploadCertificateRequest.php b/src/Requests/Crypto/UploadCertificateRequest.php index 4ea4bfa..2dde14f 100644 --- a/src/Requests/Crypto/UploadCertificateRequest.php +++ b/src/Requests/Crypto/UploadCertificateRequest.php @@ -1,30 +1,52 @@ +// use Apoloz\AlfaBank\Data\Crypto\CertificateData; use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; +//
+//
+// class UploadCertificateRequest extends Request implements HasBody { use HasJsonBody; + //
+ // protected Method $method = Method::POST; + //
+ //
+ // public function __construct( protected string $type, protected string $certificateBody, ) {} + //
+ //
+ // public function resolveEndpoint(): string { return '/api/gd/v1/crypto-service/certificates'; } + //
+ //
+ // protected function defaultBody(): array { return [ @@ -32,9 +54,13 @@ class UploadCertificateRequest extends Request implements HasBody 'certificateBody' => $this->certificateBody, ]; } + //
+ //
+ // public function createDtoFromResponse(Response $response): CertificateData { return CertificateData::from($response->json()); } + //
} diff --git a/src/Requests/CustomerInfo/GetCustomerInfoRequest.php b/src/Requests/CustomerInfo/GetCustomerInfoRequest.php new file mode 100644 index 0000000..2797a6f --- /dev/null +++ b/src/Requests/CustomerInfo/GetCustomerInfoRequest.php @@ -0,0 +1,23 @@ +json()); + } +} diff --git a/src/Requests/DFA/GetDfaInitialPlacementListRequest.php b/src/Requests/DFA/GetDfaInitialPlacementListRequest.php new file mode 100644 index 0000000..36c014b --- /dev/null +++ b/src/Requests/DFA/GetDfaInitialPlacementListRequest.php @@ -0,0 +1,41 @@ +limit = $limit; + $this->offset = $offset; + } + + public function resolveEndpoint(): string + { + return '/api/dfa/gd/v1/dfa-profile/initial-placement-list'; + } + + protected function defaultQuery(): array + { + return $this->getPaginationQuery(); + } + + public function createDtoFromResponse(Response $response): DataCollection + { + return DfaInitialPlacementData::collect($response->json('items') ?? [], DataCollection::class); + } +} diff --git a/src/Requests/DebitCards/CreateDebitCardApplicationRequest.php b/src/Requests/DebitCards/CreateDebitCardApplicationRequest.php new file mode 100644 index 0000000..48e6909 --- /dev/null +++ b/src/Requests/DebitCards/CreateDebitCardApplicationRequest.php @@ -0,0 +1,37 @@ +data->toArray(); + } + + public function createDtoFromResponse(Response $response): DebitCardApplicationResponseData + { + return DebitCardApplicationResponseData::from($response->json()); + } +} diff --git a/src/Requests/Escrow/GetBuildProjectsRequest.php b/src/Requests/Escrow/GetBuildProjectsRequest.php new file mode 100644 index 0000000..3cc1523 --- /dev/null +++ b/src/Requests/Escrow/GetBuildProjectsRequest.php @@ -0,0 +1,24 @@ +json(), DataCollection::class); + } +} diff --git a/src/Requests/Escrow/GetEscrowOrdersRequest.php b/src/Requests/Escrow/GetEscrowOrdersRequest.php new file mode 100644 index 0000000..9413213 --- /dev/null +++ b/src/Requests/Escrow/GetEscrowOrdersRequest.php @@ -0,0 +1,46 @@ +limit = $limit; + $this->offset = $offset; + } + + public function resolveEndpoint(): string + { + return '/api/escrow-orders'; + } + + protected function defaultQuery(): array + { + return array_merge([ + 'filterFromDate' => $this->filterFromDate, + 'filterToDate' => $this->filterToDate, + ], $this->getPaginationQuery()); + } + + public function createDtoFromResponse(Response $response): DataCollection + { + return EscrowOrderData::collect($response->json('orders') ?? [], DataCollection::class); + } +} diff --git a/src/Requests/FileExchange/DownloadFileRequest.php b/src/Requests/FileExchange/DownloadFileRequest.php index d6d4ff2..57d1a38 100644 --- a/src/Requests/FileExchange/DownloadFileRequest.php +++ b/src/Requests/FileExchange/DownloadFileRequest.php @@ -1,27 +1,59 @@ +// +use Apoloz\AlfaBank\Data\Shared\DownloadedFileData; use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; +//
+//
+// class DownloadFileRequest extends Request { + //
+ // protected Method $method = Method::GET; + //
+ //
+ // public function __construct( protected string $downloadUrl ) {} + //
+ // public function resolveEndpoint(): string { return $this->downloadUrl; } + //
+ //
+ // protected function defaultHeaders(): array { return [ 'Accept' => '*/*', ]; } + //
+ + public function createDtoFromResponse(Response $response): DownloadedFileData + { + return DownloadedFileData::from([ + 'content' => $response->body(), + 'size' => strlen($response->body()), + ]); + } } diff --git a/src/Requests/FileExchange/UploadFileRequest.php b/src/Requests/FileExchange/UploadFileRequest.php index 53f00d0..aa450f3 100644 --- a/src/Requests/FileExchange/UploadFileRequest.php +++ b/src/Requests/FileExchange/UploadFileRequest.php @@ -1,29 +1,52 @@ +// +use Apoloz\AlfaBank\Data\Shared\GeneralStatusData; use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; use Saloon\Traits\Body\HasStreamBody; +//
+//
+// class UploadFileRequest extends Request implements HasBody { use HasStreamBody; + //
+ // protected Method $method = Method::PUT; + //
+ //
+ // public function __construct( protected string $uploadUrl, - protected $fileStream, + protected $fileStream, // PHP resource type for streams protected int $fileSize ) {} + //
+ // public function resolveEndpoint(): string { return $this->uploadUrl; } + //
+ //
+ // protected function defaultHeaders(): array { return [ @@ -32,9 +55,18 @@ class UploadFileRequest extends Request implements HasBody 'Content-Type' => 'application/octet-stream', ]; } + //
+ //
+ // protected function defaultBody(): mixed { return $this->fileStream; } + //
+ + public function createDtoFromResponse(Response $response): GeneralStatusData + { + return GeneralStatusData::from(['success' => $response->successful()]); + } } diff --git a/src/Requests/Limits/GetGeneralLimitsRequest.php b/src/Requests/Limits/GetGeneralLimitsRequest.php new file mode 100644 index 0000000..91fa3d3 --- /dev/null +++ b/src/Requests/Limits/GetGeneralLimitsRequest.php @@ -0,0 +1,23 @@ +json()); + } +} diff --git a/src/Requests/Loans/GetCreditOffersRequest.php b/src/Requests/Loans/GetCreditOffersRequest.php new file mode 100644 index 0000000..207d6f0 --- /dev/null +++ b/src/Requests/Loans/GetCreditOffersRequest.php @@ -0,0 +1,24 @@ +json('offers') ?? [], DataCollection::class); + } +} diff --git a/src/Requests/Logistics/ExecuteOrderActionRequest.php b/src/Requests/Logistics/ExecuteOrderActionRequest.php new file mode 100644 index 0000000..1942d29 --- /dev/null +++ b/src/Requests/Logistics/ExecuteOrderActionRequest.php @@ -0,0 +1,41 @@ +channelId}/providers/{$this->providerId}/orders/{$this->orderId}/actions/{$this->action}"; + } + + protected function defaultBody(): array + { + return $this->data->toArray(); + } + + public function createDtoFromResponse(Response $response): GeneralStatusData + { + return GeneralStatusData::from(['success' => $response->successful()]); + } +} \ No newline at end of file diff --git a/src/Requests/Logistics/GetDeliveriesRequest.php b/src/Requests/Logistics/GetDeliveriesRequest.php new file mode 100644 index 0000000..b71a491 --- /dev/null +++ b/src/Requests/Logistics/GetDeliveriesRequest.php @@ -0,0 +1,39 @@ + $this->dateTime, + 'limit' => $this->limit, + 'offset' => $this->offset, + ]; + } + + public function createDtoFromResponse(Response $response): DataCollection + { + return DeliveryData::collect($response->json('items') ?? [], DataCollection::class); + } +} diff --git a/src/Requests/Lounge/GetLoungeBalanceRequest.php b/src/Requests/Lounge/GetLoungeBalanceRequest.php new file mode 100644 index 0000000..3cb266d --- /dev/null +++ b/src/Requests/Lounge/GetLoungeBalanceRequest.php @@ -0,0 +1,23 @@ +json()); + } +} diff --git a/src/Requests/Loyalty/GetLoyaltyAccountsRequest.php b/src/Requests/Loyalty/GetLoyaltyAccountsRequest.php new file mode 100644 index 0000000..dd5b0de --- /dev/null +++ b/src/Requests/Loyalty/GetLoyaltyAccountsRequest.php @@ -0,0 +1,24 @@ +json('accounts') ?? [], DataCollection::class); + } +} diff --git a/src/Requests/Loyalty/GetLoyaltyTransactionsRequest.php b/src/Requests/Loyalty/GetLoyaltyTransactionsRequest.php new file mode 100644 index 0000000..b2b840f --- /dev/null +++ b/src/Requests/Loyalty/GetLoyaltyTransactionsRequest.php @@ -0,0 +1,38 @@ +data->toArray(); + } + + public function createDtoFromResponse(Response $response): DataCollection + { + return LoyaltyTransactionData::collect($response->json('transactions') ?? [], DataCollection::class); + } +} \ No newline at end of file diff --git a/src/Requests/NominalAccounts/Beneficiaries/CreateBeneficiaryRequest.php b/src/Requests/NominalAccounts/Beneficiaries/CreateBeneficiaryRequest.php index 8692ae1..02af661 100644 --- a/src/Requests/NominalAccounts/Beneficiaries/CreateBeneficiaryRequest.php +++ b/src/Requests/NominalAccounts/Beneficiaries/CreateBeneficiaryRequest.php @@ -1,7 +1,14 @@ +// use Apoloz\AlfaBank\Data\NominalAccounts\Beneficiaries\CreateBeneficiaryRequestData; use Apoloz\AlfaBank\Data\NominalAccounts\Beneficiaries\CreateBeneficiaryResponseData; use Saloon\Contracts\Body\HasBody; @@ -9,29 +16,47 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; +//
+//
+// class CreateBeneficiaryRequest extends Request implements HasBody { use HasJsonBody; + //
+ // protected Method $method = Method::POST; + //
+ //
+ // public function __construct( protected CreateBeneficiaryRequestData $data ) {} + //
+ //
+ // public function resolveEndpoint(): string { return '/api/na/jp/v1/beneficiaries'; } + //
+ //
+ // protected function defaultBody(): array { return $this->data->toArray(); } + //
+ //
+ // public function createDtoFromResponse(Response $response): CreateBeneficiaryResponseData { return CreateBeneficiaryResponseData::from($response->json()); } + //
} diff --git a/src/Requests/NominalAccounts/Beneficiaries/GetBeneficiariesRequest.php b/src/Requests/NominalAccounts/Beneficiaries/GetBeneficiariesRequest.php index bd58cf5..dfe23aa 100644 --- a/src/Requests/NominalAccounts/Beneficiaries/GetBeneficiariesRequest.php +++ b/src/Requests/NominalAccounts/Beneficiaries/GetBeneficiariesRequest.php @@ -1,28 +1,42 @@ +// use Apoloz\AlfaBank\Data\NominalAccounts\BeneficiaryCollectionData; +use Apoloz\AlfaBank\Traits\HasAlfaPagination; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; +use Saloon\PaginationPlugin\Contracts\Paginatable; -class GetBeneficiariesRequest extends Request +class GetBeneficiariesRequest extends Request implements Paginatable { + use HasAlfaPagination; + protected Method $method = Method::GET; public function __construct( protected string $accountNumber, protected string $type, - protected ?int $limit = null, - protected ?int $offset = null, + ?int $limit = null, + ?int $offset = null, protected ?string $inn = null, protected ?string $lastName = null, protected ?string $firstName = null, protected ?string $middleName = null, protected ?string $name = null, protected ?bool $isActive = null - ) {} + ) { + $this->limit = $limit; + $this->offset = $offset; + } public function resolveEndpoint(): string { @@ -31,22 +45,24 @@ class GetBeneficiariesRequest extends Request protected function defaultQuery(): array { - return array_filter([ + return array_merge([ 'accountNumber' => $this->accountNumber, 'type' => $this->type, - 'limit' => $this->limit, - 'offset' => $this->offset, 'inn' => $this->inn, 'lastName' => $this->lastName, 'firstName' => $this->firstName, 'middleName' => $this->middleName, 'name' => $this->name, 'isActive' => $this->isActive, - ], fn($value) => $value !== null); + ], $this->getPaginationQuery()); } + //
+ //
+ // public function createDtoFromResponse(Response $response): BeneficiaryCollectionData { return BeneficiaryCollectionData::from($response->json()); } + //
} diff --git a/src/Requests/NominalAccounts/Beneficiaries/GetBeneficiaryBalanceRequest.php b/src/Requests/NominalAccounts/Beneficiaries/GetBeneficiaryBalanceRequest.php index faa7711..d25941f 100644 --- a/src/Requests/NominalAccounts/Beneficiaries/GetBeneficiaryBalanceRequest.php +++ b/src/Requests/NominalAccounts/Beneficiaries/GetBeneficiaryBalanceRequest.php @@ -1,27 +1,48 @@ +// use Apoloz\AlfaBank\Data\NominalAccounts\Beneficiaries\BeneficiaryBalanceData; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; +//
+//
+// class GetBeneficiaryBalanceRequest extends Request { + //
+ // protected Method $method = Method::GET; + //
+ //
+ // public function __construct( protected string $beneficiaryId, protected string $accountNumber, protected string $date ) {} + //
+ //
+ // public function resolveEndpoint(): string { return "/api/na/jp/v1/beneficiaries/{$this->beneficiaryId}/balance"; } + //
+ //
+ // protected function defaultQuery(): array { return [ @@ -29,9 +50,13 @@ class GetBeneficiaryBalanceRequest extends Request 'date' => $this->date, ]; } + //
+ //
+ // public function createDtoFromResponse(Response $response): BeneficiaryBalanceData { return BeneficiaryBalanceData::from($response->json()); } + //
} diff --git a/src/Requests/NominalAccounts/Beneficiaries/GetBeneficiaryTransactionsRequest.php b/src/Requests/NominalAccounts/Beneficiaries/GetBeneficiaryTransactionsRequest.php index 960bf3f..61966de 100644 --- a/src/Requests/NominalAccounts/Beneficiaries/GetBeneficiaryTransactionsRequest.php +++ b/src/Requests/NominalAccounts/Beneficiaries/GetBeneficiaryTransactionsRequest.php @@ -1,16 +1,33 @@ +// use Apoloz\AlfaBank\Data\NominalAccounts\Beneficiaries\BeneficiaryTransactionCollectionData; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; +//
+//
+// class GetBeneficiaryTransactionsRequest extends Request { + //
+ // protected Method $method = Method::GET; + //
+ //
+ // public function __construct( protected string $beneficiaryId, protected string $dateFrom, @@ -18,12 +35,18 @@ class GetBeneficiaryTransactionsRequest extends Request protected ?int $limit = 100, protected ?int $offset = 0 ) {} + //
+ //
+ // public function resolveEndpoint(): string { return "/api/na/jp/v1/beneficiaries/{$this->beneficiaryId}/transactions"; } + //
+ //
+ // protected function defaultQuery(): array { return [ @@ -33,9 +56,14 @@ class GetBeneficiaryTransactionsRequest extends Request 'offset' => $this->offset, ]; } + //
+ //
+ // public function createDtoFromResponse(Response $response): BeneficiaryTransactionCollectionData { return BeneficiaryTransactionCollectionData::from($response->json()); } + //
} +//
diff --git a/src/Requests/NominalAccounts/Beneficiaries/Requisites/AddRequisiteRequest.php b/src/Requests/NominalAccounts/Beneficiaries/Requisites/AddRequisiteRequest.php index 8fa4a52..f16443a 100644 --- a/src/Requests/NominalAccounts/Beneficiaries/Requisites/AddRequisiteRequest.php +++ b/src/Requests/NominalAccounts/Beneficiaries/Requisites/AddRequisiteRequest.php @@ -1,7 +1,15 @@ +// use Apoloz\AlfaBank\Data\NominalAccounts\Beneficiaries\Requisites\CreateRequisiteRequestData; use Apoloz\AlfaBank\Data\NominalAccounts\Beneficiaries\Requisites\RequisiteData; use Saloon\Contracts\Body\HasBody; @@ -9,30 +17,48 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; +//
+//
+// class AddRequisiteRequest extends Request implements HasBody { use HasJsonBody; + //
+ // protected Method $method = Method::POST; + //
+ //
+ // public function __construct( protected string $beneficiaryId, protected CreateRequisiteRequestData $data ) {} + //
+ //
+ // public function resolveEndpoint(): string { return "/api/na/jp/v1/beneficiaries/{$this->beneficiaryId}/requisites"; } + //
+ //
+ // protected function defaultBody(): array { return $this->data->toArray(); } + //
+ //
+ // public function createDtoFromResponse(Response $response): RequisiteData { return RequisiteData::from($response->json()); } + //
} diff --git a/src/Requests/NominalAccounts/Beneficiaries/Requisites/GetRequisitesRequest.php b/src/Requests/NominalAccounts/Beneficiaries/Requisites/GetRequisitesRequest.php index 4c60c47..5d03b29 100644 --- a/src/Requests/NominalAccounts/Beneficiaries/Requisites/GetRequisitesRequest.php +++ b/src/Requests/NominalAccounts/Beneficiaries/Requisites/GetRequisitesRequest.php @@ -1,28 +1,51 @@ +// use Apoloz\AlfaBank\Data\NominalAccounts\Beneficiaries\Requisites\RequisiteData; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; use Spatie\LaravelData\DataCollection; +//
+//
+// class GetRequisitesRequest extends Request { + //
+ // protected Method $method = Method::GET; + //
+ //
+ // public function __construct( protected string $beneficiaryId ) {} + //
+ //
+ // public function resolveEndpoint(): string { return "/api/na/jp/v1/beneficiaries/{$this->beneficiaryId}/requisites"; } + //
+ //
+ // public function createDtoFromResponse(Response $response): DataCollection { return RequisiteData::collect($response->json(), DataCollection::class); } + //
} diff --git a/src/Requests/NominalAccounts/Beneficiaries/Requisites/UpdateRequisiteRequest.php b/src/Requests/NominalAccounts/Beneficiaries/Requisites/UpdateRequisiteRequest.php index c97fa04..45dc51c 100644 --- a/src/Requests/NominalAccounts/Beneficiaries/Requisites/UpdateRequisiteRequest.php +++ b/src/Requests/NominalAccounts/Beneficiaries/Requisites/UpdateRequisiteRequest.php @@ -1,23 +1,32 @@ +// +use Apoloz\AlfaBank\Data\Shared\GeneralStatusData; use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; class UpdateRequisiteRequest extends Request implements HasBody { use HasJsonBody; - protected Method $method = Method::PATCH; + protected Method $method = Method::PUT; public function __construct( protected string $beneficiaryId, protected string $requisiteId, - protected UpdateRequisiteRequestData $data + protected array $data ) {} public function resolveEndpoint(): string @@ -27,6 +36,11 @@ class UpdateRequisiteRequest extends Request implements HasBody protected function defaultBody(): array { - return $this->data->toArray(); + return $this->data; + } + + public function createDtoFromResponse(Response $response): GeneralStatusData + { + return GeneralStatusData::from(['success' => $response->successful()]); } } diff --git a/src/Requests/NominalAccounts/Beneficiaries/ScoreBeneficiaryRequest.php b/src/Requests/NominalAccounts/Beneficiaries/ScoreBeneficiaryRequest.php index bae5d7a..42b804d 100644 --- a/src/Requests/NominalAccounts/Beneficiaries/ScoreBeneficiaryRequest.php +++ b/src/Requests/NominalAccounts/Beneficiaries/ScoreBeneficiaryRequest.php @@ -1,27 +1,49 @@ +// use Apoloz\AlfaBank\Data\NominalAccounts\Beneficiaries\CreateBeneficiaryResponseData; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; +//
+//
+// class ScoreBeneficiaryRequest extends Request { + //
+ // protected Method $method = Method::POST; + //
+ //
+ // public function __construct( protected string $beneficiaryId ) {} + //
+ //
+ // public function resolveEndpoint(): string { return "/api/na/jp/v1/beneficiaries/{$this->beneficiaryId}/scoring"; } + //
+ //
+ // public function createDtoFromResponse(Response $response): CreateBeneficiaryResponseData { return CreateBeneficiaryResponseData::from($response->json()); } + //
} diff --git a/src/Requests/NominalAccounts/Beneficiaries/UpdateBeneficiaryRequest.php b/src/Requests/NominalAccounts/Beneficiaries/UpdateBeneficiaryRequest.php index 960ba87..e14ddb5 100644 --- a/src/Requests/NominalAccounts/Beneficiaries/UpdateBeneficiaryRequest.php +++ b/src/Requests/NominalAccounts/Beneficiaries/UpdateBeneficiaryRequest.php @@ -1,38 +1,63 @@ +// +use Apoloz\AlfaBank\Data\NominalAccounts\Beneficiaries\CreateBeneficiaryResponseData; // Response is status data use Apoloz\AlfaBank\Data\NominalAccounts\Beneficiaries\UpdateBeneficiaryRequestData; use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; +//
+//
+// class UpdateBeneficiaryRequest extends Request implements HasBody { use HasJsonBody; + //
+ // protected Method $method = Method::PUT; + //
+ //
+ // public function __construct( protected string $beneficiaryId, protected UpdateBeneficiaryRequestData $data ) {} + //
+ //
+ // public function resolveEndpoint(): string { return "/api/na/jp/v1/beneficiaries/{$this->beneficiaryId}"; } + //
+ //
+ // protected function defaultBody(): array { return $this->data->toArray(); } + //
+ //
+ // public function createDtoFromResponse(Response $response): CreateBeneficiaryResponseData { return CreateBeneficiaryResponseData::from($response->json()); } + //
} diff --git a/src/Requests/NominalAccounts/Deals/AddDealRequest.php b/src/Requests/NominalAccounts/Deals/AddDealRequest.php index eb45e5a..8e0c906 100644 --- a/src/Requests/NominalAccounts/Deals/AddDealRequest.php +++ b/src/Requests/NominalAccounts/Deals/AddDealRequest.php @@ -1,7 +1,14 @@ +// use Apoloz\AlfaBank\Data\NominalAccounts\Deals\DealData; use Apoloz\AlfaBank\Data\NominalAccounts\Payouts\DigestSignatureData; use Saloon\Contracts\Body\HasBody; @@ -9,26 +16,44 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; +use Spatie\LaravelData\Attributes\DataCollectionOf; use Spatie\LaravelData\DataCollection; +//
+//
+// class AddDealRequest extends Request implements HasBody { + //
+ // use HasJsonBody; + //
+ //
+ // protected Method $method = Method::POST; + //
+ //
+ // public function __construct( protected string $accountNumber, protected string $expireDate, #[DataCollectionOf(DigestSignatureData::class)] protected DataCollection $digestSignatures ) {} + //
+ //
+ // public function resolveEndpoint(): string { return '/api/na/jp/v1/deals'; } + //
+ //
+ // protected function defaultBody(): array { return [ @@ -37,9 +62,13 @@ class AddDealRequest extends Request implements HasBody 'digestSignatures' => $this->digestSignatures->toArray(), ]; } + //
+ //
+ // public function createDtoFromResponse(Response $response): DealData { return DealData::from($response->json()); } + //
} diff --git a/src/Requests/NominalAccounts/Deals/DealActionRequest.php b/src/Requests/NominalAccounts/Deals/DealActionRequest.php index ddd794f..77b913b 100644 --- a/src/Requests/NominalAccounts/Deals/DealActionRequest.php +++ b/src/Requests/NominalAccounts/Deals/DealActionRequest.php @@ -1,38 +1,41 @@ dealId}/actions/{$this->action}"; } protected function defaultBody(): array { - return [ - 'dealId' => $this->dealId, - 'action' => $this->action, - 'digestSignatures' => $this->digestSignatures->toArray(), - ]; + return $this->data; + } + + public function createDtoFromResponse(Response $response): GeneralStatusData + { + return GeneralStatusData::from(['success' => $response->successful()]); } } diff --git a/src/Requests/NominalAccounts/Deals/GetDealPayoffsRequest.php b/src/Requests/NominalAccounts/Deals/GetDealPayoffsRequest.php index 0623048..5396a4d 100644 --- a/src/Requests/NominalAccounts/Deals/GetDealPayoffsRequest.php +++ b/src/Requests/NominalAccounts/Deals/GetDealPayoffsRequest.php @@ -1,28 +1,50 @@ +// use Apoloz\AlfaBank\Data\NominalAccounts\Deals\DealPayoffData; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; use Spatie\LaravelData\DataCollection; +//
+//
+// class GetDealPayoffsRequest extends Request { + //
+ // protected Method $method = Method::GET; + //
+ //
+ // public function __construct( protected string $dealId ) {} + //
+ //
+ // public function resolveEndpoint(): string { return "/api/na/jp/v1/deals/{$this->dealId}/payoffs"; } + //
+ //
+ // public function createDtoFromResponse(Response $response): DataCollection { return DealPayoffData::collect($response->json(), DataCollection::class); } + //
} diff --git a/src/Requests/NominalAccounts/Deals/GetDealRequest.php b/src/Requests/NominalAccounts/Deals/GetDealRequest.php index 4aa37d4..643407d 100644 --- a/src/Requests/NominalAccounts/Deals/GetDealRequest.php +++ b/src/Requests/NominalAccounts/Deals/GetDealRequest.php @@ -1,27 +1,49 @@ +// use Apoloz\AlfaBank\Data\NominalAccounts\Deals\DealData; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; +//
+//
+// class GetDealRequest extends Request { + //
+ // protected Method $method = Method::GET; + //
+ //
+ // public function __construct( protected string $dealId ) {} + //
+ //
+ // public function resolveEndpoint(): string { return "/api/na/jp/v1/deals/{$this->dealId}"; } + //
+ //
+ // public function createDtoFromResponse(Response $response): DealData { return DealData::from($response->json()); } + //
} diff --git a/src/Requests/NominalAccounts/Deals/GetDealRequestStatusRequest.php b/src/Requests/NominalAccounts/Deals/GetDealRequestStatusRequest.php index b372222..4bc08db 100644 --- a/src/Requests/NominalAccounts/Deals/GetDealRequestStatusRequest.php +++ b/src/Requests/NominalAccounts/Deals/GetDealRequestStatusRequest.php @@ -1,28 +1,51 @@ +// use Apoloz\AlfaBank\Data\NominalAccounts\Deals\DealRequestStatusData; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; +//
+//
+// class GetDealRequestStatusRequest extends Request { + //
+ // protected Method $method = Method::GET; + //
+ //
+ // public function __construct( protected string $dealId, protected string $requestId ) {} + //
+ //
+ // public function resolveEndpoint(): string { return "/api/na/jp/v1/deals/{$this->dealId}/requests/{$this->requestId}/status"; } + //
+ //
+ // public function createDtoFromResponse(Response $response): DealRequestStatusData { return DealRequestStatusData::from($response->json()); } + //
} diff --git a/src/Requests/NominalAccounts/Deals/GetDealsRequest.php b/src/Requests/NominalAccounts/Deals/GetDealsRequest.php index 13b8f0d..5570efc 100644 --- a/src/Requests/NominalAccounts/Deals/GetDealsRequest.php +++ b/src/Requests/NominalAccounts/Deals/GetDealsRequest.php @@ -1,27 +1,48 @@ +// use Apoloz\AlfaBank\Data\NominalAccounts\Deals\DealCollectionData; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; +//
+//
+// class GetDealsRequest extends Request { + //
+ // protected Method $method = Method::GET; + //
+ //
+ // public function __construct( protected string $accountNumber, protected ?int $limit = 100, protected ?int $offset = 0 ) {} + //
+ //
+ // public function resolveEndpoint(): string { return '/api/na/jp/v1/deals'; } + //
+ //
+ // protected function defaultQuery(): array { return [ @@ -30,9 +51,13 @@ class GetDealsRequest extends Request 'offset' => $this->offset, ]; } + //
+ //
+ // public function createDtoFromResponse(Response $response): DealCollectionData { return DealCollectionData::from($response->json()); } + //
} diff --git a/src/Requests/NominalAccounts/Deals/Steps/GetStepContractorsRequest.php b/src/Requests/NominalAccounts/Deals/Steps/GetStepContractorsRequest.php index 279a8e9..82a9d38 100644 --- a/src/Requests/NominalAccounts/Deals/Steps/GetStepContractorsRequest.php +++ b/src/Requests/NominalAccounts/Deals/Steps/GetStepContractorsRequest.php @@ -1,29 +1,51 @@ +// use Apoloz\AlfaBank\Data\NominalAccounts\Deals\Steps\StepContractorData; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; use Spatie\LaravelData\DataCollection; +//
+//
+// class GetStepContractorsRequest extends Request { + //
+ // protected Method $method = Method::GET; + //
+ //
+ // public function __construct( protected string $dealId, protected string $stepId, protected ?string $contractorId = null, protected ?string $role = null ) {} + //
+ //
+ // public function resolveEndpoint(): string { return "/api/na/jp/v1/deals/{$this->dealId}/steps/{$this->stepId}/contractors"; } + //
+ //
+ // protected function defaultQuery(): array { return array_filter([ @@ -31,9 +53,13 @@ class GetStepContractorsRequest extends Request 'role' => $this->role, ]); } + //
+ //
+ // public function createDtoFromResponse(Response $response): DataCollection { return StepContractorData::collect($response->json(), DataCollection::class); } + //
} diff --git a/src/Requests/NominalAccounts/Deals/Steps/GetStepRequest.php b/src/Requests/NominalAccounts/Deals/Steps/GetStepRequest.php index 3f4fc7a..78cc816 100644 --- a/src/Requests/NominalAccounts/Deals/Steps/GetStepRequest.php +++ b/src/Requests/NominalAccounts/Deals/Steps/GetStepRequest.php @@ -1,28 +1,51 @@ +// use Apoloz\AlfaBank\Data\NominalAccounts\Deals\Steps\StepData; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; +//
+//
+// class GetStepRequest extends Request { + //
+ // protected Method $method = Method::GET; + //
+ //
+ // public function __construct( protected string $dealId, protected string $stepId ) {} + //
+ //
+ // public function resolveEndpoint(): string { return "/api/na/jp/v1/deals/{$this->dealId}/steps/{$this->stepId}"; } + //
+ //
+ // public function createDtoFromResponse(Response $response): StepData { return StepData::from($response->json()); } + //
} diff --git a/src/Requests/NominalAccounts/Deals/Steps/GetStepsRequest.php b/src/Requests/NominalAccounts/Deals/Steps/GetStepsRequest.php index 316c9eb..7ba38a4 100644 --- a/src/Requests/NominalAccounts/Deals/Steps/GetStepsRequest.php +++ b/src/Requests/NominalAccounts/Deals/Steps/GetStepsRequest.php @@ -1,28 +1,50 @@ +// use Apoloz\AlfaBank\Data\NominalAccounts\Deals\Steps\StepData; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; use Spatie\LaravelData\DataCollection; +//
+//
+// class GetStepsRequest extends Request { + //
+ // protected Method $method = Method::GET; + //
+ //
+ // public function __construct( protected string $dealId, protected ?int $limit = 50, protected ?int $offset = 0 ) {} + //
+ //
+ // public function resolveEndpoint(): string { return "/api/na/jp/v1/deals/{$this->dealId}/steps"; } + //
+ //
+ // protected function defaultQuery(): array { return [ @@ -30,9 +52,13 @@ class GetStepsRequest extends Request 'offset' => $this->offset, ]; } + //
+ //
+ // public function createDtoFromResponse(Response $response): DataCollection { return StepData::collect($response->json(), DataCollection::class); } + //
} diff --git a/src/Requests/NominalAccounts/Deals/Steps/ManageContractorRequest.php b/src/Requests/NominalAccounts/Deals/Steps/ManageContractorRequest.php index b1fa98f..14bb4d2 100644 --- a/src/Requests/NominalAccounts/Deals/Steps/ManageContractorRequest.php +++ b/src/Requests/NominalAccounts/Deals/Steps/ManageContractorRequest.php @@ -1,30 +1,46 @@ +// +use Apoloz\AlfaBank\Data\Shared\GeneralStatusData; use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; class ManageContractorRequest extends Request implements HasBody { use HasJsonBody; - protected Method $method = Method::PATCH; + protected Method $method = Method::POST; public function __construct( - protected ContractorActionRequestData $data + protected string $dealId, + protected string $stepId, + protected array $data ) {} public function resolveEndpoint(): string { - return '/api/na/jp/v1/deals/steps/contractors'; + return "/api/na/jp/v1/deals/{$this->dealId}/steps/{$this->stepId}/contractors"; } protected function defaultBody(): array { - return $this->data->toArray(); + return $this->data; + } + + public function createDtoFromResponse(Response $response): GeneralStatusData + { + return GeneralStatusData::from(['success' => $response->successful()]); } } diff --git a/src/Requests/NominalAccounts/Deals/Steps/ManageStepRequest.php b/src/Requests/NominalAccounts/Deals/Steps/ManageStepRequest.php index 70d80ef..57d4d7b 100644 --- a/src/Requests/NominalAccounts/Deals/Steps/ManageStepRequest.php +++ b/src/Requests/NominalAccounts/Deals/Steps/ManageStepRequest.php @@ -1,7 +1,15 @@ +// use Apoloz\AlfaBank\Data\NominalAccounts\Deals\Steps\StepActionRequestData; use Apoloz\AlfaBank\Data\NominalAccounts\Deals\Steps\StepActionResponseData; use Saloon\Contracts\Body\HasBody; @@ -9,29 +17,47 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; +//
+//
+// class ManageStepRequest extends Request implements HasBody { use HasJsonBody; + //
+ // protected Method $method = Method::POST; + //
+ //
+ // public function __construct( protected StepActionRequestData $data ) {} + //
+ //
+ // public function resolveEndpoint(): string { return '/api/na/jp/v1/deals/steps'; } + //
+ //
+ // protected function defaultBody(): array { return $this->data->toArray(); } + //
+ //
+ // public function createDtoFromResponse(Response $response): StepActionResponseData { return StepActionResponseData::from($response->json()); } + //
} diff --git a/src/Requests/NominalAccounts/Deals/ValidateDealRequest.php b/src/Requests/NominalAccounts/Deals/ValidateDealRequest.php index 5b877d2..422382c 100644 --- a/src/Requests/NominalAccounts/Deals/ValidateDealRequest.php +++ b/src/Requests/NominalAccounts/Deals/ValidateDealRequest.php @@ -1,27 +1,49 @@ +// use Apoloz\AlfaBank\Data\NominalAccounts\Deals\ValidateDealResponseData; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; +//
+//
+// class ValidateDealRequest extends Request { + //
+ // protected Method $method = Method::PATCH; + //
+ //
+ // public function __construct( protected string $dealId ) {} + //
+ //
+ // public function resolveEndpoint(): string { return "/api/na/jp/v1/deals/{$this->dealId}/is-valid"; } + //
+ //
+ // public function createDtoFromResponse(Response $response): ValidateDealResponseData { return ValidateDealResponseData::from($response->json()); } + //
} diff --git a/src/Requests/NominalAccounts/GetCounterpartyRequestStatusRequest.php b/src/Requests/NominalAccounts/GetCounterpartyRequestStatusRequest.php index 4720021..32c36ba 100644 --- a/src/Requests/NominalAccounts/GetCounterpartyRequestStatusRequest.php +++ b/src/Requests/NominalAccounts/GetCounterpartyRequestStatusRequest.php @@ -1,27 +1,49 @@ +// use Apoloz\AlfaBank\Data\NominalAccounts\CounterpartyRequestStatusData; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; +//
+//
+// class GetCounterpartyRequestStatusRequest extends Request { + //
+ // protected Method $method = Method::GET; + //
+ //
+ // public function __construct( protected string $requestId ) {} + //
+ //
+ // public function resolveEndpoint(): string { return "/api/na/jp/v1/beneficiaries/requests/{$this->requestId}/status"; } + //
+ //
+ // public function createDtoFromResponse(Response $response): CounterpartyRequestStatusData { return CounterpartyRequestStatusData::from($response->json()); } + //
} diff --git a/src/Requests/NominalAccounts/GetNominalAccountsRequest.php b/src/Requests/NominalAccounts/GetNominalAccountsRequest.php index c62f34b..e407b50 100644 --- a/src/Requests/NominalAccounts/GetNominalAccountsRequest.php +++ b/src/Requests/NominalAccounts/GetNominalAccountsRequest.php @@ -1,8 +1,16 @@ +// use Apoloz\AlfaBank\Data\NominalAccounts\NominalAccountData; +use Apoloz\AlfaBank\Traits\HasAlfaPagination; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; @@ -10,15 +18,33 @@ use Spatie\LaravelData\DataCollection; class GetNominalAccountsRequest extends Request { + use HasAlfaPagination; + protected Method $method = Method::GET; + public function __construct( + ?int $limit = 100, + ?int $offset = 0 + ) { + $this->limit = $limit; + $this->offset = $offset; + } + public function resolveEndpoint(): string { return '/api/na/jp/v1/nominal-accounts'; } + protected function defaultQuery(): array + { + return $this->getPaginationQuery(); + } + + //
+ // public function createDtoFromResponse(Response $response): DataCollection { return NominalAccountData::collect($response->json(), DataCollection::class); } + //
} diff --git a/src/Requests/NominalAccounts/Identification/GetIdentifyStatusRequest.php b/src/Requests/NominalAccounts/Identification/GetIdentifyStatusRequest.php index 7d80e00..e871253 100644 --- a/src/Requests/NominalAccounts/Identification/GetIdentifyStatusRequest.php +++ b/src/Requests/NominalAccounts/Identification/GetIdentifyStatusRequest.php @@ -1,28 +1,50 @@ +// use Apoloz\AlfaBank\Data\NominalAccounts\Identification\IdentifyResponseData; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; +//
+//
+// class GetIdentifyStatusRequest extends Request { + //
+ // protected Method $method = Method::GET; + //
+ //
+ // public function __construct( protected string $id, protected string $identifyId ) {} + //
+ //
+ // public function resolveEndpoint(): string { return "/api/na/jp/v1/unidentified-transactions/{$this->id}/identify/{$this->identifyId}"; } + //
+ //
+ // public function createDtoFromResponse(Response $response): IdentifyResponseData { return IdentifyResponseData::from($response->json()); } + //
} diff --git a/src/Requests/NominalAccounts/Identification/GetUnidentifiedTransactionsRequest.php b/src/Requests/NominalAccounts/Identification/GetUnidentifiedTransactionsRequest.php index c15e580..1639449 100644 --- a/src/Requests/NominalAccounts/Identification/GetUnidentifiedTransactionsRequest.php +++ b/src/Requests/NominalAccounts/Identification/GetUnidentifiedTransactionsRequest.php @@ -1,27 +1,48 @@ +// use Apoloz\AlfaBank\Data\NominalAccounts\Identification\UnidentifiedTransactionCollectionData; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; +//
+//
+// class GetUnidentifiedTransactionsRequest extends Request { + //
+ // protected Method $method = Method::GET; + //
+ //
+ // public function __construct( protected string $accountNumber, protected ?int $limit = null, protected ?int $offset = null, ) {} + //
+ //
+ // public function resolveEndpoint(): string { return '/api/na/jp/v1/unidentified-transactions'; } + //
+ //
+ // protected function defaultQuery(): array { return array_filter([ @@ -30,9 +51,13 @@ class GetUnidentifiedTransactionsRequest extends Request 'offset' => $this->offset, ]); } + //
+ //
+ // public function createDtoFromResponse(Response $response): UnidentifiedTransactionCollectionData { return UnidentifiedTransactionCollectionData::from($response->json()); } + //
} diff --git a/src/Requests/NominalAccounts/Identification/IdentifyTransactionRequest.php b/src/Requests/NominalAccounts/Identification/IdentifyTransactionRequest.php index 37ee602..74ec7d3 100644 --- a/src/Requests/NominalAccounts/Identification/IdentifyTransactionRequest.php +++ b/src/Requests/NominalAccounts/Identification/IdentifyTransactionRequest.php @@ -1,7 +1,14 @@ +// use Apoloz\AlfaBank\Data\NominalAccounts\Identification\IdentifyRequestData; use Apoloz\AlfaBank\Data\NominalAccounts\Identification\IdentifyResponseData; use Saloon\Contracts\Body\HasBody; @@ -9,30 +16,48 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; +//
+//
+// class IdentifyTransactionRequest extends Request implements HasBody { use HasJsonBody; + //
+ // protected Method $method = Method::POST; + //
+ //
+ // public function __construct( protected string $id, protected IdentifyRequestData $data ) {} + //
+ //
+ // public function resolveEndpoint(): string { return "/api/na/jp/v1/unidentified-transactions/{$this->id}/identify"; } + //
+ //
+ // protected function defaultBody(): array { return $this->data->toArray(); } + //
+ //
+ // public function createDtoFromResponse(Response $response): IdentifyResponseData { return IdentifyResponseData::from($response->json()); } + //
} diff --git a/src/Requests/NominalAccounts/Identification/RefundUnidentifiedTransactionRequest.php b/src/Requests/NominalAccounts/Identification/RefundUnidentifiedTransactionRequest.php index d7b0aa1..248eabf 100644 --- a/src/Requests/NominalAccounts/Identification/RefundUnidentifiedTransactionRequest.php +++ b/src/Requests/NominalAccounts/Identification/RefundUnidentifiedTransactionRequest.php @@ -1,7 +1,14 @@ +// use Apoloz\AlfaBank\Data\NominalAccounts\Identification\RefundUnidentifiedTransactionRequestData; use Apoloz\AlfaBank\Data\NominalAccounts\Identification\RefundUnidentifiedTransactionResponseData; use Saloon\Contracts\Body\HasBody; @@ -9,30 +16,48 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; +//
+//
+// class RefundUnidentifiedTransactionRequest extends Request implements HasBody { use HasJsonBody; + //
+ // protected Method $method = Method::POST; + //
+ //
+ // public function __construct( protected string $id, protected RefundUnidentifiedTransactionRequestData $data ) {} + //
+ //
+ // public function resolveEndpoint(): string { return "/api/na/jp/v1/unidentified-transactions/{$this->id}/refunds"; } + //
+ //
+ // protected function defaultBody(): array { return $this->data->toArray(); } + //
+ //
+ // public function createDtoFromResponse(Response $response): RefundUnidentifiedTransactionResponseData { return RefundUnidentifiedTransactionResponseData::from($response->json()); } + //
} diff --git a/src/Requests/NominalAccounts/Payouts/CreatePayoffRequest.php b/src/Requests/NominalAccounts/Payouts/CreatePayoffRequest.php index f593a19..081b52c 100644 --- a/src/Requests/NominalAccounts/Payouts/CreatePayoffRequest.php +++ b/src/Requests/NominalAccounts/Payouts/CreatePayoffRequest.php @@ -1,7 +1,14 @@ +// use Apoloz\AlfaBank\Data\NominalAccounts\Payouts\PayoffRequestData; use Apoloz\AlfaBank\Data\NominalAccounts\Payouts\PayoffResponseData; use Saloon\Contracts\Body\HasBody; @@ -9,29 +16,47 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; +//
+//
+// class CreatePayoffRequest extends Request implements HasBody { use HasJsonBody; + //
+ // protected Method $method = Method::POST; + //
+ //
+ // public function __construct( protected PayoffRequestData $data ) {} + //
+ //
+ // public function resolveEndpoint(): string { return '/api/na/jp/v1/payoffs'; } + //
+ //
+ // protected function defaultBody(): array { return $this->data->toArray(); } + //
+ //
+ // public function createDtoFromResponse(Response $response): PayoffResponseData { return PayoffResponseData::from($response->json()); } + //
} diff --git a/src/Requests/NominalAccounts/Payouts/CreateTaxRefundRequest.php b/src/Requests/NominalAccounts/Payouts/CreateTaxRefundRequest.php index 6b3cf2a..442ac07 100644 --- a/src/Requests/NominalAccounts/Payouts/CreateTaxRefundRequest.php +++ b/src/Requests/NominalAccounts/Payouts/CreateTaxRefundRequest.php @@ -1,7 +1,14 @@ +// use Apoloz\AlfaBank\Data\NominalAccounts\Payouts\TaxRefundRequestData; use Apoloz\AlfaBank\Data\NominalAccounts\Payouts\TaxRefundResponseData; use Saloon\Contracts\Body\HasBody; @@ -9,29 +16,47 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; +//
+//
+// class CreateTaxRefundRequest extends Request implements HasBody { use HasJsonBody; + //
+ // protected Method $method = Method::POST; + //
+ //
+ // public function __construct( protected TaxRefundRequestData $data ) {} + //
+ //
+ // public function resolveEndpoint(): string { return '/api/na/jp/v1/tax-refund'; } + //
+ //
+ // protected function defaultBody(): array { return $this->data->toArray(); } + //
+ //
+ // public function createDtoFromResponse(Response $response): TaxRefundResponseData { return TaxRefundResponseData::from($response->json()); } + //
} diff --git a/src/Requests/NominalAccounts/Payouts/GetPayoffRequest.php b/src/Requests/NominalAccounts/Payouts/GetPayoffRequest.php index bf1be39..aba485f 100644 --- a/src/Requests/NominalAccounts/Payouts/GetPayoffRequest.php +++ b/src/Requests/NominalAccounts/Payouts/GetPayoffRequest.php @@ -1,27 +1,49 @@ +// use Apoloz\AlfaBank\Data\NominalAccounts\Payouts\DetailedPayoffData; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; +//
+//
+// class GetPayoffRequest extends Request { + //
+ // protected Method $method = Method::GET; + //
+ //
+ // public function __construct( protected string $payoffId ) {} + //
+ //
+ // public function resolveEndpoint(): string { return "/api/na/jp/v1/payoffs/{$this->payoffId}"; } + //
+ //
+ // public function createDtoFromResponse(Response $response): DetailedPayoffData { return DetailedPayoffData::from($response->json()); } + //
} diff --git a/src/Requests/NominalAccounts/Payouts/GetPayoffStatusRequest.php b/src/Requests/NominalAccounts/Payouts/GetPayoffStatusRequest.php index 547d19f..55f387b 100644 --- a/src/Requests/NominalAccounts/Payouts/GetPayoffStatusRequest.php +++ b/src/Requests/NominalAccounts/Payouts/GetPayoffStatusRequest.php @@ -1,27 +1,49 @@ +// use Apoloz\AlfaBank\Data\NominalAccounts\Payouts\PayoffResponseData; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; +//
+//
+// class GetPayoffStatusRequest extends Request { + //
+ // protected Method $method = Method::GET; + //
+ //
+ // public function __construct( protected string $payoffId ) {} + //
+ //
+ // public function resolveEndpoint(): string { return "/api/na/jp/v1/payoffs/{$this->payoffId}/status"; } + //
+ //
+ // public function createDtoFromResponse(Response $response): PayoffResponseData { return PayoffResponseData::from($response->json()); } + //
} diff --git a/src/Requests/NominalAccounts/Payouts/GetTaxRefundStatusRequest.php b/src/Requests/NominalAccounts/Payouts/GetTaxRefundStatusRequest.php index 00f4737..38f2a7e 100644 --- a/src/Requests/NominalAccounts/Payouts/GetTaxRefundStatusRequest.php +++ b/src/Requests/NominalAccounts/Payouts/GetTaxRefundStatusRequest.php @@ -1,27 +1,49 @@ +// use Apoloz\AlfaBank\Data\NominalAccounts\Payouts\TaxRefundResponseData; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; +//
+//
+// class GetTaxRefundStatusRequest extends Request { + //
+ // protected Method $method = Method::GET; + //
+ //
+ // public function __construct( protected string $taxRefundId ) {} + //
+ //
+ // public function resolveEndpoint(): string { return "/api/na/jp/v1/tax-refund/{$this->taxRefundId}/status"; } + //
+ //
+ // public function createDtoFromResponse(Response $response): TaxRefundResponseData { return TaxRefundResponseData::from($response->json()); } + //
} diff --git a/src/Requests/NominalAccounts/Payouts/PayoffAcceptRequest.php b/src/Requests/NominalAccounts/Payouts/PayoffAcceptRequest.php index c6ecf1a..bb51254 100644 --- a/src/Requests/NominalAccounts/Payouts/PayoffAcceptRequest.php +++ b/src/Requests/NominalAccounts/Payouts/PayoffAcceptRequest.php @@ -1,38 +1,63 @@ +// use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; +//
+//
+// class PayoffAcceptRequest extends Request implements HasBody { use HasJsonBody; + //
+ // protected Method $method = Method::PATCH; + //
+ //
+ // public function __construct( protected string $payoffId, protected bool $accept = true ) {} + //
+ //
+ // public function resolveEndpoint(): string { return "/api/na/jp/v1/payoffs/{$this->payoffId}/accept"; } + //
+ //
+ // protected function defaultBody(): array { return [ 'accept' => $this->accept, ]; } + //
+ //
+ // public function createDtoFromResponse(Response $response): bool { return (bool)($response->json('result') ?? false); } + //
} diff --git a/src/Requests/NominalAccounts/Payouts/TaxRefundAcceptRequest.php b/src/Requests/NominalAccounts/Payouts/TaxRefundAcceptRequest.php index cbd6ed9..ff437de 100644 --- a/src/Requests/NominalAccounts/Payouts/TaxRefundAcceptRequest.php +++ b/src/Requests/NominalAccounts/Payouts/TaxRefundAcceptRequest.php @@ -1,36 +1,61 @@ +// use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; +//
+//
+// class TaxRefundAcceptRequest extends Request implements HasBody { use HasJsonBody; + //
+ // protected Method $method = Method::PATCH; + //
+ //
+ // public function __construct( protected string $taxRefundId, protected bool $accept ) {} + //
+ //
+ // public function resolveEndpoint(): string { return "/api/na/jp/v1/tax-refund/{$this->taxRefundId}/accept"; } + //
+ //
+ // protected function defaultQuery(): array { return ['accept' => $this->accept ? 'true' : 'false']; } + //
+ //
+ // public function createDtoFromResponse(Response $response): bool { return (bool)($response->json('result') ?? false); } + //
} diff --git a/src/Requests/NominalAccounts/UniversalRecipients/CreateUniversalRecipientRequest.php b/src/Requests/NominalAccounts/UniversalRecipients/CreateUniversalRecipientRequest.php index e4b0311..35c564c 100644 --- a/src/Requests/NominalAccounts/UniversalRecipients/CreateUniversalRecipientRequest.php +++ b/src/Requests/NominalAccounts/UniversalRecipients/CreateUniversalRecipientRequest.php @@ -1,7 +1,15 @@ +// use Apoloz\AlfaBank\Data\NominalAccounts\UniversalRecipients\CreateUniversalRecipientRequestData; use Apoloz\AlfaBank\Data\NominalAccounts\UniversalRecipients\UniversalRecipientResponseData; use Saloon\Contracts\Body\HasBody; @@ -9,29 +17,47 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; +//
+//
+// class CreateUniversalRecipientRequest extends Request implements HasBody { use HasJsonBody; + //
+ // protected Method $method = Method::POST; + //
+ //
+ // public function __construct( protected CreateUniversalRecipientRequestData $data ) {} + //
+ //
+ // public function resolveEndpoint(): string { return '/api/na/jp/v1/universal-recipients'; } + //
+ //
+ // protected function defaultBody(): array { return $this->data->toArray(); } + //
+ //
+ // public function createDtoFromResponse(Response $response): UniversalRecipientResponseData { return UniversalRecipientResponseData::from($response->json()); } + //
} diff --git a/src/Requests/NominalAccounts/UniversalRecipients/GetUniversalRecipientRequest.php b/src/Requests/NominalAccounts/UniversalRecipients/GetUniversalRecipientRequest.php index ed02a73..f7685be 100644 --- a/src/Requests/NominalAccounts/UniversalRecipients/GetUniversalRecipientRequest.php +++ b/src/Requests/NominalAccounts/UniversalRecipients/GetUniversalRecipientRequest.php @@ -1,27 +1,50 @@ +// use Apoloz\AlfaBank\Data\NominalAccounts\UniversalRecipients\UniversalRecipientData; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; +//
+//
+// class GetUniversalRecipientRequest extends Request { + //
+ // protected Method $method = Method::GET; + //
+ //
+ // public function __construct( protected string $universalRecipientId ) {} + //
+ //
+ // public function resolveEndpoint(): string { return "/api/na/jp/v1/universal-recipients/{$this->universalRecipientId}"; } + //
+ //
+ // public function createDtoFromResponse(Response $response): UniversalRecipientData { return UniversalRecipientData::from($response->json()); } + //
} diff --git a/src/Requests/NominalAccounts/UniversalRecipients/GetUniversalRecipientsRequest.php b/src/Requests/NominalAccounts/UniversalRecipients/GetUniversalRecipientsRequest.php index edcb902..7d6e733 100644 --- a/src/Requests/NominalAccounts/UniversalRecipients/GetUniversalRecipientsRequest.php +++ b/src/Requests/NominalAccounts/UniversalRecipients/GetUniversalRecipientsRequest.php @@ -1,17 +1,33 @@ +// use Apoloz\AlfaBank\Data\NominalAccounts\UniversalRecipients\UniversalRecipientData; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; use Spatie\LaravelData\DataCollection; +//
+//
+// class GetUniversalRecipientsRequest extends Request { + //
+ // protected Method $method = Method::GET; + //
+ //
+ // public function __construct( protected string $accountNumber, protected string $type, @@ -20,12 +36,18 @@ class GetUniversalRecipientsRequest extends Request protected ?bool $isActive = null, protected ?string $date = null, ) {} + //
+ //
+ // public function resolveEndpoint(): string { return '/api/na/jp/v1/universal-recipients'; } + //
+ //
+ // protected function defaultQuery(): array { return array_filter([ @@ -37,9 +59,13 @@ class GetUniversalRecipientsRequest extends Request 'date' => $this->date, ], fn($val) => $val !== null); } + //
+ //
+ // public function createDtoFromResponse(Response $response): DataCollection { return UniversalRecipientData::collect($response->json('universalRecipients'), DataCollection::class); } + //
} diff --git a/src/Requests/NominalAccounts/UniversalRecipients/Requisites/AddUniversalRecipientRequisiteRequest.php b/src/Requests/NominalAccounts/UniversalRecipients/Requisites/AddUniversalRecipientRequisiteRequest.php index 180b8dd..48b7ac2 100644 --- a/src/Requests/NominalAccounts/UniversalRecipients/Requisites/AddUniversalRecipientRequisiteRequest.php +++ b/src/Requests/NominalAccounts/UniversalRecipients/Requisites/AddUniversalRecipientRequisiteRequest.php @@ -1,7 +1,15 @@ +// use Apoloz\AlfaBank\Data\NominalAccounts\Beneficiaries\Requisites\CreateRequisiteRequestData; use Apoloz\AlfaBank\Data\NominalAccounts\Beneficiaries\Requisites\RequisiteData; use Saloon\Contracts\Body\HasBody; @@ -9,30 +17,48 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; +//
+//
+// class AddUniversalRecipientRequisiteRequest extends Request implements HasBody { use HasJsonBody; + //
+ // protected Method $method = Method::POST; + //
+ //
+ // public function __construct( protected string $universalRecipientId, protected CreateRequisiteRequestData $data ) {} + //
+ //
+ // public function resolveEndpoint(): string { return "/api/na/jp/v1/universal-recipients/{$this->universalRecipientId}/requisites"; } + //
+ //
+ // protected function defaultBody(): array { return $this->data->toArray(); } + //
+ //
+ // public function createDtoFromResponse(Response $response): RequisiteData { return RequisiteData::from($response->json()); } + //
} diff --git a/src/Requests/NominalAccounts/UniversalRecipients/Requisites/GetUniversalRecipientRequisitesRequest.php b/src/Requests/NominalAccounts/UniversalRecipients/Requisites/GetUniversalRecipientRequisitesRequest.php index 5f72338..e12c1ea 100644 --- a/src/Requests/NominalAccounts/UniversalRecipients/Requisites/GetUniversalRecipientRequisitesRequest.php +++ b/src/Requests/NominalAccounts/UniversalRecipients/Requisites/GetUniversalRecipientRequisitesRequest.php @@ -1,28 +1,51 @@ +// use Apoloz\AlfaBank\Data\NominalAccounts\Beneficiaries\Requisites\RequisiteData; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; use Spatie\LaravelData\DataCollection; +//
+//
+// class GetUniversalRecipientRequisitesRequest extends Request { + //
+ // protected Method $method = Method::GET; + //
+ //
+ // public function __construct( protected string $universalRecipientId ) {} + //
+ //
+ // public function resolveEndpoint(): string { return "/api/na/jp/v1/universal-recipients/{$this->universalRecipientId}/requisites"; } + //
+ //
+ // public function createDtoFromResponse(Response $response): DataCollection { return RequisiteData::collect($response->json(), DataCollection::class); } + //
} diff --git a/src/Requests/NominalAccounts/UniversalRecipients/Requisites/UpdateUniversalRecipientRequisiteRequest.php b/src/Requests/NominalAccounts/UniversalRecipients/Requisites/UpdateUniversalRecipientRequisiteRequest.php index 2995c93..21bde8e 100644 --- a/src/Requests/NominalAccounts/UniversalRecipients/Requisites/UpdateUniversalRecipientRequisiteRequest.php +++ b/src/Requests/NominalAccounts/UniversalRecipients/Requisites/UpdateUniversalRecipientRequisiteRequest.php @@ -1,32 +1,46 @@ +// +use Apoloz\AlfaBank\Data\Shared\GeneralStatusData; use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; class UpdateUniversalRecipientRequisiteRequest extends Request implements HasBody { use HasJsonBody; - protected Method $method = Method::PATCH; + protected Method $method = Method::PUT; public function __construct( protected string $universalRecipientId, protected string $requisiteId, - protected UpdateRequisiteRequestData $data + protected array $data ) {} public function resolveEndpoint(): string { - return "/api/na/jp/v1/universal-recipients/{$this->universalRecipientId}/requisites/{$this->requisiteId}"; + return "/api/na/jp/v1/universal-recipients/{$this->universalRecipientId}/requisites/{$this->universalRecipientId}"; } protected function defaultBody(): array { - return $this->data->toArray(); + return $this->data; + } + + public function createDtoFromResponse(Response $response): GeneralStatusData + { + return GeneralStatusData::from(['success' => $response->successful()]); } } diff --git a/src/Requests/Orders/CreateAfishaOrderRequest.php b/src/Requests/Orders/CreateAfishaOrderRequest.php new file mode 100644 index 0000000..7748955 --- /dev/null +++ b/src/Requests/Orders/CreateAfishaOrderRequest.php @@ -0,0 +1,37 @@ +data->toArray(); + } + + public function createDtoFromResponse(Response $response): OrderResponseData + { + return OrderResponseData::from($response->json()); + } +} \ No newline at end of file diff --git a/src/Requests/Orders/CreateTravelOrderRequest.php b/src/Requests/Orders/CreateTravelOrderRequest.php new file mode 100644 index 0000000..d4df89e --- /dev/null +++ b/src/Requests/Orders/CreateTravelOrderRequest.php @@ -0,0 +1,37 @@ +data->toArray(); + } + + public function createDtoFromResponse(Response $response): OrderResponseData + { + return OrderResponseData::from($response->json()); + } +} \ No newline at end of file diff --git a/src/Requests/Partner/GetPartnerApplicationsRequest.php b/src/Requests/Partner/GetPartnerApplicationsRequest.php new file mode 100644 index 0000000..d7ea2bd --- /dev/null +++ b/src/Requests/Partner/GetPartnerApplicationsRequest.php @@ -0,0 +1,41 @@ + $this->fromDate, + 'toDate' => $this->toDate, + 'limit' => $this->limit, + 'offset' => $this->offset, + ]); + } + + public function createDtoFromResponse(Response $response): DataCollection + { + return PartnerApplicationData::collect($response->json('items') ?? [], DataCollection::class); + } +} diff --git a/src/Requests/Payments/CreatePaymentRequest.php b/src/Requests/Payments/CreatePaymentRequest.php new file mode 100644 index 0000000..4107476 --- /dev/null +++ b/src/Requests/Payments/CreatePaymentRequest.php @@ -0,0 +1,40 @@ +data->toArray(); + } + + public function createDtoFromResponse(Response $response): PaymentResponseData + { + return PaymentResponseData::from([ + 'id' => $this->data->externalId, + 'status' => $response->status() === 201 ? 'CREATED' : 'ERROR', + ]); + } +} diff --git a/src/Requests/Payments/GetPaymentStateRequest.php b/src/Requests/Payments/GetPaymentStateRequest.php new file mode 100644 index 0000000..dfdb1f4 --- /dev/null +++ b/src/Requests/Payments/GetPaymentStateRequest.php @@ -0,0 +1,27 @@ +externalId}/state"; + } + + public function createDtoFromResponse(Response $response): PaymentStateData + { + return PaymentStateData::from($response->json()); + } +} diff --git a/src/Requests/Poa/CreatePoaRequest.php b/src/Requests/Poa/CreatePoaRequest.php new file mode 100644 index 0000000..feeb0e4 --- /dev/null +++ b/src/Requests/Poa/CreatePoaRequest.php @@ -0,0 +1,28 @@ +poaNumber}/inn/{$this->inn}/request"; + } + + public function createDtoFromResponse(Response $response): PoaResponseData + { + return PoaResponseData::from($response->json()); + } +} \ No newline at end of file diff --git a/src/Requests/Poa/GetPoaRequestStatusRequest.php b/src/Requests/Poa/GetPoaRequestStatusRequest.php new file mode 100644 index 0000000..af3541c --- /dev/null +++ b/src/Requests/Poa/GetPoaRequestStatusRequest.php @@ -0,0 +1,27 @@ +requestUuid}/status"; + } + + public function createDtoFromResponse(Response $response): PoaResponseData + { + return PoaResponseData::from($response->json()); + } +} \ No newline at end of file diff --git a/src/Requests/Rates/GetOfficeRatesRequest.php b/src/Requests/Rates/GetOfficeRatesRequest.php new file mode 100644 index 0000000..2bd1ab2 --- /dev/null +++ b/src/Requests/Rates/GetOfficeRatesRequest.php @@ -0,0 +1,24 @@ +json('offices') ?? [], DataCollection::class); + } +} diff --git a/src/Requests/Registries/CreateRegistryRequest.php b/src/Requests/Registries/CreateRegistryRequest.php new file mode 100644 index 0000000..59b8894 --- /dev/null +++ b/src/Requests/Registries/CreateRegistryRequest.php @@ -0,0 +1,37 @@ +data->toArray(); + } + + public function createDtoFromResponse(Response $response): RegistryResponseData + { + return RegistryResponseData::from($response->json()); + } +} diff --git a/src/Requests/Registries/CreateSelfEncashmentRegistryRequest.php b/src/Requests/Registries/CreateSelfEncashmentRegistryRequest.php new file mode 100644 index 0000000..67e872d --- /dev/null +++ b/src/Requests/Registries/CreateSelfEncashmentRegistryRequest.php @@ -0,0 +1,37 @@ +data->toArray(); + } + + public function createDtoFromResponse(Response $response): RegistryResponseData + { + return RegistryResponseData::from($response->json()); + } +} \ No newline at end of file diff --git a/src/Requests/Registries/SignRegistryRequest.php b/src/Requests/Registries/SignRegistryRequest.php new file mode 100644 index 0000000..0625292 --- /dev/null +++ b/src/Requests/Registries/SignRegistryRequest.php @@ -0,0 +1,44 @@ + */ + protected DataCollection $signatures, + ) {} + + public function resolveEndpoint(): string + { + return "/api/jp/v1/registries/{$this->registryId}/signatures"; + } + + protected function defaultBody(): array + { + return [ + 'digestSignatures' => $this->signatures->toArray(), + ]; + } + + public function createDtoFromResponse(Response $response): GeneralStatusData + { + return GeneralStatusData::from([ + 'success' => $response->successful(), + ]); + } +} diff --git a/src/Requests/Reports/CreateAccountBalanceReportRequest.php b/src/Requests/Reports/CreateAccountBalanceReportRequest.php new file mode 100644 index 0000000..32a7502 --- /dev/null +++ b/src/Requests/Reports/CreateAccountBalanceReportRequest.php @@ -0,0 +1,39 @@ +language === 'eng' ? 'eng' : 'rus'; + return "/api/pp/v1/reports/type/account/balance/{$path}"; + } + + protected function defaultBody(): array + { + return $this->data->toArray(); + } + + public function createDtoFromResponse(Response $response): RegistryResponseData + { + return RegistryResponseData::from($response->json()); + } +} diff --git a/src/Requests/Salary/CreateAccountApplicationRequest.php b/src/Requests/Salary/CreateAccountApplicationRequest.php new file mode 100644 index 0000000..decc1f3 --- /dev/null +++ b/src/Requests/Salary/CreateAccountApplicationRequest.php @@ -0,0 +1,45 @@ + $this->signatures, + ]); + } + + protected function defaultBody(): array + { + return $this->data->toArray(); + } + + public function createDtoFromResponse(Response $response): SalaryApplicationResponseData + { + return SalaryApplicationResponseData::from($response->json()); + } +} diff --git a/src/Requests/Salary/CreatePayrollApplicationRequest.php b/src/Requests/Salary/CreatePayrollApplicationRequest.php new file mode 100644 index 0000000..11db78d --- /dev/null +++ b/src/Requests/Salary/CreatePayrollApplicationRequest.php @@ -0,0 +1,45 @@ + $this->signatures, + ]); + } + + protected function defaultBody(): array + { + return $this->data->toArray(); + } + + public function createDtoFromResponse(Response $response): SalaryApplicationResponseData + { + return SalaryApplicationResponseData::from($response->json()); + } +} diff --git a/src/Requests/Salary/GetAccountApplicationStatusRequest.php b/src/Requests/Salary/GetAccountApplicationStatusRequest.php new file mode 100644 index 0000000..56d6697 --- /dev/null +++ b/src/Requests/Salary/GetAccountApplicationStatusRequest.php @@ -0,0 +1,27 @@ +applicationId}"; + } + + public function createDtoFromResponse(Response $response): SalaryApplicationResponseData + { + return SalaryApplicationResponseData::from($response->json()); + } +} diff --git a/src/Requests/Salary/GetPayrollApplicationStatusRequest.php b/src/Requests/Salary/GetPayrollApplicationStatusRequest.php new file mode 100644 index 0000000..4486d57 --- /dev/null +++ b/src/Requests/Salary/GetPayrollApplicationStatusRequest.php @@ -0,0 +1,27 @@ +applicationId}"; + } + + public function createDtoFromResponse(Response $response): SalaryApplicationResponseData + { + return SalaryApplicationResponseData::from($response->json()); + } +} diff --git a/src/Requests/Sandbox/Payments/CreateRubPaymentRequest.php b/src/Requests/Sandbox/Payments/CreateRubPaymentRequest.php index bc1f8c4..1dc7678 100644 --- a/src/Requests/Sandbox/Payments/CreateRubPaymentRequest.php +++ b/src/Requests/Sandbox/Payments/CreateRubPaymentRequest.php @@ -1,30 +1,60 @@ +// use Apoloz\AlfaBank\Data\Sandbox\Payments\CreateRubPaymentRequestData; +use Apoloz\AlfaBank\Data\Shared\GeneralStatusData; use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; +//
+//
+// class CreateRubPaymentRequest extends Request implements HasBody { use HasJsonBody; + //
+ // protected Method $method = Method::POST; + //
+ //
+ // public function __construct( protected CreateRubPaymentRequestData $data ) {} + //
+ //
+ // public function resolveEndpoint(): string { return '/api/jp/v2/payments'; } + //
+ //
+ // protected function defaultBody(): array { return $this->data->toArray(); } + //
+ + public function createDtoFromResponse(Response $response): GeneralStatusData + { + return GeneralStatusData::from(['success' => $response->successful()]); + } } diff --git a/src/Requests/Sandbox/SignatureEncryptionTestRequest.php b/src/Requests/Sandbox/SignatureEncryptionTestRequest.php index 455a08b..af12702 100644 --- a/src/Requests/Sandbox/SignatureEncryptionTestRequest.php +++ b/src/Requests/Sandbox/SignatureEncryptionTestRequest.php @@ -1,41 +1,44 @@ +// +use Apoloz\AlfaBank\Data\Sandbox\SandboxTestResponseData; use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; -use Saloon\Traits\Body\HasStringBody; +use Saloon\Http\Response; +use Saloon\Traits\Body\HasJsonBody; class SignatureEncryptionTestRequest extends Request implements HasBody { - use HasStringBody; + use HasJsonBody; protected Method $method = Method::POST; public function __construct( - protected string $jwsBody, - protected string $requestChannelSecurity = 'SIGNATURE', - protected string $responseChannelSecurity = 'SIGNATURE' + protected string $data ) {} public function resolveEndpoint(): string { - return '/api/signature-encryption-tests/json'; - } - - protected function defaultHeaders(): array - { - return [ - 'Accept' => 'application/jose', - 'Content-Type' => 'application/jose', - 'X-Request-ChannelSecurity' => $this->requestChannelSecurity, - 'X-Response-ChannelSecurity' => $this->responseChannelSecurity, - ]; + return '/api/sandbox/v1/test/signature-encryption'; } protected function defaultBody(): ?string { - return $this->jwsBody; + return $this->data; + } + + public function createDtoFromResponse(Response $response): SandboxTestResponseData + { + return SandboxTestResponseData::from(['result' => $response->body()]); } } diff --git a/src/Requests/Sandbox/TriggerWebhookEventRequest.php b/src/Requests/Sandbox/TriggerWebhookEventRequest.php index fc3b2ff..9ffbf7e 100644 --- a/src/Requests/Sandbox/TriggerWebhookEventRequest.php +++ b/src/Requests/Sandbox/TriggerWebhookEventRequest.php @@ -1,31 +1,61 @@ +// +use Apoloz\AlfaBank\Data\Shared\GeneralStatusData; use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; +//
+//
+// class TriggerWebhookEventRequest extends Request implements HasBody { use HasJsonBody; + //
+ // protected Method $method = Method::POST; + //
+ //
+ // public function __construct( protected string $object // e.g. ul_transaction_default ) {} + //
+ //
+ // public function resolveEndpoint(): string { return '/api/v1/webhooks-events'; } + //
+ //
+ // protected function defaultBody(): array { return [ 'object' => $this->object, ]; } + //
+ + public function createDtoFromResponse(Response $response): GeneralStatusData + { + return GeneralStatusData::from(['success' => $response->successful()]); + } } diff --git a/src/Requests/Scoring/GetComplianceScoreRequest.php b/src/Requests/Scoring/GetComplianceScoreRequest.php new file mode 100644 index 0000000..e4cc491 --- /dev/null +++ b/src/Requests/Scoring/GetComplianceScoreRequest.php @@ -0,0 +1,34 @@ + $this->inn, + ]; + } + + public function createDtoFromResponse(Response $response): ComplianceScoreData + { + return ComplianceScoreData::from($response->json()); + } +} diff --git a/src/Requests/Security/GetSubscriptionsRequest.php b/src/Requests/Security/GetSubscriptionsRequest.php new file mode 100644 index 0000000..34999b6 --- /dev/null +++ b/src/Requests/Security/GetSubscriptionsRequest.php @@ -0,0 +1,35 @@ + $this->active, + ]; + } + + public function createDtoFromResponse(Response $response): DataCollection + { + return SecuritySubscriptionData::collect($response->json('subscriptions') ?? [], DataCollection::class); + } +} diff --git a/src/Requests/SelfEmployed/CreateAgreementLinkRequest.php b/src/Requests/SelfEmployed/CreateAgreementLinkRequest.php new file mode 100644 index 0000000..bb60207 --- /dev/null +++ b/src/Requests/SelfEmployed/CreateAgreementLinkRequest.php @@ -0,0 +1,37 @@ +data->toArray(); + } + + public function createDtoFromResponse(Response $response): AgreementLinkData + { + return AgreementLinkData::from($response->json()); + } +} diff --git a/src/Requests/SelfEmployed/CreatePayoutRequest.php b/src/Requests/SelfEmployed/CreatePayoutRequest.php new file mode 100644 index 0000000..ad4bf5a --- /dev/null +++ b/src/Requests/SelfEmployed/CreatePayoutRequest.php @@ -0,0 +1,37 @@ +data->toArray(); + } + + public function createDtoFromResponse(Response $response): PayoutResponseData + { + return PayoutResponseData::from($response->json()); + } +} diff --git a/src/Requests/SelfEmployed/GetActivitiesRequest.php b/src/Requests/SelfEmployed/GetActivitiesRequest.php new file mode 100644 index 0000000..ac6b7b5 --- /dev/null +++ b/src/Requests/SelfEmployed/GetActivitiesRequest.php @@ -0,0 +1,24 @@ +json(), DataCollection::class); + } +} diff --git a/src/Requests/SelfEmployed/GetTaxpayerStatusRequest.php b/src/Requests/SelfEmployed/GetTaxpayerStatusRequest.php new file mode 100644 index 0000000..ed292e0 --- /dev/null +++ b/src/Requests/SelfEmployed/GetTaxpayerStatusRequest.php @@ -0,0 +1,37 @@ +data->toArray(); + } + + public function createDtoFromResponse(Response $response): TaxpayerStatusResponseData + { + return TaxpayerStatusResponseData::from($response->json()); + } +} diff --git a/src/Requests/Statement/Formats/GetTransactions1CRequest.php b/src/Requests/Statement/Formats/GetTransactions1CRequest.php new file mode 100644 index 0000000..8658344 --- /dev/null +++ b/src/Requests/Statement/Formats/GetTransactions1CRequest.php @@ -0,0 +1,65 @@ + +// +use Apoloz\AlfaBank\Data\Shared\DownloadedFileData; +use Saloon\Enums\Method; +use Saloon\Http\Request; +use Saloon\Http\Response; +//
+ +//
+// +class GetTransactions1CRequest extends Request +{ + //
+ // + protected Method $method = Method::GET; + //
+ + //
+ // + public function __construct( + protected string $accountNumber, + protected string $dateFrom, + protected string $dateTo + ) {} + //
+ + //
+ // + public function resolveEndpoint(): string + { + return "/api/jp/v2/accounts/{$this->accountNumber}/transactions/1C"; + } + //
+ + //
+ // + protected function defaultQuery(): array + { + return [ + 'dateFrom' => $this->dateFrom, + 'dateTo' => $this->dateTo, + ]; + } + //
+ + public function createDtoFromResponse(Response $response): DownloadedFileData + { + return DownloadedFileData::from([ + 'content' => $response->body(), + 'size' => strlen($response->body()), + ]); + } +} +//
diff --git a/src/Requests/Statement/Formats/GetTransactionsMT940Request.php b/src/Requests/Statement/Formats/GetTransactionsMT940Request.php new file mode 100644 index 0000000..7666c98 --- /dev/null +++ b/src/Requests/Statement/Formats/GetTransactionsMT940Request.php @@ -0,0 +1,65 @@ + +// +use Apoloz\AlfaBank\Data\Shared\DownloadedFileData; +use Saloon\Enums\Method; +use Saloon\Http\Request; +use Saloon\Http\Response; +//
+ +//
+// +class GetTransactionsMT940Request extends Request +{ + //
+ // + protected Method $method = Method::GET; + //
+ + //
+ // + public function __construct( + protected string $accountNumber, + protected string $dateFrom, + protected string $dateTo + ) {} + //
+ + //
+ // + public function resolveEndpoint(): string + { + return "/api/jp/v1/accounts/{$this->accountNumber}/transactions/MT940"; + } + //
+ + //
+ // + protected function defaultQuery(): array + { + return [ + 'dateFrom' => $this->dateFrom, + 'dateTo' => $this->dateTo, + ]; + } + //
+ + public function createDtoFromResponse(Response $response): DownloadedFileData + { + return DownloadedFileData::from([ + 'content' => $response->body(), + 'size' => strlen($response->body()), + ]); + } +} +//
diff --git a/src/Requests/Statement/GetTransactionsRequest.php b/src/Requests/Statement/GetTransactionsRequest.php new file mode 100644 index 0000000..1b9a2a2 --- /dev/null +++ b/src/Requests/Statement/GetTransactionsRequest.php @@ -0,0 +1,59 @@ + +// +use Apoloz\AlfaBank\Data\Statement\TransactionCollectionData; +use Apoloz\AlfaBank\Traits\HasAlfaPagination; +use Saloon\Enums\Method; +use Saloon\Http\Request; +use Saloon\Http\Response; + +class GetTransactionsRequest extends Request +{ + use HasAlfaPagination; + + protected Method $method = Method::GET; + + public function __construct( + protected string $accountNumber, + protected string $executeDateFrom, + protected string $executeDateTo, + ?int $limit = 1000, + ?int $offset = 0 + ) { + $this->limit = $limit; + $this->offset = $offset; + } + + public function resolveEndpoint(): string + { + return '/api/jp/v1/statement/transactions'; + } + + protected function defaultQuery(): array + { + return array_merge([ + 'accountNumber' => $this->accountNumber, + 'executeDateFrom' => $this->executeDateFrom, + 'executeDateTo' => $this->executeDateTo, + ], $this->getPaginationQuery()); + } + // + + //
+ // + public function createDtoFromResponse(Response $response): TransactionCollectionData + { + return TransactionCollectionData::from($response->json()); + } + //
+} diff --git a/src/Requests/Webhooks/CreateWebhookRequest.php b/src/Requests/Webhooks/CreateWebhookRequest.php index b743f4c..9358107 100644 --- a/src/Requests/Webhooks/CreateWebhookRequest.php +++ b/src/Requests/Webhooks/CreateWebhookRequest.php @@ -2,12 +2,13 @@ namespace Apoloz\AlfaBank\Requests\Webhooks; -use Apoloz\AlfaBank\Data\WebhookData; +use Apoloz\AlfaBank\Data\Webhooks\Subscriptions\WebhookSubscriptionData; +use Apoloz\AlfaBank\Data\Webhooks\Subscriptions\WebhookSubscriptionRequestData; +use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; -use Saloon\Contracts\Body\HasBody; class CreateWebhookRequest extends Request implements HasBody { @@ -16,34 +17,21 @@ class CreateWebhookRequest extends Request implements HasBody protected Method $method = Method::POST; public function __construct( - protected string $object, - protected string $callbackUri, - protected ?array $accounts = null, - protected int $version = 1 + protected WebhookSubscriptionRequestData $data ) {} public function resolveEndpoint(): string { - return '/api/v1/webhooks'; + return '/api/v2/webhooks'; } protected function defaultBody(): array { - $body = [ - 'object' => $this->object, - 'callbackUri' => $this->callbackUri, - 'version' => $this->version, - ]; - - if ($this->accounts !== null) { - $body['data'] = ['accounts' => $this->accounts]; - } - - return $body; + return $this->data->toArray(); } - public function createDtoFromResponse(Response $response): WebhookData + public function createDtoFromResponse(Response $response): WebhookSubscriptionData { - return WebhookData::from($response->json()); + return WebhookSubscriptionData::from($response->json()); } -} +} \ No newline at end of file diff --git a/src/Requests/Webhooks/DeleteWebhookRequest.php b/src/Requests/Webhooks/DeleteWebhookRequest.php index 68e06e1..0bc764c 100644 --- a/src/Requests/Webhooks/DeleteWebhookRequest.php +++ b/src/Requests/Webhooks/DeleteWebhookRequest.php @@ -2,27 +2,28 @@ namespace Apoloz\AlfaBank\Requests\Webhooks; +use Apoloz\AlfaBank\Data\Shared\GeneralStatusData; use Saloon\Enums\Method; use Saloon\Http\Request; +use Saloon\Http\Response; class DeleteWebhookRequest extends Request { protected Method $method = Method::DELETE; public function __construct( - protected string $id, - protected string $object + protected string $id ) {} public function resolveEndpoint(): string { - return "/api/v1/webhooks/{$this->id}"; + return "/api/v2/webhooks/{$this->id}"; } - protected function defaultQuery(): array + public function createDtoFromResponse(Response $response): GeneralStatusData { - return [ - 'object' => $this->object, - ]; + return GeneralStatusData::from([ + 'success' => $response->status() === 204 || $response->successful(), + ]); } -} +} \ No newline at end of file diff --git a/src/Requests/Webhooks/GetWebhookRequest.php b/src/Requests/Webhooks/GetWebhookRequest.php new file mode 100644 index 0000000..29c90ec --- /dev/null +++ b/src/Requests/Webhooks/GetWebhookRequest.php @@ -0,0 +1,27 @@ +id}"; + } + + public function createDtoFromResponse(Response $response): WebhookSubscriptionData + { + return WebhookSubscriptionData::from($response->json()); + } +} \ No newline at end of file diff --git a/src/Requests/Webhooks/GetWebhooksRequest.php b/src/Requests/Webhooks/GetWebhooksRequest.php index 6f18c7f..c38d00b 100644 --- a/src/Requests/Webhooks/GetWebhooksRequest.php +++ b/src/Requests/Webhooks/GetWebhooksRequest.php @@ -2,20 +2,27 @@ namespace Apoloz\AlfaBank\Requests\Webhooks; -use Apoloz\AlfaBank\Data\WebhookCollectionData; +use Apoloz\AlfaBank\Data\Webhooks\Subscriptions\WebhookSubscriptionData; +use Apoloz\AlfaBank\Traits\HasAlfaPagination; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; +use Spatie\LaravelData\DataCollection; class GetWebhooksRequest extends Request { + use HasAlfaPagination; + protected Method $method = Method::GET; public function __construct( - protected ?int $offset = 0, - protected ?int $limit = 20, - protected ?string $object = null - ) {} + protected ?string $object = null, + ?int $limit = 20, + ?int $offset = 0 + ) { + $this->limit = $limit; + $this->offset = $offset; + } public function resolveEndpoint(): string { @@ -24,15 +31,13 @@ class GetWebhooksRequest extends Request protected function defaultQuery(): array { - return array_filter([ - 'offset' => $this->offset, - 'limit' => $this->limit, + return array_filter(array_merge([ 'object' => $this->object, - ]); + ], $this->getPaginationQuery())); } - public function createDtoFromResponse(Response $response): WebhookCollectionData + public function createDtoFromResponse(Response $response): DataCollection { - return WebhookCollectionData::from($response->json()); + return WebhookSubscriptionData::collect($response->json(), DataCollection::class); } } diff --git a/src/Requests/Webhooks/UpdateWebhookRequest.php b/src/Requests/Webhooks/UpdateWebhookRequest.php index 4d6aee5..743c977 100644 --- a/src/Requests/Webhooks/UpdateWebhookRequest.php +++ b/src/Requests/Webhooks/UpdateWebhookRequest.php @@ -2,12 +2,13 @@ namespace Apoloz\AlfaBank\Requests\Webhooks; -use Apoloz\AlfaBank\Data\WebhookData; +use Apoloz\AlfaBank\Data\Webhooks\Subscriptions\WebhookSubscriptionData; +use Apoloz\AlfaBank\Data\Webhooks\Subscriptions\WebhookSubscriptionRequestData; +use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; -use Saloon\Contracts\Body\HasBody; class UpdateWebhookRequest extends Request implements HasBody { @@ -17,11 +18,7 @@ class UpdateWebhookRequest extends Request implements HasBody public function __construct( protected string $id, - protected string $object, - protected bool $enabled = true, - protected ?string $callbackUri = null, - protected ?array $accounts = null, - protected int $version = 1 + protected WebhookSubscriptionRequestData $data ) {} public function resolveEndpoint(): string @@ -31,25 +28,11 @@ class UpdateWebhookRequest extends Request implements HasBody protected function defaultBody(): array { - $body = [ - 'object' => $this->object, - 'enabled' => $this->enabled, - 'version' => $this->version, - ]; - - if ($this->callbackUri !== null) { - $body['callbackUri'] = $this->callbackUri; - } - - if ($this->accounts !== null) { - $body['data'] = ['accounts' => $this->accounts]; - } - - return $body; + return $this->data->toArray(); } - public function createDtoFromResponse(Response $response): WebhookData + public function createDtoFromResponse(Response $response): WebhookSubscriptionData { - return WebhookData::from($response->json()); + return WebhookSubscriptionData::from($response->json()); } -} +} \ No newline at end of file diff --git a/src/Sbp/Data/CashLink/ActivateCashQrRequestData.php b/src/Sbp/Data/CashLink/ActivateCashQrRequestData.php index 3273ae9..4076f31 100644 --- a/src/Sbp/Data/CashLink/ActivateCashQrRequestData.php +++ b/src/Sbp/Data/CashLink/ActivateCashQrRequestData.php @@ -1,11 +1,25 @@ +// use Spatie\LaravelData\Data; +// +//
+// class ActivateCashQrRequestData extends Data { + //
+ // public function __construct( public string $TermNo, public string $qrcId, @@ -17,4 +31,5 @@ class ActivateCashQrRequestData extends Data public ?string $qrTtl = null, public ?string $redirectUrl = null, ) {} + //
} diff --git a/src/Sbp/Data/CashLink/RegCashQrRequestData.php b/src/Sbp/Data/CashLink/RegCashQrRequestData.php index dda02af..14a8dae 100644 --- a/src/Sbp/Data/CashLink/RegCashQrRequestData.php +++ b/src/Sbp/Data/CashLink/RegCashQrRequestData.php @@ -1,11 +1,25 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class RegCashQrRequestData extends Data { + //
+ // public function __construct( public string $TermNo, public ?string $mediaType = null, @@ -14,4 +28,5 @@ class RegCashQrRequestData extends Data public ?string $redirectUrl = null, public ?string $qrcId = null, ) {} + //
} diff --git a/src/Sbp/Data/CashLink/RegCashQrResponseData.php b/src/Sbp/Data/CashLink/RegCashQrResponseData.php index 737b022..ed07d40 100644 --- a/src/Sbp/Data/CashLink/RegCashQrResponseData.php +++ b/src/Sbp/Data/CashLink/RegCashQrResponseData.php @@ -1,19 +1,34 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class RegCashQrResponseData extends Data { + //
+ // public function __construct( public int $ErrorCode, public string $message, public ?string $TermNo = null, - public ?string $content = null, + public ?string $content = null, // The raw QR string data (e.g., base64 encoded image or URL) public ?string $mediaType = null, - public ?string $payload = null, + public ?string $payload = null, // The QR payload URL public ?string $qrcId = null, public ?string $status = null, ) {} + //
} diff --git a/src/Sbp/Data/DigitalRuble/CashLink/ChangeAccForCashUniQrRequestData.php b/src/Sbp/Data/DigitalRuble/CashLink/ChangeAccForCashUniQrRequestData.php index db179d7..bdc8744 100644 --- a/src/Sbp/Data/DigitalRuble/CashLink/ChangeAccForCashUniQrRequestData.php +++ b/src/Sbp/Data/DigitalRuble/CashLink/ChangeAccForCashUniQrRequestData.php @@ -1,21 +1,36 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class ChangeAccForCashUniQrRequestData extends Data { + //
+ // public function __construct( public string $TermNo, public ?string $messageID = null, public ?string $qrcId = null, - public ?string $account = null, - public ?string $accountDR = null, + public ?string $account = null, // Current or target conventional account + public ?string $accountDR = null, // Current or target Digital Ruble account public ?string $NewTermNo = null, public ?string $NewMerchantId = null, public ?string $redirectUrl = null, public ?string $paymentPageUrl = null, public ?string $fraudScore = null, ) {} + //
} diff --git a/src/Sbp/Data/DigitalRuble/CashLink/ChangeAccForCashUniQrResponseData.php b/src/Sbp/Data/DigitalRuble/CashLink/ChangeAccForCashUniQrResponseData.php index dc72b2f..8f0b97a 100644 --- a/src/Sbp/Data/DigitalRuble/CashLink/ChangeAccForCashUniQrResponseData.php +++ b/src/Sbp/Data/DigitalRuble/CashLink/ChangeAccForCashUniQrResponseData.php @@ -1,11 +1,25 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class ChangeAccForCashUniQrResponseData extends Data { + //
+ // public function __construct( public int $ErrorCode, public string $message, @@ -13,7 +27,7 @@ class ChangeAccForCashUniQrResponseData extends Data public ?string $qrcId = null, public ?string $TermNo = null, public ?string $account = null, - public ?string $accountDR = null, + public ?string $accountDR = null, // Digital Ruble account public ?string $redirectUrl = null, public ?string $paymentPageUrl = null, public ?string $fraudScore = null, @@ -22,4 +36,5 @@ class ChangeAccForCashUniQrResponseData extends Data public ?string $status = null, public ?string $merchantId = null, ) {} + //
} diff --git a/src/Sbp/Data/DigitalRuble/CashLink/CreateCashUniQrRequestData.php b/src/Sbp/Data/DigitalRuble/CashLink/CreateCashUniQrRequestData.php index e8a4566..c950fec 100644 --- a/src/Sbp/Data/DigitalRuble/CashLink/CreateCashUniQrRequestData.php +++ b/src/Sbp/Data/DigitalRuble/CashLink/CreateCashUniQrRequestData.php @@ -1,11 +1,25 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class CreateCashUniQrRequestData extends Data { + //
+ // public function __construct( public string $TermNo, public ?string $messageID = null, @@ -17,4 +31,5 @@ class CreateCashUniQrRequestData extends Data public ?string $qrcId = null, public ?string $notificationUrl = null, ) {} + //
} diff --git a/src/Sbp/Data/DigitalRuble/CashLink/CreateCashUniQrResponseData.php b/src/Sbp/Data/DigitalRuble/CashLink/CreateCashUniQrResponseData.php index 13d7a10..04a5124 100644 --- a/src/Sbp/Data/DigitalRuble/CashLink/CreateCashUniQrResponseData.php +++ b/src/Sbp/Data/DigitalRuble/CashLink/CreateCashUniQrResponseData.php @@ -1,20 +1,35 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class CreateCashUniQrResponseData extends Data { + //
+ // public function __construct( public int $ErrorCode, public string $message, public ?string $TermNo = null, - public ?string $payload = null, - public ?string $image = null, + public ?string $payload = null, // The raw QR string data + public ?string $image = null, // URL to the QR image public ?string $mediaType = null, public ?string $qrcId = null, public ?string $messageID = null, public ?string $status = null, ) {} + //
} diff --git a/src/Sbp/Data/DigitalRuble/CashLink/GetCashUniQrStatusRequestData.php b/src/Sbp/Data/DigitalRuble/CashLink/GetCashUniQrStatusRequestData.php index e25e3ff..226c0ec 100644 --- a/src/Sbp/Data/DigitalRuble/CashLink/GetCashUniQrStatusRequestData.php +++ b/src/Sbp/Data/DigitalRuble/CashLink/GetCashUniQrStatusRequestData.php @@ -1,11 +1,25 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class GetCashUniQrStatusRequestData extends Data { + //
+ // public function __construct( public string $TermNo, public string $uniQrId, @@ -13,4 +27,5 @@ class GetCashUniQrStatusRequestData extends Data public ?string $messageID = null, public ?string $payrrn = null, ) {} + //
} diff --git a/src/Sbp/Data/DigitalRuble/CashLink/GetCashUniQrStatusResponseData.php b/src/Sbp/Data/DigitalRuble/CashLink/GetCashUniQrStatusResponseData.php index 4df9385..88dc056 100644 --- a/src/Sbp/Data/DigitalRuble/CashLink/GetCashUniQrStatusResponseData.php +++ b/src/Sbp/Data/DigitalRuble/CashLink/GetCashUniQrStatusResponseData.php @@ -1,11 +1,25 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class GetCashUniQrStatusResponseData extends Data { + //
+ // public function __construct( public int $ErrorCode, public string $message, @@ -28,4 +42,5 @@ class GetCashUniQrStatusResponseData extends Data public ?string $merchantId = null, public ?string $paymentServiceId = null, ) {} + //
} diff --git a/src/Sbp/Data/DigitalRuble/CreateUniQrRequestData.php b/src/Sbp/Data/DigitalRuble/CreateUniQrRequestData.php index 70d6ec0..39171ae 100644 --- a/src/Sbp/Data/DigitalRuble/CreateUniQrRequestData.php +++ b/src/Sbp/Data/DigitalRuble/CreateUniQrRequestData.php @@ -1,11 +1,25 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class CreateUniQrRequestData extends Data { + //
+ // public function __construct( public string $TermNo, public string $uniQrType = '02', // 01=Multi, 02=Single @@ -23,4 +37,5 @@ class CreateUniQrRequestData extends Data public ?string $SenderID = null, public ?string $SenderBankBIC = null, ) {} + //
} diff --git a/src/Sbp/Data/DigitalRuble/Notifications/PaymentDrNotificationData.php b/src/Sbp/Data/DigitalRuble/Notifications/PaymentDrNotificationData.php index 56a61b3..0983373 100644 --- a/src/Sbp/Data/DigitalRuble/Notifications/PaymentDrNotificationData.php +++ b/src/Sbp/Data/DigitalRuble/Notifications/PaymentDrNotificationData.php @@ -1,11 +1,25 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class PaymentDrNotificationData extends Data { + //
+ // public function __construct( public string $command, public string $payrrn, @@ -20,4 +34,5 @@ class PaymentDrNotificationData extends Data public ?string $timestamp = null, public ?string $purpose = null, ) {} + //
} diff --git a/src/Sbp/Data/Management/BlockQrRequestData.php b/src/Sbp/Data/Management/BlockQrRequestData.php index f95a7aa..662b34b 100644 --- a/src/Sbp/Data/Management/BlockQrRequestData.php +++ b/src/Sbp/Data/Management/BlockQrRequestData.php @@ -1,14 +1,29 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class BlockQrRequestData extends Data { + //
+ // public function __construct( public string $TermNo, public string $qrcId, public ?string $reason = null, ) {} + //
} diff --git a/src/Sbp/Data/QrCodeRequestData.php b/src/Sbp/Data/QrCodeRequestData.php index fa7dfbd..3339ad0 100644 --- a/src/Sbp/Data/QrCodeRequestData.php +++ b/src/Sbp/Data/QrCodeRequestData.php @@ -1,32 +1,81 @@ +// +use Spatie\LaravelData\Attributes\MapName; +use Spatie\LaravelData\Attributes\MapOutputName; // Note: Not used directly here, but generally relevant for output mapping use Spatie\LaravelData\Data; +use Spatie\LaravelData\Mappers\NameMapper; // Note: Not directly used, but part of the Spatie Data ecosystem +//
+//
+// class QrCodeRequestData extends Data { + //
+ // public function __construct( - public string $TermNo, - public string $qrcType = '01', // '01' = Static, '02' = Dynamic + // + #[MapName('TermNo')] + public string $termNo, + + // + #[MapName('qrcType')] // Although same name, good to be explicit + public string $qrcType = '01', + + // public ?string $amount = null, + + // public string $currency = 'RUB', + + // public ?string $paymentPurpose = null, + + // public ?string $redirectUrl = null, + + // public ?string $messageID = null, + + // public ?string $width = null, public ?string $height = null, + + // public ?string $notificationUrl = null, // Dynamic QR specific fields - public ?string $OrderNumber = null, - public ?string $qrTtl = null, // Minutes, 1-6 + // + #[MapName('OrderNumber')] + public ?string $orderNumber = null, + + // + public ?string $qrTtl = null, + + // public ?string $subscriptionServiceId = null, public ?string $subscriptionServiceName = null, - // Optional user data for Dynamic QR - public ?string $SenderFIO = null, - public ?string $SenderID = null, - public ?string $SenderBankBIC = null, + // Optional user data for Dynamic QR (often part of sender identification) + // + #[MapName('SenderFIO')] + public ?string $senderFio = null, + + #[MapName('SenderID')] + public ?string $senderId = null, + + #[MapName('SenderBankBIC')] + public ?string $senderBankBic = null, ) {} -} \ No newline at end of file + //
+} diff --git a/src/Sbp/Data/QrCodeResponseData.php b/src/Sbp/Data/QrCodeResponseData.php index 545ab73..164d233 100644 --- a/src/Sbp/Data/QrCodeResponseData.php +++ b/src/Sbp/Data/QrCodeResponseData.php @@ -1,11 +1,25 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class QrCodeResponseData extends Data { + //
+ // public function __construct( public int $ErrorCode, public ?string $message = null, @@ -17,4 +31,6 @@ class QrCodeResponseData extends Data public ?string $messageID = null, public ?string $status = null, ) {} -} \ No newline at end of file + //
+} +//
\ No newline at end of file diff --git a/src/Sbp/Data/Registry/GetBankListResponseData.php b/src/Sbp/Data/Registry/GetBankListResponseData.php index 906d3d1..5f7f5b6 100644 --- a/src/Sbp/Data/Registry/GetBankListResponseData.php +++ b/src/Sbp/Data/Registry/GetBankListResponseData.php @@ -1,14 +1,29 @@ +// use Spatie\LaravelData\Data; +// +//
+// class GetBankListResponseData extends Data { + //
+ // public function __construct( public int $ErrorCode, public string $message, - public ?array $BankList = null, + public ?array $BankList = null, // Array of bank details, could be DataCollectionOf(BankData::class) ) {} + //
} diff --git a/src/Sbp/Data/Reversal/GetQrcReversalDataRequestData.php b/src/Sbp/Data/Reversal/GetQrcReversalDataRequestData.php index 6027282..6d0d590 100644 --- a/src/Sbp/Data/Reversal/GetQrcReversalDataRequestData.php +++ b/src/Sbp/Data/Reversal/GetQrcReversalDataRequestData.php @@ -1,11 +1,25 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class GetQrcReversalDataRequestData extends Data { + //
+ // public function __construct( public string $TermNo, public ?string $qrcId = null, @@ -17,4 +31,5 @@ class GetQrcReversalDataRequestData extends Data public ?string $messageID = null, public ?bool $ReturnRestAmount = null, ) {} + //
} diff --git a/src/Sbp/Data/Reversal/GetQrcReversalDataResponseData.php b/src/Sbp/Data/Reversal/GetQrcReversalDataResponseData.php index 31a047b..865d50a 100644 --- a/src/Sbp/Data/Reversal/GetQrcReversalDataResponseData.php +++ b/src/Sbp/Data/Reversal/GetQrcReversalDataResponseData.php @@ -1,11 +1,25 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class GetQrcReversalDataResponseData extends Data { + //
+ // public function __construct( public int $ErrorCode, public string $message, @@ -21,4 +35,5 @@ class GetQrcReversalDataResponseData extends Data public ?string $trxId = null, public ?string $restAmount = null, ) {} + //
} diff --git a/src/Sbp/Data/Reversal/GetQrcReversalStatusRequestData.php b/src/Sbp/Data/Reversal/GetQrcReversalStatusRequestData.php index ff4aa4b..6c6a25d 100644 --- a/src/Sbp/Data/Reversal/GetQrcReversalStatusRequestData.php +++ b/src/Sbp/Data/Reversal/GetQrcReversalStatusRequestData.php @@ -1,11 +1,25 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class GetQrcReversalStatusRequestData extends Data { + //
+ // public function __construct( public string $TermNo, public ?string $payrrn = null, @@ -13,4 +27,5 @@ class GetQrcReversalStatusRequestData extends Data public ?string $trxId = null, public ?string $messageID = null, ) {} + //
} diff --git a/src/Sbp/Data/Reversal/GetQrcReversalStatusResponseData.php b/src/Sbp/Data/Reversal/GetQrcReversalStatusResponseData.php index 64da818..6b89cf4 100644 --- a/src/Sbp/Data/Reversal/GetQrcReversalStatusResponseData.php +++ b/src/Sbp/Data/Reversal/GetQrcReversalStatusResponseData.php @@ -1,11 +1,25 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class GetQrcReversalStatusResponseData extends Data { + //
+ // public function __construct( public int $ErrorCode, public ?string $message = null, @@ -20,4 +34,5 @@ class GetQrcReversalStatusResponseData extends Data public ?string $trxDT = null, public ?string $trxId = null, ) {} + //
} diff --git a/src/Sbp/Data/Reversal/QrcReversalRequestData.php b/src/Sbp/Data/Reversal/QrcReversalRequestData.php index 2a94706..3fdac3f 100644 --- a/src/Sbp/Data/Reversal/QrcReversalRequestData.php +++ b/src/Sbp/Data/Reversal/QrcReversalRequestData.php @@ -1,11 +1,25 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class QrcReversalRequestData extends Data { + //
+ // public function __construct( public string $TermNo, public ?string $qrcId = null, @@ -19,4 +33,5 @@ class QrcReversalRequestData extends Data public ?bool $ReturnRestAmount = null, // extJsonParams not fully implemented as it's optional and dynamic ) {} + //
} diff --git a/src/Sbp/Data/Reversal/QrcReversalResponseData.php b/src/Sbp/Data/Reversal/QrcReversalResponseData.php index cea3140..7a4e758 100644 --- a/src/Sbp/Data/Reversal/QrcReversalResponseData.php +++ b/src/Sbp/Data/Reversal/QrcReversalResponseData.php @@ -1,11 +1,25 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class QrcReversalResponseData extends Data { + //
+ // public function __construct( public int $ErrorCode, public ?string $message = null, @@ -18,4 +32,5 @@ class QrcReversalResponseData extends Data public ?string $payrrn = null, public ?string $restAmount = null, ) {} + //
} diff --git a/src/Sbp/Data/Status/GetQrcStatusRequestData.php b/src/Sbp/Data/Status/GetQrcStatusRequestData.php index 5207d46..db8a270 100644 --- a/src/Sbp/Data/Status/GetQrcStatusRequestData.php +++ b/src/Sbp/Data/Status/GetQrcStatusRequestData.php @@ -1,15 +1,30 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class GetQrcStatusRequestData extends Data { + //
+ // public function __construct( public string $TermNo, public ?string $qrcId = null, public ?string $payrrn = null, public ?string $messageID = null, ) {} + //
} diff --git a/src/Sbp/Data/Status/GetQrcStatusResponseData.php b/src/Sbp/Data/Status/GetQrcStatusResponseData.php index 493f361..de83665 100644 --- a/src/Sbp/Data/Status/GetQrcStatusResponseData.php +++ b/src/Sbp/Data/Status/GetQrcStatusResponseData.php @@ -1,11 +1,25 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class GetQrcStatusResponseData extends Data { + //
+ // public function __construct( public int $ErrorCode, public string $message, @@ -24,4 +38,5 @@ class GetQrcStatusResponseData extends Data public ?string $messageID = null, public ?string $merchantId = null, ) {} + //
} diff --git a/src/Sbp/Data/Subscription/RegQrSubRequestData.php b/src/Sbp/Data/Subscription/RegQrSubRequestData.php index a7421bd..ad9f2d1 100644 --- a/src/Sbp/Data/Subscription/RegQrSubRequestData.php +++ b/src/Sbp/Data/Subscription/RegQrSubRequestData.php @@ -1,19 +1,34 @@ +// use Spatie\LaravelData\Data; +//
+//
+// class RegQrSubRequestData extends Data { + //
+ // public function __construct( public string $TermNo, public string $subscriptionPurpose, public string $notificationUrl, - public string $qrcType = '03', + public string $qrcType = '03', // '03' = Subscription public ?string $qrTtl = null, public ?string $redirectUrl = null, public ?string $subscriptionServiceId = null, public ?string $subscriptionServiceName = null, ) {} + //
} diff --git a/src/Sbp/Requests/CashLink/ActivateCashQrRequest.php b/src/Sbp/Requests/CashLink/ActivateCashQrRequest.php index 03dfddd..aaf2dee 100644 --- a/src/Sbp/Requests/CashLink/ActivateCashQrRequest.php +++ b/src/Sbp/Requests/CashLink/ActivateCashQrRequest.php @@ -1,29 +1,50 @@ +// use Apoloz\AlfaBank\Sbp\Data\CashLink\ActivateCashQrRequestData; use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; +//
+//
+// class ActivateCashQrRequest extends Request implements HasBody { use HasJsonBody; + //
+ // protected Method $method = Method::POST; + //
+ //
+ // public function __construct( protected ActivateCashQrRequestData $data ) {} + //
+ // public function resolveEndpoint(): string { - return ''; + return ''; // Endpoint is determined by SbpConnector's base URL } + //
+ //
+ // protected function defaultBody(): array { $body = [ @@ -44,9 +65,13 @@ class ActivateCashQrRequest extends Request implements HasBody return $body; } + //
+ //
+ // public function createDtoFromResponse(Response $response): array { return $response->json(); } + //
} diff --git a/src/Sbp/Requests/CashLink/ChangeAccountForCashQrRequest.php b/src/Sbp/Requests/CashLink/ChangeAccountForCashQrRequest.php index 1acb559..3e26466 100644 --- a/src/Sbp/Requests/CashLink/ChangeAccountForCashQrRequest.php +++ b/src/Sbp/Requests/CashLink/ChangeAccountForCashQrRequest.php @@ -1,18 +1,37 @@ +// use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Traits\Body\HasJsonBody; +// +// use Saloon\Http\Response; // Explicit import would be cleaner +//
+ +//
+// class ChangeAccountForCashQrRequest extends Request implements HasBody { use HasJsonBody; + //
+ // protected Method $method = Method::POST; + //
+ //
+ // public function __construct( protected string $TermNo, protected string $qrcId, @@ -22,11 +41,16 @@ class ChangeAccountForCashQrRequest extends Request implements HasBody protected ?string $redirectUrl = null, ) {} + //
+ // public function resolveEndpoint(): string { - return ''; + return ''; // Endpoint is determined by SbpConnector's base URL } + //
+ //
+ // protected function defaultBody(): array { $body = [ @@ -42,9 +66,13 @@ class ChangeAccountForCashQrRequest extends Request implements HasBody return $body; } + //
+ //
+ // public function createDtoFromResponse(\Saloon\Http\Response $response): array { return $response->json(); } + //
} diff --git a/src/Sbp/Requests/CashLink/ChangeTermNoForCashQrRequest.php b/src/Sbp/Requests/CashLink/ChangeTermNoForCashQrRequest.php index cb9c507..821e17c 100644 --- a/src/Sbp/Requests/CashLink/ChangeTermNoForCashQrRequest.php +++ b/src/Sbp/Requests/CashLink/ChangeTermNoForCashQrRequest.php @@ -1,28 +1,52 @@ +// use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Traits\Body\HasJsonBody; +// +// use Saloon\Http\Response; // Explicit import would be cleaner +//
+ +//
+// class ChangeTermNoForCashQrRequest extends Request implements HasBody { use HasJsonBody; + //
+ // protected Method $method = Method::POST; + //
+ //
+ // public function __construct( protected string $TermNo, protected string $qrcId ) {} + //
+ // public function resolveEndpoint(): string { - return ''; + return ''; // Endpoint is determined by SbpConnector's base URL } + //
+ //
+ // protected function defaultBody(): array { return [ @@ -31,9 +55,13 @@ class ChangeTermNoForCashQrRequest extends Request implements HasBody 'qrcId' => $this->qrcId, ]; } + //
+ //
+ // public function createDtoFromResponse(\Saloon\Http\Response $response): array { return $response->json(); } + //
} diff --git a/src/Sbp/Requests/CashLink/DeleteCashQrRequest.php b/src/Sbp/Requests/CashLink/DeleteCashQrRequest.php index 1740ac4..94d2fa2 100644 --- a/src/Sbp/Requests/CashLink/DeleteCashQrRequest.php +++ b/src/Sbp/Requests/CashLink/DeleteCashQrRequest.php @@ -1,28 +1,52 @@ +// use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Traits\Body\HasJsonBody; +// +// use Saloon\Http\Response; // Explicit import would be cleaner +//
+ +//
+// class DeleteCashQrRequest extends Request implements HasBody { use HasJsonBody; + //
+ // protected Method $method = Method::POST; + //
+ //
+ // public function __construct( protected string $TermNo, protected string $qrcId ) {} + //
+ // public function resolveEndpoint(): string { - return ''; + return ''; // Endpoint is determined by SbpConnector's base URL } + //
+ //
+ // protected function defaultBody(): array { return [ @@ -31,9 +55,13 @@ class DeleteCashQrRequest extends Request implements HasBody 'qrcId' => $this->qrcId, ]; } + //
+ //
+ // public function createDtoFromResponse(\Saloon\Http\Response $response): array { return $response->json(); } + //
} diff --git a/src/Sbp/Requests/CashLink/GetCashQrHistoryRequest.php b/src/Sbp/Requests/CashLink/GetCashQrHistoryRequest.php index b46a338..3149732 100644 --- a/src/Sbp/Requests/CashLink/GetCashQrHistoryRequest.php +++ b/src/Sbp/Requests/CashLink/GetCashQrHistoryRequest.php @@ -1,18 +1,37 @@ +// use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Traits\Body\HasJsonBody; +// +// use Saloon\Http\Response; // Explicit import would be cleaner +//
+ +//
+// class GetCashQrHistoryRequest extends Request implements HasBody { use HasJsonBody; + //
+ // protected Method $method = Method::POST; + //
+ //
+ // public function __construct( protected string $TermNo, protected string $qrcId, @@ -20,11 +39,16 @@ class GetCashQrHistoryRequest extends Request implements HasBody protected string $dateTo ) {} + //
+ // public function resolveEndpoint(): string { - return ''; + return ''; // Endpoint is determined by SbpConnector's base URL } + //
+ //
+ // protected function defaultBody(): array { return [ @@ -35,9 +59,13 @@ class GetCashQrHistoryRequest extends Request implements HasBody 'dateTo' => $this->dateTo, ]; } + //
+ //
+ // public function createDtoFromResponse(\Saloon\Http\Response $response): array { return $response->json(); } + //
} diff --git a/src/Sbp/Requests/CashLink/GetCashQrStatusRequest.php b/src/Sbp/Requests/CashLink/GetCashQrStatusRequest.php index 7d6987d..2b8ca70 100644 --- a/src/Sbp/Requests/CashLink/GetCashQrStatusRequest.php +++ b/src/Sbp/Requests/CashLink/GetCashQrStatusRequest.php @@ -1,28 +1,52 @@ +// use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Traits\Body\HasJsonBody; +// +// use Saloon\Http\Response; // Explicit import would be cleaner +//
+ +//
+// class GetCashQrStatusRequest extends Request implements HasBody { use HasJsonBody; + //
+ // protected Method $method = Method::POST; + //
+ //
+ // public function __construct( protected string $TermNo, protected string $qrcId ) {} + //
+ // public function resolveEndpoint(): string { - return ''; + return ''; // Endpoint is determined by SbpConnector's base URL } + //
+ //
+ // protected function defaultBody(): array { return [ @@ -31,9 +55,13 @@ class GetCashQrStatusRequest extends Request implements HasBody 'qrcId' => $this->qrcId, ]; } + //
+ //
+ // public function createDtoFromResponse(\Saloon\Http\Response $response): array { return $response->json(); } + //
} diff --git a/src/Sbp/Requests/CashLink/RegCashQrRequest.php b/src/Sbp/Requests/CashLink/RegCashQrRequest.php index 644f37b..3c4008c 100644 --- a/src/Sbp/Requests/CashLink/RegCashQrRequest.php +++ b/src/Sbp/Requests/CashLink/RegCashQrRequest.php @@ -1,7 +1,15 @@ +// use Apoloz\AlfaBank\Sbp\Data\CashLink\RegCashQrRequestData; use Apoloz\AlfaBank\Sbp\Data\CashLink\RegCashQrResponseData; use Saloon\Contracts\Body\HasBody; @@ -9,22 +17,35 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; +//
+//
+// class RegCashQrRequest extends Request implements HasBody { use HasJsonBody; + //
+ // protected Method $method = Method::POST; + //
+ //
+ // public function __construct( protected RegCashQrRequestData $data ) {} + //
+ // public function resolveEndpoint(): string { - return ''; + return ''; // Endpoint is determined by SbpConnector's base URL } + //
+ //
+ // protected function defaultBody(): array { $body = [ @@ -40,9 +61,13 @@ class RegCashQrRequest extends Request implements HasBody return $body; } + //
+ //
+ // public function createDtoFromResponse(Response $response): RegCashQrResponseData { return RegCashQrResponseData::from($response->json()); } + //
} diff --git a/src/Sbp/Requests/DigitalRuble/CashLink/ChangeAccForCashUniQrRequest.php b/src/Sbp/Requests/DigitalRuble/CashLink/ChangeAccForCashUniQrRequest.php index c3efc2d..310b16c 100644 --- a/src/Sbp/Requests/DigitalRuble/CashLink/ChangeAccForCashUniQrRequest.php +++ b/src/Sbp/Requests/DigitalRuble/CashLink/ChangeAccForCashUniQrRequest.php @@ -1,7 +1,15 @@ +// use Apoloz\AlfaBank\Sbp\Data\DigitalRuble\CashLink\ChangeAccForCashUniQrRequestData; use Apoloz\AlfaBank\Sbp\Data\DigitalRuble\CashLink\ChangeAccForCashUniQrResponseData; use Saloon\Contracts\Body\HasBody; @@ -9,22 +17,35 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; +//
+//
+// class ChangeAccForCashUniQrRequest extends Request implements HasBody { use HasJsonBody; + //
+ // protected Method $method = Method::POST; + //
+ //
+ // public function __construct( protected ChangeAccForCashUniQrRequestData $data ) {} + //
+ // public function resolveEndpoint(): string { - return ''; + return ''; // Endpoint is determined by SbpConnector's base URL } + //
+ //
+ // protected function defaultBody(): array { $body = [ @@ -44,9 +65,13 @@ class ChangeAccForCashUniQrRequest extends Request implements HasBody return $body; } + //
+ //
+ // public function createDtoFromResponse(Response $response): ChangeAccForCashUniQrResponseData { return ChangeAccForCashUniQrResponseData::from($response->json()); } + //
} diff --git a/src/Sbp/Requests/DigitalRuble/CashLink/CreateCashUniQrRequest.php b/src/Sbp/Requests/DigitalRuble/CashLink/CreateCashUniQrRequest.php index 272945f..45a5d6a 100644 --- a/src/Sbp/Requests/DigitalRuble/CashLink/CreateCashUniQrRequest.php +++ b/src/Sbp/Requests/DigitalRuble/CashLink/CreateCashUniQrRequest.php @@ -1,7 +1,15 @@ +// use Apoloz\AlfaBank\Sbp\Data\DigitalRuble\CashLink\CreateCashUniQrRequestData; use Apoloz\AlfaBank\Sbp\Data\DigitalRuble\CashLink\CreateCashUniQrResponseData; use Saloon\Contracts\Body\HasBody; @@ -9,22 +17,35 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; +//
+//
+// class CreateCashUniQrRequest extends Request implements HasBody { use HasJsonBody; + //
+ // protected Method $method = Method::POST; + //
+ //
+ // public function __construct( protected CreateCashUniQrRequestData $data ) {} + //
+ // public function resolveEndpoint(): string { - return ''; + return ''; // Endpoint is determined by SbpConnector's base URL } + //
+ //
+ // protected function defaultBody(): array { $body = [ @@ -46,9 +67,13 @@ class CreateCashUniQrRequest extends Request implements HasBody return $body; } + //
+ //
+ // public function createDtoFromResponse(Response $response): CreateCashUniQrResponseData { return CreateCashUniQrResponseData::from($response->json()); } + //
} diff --git a/src/Sbp/Requests/DigitalRuble/CashLink/GetCashUniQrStatusRequest.php b/src/Sbp/Requests/DigitalRuble/CashLink/GetCashUniQrStatusRequest.php index 6413217..e40a6c0 100644 --- a/src/Sbp/Requests/DigitalRuble/CashLink/GetCashUniQrStatusRequest.php +++ b/src/Sbp/Requests/DigitalRuble/CashLink/GetCashUniQrStatusRequest.php @@ -1,7 +1,15 @@ +// use Apoloz\AlfaBank\Sbp\Data\DigitalRuble\CashLink\GetCashUniQrStatusRequestData; use Apoloz\AlfaBank\Sbp\Data\DigitalRuble\CashLink\GetCashUniQrStatusResponseData; use Saloon\Contracts\Body\HasBody; @@ -9,22 +17,35 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; +//
+//
+// class GetCashUniQrStatusRequest extends Request implements HasBody { use HasJsonBody; + //
+ // protected Method $method = Method::POST; + //
+ //
+ // public function __construct( protected GetCashUniQrStatusRequestData $data ) {} + //
+ // public function resolveEndpoint(): string { - return ''; + return ''; // Endpoint is determined by SbpConnector's base URL } + //
+ //
+ // protected function defaultBody(): array { $body = [ @@ -39,9 +60,13 @@ class GetCashUniQrStatusRequest extends Request implements HasBody return $body; } + //
+ //
+ // public function createDtoFromResponse(Response $response): GetCashUniQrStatusResponseData { return GetCashUniQrStatusResponseData::from($response->json()); } + //
} diff --git a/src/Sbp/Requests/DigitalRuble/CreateUniQrRequest.php b/src/Sbp/Requests/DigitalRuble/CreateUniQrRequest.php index 2b2ffae..a06a111 100644 --- a/src/Sbp/Requests/DigitalRuble/CreateUniQrRequest.php +++ b/src/Sbp/Requests/DigitalRuble/CreateUniQrRequest.php @@ -1,28 +1,52 @@ +// use Apoloz\AlfaBank\Sbp\Data\DigitalRuble\CreateUniQrRequestData; use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Traits\Body\HasJsonBody; +// +// use Saloon\Http\Response; // Explicit import would be cleaner +//
+ +//
+// class CreateUniQrRequest extends Request implements HasBody { use HasJsonBody; + //
+ // protected Method $method = Method::POST; + //
+ //
+ // public function __construct( protected CreateUniQrRequestData $data ) {} + //
+ // public function resolveEndpoint(): string { - return ''; + return ''; // Endpoint is determined by SbpConnector's base URL } + //
+ //
+ // protected function defaultBody(): array { $body = [ @@ -54,9 +78,13 @@ class CreateUniQrRequest extends Request implements HasBody return $body; } + //
+ //
+ // public function createDtoFromResponse(\Saloon\Http\Response $response): array { return $response->json(); } + //
} diff --git a/src/Sbp/Requests/DigitalRuble/GetUniQrStatusRequest.php b/src/Sbp/Requests/DigitalRuble/GetUniQrStatusRequest.php index 8578964..2145aa0 100644 --- a/src/Sbp/Requests/DigitalRuble/GetUniQrStatusRequest.php +++ b/src/Sbp/Requests/DigitalRuble/GetUniQrStatusRequest.php @@ -1,28 +1,52 @@ +// use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Traits\Body\HasJsonBody; +// +// use Saloon\Http\Response; // Explicit import would be cleaner +//
+ +//
+// class GetUniQrStatusRequest extends Request implements HasBody { use HasJsonBody; + //
+ // protected Method $method = Method::POST; + //
+ //
+ // public function __construct( protected string $TermNo, protected array $uniQrIds, // Array of strings ) {} + //
+ // public function resolveEndpoint(): string { - return ''; + return ''; // Endpoint is determined by SbpConnector's base URL } + //
+ //
+ // protected function defaultBody(): array { return [ @@ -31,9 +55,13 @@ class GetUniQrStatusRequest extends Request implements HasBody 'uniQrIds' => $this->uniQrIds, ]; } + //
+ //
+ // public function createDtoFromResponse(\Saloon\Http\Response $response): array { return $response->json(); } + //
} diff --git a/src/Sbp/Requests/DigitalRuble/Notifications/ParsePaymentDrNotification.php b/src/Sbp/Requests/DigitalRuble/Notifications/ParsePaymentDrNotification.php index 5861770..571b8cc 100644 --- a/src/Sbp/Requests/DigitalRuble/Notifications/ParsePaymentDrNotification.php +++ b/src/Sbp/Requests/DigitalRuble/Notifications/ParsePaymentDrNotification.php @@ -1,14 +1,29 @@ +// use Apoloz\AlfaBank\Sbp\Data\DigitalRuble\Notifications\PaymentDrNotificationData; -use Illuminate\Http\Request; +use Illuminate\Http\Request; // Assuming this is used to get the raw incoming data, not directly as a Saloon Request. +//
+//
+// class ParsePaymentDrNotification { + //
+ // public function execute(array $data): PaymentDrNotificationData { return PaymentDrNotificationData::from($data); } + //
} diff --git a/src/Sbp/Requests/GetQrCodeRequest.php b/src/Sbp/Requests/GetQrCodeRequest.php index 6c5c1d0..7eef299 100644 --- a/src/Sbp/Requests/GetQrCodeRequest.php +++ b/src/Sbp/Requests/GetQrCodeRequest.php @@ -1,7 +1,16 @@ +// use Apoloz\AlfaBank\Sbp\Data\QrCodeRequestData; use Apoloz\AlfaBank\Sbp\Data\QrCodeResponseData; use Saloon\Contracts\Body\HasBody; @@ -9,61 +18,47 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; +//
+//
+// class GetQrCodeRequest extends Request implements HasBody { use HasJsonBody; + //
+ // protected Method $method = Method::POST; + //
+ //
+ // public function __construct( protected QrCodeRequestData $data ) {} + //
+ //
+ // public function resolveEndpoint(): string { - return ''; + return '/qrcs'; } + //
+ //
+ // protected function defaultBody(): array { - $body = [ - 'command' => 'GetQRCd', - 'TermNo' => $this->data->TermNo, - 'qrcType' => $this->data->qrcType, - 'currency' => $this->data->currency, - ]; - - // Common Fields - if ($this->data->amount !== null) $body['amount'] = $this->data->amount; - if ($this->data->paymentPurpose !== null) $body['paymentPurpose'] = $this->data->paymentPurpose; - if ($this->data->redirectUrl !== null) $body['redirectUrl'] = $this->data->redirectUrl; - if ($this->data->messageID !== null) $body['messageID'] = $this->data->messageID; - if ($this->data->width !== null) $body['width'] = $this->data->width; - if ($this->data->height !== null) $body['height'] = $this->data->height; - - // Dynamic QR Fields - if ($this->data->OrderNumber !== null) $body['OrderNumber'] = $this->data->OrderNumber; - if ($this->data->qrTtl !== null) $body['qrTtl'] = $this->data->qrTtl; - if ($this->data->subscriptionServiceId !== null) $body['subscriptionServiceId'] = $this->data->subscriptionServiceId; - if ($this->data->subscriptionServiceName !== null) $body['subscriptionServiceName'] = $this->data->subscriptionServiceName; - - // Query Data nesting - $queryData = []; - if ($this->data->notificationUrl !== null) $queryData['notificationUrl'] = $this->data->notificationUrl; - if ($this->data->SenderFIO !== null) $queryData['SenderFIO'] = $this->data->SenderFIO; - if ($this->data->SenderID !== null) $queryData['SenderID'] = $this->data->SenderID; - if ($this->data->SenderBankBIC !== null) $queryData['SenderBankBIC'] = $this->data->SenderBankBIC; - - if (!empty($queryData)) { - $body['queryData'] = $queryData; - } - - return $body; + return $this->data->toArray(); } + //
+ //
+ // public function createDtoFromResponse(Response $response): QrCodeResponseData { return QrCodeResponseData::from($response->json()); } + //
} \ No newline at end of file diff --git a/src/Sbp/Requests/Management/BlockQrRequest.php b/src/Sbp/Requests/Management/BlockQrRequest.php index 9701e8c..214d8e2 100644 --- a/src/Sbp/Requests/Management/BlockQrRequest.php +++ b/src/Sbp/Requests/Management/BlockQrRequest.php @@ -1,28 +1,52 @@ +// use Apoloz\AlfaBank\Sbp\Data\Management\BlockQrRequestData; use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Traits\Body\HasJsonBody; +// +// use Saloon\Http\Response; // Explicit import would be cleaner +//
+ +//
+// class BlockQrRequest extends Request implements HasBody { use HasJsonBody; + //
+ // protected Method $method = Method::POST; + //
+ //
+ // public function __construct( protected BlockQrRequestData $data ) {} + //
+ // public function resolveEndpoint(): string { - return ''; + return ''; // Endpoint is determined by SbpConnector's base URL } + //
+ //
+ // protected function defaultBody(): array { $body = [ @@ -37,9 +61,13 @@ class BlockQrRequest extends Request implements HasBody return $body; } + //
+ //
+ // public function createDtoFromResponse(\Saloon\Http\Response $response): array { return $response->json(); } + //
} diff --git a/src/Sbp/Requests/Management/GetBlockedQrListRequest.php b/src/Sbp/Requests/Management/GetBlockedQrListRequest.php index a13a1e0..e2d93a3 100644 --- a/src/Sbp/Requests/Management/GetBlockedQrListRequest.php +++ b/src/Sbp/Requests/Management/GetBlockedQrListRequest.php @@ -1,27 +1,51 @@ +// use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Traits\Body\HasJsonBody; +// +// use Saloon\Http\Response; // Explicit import would be cleaner +//
+ +//
+// class GetBlockedQrListRequest extends Request implements HasBody { use HasJsonBody; + //
+ // protected Method $method = Method::POST; + //
+ //
+ // public function __construct( protected string $TermNo ) {} + //
+ // public function resolveEndpoint(): string { - return ''; + return ''; // Endpoint is determined by SbpConnector's base URL } + //
+ //
+ // protected function defaultBody(): array { return [ @@ -29,9 +53,13 @@ class GetBlockedQrListRequest extends Request implements HasBody 'TermNo' => $this->TermNo, ]; } + //
+ //
+ // public function createDtoFromResponse(\Saloon\Http\Response $response): array { return $response->json(); } + //
} diff --git a/src/Sbp/Requests/Management/UnblockQrRequest.php b/src/Sbp/Requests/Management/UnblockQrRequest.php index 2a8d905..73ed5b4 100644 --- a/src/Sbp/Requests/Management/UnblockQrRequest.php +++ b/src/Sbp/Requests/Management/UnblockQrRequest.php @@ -1,28 +1,52 @@ +// use Apoloz\AlfaBank\Sbp\Data\Management\BlockQrRequestData; use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Traits\Body\HasJsonBody; +// +// use Saloon\Http\Response; // Explicit import would be cleaner +//
+ +//
+// class UnblockQrRequest extends Request implements HasBody { use HasJsonBody; + //
+ // protected Method $method = Method::POST; + //
+ //
+ // public function __construct( protected BlockQrRequestData $data ) {} + //
+ // public function resolveEndpoint(): string { - return ''; + return ''; // Endpoint is determined by SbpConnector's base URL } + //
+ //
+ // protected function defaultBody(): array { return [ @@ -31,9 +55,13 @@ class UnblockQrRequest extends Request implements HasBody 'qrcId' => $this->data->qrcId, ]; } + //
+ //
+ // public function createDtoFromResponse(\Saloon\Http\Response $response): array { return $response->json(); } + //
} diff --git a/src/Sbp/Requests/Registry/GetBankListRequest.php b/src/Sbp/Requests/Registry/GetBankListRequest.php index 17b44a9..e058cce 100644 --- a/src/Sbp/Requests/Registry/GetBankListRequest.php +++ b/src/Sbp/Requests/Registry/GetBankListRequest.php @@ -1,30 +1,51 @@ +// use Apoloz\AlfaBank\Sbp\Data\Registry\GetBankListResponseData; use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; +//
+//
+// class GetBankListRequest extends Request implements HasBody { use HasJsonBody; + //
+ // protected Method $method = Method::POST; + //
+ //
+ // public function __construct( protected ?string $messageID = null, protected ?string $BankMemberIdFilter = null ) {} + //
+ // public function resolveEndpoint(): string { - return ''; + return ''; // Endpoint is determined by SbpConnector's base URL } + //
+ //
+ // protected function defaultBody(): array { $body = [ @@ -36,9 +57,13 @@ class GetBankListRequest extends Request implements HasBody return $body; } + //
+ //
+ // public function createDtoFromResponse(Response $response): GetBankListResponseData { return GetBankListResponseData::from($response->json()); } + //
} diff --git a/src/Sbp/Requests/Reversal/GetQrcReversalDataRequest.php b/src/Sbp/Requests/Reversal/GetQrcReversalDataRequest.php index 6e15926..08aef4d 100644 --- a/src/Sbp/Requests/Reversal/GetQrcReversalDataRequest.php +++ b/src/Sbp/Requests/Reversal/GetQrcReversalDataRequest.php @@ -1,7 +1,15 @@ +// use Apoloz\AlfaBank\Sbp\Data\Reversal\GetQrcReversalDataRequestData; use Apoloz\AlfaBank\Sbp\Data\Reversal\GetQrcReversalDataResponseData; use Saloon\Contracts\Body\HasBody; @@ -9,22 +17,35 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; +//
+//
+// class GetQrcReversalDataRequest extends Request implements HasBody { use HasJsonBody; + //
+ // protected Method $method = Method::POST; + //
+ //
+ // public function __construct( protected GetQrcReversalDataRequestData $data ) {} + //
+ // public function resolveEndpoint(): string { - return ''; + return ''; // Endpoint is determined by SbpConnector's base URL } + //
+ //
+ // protected function defaultBody(): array { $body = [ @@ -43,9 +64,13 @@ class GetQrcReversalDataRequest extends Request implements HasBody return $body; } + //
+ //
+ // public function createDtoFromResponse(Response $response): GetQrcReversalDataResponseData { return GetQrcReversalDataResponseData::from($response->json()); } + //
} diff --git a/src/Sbp/Requests/Reversal/GetQrcReversalStatusRequest.php b/src/Sbp/Requests/Reversal/GetQrcReversalStatusRequest.php index 980ed3b..9219eca 100644 --- a/src/Sbp/Requests/Reversal/GetQrcReversalStatusRequest.php +++ b/src/Sbp/Requests/Reversal/GetQrcReversalStatusRequest.php @@ -1,7 +1,15 @@ +// use Apoloz\AlfaBank\Sbp\Data\Reversal\GetQrcReversalStatusRequestData; use Apoloz\AlfaBank\Sbp\Data\Reversal\GetQrcReversalStatusResponseData; use Saloon\Contracts\Body\HasBody; @@ -9,22 +17,35 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; +//
+//
+// class GetQrcReversalStatusRequest extends Request implements HasBody { use HasJsonBody; + //
+ // protected Method $method = Method::POST; + //
+ //
+ // public function __construct( protected GetQrcReversalStatusRequestData $data ) {} + //
+ // public function resolveEndpoint(): string { - return ''; + return ''; // Endpoint is determined by SbpConnector's base URL } + //
+ //
+ // protected function defaultBody(): array { $body = [ @@ -39,9 +60,13 @@ class GetQrcReversalStatusRequest extends Request implements HasBody return $body; } + //
+ //
+ // public function createDtoFromResponse(Response $response): GetQrcReversalStatusResponseData { return GetQrcReversalStatusResponseData::from($response->json()); } + //
} diff --git a/src/Sbp/Requests/Reversal/QrcReversalRequest.php b/src/Sbp/Requests/Reversal/QrcReversalRequest.php index d616a9c..a82561e 100644 --- a/src/Sbp/Requests/Reversal/QrcReversalRequest.php +++ b/src/Sbp/Requests/Reversal/QrcReversalRequest.php @@ -1,7 +1,15 @@ +// use Apoloz\AlfaBank\Sbp\Data\Reversal\QrcReversalRequestData; use Apoloz\AlfaBank\Sbp\Data\Reversal\QrcReversalResponseData; use Saloon\Contracts\Body\HasBody; @@ -9,22 +17,35 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; +//
+//
+// class QrcReversalRequest extends Request implements HasBody { use HasJsonBody; + //
+ // protected Method $method = Method::POST; + //
+ //
+ // public function __construct( protected QrcReversalRequestData $data ) {} + //
+ // public function resolveEndpoint(): string { - return ''; + return ''; // Endpoint is determined by SbpConnector's base URL } + //
+ //
+ // protected function defaultBody(): array { $body = [ @@ -44,9 +65,13 @@ class QrcReversalRequest extends Request implements HasBody return $body; } + //
+ //
+ // public function createDtoFromResponse(Response $response): QrcReversalResponseData { return QrcReversalResponseData::from($response->json()); } + //
} diff --git a/src/Sbp/Requests/Status/GetQrcStatusRequest.php b/src/Sbp/Requests/Status/GetQrcStatusRequest.php index 456cc44..eba2bc5 100644 --- a/src/Sbp/Requests/Status/GetQrcStatusRequest.php +++ b/src/Sbp/Requests/Status/GetQrcStatusRequest.php @@ -1,7 +1,15 @@ +// use Apoloz\AlfaBank\Sbp\Data\Status\GetQrcStatusRequestData; use Apoloz\AlfaBank\Sbp\Data\Status\GetQrcStatusResponseData; use Saloon\Contracts\Body\HasBody; @@ -9,22 +17,35 @@ use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; +//
+//
+// class GetQrcStatusRequest extends Request implements HasBody { use HasJsonBody; + //
+ // protected Method $method = Method::POST; + //
+ //
+ // public function __construct( protected GetQrcStatusRequestData $data ) {} + //
+ // public function resolveEndpoint(): string { - return ''; + return ''; // Endpoint is determined by SbpConnector's base URL } + //
+ //
+ // protected function defaultBody(): array { $body = [ @@ -38,9 +59,13 @@ class GetQrcStatusRequest extends Request implements HasBody return $body; } + //
+ //
+ // public function createDtoFromResponse(Response $response): GetQrcStatusResponseData { return GetQrcStatusResponseData::from($response->json()); } + //
} diff --git a/src/Sbp/Requests/Subscription/GetQrSubStatusRequest.php b/src/Sbp/Requests/Subscription/GetQrSubStatusRequest.php index b9e1427..f4c0735 100644 --- a/src/Sbp/Requests/Subscription/GetQrSubStatusRequest.php +++ b/src/Sbp/Requests/Subscription/GetQrSubStatusRequest.php @@ -1,29 +1,53 @@ +// use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Traits\Body\HasJsonBody; +// +// use Saloon\Http\Response; // Explicit import would be cleaner +//
+ +//
+// class GetQrSubStatusRequest extends Request implements HasBody { use HasJsonBody; + //
+ // protected Method $method = Method::POST; + //
+ //
+ // public function __construct( protected string $TermNo, protected string $qrcId, protected ?string $RQperiod = null ) {} + //
+ // public function resolveEndpoint(): string { - return ''; + return ''; // Endpoint is determined by SbpConnector's base URL } + //
+ //
+ // protected function defaultBody(): array { $body = [ @@ -38,9 +62,13 @@ class GetQrSubStatusRequest extends Request implements HasBody return $body; } + //
+ //
+ // public function createDtoFromResponse(\Saloon\Http\Response $response): array { return $response->json(); } + //
} diff --git a/src/Sbp/Requests/Subscription/QrcSubPayPetitionRequest.php b/src/Sbp/Requests/Subscription/QrcSubPayPetitionRequest.php index b1bcbcc..71b094a 100644 --- a/src/Sbp/Requests/Subscription/QrcSubPayPetitionRequest.php +++ b/src/Sbp/Requests/Subscription/QrcSubPayPetitionRequest.php @@ -1,19 +1,35 @@ +// use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; +//
+//
+// class QrcSubPayPetitionRequest extends Request implements HasBody { use HasJsonBody; + //
+ // protected Method $method = Method::POST; + //
+ //
+ // public function __construct( protected string $TermNo, protected string $subscriptionToken, @@ -21,11 +37,16 @@ class QrcSubPayPetitionRequest extends Request implements HasBody protected string $qrcId, ) {} + //
+ // public function resolveEndpoint(): string { - return ''; + return ''; // Endpoint is determined by SbpConnector's base URL } + //
+ //
+ // protected function defaultBody(): array { return [ @@ -36,9 +57,13 @@ class QrcSubPayPetitionRequest extends Request implements HasBody 'qrcId' => $this->qrcId, ]; } + //
+ //
+ // public function createDtoFromResponse(Response $response): array { return $response->json(); } + //
} diff --git a/src/Sbp/Requests/Subscription/RegQrSubRequest.php b/src/Sbp/Requests/Subscription/RegQrSubRequest.php index cb3603d..4121411 100644 --- a/src/Sbp/Requests/Subscription/RegQrSubRequest.php +++ b/src/Sbp/Requests/Subscription/RegQrSubRequest.php @@ -1,29 +1,50 @@ +// use Apoloz\AlfaBank\Sbp\Data\Subscription\RegQrSubRequestData; use Saloon\Contracts\Body\HasBody; use Saloon\Enums\Method; use Saloon\Http\Request; use Saloon\Http\Response; use Saloon\Traits\Body\HasJsonBody; +//
+//
+// class RegQrSubRequest extends Request implements HasBody { use HasJsonBody; + //
+ // protected Method $method = Method::POST; + //
+ //
+ // public function __construct( protected RegQrSubRequestData $data ) {} + //
+ // public function resolveEndpoint(): string { - return ''; + return ''; // Endpoint is determined by SbpConnector's base URL } + //
+ //
+ // protected function defaultBody(): array { $body = [ @@ -43,9 +64,13 @@ class RegQrSubRequest extends Request implements HasBody return $body; } + //
+ //
+ // public function createDtoFromResponse(Response $response): array { return $response->json(); } + //
} diff --git a/src/Sbp/SbpConnector.php b/src/Sbp/SbpConnector.php index faae6a2..fb120fb 100644 --- a/src/Sbp/SbpConnector.php +++ b/src/Sbp/SbpConnector.php @@ -2,74 +2,108 @@ namespace Apoloz\AlfaBank\Sbp; -use Apoloz\AlfaBank\Enums\Environment; +use Apoloz\AlfaBank\Core\AlfaConfig; +use Apoloz\AlfaBank\Http\Middleware\AlfaResponseMiddleware; +use Apoloz\AlfaBank\Http\Middleware\RequestIdMiddleware; +use Apoloz\AlfaBank\Http\Pagination\AlfaOffsetPaginator; +use Apoloz\AlfaBank\Utils\CmsSigner; use Saloon\Http\Connector; use Saloon\Http\PendingRequest; +use Saloon\Http\Request; +use Saloon\PaginationPlugin\Contracts\HasPagination; +use Saloon\PaginationPlugin\OffsetPaginator; +use Saloon\Traits\RequestProperties\HasTries; use Saloon\Traits\Plugins\HasTimeout; -class SbpConnector extends Connector +class SbpConnector extends Connector implements HasPagination { use HasTimeout; + use HasTries; public function __construct( - protected string $certificatePath, - protected string $privateKeyPath, - protected string $keyNameAlias, // 'key-name' header - protected Environment $environment = Environment::SANDBOX - ) {} + protected AlfaConfig $alfaConfig, + protected string $keyNameAlias + ) { + $this->tries = 3; + $this->retryInterval = 500; + } public function resolveBaseUrl(): string { - return match ($this->environment) { - Environment::SANDBOX => 'https://217.12.103.132:2443/fsCryptoProxy', - Environment::PRODUCTION => 'https://pay.alfabank.ru/fsCryptoProxy', // Placeholder, user needs to confirm - }; + return $this->alfaConfig->baseUrl; } protected function defaultConfig(): array { - return [ - 'cert' => $this->certificatePath, - 'ssl_key' => $this->privateKeyPath, + $config = [ + 'cert' => $this->alfaConfig->certificatePath, + 'ssl_key' => $this->alfaConfig->privateKeyPath, 'verify' => true, ]; + + if ($this->alfaConfig->caBundlePath) { + $config['verify'] = $this->alfaConfig->caBundlePath; + } + + return $config; } - protected function defaultHeaders(): array + protected function defaultMiddleware(): array { - return [ - 'Content-Type' => 'application/x-www-form-urlencoded', // As per doc, despite body being JSON - 'key-name' => $this->keyNameAlias, - ]; + return []; } public function boot(PendingRequest $pendingRequest): void { - // Calculate Signature - $body = $pendingRequest->body()?->all(); - - if (empty($body)) { + $pendingRequest->middleware()->onResponse(new AlfaResponseMiddleware()); + $pendingRequest->middleware()->onRequest(new RequestIdMiddleware()); + + $bodyRepo = $pendingRequest->body(); + if (!$bodyRepo || empty($bodyRepo->all())) { return; } + $body = $bodyRepo->all(); + + // SBP requires minified JSON for signing. Canonicalization (JCS) is the formal term. + $jsonBody = CmsSigner::canonicalize($body); - // Minify JSON for signature - $jsonBody = json_encode($body, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + // Sign the body using the configured private key (prefer signing key, fallback to transport key). + $keyPath = $this->alfaConfig->signingPrivateKeyPath ?: $this->alfaConfig->privateKeyPath; - // Remove spaces/newlines as per doc requirement "exclude all spaces, tabs, newlines" - // Standard json_encode without pretty print usually does this, but let's be sure. - // The doc example: `{"command":"GetQRCd",...}` implies no spaces after colons/commas. + if (!file_exists($keyPath)) { + throw new \RuntimeException("Private key file not found: $keyPath"); + } + + $privateKey = openssl_pkey_get_private( + file_get_contents($keyPath) + ); - $privateKey = file_get_contents($this->privateKeyPath); - $pkeyId = openssl_get_privatekey($privateKey); - - openssl_sign($jsonBody, $signature, $pkeyId, OPENSSL_ALGO_SHA256); + if (!$privateKey) { + throw new \RuntimeException("Could not load private key for SBP signing from: $keyPath"); + } + + $signature = ''; + openssl_sign($jsonBody, $signature, $privateKey, OPENSSL_ALGO_SHA256); $signatureBase64 = base64_encode($signature); - + + // Mutate the request to add SBP-specific headers and set the exact signed body. $pendingRequest->headers()->add('Authorization', $signatureBase64); + // The SBP API documentation specifies this content-type for signed requests. + $pendingRequest->headers()->add('Content-Type', 'application/x-www-form-urlencoded'); - // Ensure the body sent is the exact string we signed? - // Saloon will encode the array to JSON again. - // We should enforce the body to be the string we just signed to ensure consistency. - $pendingRequest->body()->set($jsonBody); + // Note: Saloon/Guzzle might re-serialize the body. + // For strict SBP signing, ensuring the body sent matches $jsonBody exactly is crucial. + // Saloon's HasJsonBody trait usually handles this fine as long as array order is preserved. } -} + + public function withCaBundle(string $path): static + { + $this->config()->merge(['verify' => $path]); + return $this; + } + + public function paginate(Request $request): OffsetPaginator + { + return new AlfaOffsetPaginator($this, $request); + } +} \ No newline at end of file diff --git a/src/Services/OpenSslCmsSigner.php b/src/Services/OpenSslCmsSigner.php new file mode 100644 index 0000000..b834729 --- /dev/null +++ b/src/Services/OpenSslCmsSigner.php @@ -0,0 +1,74 @@ + +// +use Apoloz\AlfaBank\Contracts\SignerInterface; +//
+ +//
+// +class OpenSslCmsSigner implements SignerInterface +{ + //
+ // + public function __construct( + protected string $certificatePath, + protected string $privateKeyPath + ) {} + //
+ + //
+ // + public function sign(string $data): string + { + $privateKey = openssl_pkey_get_private(file_get_contents($this->privateKeyPath)); // assuming no passphrase for simplicity here + if (!$privateKey) { + throw new \Exception("Could not read private key for signing from {$this->privateKeyPath}."); + } + + $cert = openssl_x509_read(file_get_contents($this->certificatePath)); + if (!$cert) { + throw new \Exception("Could not read certificate for signing from {$this->certificatePath}."); + } + + $inFile = tempnam(sys_get_temp_dir(), 'alfa_in'); + $outFile = tempnam(sys_get_temp_dir(), 'alfa_out'); + + file_put_contents($inFile, $data); + + $status = openssl_pkcs7_sign( + $inFile, + $outFile, + $cert, + $privateKey, + [], + PKCS7_BINARY | PKCS7_DETACHED + ); + + if (!$status) { + unlink($inFile); + unlink($outFile); + throw new \Exception("Signing failed: " . openssl_error_string()); + } + + $signedData = file_get_contents($outFile); + unlink($inFile); + unlink($outFile); + + $parts = explode("\n\n", $signedData); + $signature = end($parts); + + return str_replace(["\n", "\r"], '', $signature); + } + //
+} +//
diff --git a/src/Services/PhpseclibCardEncryptor.php b/src/Services/PhpseclibCardEncryptor.php new file mode 100644 index 0000000..64ecc5a --- /dev/null +++ b/src/Services/PhpseclibCardEncryptor.php @@ -0,0 +1,45 @@ + +// +use Apoloz\AlfaBank\Contracts\EncryptorInterface; +use phpseclib3\Crypt\PublicKeyLoader; +use phpseclib3\Crypt\RSA; +//
+ +//
+// +class PhpseclibCardEncryptor implements EncryptorInterface +{ + //
+ // + public function __construct( + protected string $publicKeyPem + ) {} + //
+ + //
+ // + public function encrypt(string $data): string + { + /** @var RSA $key */ + $key = PublicKeyLoader::load($this->publicKeyPem); + + $key = $key->withHash('sha256') + ->withMGFHash('sha1') // MGF1 with SHA1 is standard for banking + ->withPadding(RSA::ENCRYPTION_OAEP); + + return base64_encode($key->encrypt($data)); + } + //
+} +//
diff --git a/src/Traits/HasAlfaPagination.php b/src/Traits/HasAlfaPagination.php index 1ac82ee..5dff825 100644 --- a/src/Traits/HasAlfaPagination.php +++ b/src/Traits/HasAlfaPagination.php @@ -1,19 +1,32 @@ +// trait HasAlfaPagination { - public function __construct( - protected ?int $limit = 1000, - protected ?int $offset = 0 - ) {} + protected ?int $limit = 1000; + protected ?int $offset = 0; protected function getPaginationQuery(): array { return array_filter([ 'limit' => $this->limit, 'offset' => $this->offset, - ], fn($v) => $val !== null); + ], fn($v) => $v !== null); + } + + public function getLimit(): ?int + { + return $this->limit; } } +//
diff --git a/src/Utils/CardEncryptor.php b/src/Utils/CardEncryptor.php index cc6a177..e0cc6ee 100644 --- a/src/Utils/CardEncryptor.php +++ b/src/Utils/CardEncryptor.php @@ -1,10 +1,22 @@ +// use phpseclib3\Crypt\PublicKeyLoader; use phpseclib3\Crypt\RSA; +//
+//
+// class CardEncryptor { /** @@ -13,6 +25,8 @@ class CardEncryptor * * Requires phpseclib3/phpseclib. */ + //
+ // public static function encrypt(string $pan, string $publicKeyPem): string { /** @var RSA $publicKey */ @@ -25,4 +39,6 @@ class CardEncryptor return base64_encode($algo->encrypt($pan)); } + //
} +//
diff --git a/src/Utils/CmsSigner.php b/src/Utils/CmsSigner.php index 94cf58b..70921d6 100644 --- a/src/Utils/CmsSigner.php +++ b/src/Utils/CmsSigner.php @@ -1,7 +1,16 @@ +// class CmsSigner { /** @@ -14,6 +23,8 @@ class CmsSigner * @return string Base64 encoded PKCS#7 signature. * @throws \Exception */ + //
+ // public static function sign(string $data, string $certificatePem, string $privateKeyPem, ?string $passphrase = null): string { $privateKey = openssl_pkey_get_private($privateKeyPem, $passphrase ?? ''); @@ -58,17 +69,23 @@ class CmsSigner return str_replace(["\n", "\r"], '', $signature); } + //
/** * Minimal implementation of JCS (JSON Canonicalization Scheme) - RFC 8785. * Sorts keys and ensures consistent formatting. */ + //
+ // public static function canonicalize(array $data): string { self::recursiveSort($data); return json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); } + //
+ //
+ // private static function recursiveSort(array &$data): void { if (self::isAssoc($data)) { @@ -80,10 +97,15 @@ class CmsSigner } } } + //
+ //
+ // private static function isAssoc(array $arr): bool { if ([] === $arr) return false; return array_keys($arr) !== range(0, count($arr) - 1); } + //
} +//
diff --git a/src/Utils/JweEncryptor.php b/src/Utils/JweEncryptor.php index d6bad44..93da47e 100644 --- a/src/Utils/JweEncryptor.php +++ b/src/Utils/JweEncryptor.php @@ -1,16 +1,30 @@ +// use phpseclib3\Crypt\RSA; use phpseclib3\Crypt\PublicKeyLoader; use phpseclib3\Crypt\Random; +//
+//
+// class JweEncryptor { /** * Encrypts a JWS string using RSA-OAEP-256 and A256GCM. */ + //
+ // public static function encrypt(string $jws, string $publicKeyPem, string $certificateSerialNumber): string { $header = [ @@ -52,10 +66,13 @@ class JweEncryptor return $base64Header . '.' . $base64EncryptedKey . '.' . $base64Iv . '.' . $base64Ciphertext . '.' . $base64Tag; } + //
/** * Decrypts a JWE string. */ + //
+ // public static function decrypt(string $jwe, string $privateKeyPem, ?string $passphrase = null): ?string { $parts = explode('.', $jwe); @@ -86,4 +103,6 @@ class JweEncryptor return $decrypted ?: null; } + //
} +//
diff --git a/src/Utils/JwsSigner.php b/src/Utils/JwsSigner.php index 580742a..1c21c11 100644 --- a/src/Utils/JwsSigner.php +++ b/src/Utils/JwsSigner.php @@ -1,15 +1,29 @@ +// use phpseclib3\Crypt\RSA; use phpseclib3\Crypt\PublicKeyLoader; +//
+//
+// class JwsSigner { /** * Signs data using RS256 (JWS Compact Serialization). */ + //
+ // public static function sign(array $payload, string $privateKeyPem, string $certificateSerialNumber, ?string $passphrase = null): string { $header = [ @@ -32,10 +46,13 @@ class JwsSigner return $dataToSign . '.' . $base64Signature; } + //
/** * Verifies an RS256 JWS. */ + //
+ // public static function verify(string $jws, string $publicKeyPem): ?array { $parts = explode('.', $jws); @@ -57,14 +74,22 @@ class JwsSigner return null; } + //
+ //
+ // public static function base64UrlEncode(string $data): string { return str_replace(['+', '/', '='], ['-', '_', ''], base64_encode($data)); } + //
+ //
+ // public static function base64UrlDecode(string $data): string { return base64_decode(str_replace(['-', '_'], ['+', '/'], $data)); } + //
} +//
diff --git a/src/Utils/Uuid.php b/src/Utils/Uuid.php new file mode 100644 index 0000000..f6ff165 --- /dev/null +++ b/src/Utils/Uuid.php @@ -0,0 +1,16 @@ + 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...'); +}); diff --git a/tests/Feature/AdvancedModulesTest.php b/tests/Feature/AdvancedModulesTest.php new file mode 100644 index 0000000..3a1c9d6 --- /dev/null +++ b/tests/Feature/AdvancedModulesTest.php @@ -0,0 +1,49 @@ + 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); +}); diff --git a/tests/Feature/AffIntegrationTest.php b/tests/Feature/AffIntegrationTest.php new file mode 100644 index 0000000..36ed3d0 --- /dev/null +++ b/tests/Feature/AffIntegrationTest.php @@ -0,0 +1,34 @@ + 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); +}); diff --git a/tests/Feature/Auth/AuthenticationTest.php b/tests/Feature/Auth/AuthenticationTest.php index 7c477e2..12c2abc 100644 --- a/tests/Feature/Auth/AuthenticationTest.php +++ b/tests/Feature/Auth/AuthenticationTest.php @@ -1,6 +1,7 @@ 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(); +}); diff --git a/tests/Feature/Auth/UserInfoTest.php b/tests/Feature/Auth/UserInfoTest.php new file mode 100644 index 0000000..102da0c --- /dev/null +++ b/tests/Feature/Auth/UserInfoTest.php @@ -0,0 +1,28 @@ + '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'); +}); diff --git a/tests/Feature/B2C/B2CPaymentTest.php b/tests/Feature/B2C/B2CPaymentTest.php new file mode 100644 index 0000000..c92f37e --- /dev/null +++ b/tests/Feature/B2C/B2CPaymentTest.php @@ -0,0 +1,59 @@ + 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); +}); diff --git a/tests/Feature/Cards/CardTest.php b/tests/Feature/Cards/CardTest.php new file mode 100644 index 0000000..0a0b6da --- /dev/null +++ b/tests/Feature/Cards/CardTest.php @@ -0,0 +1,32 @@ + 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'); +}); diff --git a/tests/Feature/CloudSignature/CloudSignatureTest.php b/tests/Feature/CloudSignature/CloudSignatureTest.php new file mode 100644 index 0000000..a0557e9 --- /dev/null +++ b/tests/Feature/CloudSignature/CloudSignatureTest.php @@ -0,0 +1,64 @@ + 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); +}); diff --git a/tests/Feature/CustomerInfo/CustomerInfoTest.php b/tests/Feature/CustomerInfo/CustomerInfoTest.php new file mode 100644 index 0000000..3157896 --- /dev/null +++ b/tests/Feature/CustomerInfo/CustomerInfoTest.php @@ -0,0 +1,28 @@ + 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'); +}); diff --git a/tests/Feature/ErrorHandlingTest.php b/tests/Feature/ErrorHandlingTest.php new file mode 100644 index 0000000..7e2f141 --- /dev/null +++ b/tests/Feature/ErrorHandlingTest.php @@ -0,0 +1,40 @@ + 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'); diff --git a/tests/Feature/Escrow/EscrowTest.php b/tests/Feature/Escrow/EscrowTest.php new file mode 100644 index 0000000..7d46b21 --- /dev/null +++ b/tests/Feature/Escrow/EscrowTest.php @@ -0,0 +1,40 @@ + 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); +}); diff --git a/tests/Feature/FileExchangeTest.php b/tests/Feature/FileExchangeTest.php new file mode 100644 index 0000000..fe6500c --- /dev/null +++ b/tests/Feature/FileExchangeTest.php @@ -0,0 +1,51 @@ + 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'); +}); diff --git a/tests/Feature/LogisticsTest.php b/tests/Feature/LogisticsTest.php new file mode 100644 index 0000000..e03a0d5 --- /dev/null +++ b/tests/Feature/LogisticsTest.php @@ -0,0 +1,19 @@ + 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); +}); diff --git a/tests/Feature/Loyalty/LoyaltyTest.php b/tests/Feature/Loyalty/LoyaltyTest.php new file mode 100644 index 0000000..8ac9177 --- /dev/null +++ b/tests/Feature/Loyalty/LoyaltyTest.php @@ -0,0 +1,34 @@ + 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); +}); diff --git a/tests/Feature/MiscellaneousTest.php b/tests/Feature/MiscellaneousTest.php new file mode 100644 index 0000000..a6e990b --- /dev/null +++ b/tests/Feature/MiscellaneousTest.php @@ -0,0 +1,49 @@ + 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); +}); diff --git a/tests/Feature/NominalAccounts/BeneficiariesTest.php b/tests/Feature/NominalAccounts/BeneficiariesTest.php index db8a14e..d925ff6 100644 --- a/tests/Feature/NominalAccounts/BeneficiariesTest.php +++ b/tests/Feature/NominalAccounts/BeneficiariesTest.php @@ -1,10 +1,12 @@ 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') ]) ); diff --git a/tests/Feature/NominalAccounts/DealsTest.php b/tests/Feature/NominalAccounts/DealsTest.php new file mode 100644 index 0000000..bfdadc8 --- /dev/null +++ b/tests/Feature/NominalAccounts/DealsTest.php @@ -0,0 +1,40 @@ + 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'); +}); diff --git a/tests/Feature/NominalAccounts/NominalAccountsTest.php b/tests/Feature/NominalAccounts/NominalAccountsTest.php index 76c6046..730d3fb 100644 --- a/tests/Feature/NominalAccounts/NominalAccountsTest.php +++ b/tests/Feature/NominalAccounts/NominalAccountsTest.php @@ -1,6 +1,7 @@ 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()); diff --git a/tests/Feature/Partner/PartnerTest.php b/tests/Feature/Partner/PartnerTest.php new file mode 100644 index 0000000..3e13835 --- /dev/null +++ b/tests/Feature/Partner/PartnerTest.php @@ -0,0 +1,34 @@ + 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); +}); diff --git a/tests/Feature/Payments/PaymentTest.php b/tests/Feature/Payments/PaymentTest.php new file mode 100644 index 0000000..42e7d95 --- /dev/null +++ b/tests/Feature/Payments/PaymentTest.php @@ -0,0 +1,57 @@ + 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'); +}); diff --git a/tests/Feature/Poa/PoaTest.php b/tests/Feature/Poa/PoaTest.php new file mode 100644 index 0000000..565e39f --- /dev/null +++ b/tests/Feature/Poa/PoaTest.php @@ -0,0 +1,36 @@ + 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'); +}); diff --git a/tests/Feature/Registries/RegistryTest.php b/tests/Feature/Registries/RegistryTest.php new file mode 100644 index 0000000..1d337c9 --- /dev/null +++ b/tests/Feature/Registries/RegistryTest.php @@ -0,0 +1,65 @@ + 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); +}); diff --git a/tests/Feature/Reports/ReportTest.php b/tests/Feature/Reports/ReportTest.php new file mode 100644 index 0000000..60ce358 --- /dev/null +++ b/tests/Feature/Reports/ReportTest.php @@ -0,0 +1,23 @@ + 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'); +}); diff --git a/tests/Feature/Salary/SalaryTest.php b/tests/Feature/Salary/SalaryTest.php new file mode 100644 index 0000000..ab3e3b4 --- /dev/null +++ b/tests/Feature/Salary/SalaryTest.php @@ -0,0 +1,85 @@ + 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); +}); diff --git a/tests/Feature/Sbp/ConfigTest.php b/tests/Feature/Sbp/ConfigTest.php new file mode 100644 index 0000000..50c8d72 --- /dev/null +++ b/tests/Feature/Sbp/ConfigTest.php @@ -0,0 +1,30 @@ + 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); +}); diff --git a/tests/Feature/Sbp/QrCodeTest.php b/tests/Feature/Sbp/QrCodeTest.php index 9229231..920f8b3 100644 --- a/tests/Feature/Sbp/QrCodeTest.php +++ b/tests/Feature/Sbp/QrCodeTest.php @@ -1,6 +1,7 @@ withMockClient($mockClient); $payload = new QrCodeRequestData( - TermNo: 'test-term', + termNo: 'test-term', amount: '100' ); diff --git a/tests/Feature/Sbp/ReversalTest.php b/tests/Feature/Sbp/ReversalTest.php new file mode 100644 index 0000000..91446b1 --- /dev/null +++ b/tests/Feature/Sbp/ReversalTest.php @@ -0,0 +1,37 @@ + 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'); +}); diff --git a/tests/Feature/ScoringAndSecurityTest.php b/tests/Feature/ScoringAndSecurityTest.php new file mode 100644 index 0000000..a3cf5fe --- /dev/null +++ b/tests/Feature/ScoringAndSecurityTest.php @@ -0,0 +1,35 @@ + 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); +}); diff --git a/tests/Feature/SelfEmployed/AgreementTest.php b/tests/Feature/SelfEmployed/AgreementTest.php new file mode 100644 index 0000000..ab982c0 --- /dev/null +++ b/tests/Feature/SelfEmployed/AgreementTest.php @@ -0,0 +1,30 @@ + 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/...'); +}); diff --git a/tests/Feature/SelfEmployed/DictionaryTest.php b/tests/Feature/SelfEmployed/DictionaryTest.php new file mode 100644 index 0000000..33aab91 --- /dev/null +++ b/tests/Feature/SelfEmployed/DictionaryTest.php @@ -0,0 +1,25 @@ + 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'); +}); diff --git a/tests/Feature/SelfEmployed/PayoutTest.php b/tests/Feature/SelfEmployed/PayoutTest.php new file mode 100644 index 0000000..e6545fb --- /dev/null +++ b/tests/Feature/SelfEmployed/PayoutTest.php @@ -0,0 +1,33 @@ + 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'); +}); diff --git a/tests/Feature/SelfEmployed/TaxpayerStatusTest.php b/tests/Feature/SelfEmployed/TaxpayerStatusTest.php new file mode 100644 index 0000000..46c7e00 --- /dev/null +++ b/tests/Feature/SelfEmployed/TaxpayerStatusTest.php @@ -0,0 +1,38 @@ + 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'); +}); diff --git a/tests/Feature/Statement/StatementTest.php b/tests/Feature/Statement/StatementTest.php new file mode 100644 index 0000000..35b2659 --- /dev/null +++ b/tests/Feature/Statement/StatementTest.php @@ -0,0 +1,42 @@ + 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); +}); diff --git a/tests/Feature/Utils/CryptoTest.php b/tests/Feature/Utils/CryptoTest.php new file mode 100644 index 0000000..8514871 --- /dev/null +++ b/tests/Feature/Utils/CryptoTest.php @@ -0,0 +1,36 @@ + 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}'); +}); diff --git a/tests/Feature/Utils/UuidTest.php b/tests/Feature/Utils/UuidTest.php new file mode 100644 index 0000000..0355970 --- /dev/null +++ b/tests/Feature/Utils/UuidTest.php @@ -0,0 +1,37 @@ +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); +}); diff --git a/tests/Feature/Webhooks/WebhookManagementTest.php b/tests/Feature/Webhooks/WebhookManagementTest.php new file mode 100644 index 0000000..701e885 --- /dev/null +++ b/tests/Feature/Webhooks/WebhookManagementTest.php @@ -0,0 +1,51 @@ + 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); +}); \ No newline at end of file diff --git a/tests/Feature/Webhooks/WebhookTest.php b/tests/Feature/Webhooks/WebhookTest.php new file mode 100644 index 0000000..1abd84e --- /dev/null +++ b/tests/Feature/Webhooks/WebhookTest.php @@ -0,0 +1,36 @@ + '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'); +}); \ No newline at end of file diff --git a/tests/Pest.php b/tests/Pest.php new file mode 100644 index 0000000..bc08f25 --- /dev/null +++ b/tests/Pest.php @@ -0,0 +1,128 @@ +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 + ); +}