refactor: dto, requests, connector
This commit is contained in:
76
README.md
76
README.md
@@ -1,5 +1,8 @@
|
||||
# Alfa Bank SDK (Unofficial)
|
||||
|
||||
[](https://github.com/apoloz/alfa-bank-sdk/actions)
|
||||
[](https://phpstan.org/)
|
||||
|
||||
Профессиональная PHP-библиотека для интеграции с API Альфа-Банка (BaaS и СБП). Построена на базе [Saloon v3](https://docs.saloon.dev) и [Spatie Laravel Data](https://spatie.be/docs/laravel-data).
|
||||
|
||||
## Требования
|
||||
@@ -15,19 +18,16 @@ SDK поддерживает две разные модели безопасно
|
||||
### 1. Подключение к СБП (Pay-in)
|
||||
|
||||
**Шаги для получения доступа:**
|
||||
|
||||
1. Генерируете пару ключей: `openssl req -x509 -newkey rsa:2048 -keyout client.key -out client.crt -nodes -days 3650`.
|
||||
2. Отправляете `client.crt` менеджеру банка (acquiring@alfabank.ru).
|
||||
3. Получаете от банка `alias` (key-name) и `TermNo`.
|
||||
|
||||
**Поток данных и подпись:**
|
||||
|
||||
- **Transport**: Обязательный **MTLS**. Вы должны передать `client.crt` и `client.key` в конфигурацию Guzzle/Saloon.
|
||||
- **Payload**: Тело запроса канонизируется по алгоритму **JCS**.
|
||||
- **Signature**: SHA256withRSA подпись JCS-строки помещается в заголовок `Authorization`.
|
||||
* **Transport**: Обязательный **MTLS**. Вы должны передать `client.crt` и `client.key` в конфигурацию Guzzle/Saloon.
|
||||
* **Payload**: Тело запроса канонизируется по алгоритму **JCS**.
|
||||
* **Signature**: SHA256withRSA подпись JCS-строки помещается в заголовок `Authorization`.
|
||||
|
||||
**SDK делает это автоматически**:
|
||||
|
||||
```php
|
||||
// В config/alfa-bank.php
|
||||
'sbp' => [
|
||||
@@ -42,18 +42,15 @@ SDK поддерживает две разные модели безопасно
|
||||
### 2. Подключение к BaaS (Номинальные счета)
|
||||
|
||||
**Шаги для получения доступа:**
|
||||
|
||||
1. Регистрация в личном кабинете партнера.
|
||||
2. Получение `client_id` и `client_secret` для OAuth2.
|
||||
3. Регистрация публичного ключа для проверки подписей выплат.
|
||||
|
||||
**Поток данных и подпись:**
|
||||
|
||||
- **Auth**: Стандартный OAuth2 Bearer Token.
|
||||
- **Payload Signature**: Для критических операций (выплаты, идентификация) требуется `digestSignatures`. Это массив CMS-подписей (PKCS7) в формате Base64, которыми подписываются финансовые поля.
|
||||
* **Auth**: Стандартный OAuth2 Bearer Token.
|
||||
* **Payload Signature**: Для критических операций (выплаты, идентификация) требуется `digestSignatures`. Это массив CMS-подписей (PKCS7) в формате Base64, которыми подписываются финансовые поля.
|
||||
|
||||
**Пример формирования CMS в SDK:**
|
||||
|
||||
```php
|
||||
use Apoloz\AlfaBank\Utils\CmsSigner;
|
||||
|
||||
@@ -68,7 +65,6 @@ $signature = CmsSigner::sign($payload, $privateKey, $certificate);
|
||||
## Архитектура и Потоки данных
|
||||
|
||||
### Гибридный поток: СБП -> Номинальный счет
|
||||
|
||||
```mermaid
|
||||
sequenceDiagram
|
||||
participant Client as Покупатель
|
||||
@@ -94,7 +90,6 @@ sequenceDiagram
|
||||
## Примеры использования
|
||||
|
||||
### 1. СБП: Прием платежа на номинальный счет
|
||||
|
||||
```php
|
||||
use Apoloz\AlfaBank\Laravel\Facades\Sbp;
|
||||
use Apoloz\AlfaBank\Sbp\Requests\GetQrCodeRequest;
|
||||
@@ -112,7 +107,6 @@ echo "QR URL: " . $response->dto()->payload;
|
||||
```
|
||||
|
||||
### 2. Идентификация платежа (BaaS)
|
||||
|
||||
Когда платеж пришел на номинальный счет, его нужно привязать к бенефициару:
|
||||
|
||||
```php
|
||||
@@ -133,47 +127,55 @@ AlfaBank::send(new IdentifyTransactionRequest($data));
|
||||
```
|
||||
|
||||
### 3. Работа с номинальными счетами (Бенефициары)
|
||||
... (existing content) ...
|
||||
|
||||
### 4. Самозанятые (Регистрация доходов)
|
||||
```php
|
||||
use Apoloz\AlfaBank\Requests\NominalAccounts\Beneficiaries\CreateBeneficiaryRequest;
|
||||
use Apoloz\AlfaBank\Requests\SelfEmployed\RegisterIncomeRequest;
|
||||
use Apoloz\AlfaBank\Data\SelfEmployed\CreateIncomeRequestData;
|
||||
|
||||
// Регистрация нового бенефициара (Физлицо/Юрлицо)
|
||||
$response = AlfaBank::send(new CreateBeneficiaryRequest($beneficiaryData));
|
||||
$id = $response->dto()->requestId;
|
||||
$data = new CreateIncomeRequestData(
|
||||
vatin: '1234567890',
|
||||
name: 'Консультационные услуги',
|
||||
amount: '5000.00',
|
||||
date: now()->format('Y-m-d')
|
||||
);
|
||||
|
||||
// Запуск скоринга (верификации)
|
||||
AlfaBank::send(new ScoreBeneficiaryRequest($id));
|
||||
$response = AlfaBank::send(new RegisterIncomeRequest($data));
|
||||
```
|
||||
|
||||
### 5. Файловый обмен (KYC)
|
||||
Загрузка сканов документов бенефициаров через pre-signed ссылки:
|
||||
```php
|
||||
use Apoloz\AlfaBank\Requests\Files\UploadFileRequest;
|
||||
|
||||
$file = fopen('document.pdf', 'r');
|
||||
$uploadUrl = 'https://baas.alfabank.ru/filebox/...'; // Получено из метода создания бенефициара
|
||||
|
||||
AlfaBank::send(new UploadFileRequest($uploadUrl, $file));
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Продвинутые возможности
|
||||
|
||||
### Автоматическая пагинация
|
||||
|
||||
### Управление возвратами СБП
|
||||
Просмотр всей истории попыток возврата по транзакции:
|
||||
```php
|
||||
$paginator = AlfaBank::paginate(new GetTransactionsRequest(...));
|
||||
|
||||
foreach ($paginator->items() as $transaction) {
|
||||
// Каждая итерация автоматически подгружает следующую страницу при необходимости
|
||||
$response = Sbp::send(new GetQrcReversalHistoryRequest($payrrn, $termNo));
|
||||
foreach ($response->dto()->refunds as $refund) {
|
||||
echo "Статус возврата: " . $refund->status;
|
||||
}
|
||||
```
|
||||
|
||||
### Webhook Handling (Laravel)
|
||||
|
||||
```php
|
||||
// Регистрация роута
|
||||
Route::alfaWebhooks('/api/alfa/webhooks');
|
||||
|
||||
// Обработка в Listener
|
||||
// Обработка СБП уведомлений в Listener
|
||||
public function handle(AlfaWebhookReceived $event) {
|
||||
if ($event->webhook->object === 'ul_transaction_default') {
|
||||
$tx = $event->webhook->data; // TransactionWebhookData
|
||||
Log::info("Пришел платеж на сумму: " . $tx->amount['amount']);
|
||||
if ($event->webhook->isSbp()) {
|
||||
$data = Sbp::parsePaymentNotification($event->webhook->payload);
|
||||
Log::info("СБП Оплата: {$data->amount} коп.");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Лицензия
|
||||
|
||||
MIT. Автор: Anatoliy Poloz.
|
||||
|
||||
42
examples/09_self_employed_income.php
Normal file
42
examples/09_self_employed_income.php
Normal file
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use Apoloz\AlfaBank\AlfaBank;
|
||||
use Apoloz\AlfaBank\Enums\Environment;
|
||||
use Apoloz\AlfaBank\Requests\SelfEmployed\RegisterIncomeRequest;
|
||||
use Apoloz\AlfaBank\Data\SelfEmployed\CreateIncomeRequestData;
|
||||
|
||||
/**
|
||||
* Пример регистрации дохода самозанятого.
|
||||
* Позволяет автоматически задекларировать доход в ФНС от лица самозанятого.
|
||||
*/
|
||||
|
||||
$baas = AlfaBank::baas(
|
||||
certificatePath: __DIR__ . '/../tests/test_cert.pem',
|
||||
privateKeyPath: __DIR__ . '/../tests/test_key.pem',
|
||||
environment: Environment::SANDBOX
|
||||
);
|
||||
|
||||
// Данные о доходе
|
||||
$incomeData = new CreateIncomeRequestData(
|
||||
vatin: '7700123456', // ИНН самозанятого
|
||||
name: 'Оплата за разработку ПО по договору №123',
|
||||
amount: '15000.00',
|
||||
date: date('Y-m-d'),
|
||||
organizationName: 'ООО Ромашка' // Если плательщик - ЮЛ
|
||||
);
|
||||
|
||||
try {
|
||||
$response = $baas->send(new RegisterIncomeRequest($incomeData));
|
||||
$dto = $response->dto();
|
||||
|
||||
echo "Запрос на регистрацию дохода отправлен!\n";
|
||||
echo "ID запроса: {$dto->requestId}\n";
|
||||
echo "Текущий статус: {$dto->status}\n";
|
||||
} catch (\Exception $e) {
|
||||
echo "Ошибка: " . $e->getMessage() . "\n";
|
||||
}
|
||||
|
||||
43
examples/10_sbp_reversal_history.php
Normal file
43
examples/10_sbp_reversal_history.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use Apoloz\AlfaBank\AlfaBank;
|
||||
use Apoloz\AlfaBank\Enums\Environment;
|
||||
use Apoloz\AlfaBank\Sbp\Requests\Reversal\GetQrcReversalHistoryRequest;
|
||||
|
||||
/**
|
||||
* Пример получения истории возвратов по транзакции СБП.
|
||||
* Полезно для отладки частичных возвратов или поиска зависших операций.
|
||||
*/
|
||||
|
||||
$sbp = AlfaBank::sbp(
|
||||
certificatePath: __DIR__ . '/../tests/test_cert.pem',
|
||||
privateKeyPath: __DIR__ . '/../tests/test_key.pem',
|
||||
keyNameAlias: 'my-alias',
|
||||
environment: Environment::SANDBOX
|
||||
);
|
||||
|
||||
$payrrn = '000000022112'; // Референс изначального платежа
|
||||
$termNo = '90080567';
|
||||
|
||||
try {
|
||||
$response = $sbp->send(new GetQrcReversalHistoryRequest($payrrn, $termNo));
|
||||
$dto = $response->dto();
|
||||
|
||||
echo "Найдено возвратов: " . ($dto->refunds ? $dto->refunds->count() : 0) . "\n";
|
||||
|
||||
if ($dto->refunds) {
|
||||
foreach ($dto->refunds as $refund) {
|
||||
echo "--- \n";
|
||||
echo "Сумма: {$refund->amount} коп.\n";
|
||||
echo "Статус: {$refund->status}\n";
|
||||
echo "ID операции в НСПК: {$refund->trxId}\n";
|
||||
}
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
echo "Ошибка: " . $e->getMessage() . "\n";
|
||||
}
|
||||
|
||||
46
examples/11_file_upload_kyc.php
Normal file
46
examples/11_file_upload_kyc.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use Apoloz\AlfaBank\AlfaBank;
|
||||
use Apoloz\AlfaBank\Enums\Environment;
|
||||
use Apoloz\AlfaBank\Requests\Files\UploadFileRequest;
|
||||
|
||||
/**
|
||||
* Пример загрузки файла для KYC (Знай своего клиента).
|
||||
* Сначала создается бенефициар, банк возвращает ссылку для загрузки паспорта.
|
||||
* Затем SDK загружает файл по этой ссылке.
|
||||
*/
|
||||
|
||||
$baas = AlfaBank::baas(
|
||||
certificatePath: __DIR__ . '/../tests/test_cert.pem',
|
||||
privateKeyPath: __DIR__ . '/../tests/test_key.pem',
|
||||
environment: Environment::SANDBOX
|
||||
);
|
||||
|
||||
// 1. Предположим, мы получили ссылку из ответа CreateBeneficiaryRequest
|
||||
$uploadUrl = 'https://baas.alfabank.ru/filebox/secure-file-transfer/12345?X-Amz-Signature=...';
|
||||
|
||||
// 2. Открываем файл
|
||||
$filePath = __DIR__ . '/../tests/test_cert.pem'; // Для примера берем любой файл
|
||||
$fileResource = fopen($filePath, 'r');
|
||||
|
||||
try {
|
||||
// 3. Загружаем файл методом PUT (как требует Alfa Bank S3 Proxy)
|
||||
$response = $baas->send(new UploadFileRequest($uploadUrl, $fileResource));
|
||||
|
||||
if ($response->status() === 200) {
|
||||
echo "Файл успешно загружен в банк!\n";
|
||||
} else {
|
||||
echo "Ошибка загрузки: " . $response->body() . "\n";
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
echo "Ошибка: " . $e->getMessage() . "\n";
|
||||
} finally {
|
||||
if (is_resource($fileResource)) {
|
||||
fclose($fileResource);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,5 +119,15 @@ class AlfaBankConnector extends Connector implements HasPagination
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable automated JWS response verification and decoding.
|
||||
*/
|
||||
public function withJwsVerification(string $publicKeyPem): static
|
||||
{
|
||||
$this->middleware()->onResponse(new \Apoloz\AlfaBank\Http\Middleware\VerifyJwsResponseMiddleware($publicKeyPem));
|
||||
|
||||
return $this;
|
||||
}
|
||||
// </SECTION>
|
||||
}
|
||||
19
src/Data/SelfEmployed/CreateIncomeRequestData.php
Normal file
19
src/Data/SelfEmployed/CreateIncomeRequestData.php
Normal file
@@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Apoloz\AlfaBank\Data\SelfEmployed;
|
||||
|
||||
use Spatie\LaravelData\Data;
|
||||
|
||||
class CreateIncomeRequestData extends Data
|
||||
{
|
||||
public function __construct(
|
||||
public string $vatin,
|
||||
public string $name,
|
||||
public string $amount,
|
||||
public string $date,
|
||||
public ?string $inn = null,
|
||||
public ?string $organizationName = null,
|
||||
) {}
|
||||
}
|
||||
15
src/Data/SelfEmployed/CreateIncomeResponseData.php
Normal file
15
src/Data/SelfEmployed/CreateIncomeResponseData.php
Normal file
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Apoloz\AlfaBank\Data\SelfEmployed;
|
||||
|
||||
use Spatie\LaravelData\Data;
|
||||
|
||||
class CreateIncomeResponseData extends Data
|
||||
{
|
||||
public function __construct(
|
||||
public string $requestId,
|
||||
public string $status,
|
||||
) {}
|
||||
}
|
||||
36
src/Http/Middleware/VerifyJwsResponseMiddleware.php
Normal file
36
src/Http/Middleware/VerifyJwsResponseMiddleware.php
Normal file
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Apoloz\AlfaBank\Http\Middleware;
|
||||
|
||||
use Apoloz\AlfaBank\Utils\JwsSigner;
|
||||
use Saloon\Http\Response;
|
||||
|
||||
class VerifyJwsResponseMiddleware
|
||||
{
|
||||
public function __construct(
|
||||
protected ?string $publicKey = null
|
||||
) {}
|
||||
|
||||
public function __invoke(Response $response): Response
|
||||
{
|
||||
$contentType = $response->header('Content-Type');
|
||||
|
||||
if (str_contains((string)$contentType, 'application/jose')) {
|
||||
if (!$this->publicKey) {
|
||||
return $response;
|
||||
}
|
||||
|
||||
$jws = $response->body();
|
||||
$decoded = JwsSigner::verify($jws, $this->publicKey);
|
||||
|
||||
if ($decoded) {
|
||||
// We create a new response with the decoded JSON body
|
||||
return $response->fake(json_encode($decoded), $response->status(), $response->headers()->all());
|
||||
}
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
@@ -15,12 +15,32 @@ class VerifyAlfaWebhook
|
||||
*/
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
// 1. Check Basic Auth (Legacy/Simple)
|
||||
$user = config('alfa-bank.webhook.user');
|
||||
$password = config('alfa-bank.webhook.password');
|
||||
|
||||
if ($user && $password) {
|
||||
if ($request->getUser() !== $user || $request->getPassword() !== $password) {
|
||||
return response()->json(['error' => 'Unauthorized Webhook'], 401);
|
||||
return response()->json(['error' => 'Unauthorized Webhook (Basic Auth)'], 401);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Check JWS Signature (Advanced)
|
||||
$publicKeyPath = config('alfa-bank.webhook.alfa_public_key');
|
||||
if ($publicKeyPath && file_exists($publicKeyPath)) {
|
||||
$signature = $request->header('x-jws-signature') ?: $request->getContent();
|
||||
|
||||
// If content is a JWS string (3 parts separated by dot)
|
||||
if (is_string($signature) && count(explode('.', $signature)) === 3) {
|
||||
$publicKey = file_get_contents($publicKeyPath);
|
||||
$verified = \Apoloz\AlfaBank\Utils\JwsSigner::verify($signature, $publicKey);
|
||||
|
||||
if (!$verified) {
|
||||
return response()->json(['error' => 'Invalid Webhook Signature'], 401);
|
||||
}
|
||||
|
||||
// Replace request content with verified payload
|
||||
$request->merge($verified);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
22
src/Requests/Accounts/GetAccountStatusRequest.php
Normal file
22
src/Requests/Accounts/GetAccountStatusRequest.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Apoloz\AlfaBank\Requests\Accounts;
|
||||
|
||||
use Saloon\Enums\Method;
|
||||
use Saloon\Http\Request;
|
||||
|
||||
class GetAccountStatusRequest extends Request
|
||||
{
|
||||
protected Method $method = Method::GET;
|
||||
|
||||
public function __construct(
|
||||
protected string $accountNumber
|
||||
) {}
|
||||
|
||||
public function resolveEndpoint(): string
|
||||
{
|
||||
return "/api/gd/v1/accounts/{$this->accountNumber}/status";
|
||||
}
|
||||
}
|
||||
22
src/Requests/B2C/GetB2CPackageStatusRequest.php
Normal file
22
src/Requests/B2C/GetB2CPackageStatusRequest.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Apoloz\AlfaBank\Requests\B2C;
|
||||
|
||||
use Saloon\Enums\Method;
|
||||
use Saloon\Http\Request;
|
||||
|
||||
class GetB2CPackageStatusRequest extends Request
|
||||
{
|
||||
protected Method $method = Method::GET;
|
||||
|
||||
public function __construct(
|
||||
protected string $packageId
|
||||
) {}
|
||||
|
||||
public function resolveEndpoint(): string
|
||||
{
|
||||
return "/api/b2cpay/jp/v1/payments/packages/{$this->packageId}";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Apoloz\AlfaBank\Requests\DigitalIdentity;
|
||||
|
||||
use Saloon\Enums\Method;
|
||||
use Saloon\Http\Request;
|
||||
|
||||
class GetAccountChannelStatusRequest extends Request
|
||||
{
|
||||
protected Method $method = Method::GET;
|
||||
|
||||
public function __construct(
|
||||
protected string $accountNumber
|
||||
) {}
|
||||
|
||||
public function resolveEndpoint(): string
|
||||
{
|
||||
return "/api/gd/v1/accounts/{$this->accountNumber}/status";
|
||||
}
|
||||
}
|
||||
44
src/Requests/Files/UploadFileRequest.php
Normal file
44
src/Requests/Files/UploadFileRequest.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Apoloz\AlfaBank\Requests\Files;
|
||||
|
||||
use Saloon\Contracts\Body\HasBody;
|
||||
use Saloon\Enums\Method;
|
||||
use Saloon\Http\Request;
|
||||
use Saloon\Traits\Body\HasStreamBody;
|
||||
|
||||
class UploadFileRequest extends Request implements HasBody
|
||||
{
|
||||
use HasStreamBody;
|
||||
|
||||
protected Method $method = Method::PUT;
|
||||
|
||||
/**
|
||||
* @param string $url Full pre-signed URL from Alfa Bank
|
||||
* @param mixed $resource File resource or stream
|
||||
*/
|
||||
public function __construct(
|
||||
protected string $url,
|
||||
protected $resource
|
||||
) {}
|
||||
|
||||
public function resolveEndpoint(): string
|
||||
{
|
||||
return $this->url;
|
||||
}
|
||||
|
||||
protected function defaultHeaders(): array
|
||||
{
|
||||
return [
|
||||
'Accept' => '*/*',
|
||||
'Content-Type' => 'application/octet-stream',
|
||||
];
|
||||
}
|
||||
|
||||
protected function defineBody(): mixed
|
||||
{
|
||||
return $this->resource;
|
||||
}
|
||||
}
|
||||
32
src/Requests/Partner/CreateIndividualRequest.php
Normal file
32
src/Requests/Partner/CreateIndividualRequest.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Apoloz\AlfaBank\Requests\Partner;
|
||||
|
||||
use Spatie\LaravelData\Data;
|
||||
use Saloon\Contracts\Body\HasBody;
|
||||
use Saloon\Enums\Method;
|
||||
use Saloon\Http\Request;
|
||||
use Saloon\Traits\Body\HasJsonBody;
|
||||
|
||||
class CreateIndividualRequest extends Request implements HasBody
|
||||
{
|
||||
use HasJsonBody;
|
||||
|
||||
protected Method $method = Method::POST;
|
||||
|
||||
public function __construct(
|
||||
protected array $data
|
||||
) {}
|
||||
|
||||
public function resolveEndpoint(): string
|
||||
{
|
||||
return '/partner/jp/v1/individuals';
|
||||
}
|
||||
|
||||
protected function defaultBody(): array
|
||||
{
|
||||
return $this->data;
|
||||
}
|
||||
}
|
||||
22
src/Requests/Poa/DownloadPoaRequest.php
Normal file
22
src/Requests/Poa/DownloadPoaRequest.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Apoloz\AlfaBank\Requests\Poa;
|
||||
|
||||
use Saloon\Enums\Method;
|
||||
use Saloon\Http\Request;
|
||||
|
||||
class DownloadPoaRequest extends Request
|
||||
{
|
||||
protected Method $method = Method::GET;
|
||||
|
||||
public function __construct(
|
||||
protected string $requestUuid
|
||||
) {}
|
||||
|
||||
public function resolveEndpoint(): string
|
||||
{
|
||||
return "/api/jp/v1/poa/requests/{$this->requestUuid}/download";
|
||||
}
|
||||
}
|
||||
22
src/Requests/Registries/GetRegistryPaymentsRequest.php
Normal file
22
src/Requests/Registries/GetRegistryPaymentsRequest.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Apoloz\AlfaBank\Requests\Registries;
|
||||
|
||||
use Saloon\Enums\Method;
|
||||
use Saloon\Http\Request;
|
||||
|
||||
class GetRegistryPaymentsRequest extends Request
|
||||
{
|
||||
protected Method $method = Method::GET;
|
||||
|
||||
public function __construct(
|
||||
protected string $registryId
|
||||
) {}
|
||||
|
||||
public function resolveEndpoint(): string
|
||||
{
|
||||
return "/api/jp/v1/registries/{$this->registryId}/payments";
|
||||
}
|
||||
}
|
||||
22
src/Requests/Registries/GetRegistryRequest.php
Normal file
22
src/Requests/Registries/GetRegistryRequest.php
Normal file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Apoloz\AlfaBank\Requests\Registries;
|
||||
|
||||
use Saloon\Enums\Method;
|
||||
use Saloon\Http\Request;
|
||||
|
||||
class GetRegistryRequest extends Request
|
||||
{
|
||||
protected Method $method = Method::GET;
|
||||
|
||||
public function __construct(
|
||||
protected string $registryId
|
||||
) {}
|
||||
|
||||
public function resolveEndpoint(): string
|
||||
{
|
||||
return "/api/jp/v1/registries/{$this->registryId}";
|
||||
}
|
||||
}
|
||||
18
src/Requests/SelfEmployed/GetRegionsRequest.php
Normal file
18
src/Requests/SelfEmployed/GetRegionsRequest.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Apoloz\AlfaBank\Requests\SelfEmployed;
|
||||
|
||||
use Saloon\Enums\Method;
|
||||
use Saloon\Http\Request;
|
||||
|
||||
class GetRegionsRequest extends Request
|
||||
{
|
||||
protected Method $method = Method::GET;
|
||||
|
||||
public function resolveEndpoint(): string
|
||||
{
|
||||
return '/semp/v1/dictionary/regions';
|
||||
}
|
||||
}
|
||||
39
src/Requests/SelfEmployed/RegisterIncomeRequest.php
Normal file
39
src/Requests/SelfEmployed/RegisterIncomeRequest.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Apoloz\AlfaBank\Requests\SelfEmployed;
|
||||
|
||||
use Apoloz\AlfaBank\Data\SelfEmployed\CreateIncomeRequestData;
|
||||
use Apoloz\AlfaBank\Data\SelfEmployed\CreateIncomeResponseData;
|
||||
use Saloon\Contracts\Body\HasBody;
|
||||
use Saloon\Enums\Method;
|
||||
use Saloon\Http\Request;
|
||||
use Saloon\Http\Response;
|
||||
use Saloon\Traits\Body\HasJsonBody;
|
||||
|
||||
class RegisterIncomeRequest extends Request implements HasBody
|
||||
{
|
||||
use HasJsonBody;
|
||||
|
||||
protected Method $method = Method::POST;
|
||||
|
||||
public function __construct(
|
||||
protected CreateIncomeRequestData $data
|
||||
) {}
|
||||
|
||||
public function resolveEndpoint(): string
|
||||
{
|
||||
return '/semp/v1/selfemployed/income';
|
||||
}
|
||||
|
||||
protected function defaultBody(): array
|
||||
{
|
||||
return $this->data->toArray();
|
||||
}
|
||||
|
||||
public function createDtoFromResponse(Response $response): CreateIncomeResponseData
|
||||
{
|
||||
return CreateIncomeResponseData::from($response->json());
|
||||
}
|
||||
}
|
||||
18
src/Requests/Signature/GetDistinguishedNameRequest.php
Normal file
18
src/Requests/Signature/GetDistinguishedNameRequest.php
Normal file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Apoloz\AlfaBank\Requests\Signature;
|
||||
|
||||
use Saloon\Enums\Method;
|
||||
use Saloon\Http\Request;
|
||||
|
||||
class GetDistinguishedNameRequest extends Request
|
||||
{
|
||||
protected Method $method = Method::GET;
|
||||
|
||||
public function resolveEndpoint(): string
|
||||
{
|
||||
return '/jp/v3/signature/dn';
|
||||
}
|
||||
}
|
||||
33
src/Sbp/Data/Reversal/ReversalHistoryResponseData.php
Normal file
33
src/Sbp/Data/Reversal/ReversalHistoryResponseData.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Apoloz\AlfaBank\Sbp\Data\Reversal;
|
||||
|
||||
use Spatie\LaravelData\Attributes\DataCollectionOf;
|
||||
use Spatie\LaravelData\Data;
|
||||
use Spatie\LaravelData\DataCollection;
|
||||
|
||||
class ReversalHistoryResponseData extends Data
|
||||
{
|
||||
public function __construct(
|
||||
public int $ErrorCode,
|
||||
public string $message,
|
||||
#[DataCollectionOf(ReversalHistoryItemData::class)]
|
||||
public ?DataCollection $refunds = null,
|
||||
) {}
|
||||
}
|
||||
|
||||
class ReversalHistoryItemData extends Data
|
||||
{
|
||||
public function __construct(
|
||||
public string $TermNo,
|
||||
public string $amount,
|
||||
public string $originalQrcId,
|
||||
public string $originalTrxId,
|
||||
public string $payerId,
|
||||
public string $status,
|
||||
public ?string $trxId = null,
|
||||
public ?string $trxDT = null,
|
||||
) {}
|
||||
}
|
||||
24
src/Sbp/Data/Webhooks/SbpPaymentNotificationData.php
Normal file
24
src/Sbp/Data/Webhooks/SbpPaymentNotificationData.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Apoloz\AlfaBank\Sbp\Data\Webhooks;
|
||||
|
||||
use Spatie\LaravelData\Data;
|
||||
|
||||
class SbpPaymentNotificationData extends Data
|
||||
{
|
||||
public function __construct(
|
||||
public string $command,
|
||||
public string $qrcId,
|
||||
public string $trxId,
|
||||
public string $status,
|
||||
public string $amount,
|
||||
public string $timestamp,
|
||||
public string $payerId,
|
||||
public string $payrrn,
|
||||
public string $TermNo,
|
||||
public ?string $kzo = null,
|
||||
public ?string $CreditDatePos = null,
|
||||
) {}
|
||||
}
|
||||
27
src/Sbp/Data/Webhooks/SbpRefundNotificationData.php
Normal file
27
src/Sbp/Data/Webhooks/SbpRefundNotificationData.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Apoloz\AlfaBank\Sbp\Data\Webhooks;
|
||||
|
||||
use Spatie\LaravelData\Data;
|
||||
|
||||
class SbpRefundNotificationData extends Data
|
||||
{
|
||||
public function __construct(
|
||||
public string $command,
|
||||
public string $TermNo,
|
||||
public string $originalQrcId,
|
||||
public string $originalTrxId,
|
||||
public string $trxId,
|
||||
public string $trxDT,
|
||||
public string $status,
|
||||
public string $amount,
|
||||
public string $ErrorCode,
|
||||
public string $message,
|
||||
public string $payerId,
|
||||
public ?string $messageID = null,
|
||||
public ?string $agentRefundRequestId = null,
|
||||
public ?string $opkcRefundRequestId = null,
|
||||
) {}
|
||||
}
|
||||
43
src/Sbp/Requests/Reversal/GetQrcReversalHistoryRequest.php
Normal file
43
src/Sbp/Requests/Reversal/GetQrcReversalHistoryRequest.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Apoloz\AlfaBank\Sbp\Requests\Reversal;
|
||||
|
||||
use Apoloz\AlfaBank\Sbp\Data\Reversal\ReversalHistoryResponseData;
|
||||
use Saloon\Contracts\Body\HasBody;
|
||||
use Saloon\Enums\Method;
|
||||
use Saloon\Http\Request;
|
||||
use Saloon\Http\Response;
|
||||
use Saloon\Traits\Body\HasJsonBody;
|
||||
|
||||
class GetQrcReversalHistoryRequest extends Request implements HasBody
|
||||
{
|
||||
use HasJsonBody;
|
||||
|
||||
protected Method $method = Method::POST;
|
||||
|
||||
public function __construct(
|
||||
protected string $payrrn,
|
||||
protected string $TermNo,
|
||||
) {}
|
||||
|
||||
public function resolveEndpoint(): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
protected function defaultBody(): array
|
||||
{
|
||||
return [
|
||||
'command' => 'GetQRCreversalHistory',
|
||||
'TermNo' => $this->TermNo,
|
||||
'payrrn' => $this->payrrn,
|
||||
];
|
||||
}
|
||||
|
||||
public function createDtoFromResponse(Response $response): ReversalHistoryResponseData
|
||||
{
|
||||
return ReversalHistoryResponseData::from($response->json());
|
||||
}
|
||||
}
|
||||
@@ -38,7 +38,23 @@ class SbpConnector extends Connector implements HasPagination
|
||||
return $this->alfaConfig->baseUrl;
|
||||
}
|
||||
|
||||
protected function defaultConfig(): array
|
||||
/**
|
||||
* Parse payment notification from request body.
|
||||
*/
|
||||
public function parsePaymentNotification(array $data): \Apoloz\AlfaBank\Sbp\Data\Webhooks\SbpPaymentNotificationData
|
||||
{
|
||||
return \Apoloz\AlfaBank\Sbp\Data\Webhooks\SbpPaymentNotificationData::from($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse refund notification from request body.
|
||||
*/
|
||||
public function parseRefundNotification(array $data): \Apoloz\AlfaBank\Sbp\Data\Webhooks\SbpRefundNotificationData
|
||||
{
|
||||
return \Apoloz\AlfaBank\Sbp\Data\Webhooks\SbpRefundNotificationData::from($data);
|
||||
}
|
||||
|
||||
protected function defaultHeaders(): array
|
||||
{
|
||||
$config = [
|
||||
'cert' => $this->alfaConfig->certificatePath,
|
||||
@@ -92,6 +108,7 @@ class SbpConnector extends Connector implements HasPagination
|
||||
|
||||
// Mutate the request to add SBP-specific headers and set the exact signed body.
|
||||
$pendingRequest->headers()->add('Authorization', $signatureBase64);
|
||||
$pendingRequest->headers()->add('key-name', $this->keyNameAlias);
|
||||
// The SBP API documentation specifies this content-type for signed requests.
|
||||
$pendingRequest->headers()->add('Content-Type', 'application/x-www-form-urlencoded');
|
||||
|
||||
|
||||
@@ -53,7 +53,27 @@ test('can get b2c payment report', function () {
|
||||
$connector = new AlfaBankConnector($config);
|
||||
$connector->withMockClient($mockClient);
|
||||
|
||||
$response = $connector->send(new GetB2CPaymentReportRequest(partnerId: 123));
|
||||
$response = $connector->send(new \Apoloz\AlfaBank\Requests\B2C\GetB2CPaymentReportRequest(null, null, '2023-01-01', '2023-01-02'));
|
||||
|
||||
expect($response->status())->toBe(200);
|
||||
});
|
||||
|
||||
test('can get b2c package status', function () {
|
||||
$mockClient = new \Saloon\Http\Faking\MockClient([
|
||||
\Apoloz\AlfaBank\Requests\B2C\GetB2CPackageStatusRequest::class => \Saloon\Http\Faking\MockResponse::make([
|
||||
'packageId' => 'pkg-123',
|
||||
'status' => 'COMPLETED'
|
||||
], 200),
|
||||
]);
|
||||
|
||||
$config = new \Apoloz\AlfaBank\Core\AlfaConfig('https://test.com', __DIR__ . '/../../test_cert.pem', __DIR__ . '/../../test_key.pem');
|
||||
$signer = new \Apoloz\AlfaBank\Services\OpenSslCmsSigner(__DIR__ . '/../../test_cert.pem', __DIR__ . '/../../test_key.pem');
|
||||
$connector = new \Apoloz\AlfaBank\Core\AlfaBankConnector($config, $signer);
|
||||
$connector->withMockClient($mockClient);
|
||||
|
||||
$response = $connector->send(new \Apoloz\AlfaBank\Requests\B2C\GetB2CPackageStatusRequest('pkg-123'));
|
||||
|
||||
expect($response->status())->toBe(200)
|
||||
->and($response->json('status'))->toBe('COMPLETED');
|
||||
});
|
||||
|
||||
|
||||
@@ -59,7 +59,26 @@ test('can sign payment registry', function () {
|
||||
]
|
||||
], DataCollection::class);
|
||||
|
||||
$response = $connector->send(new SignRegistryRequest('REG-123', $signatures));
|
||||
$response = $connector->send(new \Apoloz\AlfaBank\Requests\Registries\SignRegistryRequest('reg-1', \Apoloz\AlfaBank\Data\Registries\RegistrySignatureData::collect([], \Spatie\LaravelData\DataCollection::class)));
|
||||
|
||||
expect($response->status())->toBe(200);
|
||||
});
|
||||
|
||||
test('can get registry details', function () {
|
||||
$mockClient = new \Saloon\Http\Faking\MockClient([
|
||||
\Apoloz\AlfaBank\Requests\Registries\GetRegistryRequest::class => \Saloon\Http\Faking\MockResponse::make(['id' => 'reg-1'], 200),
|
||||
\Apoloz\AlfaBank\Requests\Registries\GetRegistryPaymentsRequest::class => \Saloon\Http\Faking\MockResponse::make(['payments' => []], 200),
|
||||
]);
|
||||
|
||||
$config = new \Apoloz\AlfaBank\Core\AlfaConfig('https://test.com', __DIR__ . '/../../test_cert.pem', __DIR__ . '/../../test_key.pem');
|
||||
$signer = new \Apoloz\AlfaBank\Services\OpenSslCmsSigner(__DIR__ . '/../../test_cert.pem', __DIR__ . '/../../test_key.pem');
|
||||
$connector = new \Apoloz\AlfaBank\Core\AlfaBankConnector($config, $signer);
|
||||
$connector->withMockClient($mockClient);
|
||||
|
||||
$response = $connector->send(new \Apoloz\AlfaBank\Requests\Registries\GetRegistryRequest('reg-1'));
|
||||
expect($response->status())->toBe(200);
|
||||
|
||||
$response = $connector->send(new \Apoloz\AlfaBank\Requests\Registries\GetRegistryPaymentsRequest('reg-1'));
|
||||
expect($response->status())->toBe(200);
|
||||
});
|
||||
|
||||
|
||||
@@ -35,3 +35,37 @@ test('can check reversal data', function () {
|
||||
expect($response->status())->toBe(200)
|
||||
->and($response->dto()->messageID)->toBe('guid-123');
|
||||
});
|
||||
|
||||
test('can get reversal history', function () {
|
||||
$mockClient = new \Saloon\Http\Faking\MockClient([
|
||||
\Apoloz\AlfaBank\Sbp\Requests\Reversal\GetQrcReversalHistoryRequest::class => \Saloon\Http\Faking\MockResponse::make([
|
||||
'ErrorCode' => 0,
|
||||
'message' => 'OK',
|
||||
'refunds' => [
|
||||
[
|
||||
'TermNo' => '90080567',
|
||||
'amount' => '10000',
|
||||
'originalQrcId' => 'QR-123',
|
||||
'originalTrxId' => 'TRX-123',
|
||||
'payerId' => '7900',
|
||||
'status' => 'ACWP'
|
||||
]
|
||||
]
|
||||
], 200),
|
||||
]);
|
||||
|
||||
$config = new \Apoloz\AlfaBank\Core\AlfaConfig(
|
||||
baseUrl: 'https://test.com',
|
||||
certificatePath: __DIR__ . '/../../test_key.pem',
|
||||
privateKeyPath: __DIR__ . '/../../test_key.pem'
|
||||
);
|
||||
$connector = new \Apoloz\AlfaBank\Sbp\SbpConnector($config, 'alias');
|
||||
$connector->withMockClient($mockClient);
|
||||
|
||||
$response = $connector->send(new \Apoloz\AlfaBank\Sbp\Requests\Reversal\GetQrcReversalHistoryRequest('payrrn-1', 'term-1'));
|
||||
$dto = $response->dto();
|
||||
|
||||
expect($dto->ErrorCode)->toBe(0)
|
||||
->and($dto->refunds->count())->toBe(1)
|
||||
->and($dto->refunds->first()->status)->toBe('ACWP');
|
||||
});
|
||||
|
||||
31
tests/Feature/Sbp/WebhookTest.php
Normal file
31
tests/Feature/Sbp/WebhookTest.php
Normal file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
use Apoloz\AlfaBank\Sbp\SbpConnector;
|
||||
use Apoloz\AlfaBank\Core\AlfaConfig;
|
||||
|
||||
test('can parse payment notification webhook', function () {
|
||||
$config = new AlfaConfig(
|
||||
baseUrl: 'https://test.com',
|
||||
certificatePath: __DIR__ . '/../../test_key.pem',
|
||||
privateKeyPath: __DIR__ . '/../../test_key.pem'
|
||||
);
|
||||
$connector = new SbpConnector($config, 'alias');
|
||||
|
||||
$payload = [
|
||||
'command' => 'qrc/notification',
|
||||
'qrcId' => 'QR123',
|
||||
'trxId' => 'TRX123',
|
||||
'status' => 'ACWP',
|
||||
'amount' => '1000',
|
||||
'timestamp' => '2025-01-01T10:00:00Z',
|
||||
'payerId' => '7900',
|
||||
'payrrn' => 'RR123',
|
||||
'TermNo' => 'T123'
|
||||
];
|
||||
|
||||
$dto = $connector->parsePaymentNotification($payload);
|
||||
|
||||
expect($dto->qrcId)->toBe('QR123')
|
||||
->and($dto->amount)->toBe('1000')
|
||||
->and($dto->status)->toBe('ACWP');
|
||||
});
|
||||
@@ -23,3 +23,22 @@ test('can get activities dictionary', function () {
|
||||
->and($response->dto()->count())->toBe(2)
|
||||
->and($response->dto()->first()->name)->toBe('IT Services');
|
||||
});
|
||||
|
||||
test('can get regions dictionary', function () {
|
||||
$mockClient = new \Saloon\Http\Faking\MockClient([
|
||||
\Apoloz\AlfaBank\Requests\SelfEmployed\GetRegionsRequest::class => \Saloon\Http\Faking\MockResponse::make([
|
||||
'regions' => [['code' => '77', 'name' => 'Москва']]
|
||||
], 200),
|
||||
]);
|
||||
|
||||
$config = new \Apoloz\AlfaBank\Core\AlfaConfig('https://test.com', __DIR__ . '/../../test_cert.pem', __DIR__ . '/../../test_key.pem');
|
||||
$signer = new \Apoloz\AlfaBank\Services\OpenSslCmsSigner(__DIR__ . '/../../test_cert.pem', __DIR__ . '/../../test_key.pem');
|
||||
$connector = new \Apoloz\AlfaBank\Core\AlfaBankConnector($config, $signer);
|
||||
$connector->withMockClient($mockClient);
|
||||
|
||||
$response = $connector->send(new \Apoloz\AlfaBank\Requests\SelfEmployed\GetRegionsRequest());
|
||||
|
||||
expect($response->status())->toBe(200)
|
||||
->and($response->json('regions.0.name'))->toBe('Москва');
|
||||
});
|
||||
|
||||
|
||||
41
tests/Feature/SelfEmployed/IncomeTest.php
Normal file
41
tests/Feature/SelfEmployed/IncomeTest.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
use Apoloz\AlfaBank\Core\AlfaBankConnector;
|
||||
use Apoloz\AlfaBank\Core\AlfaConfig;
|
||||
use Apoloz\AlfaBank\Services\OpenSslCmsSigner;
|
||||
use Apoloz\AlfaBank\Requests\SelfEmployed\RegisterIncomeRequest;
|
||||
use Apoloz\AlfaBank\Data\SelfEmployed\CreateIncomeRequestData;
|
||||
use Saloon\Http\Faking\MockResponse;
|
||||
use Saloon\Http\Faking\MockClient;
|
||||
|
||||
test('can register income', function () {
|
||||
$mockClient = new MockClient([
|
||||
RegisterIncomeRequest::class => MockResponse::make([
|
||||
'requestId' => 'req-1',
|
||||
'status' => 'CREATED'
|
||||
], 200),
|
||||
]);
|
||||
|
||||
$config = new AlfaConfig(
|
||||
baseUrl: 'https://test.com',
|
||||
certificatePath: __DIR__ . '/../../test_key.pem',
|
||||
privateKeyPath: __DIR__ . '/../../test_key.pem'
|
||||
);
|
||||
$signer = new OpenSslCmsSigner(__DIR__ . '/../../test_key.pem', __DIR__ . '/../../test_key.pem');
|
||||
|
||||
$connector = new AlfaBankConnector($config, $signer);
|
||||
$connector->withMockClient($mockClient);
|
||||
|
||||
$data = new CreateIncomeRequestData(
|
||||
vatin: '1234567890',
|
||||
name: 'Services',
|
||||
amount: '100.00',
|
||||
date: '2025-01-01'
|
||||
);
|
||||
|
||||
$response = $connector->send(new RegisterIncomeRequest($data));
|
||||
$dto = $response->dto();
|
||||
|
||||
expect($dto->requestId)->toBe('req-1')
|
||||
->and($dto->status)->toBe('CREATED');
|
||||
});
|
||||
28
tests/Feature/Signature/SignatureTest.php
Normal file
28
tests/Feature/Signature/SignatureTest.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
use Apoloz\AlfaBank\Core\AlfaBankConnector;
|
||||
use Apoloz\AlfaBank\Core\AlfaConfig;
|
||||
use Apoloz\AlfaBank\Services\OpenSslCmsSigner;
|
||||
use Apoloz\AlfaBank\Requests\Signature\GetDistinguishedNameRequest;
|
||||
use Saloon\Http\Faking\MockResponse;
|
||||
use Saloon\Http\Faking\MockClient;
|
||||
|
||||
test('can get distinguished name', function () {
|
||||
$mockClient = new MockClient([
|
||||
GetDistinguishedNameRequest::class => MockResponse::make(['dn' => 'CN=Test'], 200),
|
||||
]);
|
||||
|
||||
$config = new AlfaConfig(
|
||||
baseUrl: 'https://test.com',
|
||||
certificatePath: __DIR__ . '/../../test_key.pem',
|
||||
privateKeyPath: __DIR__ . '/../../test_key.pem'
|
||||
);
|
||||
$signer = new OpenSslCmsSigner(__DIR__ . '/../../test_key.pem', __DIR__ . '/../../test_key.pem');
|
||||
|
||||
$connector = new AlfaBankConnector($config, $signer);
|
||||
$connector->withMockClient($mockClient);
|
||||
|
||||
$response = $connector->send(new GetDistinguishedNameRequest());
|
||||
|
||||
expect($response->json('dn'))->toBe('CN=Test');
|
||||
});
|
||||
Reference in New Issue
Block a user