diff --git a/README.md b/README.md
index cdb8eac..291ba8a 100644
--- a/README.md
+++ b/README.md
@@ -101,6 +101,7 @@ try {
| `getPackList(?DateTimeInterface $date = null): ?array` | `GET GetPackList` | список идентификаторов контейнеров, готовых к получению |
| `getPackListResponse(DateTimeInterface\|string\|null $since = null)` | `GET GetPackList` | список контейнеров и отметка времени последнего из них |
| `getPack(string $id): Packet` | `GET GetPack` | транспортный контейнер |
+| `getSettings(string $inn, string $bic, ?string $account = null): Settings` | `POST GetSettings` | настройки обмена с банком |
Отметка времени для списка контейнеров задаётся по часам сервера банка и передаётся
в формате `dd.MM.yyyy HH:mm:ss`. Чтобы получать только новые контейнеры, передавайте
@@ -116,6 +117,22 @@ foreach ($list->getPacketID() as $id) {
$lastTimestamp = $list->getTimeStampLastPacket() ?? $lastTimestamp; // сохранить до следующего запроса
```
+### Настройки обмена
+
+Настройки обмена с банком можно получить автоматически. Если идентификатор клиента в банке ещё
+неизвестен, в `customerId` передаётся `'0'`; из настроек его можно взять для дальнейшей работы:
+
+```php
+$settings = (new Client(['customerId' => '0'] + $credentials))
+ ->getSettings(inn: '7705260699', bic: '044525888', account: '40702810813123123222');
+
+$data = $settings->getData();
+$data->getCustomerID(); // идентификатор клиента в банке
+$data->getBankServerAddress(); // адрес ресурса банка
+$data->getDocKinds(); // виды документов, которыми возможен обмен: ['03', '05', '10', ...]
+$data->getLogon()->getLogin()?->getUser();
+```
+
### Ошибки
- `TTBooking\DirectBank\Exceptions\ClientException` — банк вернул ошибку (`ResultBank/Error`),
diff --git a/src/Client.php b/src/Client.php
index 8a7b3b1..dec61cc 100644
--- a/src/Client.php
+++ b/src/Client.php
@@ -21,6 +21,7 @@
use TTBooking\DirectBank\Objects\GetPacketListResponseType;
use TTBooking\DirectBank\Objects\Packet;
use TTBooking\DirectBank\Objects\ResultBank;
+use TTBooking\DirectBank\Objects\Settings;
class Client implements ClientInterface
{
@@ -125,6 +126,35 @@ protected function requestPackList(?\DateTimeInterface $since): ?GetPacketListRe
return $result->getSuccess()->getGetPacketListResponse();
}
+ /**
+ * Настройки обмена с банком в автоматическом режиме (GetSettings).
+ * Если идентификатор клиента в банке ещё неизвестен, в настройке customerId передаётся '0'.
+ *
+ * @param string $inn ИНН организации, для которой запрашиваются настройки
+ * @param string $bic БИК банка
+ * @param string|null $account номер расчётного счёта; для зарплатных проектов может отсутствовать
+ */
+ public function getSettings(string $inn, string $bic, ?string $account = null): Settings
+ {
+ $headers = array_filter([
+ 'inn' => $inn,
+ 'bic' => $bic,
+ 'account' => $account,
+ 'availableapiversion' => $this->settings['availableApiVersion'],
+ ], fn($value) => $value !== null);
+
+ $result = $this->invoke('POST', 'GetSettings', headers: $headers);
+
+ $response = $result->getSuccess()->getGetSettingsResponse()
+ ?? throw new UnexpectedResponseException('Bank response to GetSettings has no GetSettingsResponse.');
+
+ try {
+ return $response->getSettings();
+ } catch (\Throwable $e) {
+ throw new UnexpectedResponseException('Unable to parse settings from GetSettings response: ' . $e->getMessage(), 0, $e);
+ }
+ }
+
public function getPack(string $uid): Packet
{
$result = $this->invoke('GET', 'GetPack', query: ['id' => $uid]);
diff --git a/src/ClientInterface.php b/src/ClientInterface.php
index e84cc91..6c90497 100644
--- a/src/ClientInterface.php
+++ b/src/ClientInterface.php
@@ -5,6 +5,7 @@
use TTBooking\DirectBank\Objects\GetPacketListResponseType;
use TTBooking\DirectBank\Objects\Packet;
+use TTBooking\DirectBank\Objects\Settings;
interface ClientInterface
{
@@ -19,4 +20,6 @@ public function getPackList(?\DateTimeInterface $dateTime = null): ?array;
public function getPackListResponse(\DateTimeInterface|string|null $since = null): GetPacketListResponseType;
public function getPack(string $uid) : Packet;
+
+ public function getSettings(string $inn, string $bic, ?string $account = null): Settings;
}
diff --git a/src/Objects/GetSettingsDataType.php b/src/Objects/GetSettingsDataType.php
new file mode 100644
index 0000000..ed48b0c
--- /dev/null
+++ b/src/Objects/GetSettingsDataType.php
@@ -0,0 +1,31 @@
+value;
+ }
+
+ public function getDockind(): string
+ {
+ return $this->dockind;
+ }
+}
diff --git a/src/Objects/GetSettingsResponseType.php b/src/Objects/GetSettingsResponseType.php
new file mode 100644
index 0000000..5d9cde0
--- /dev/null
+++ b/src/Objects/GetSettingsResponseType.php
@@ -0,0 +1,77 @@
+id;
+ }
+
+ public function getFormatVersion(): string
+ {
+ return $this->formatVersion;
+ }
+
+ public function getCreationDate(): string
+ {
+ return $this->creationDate;
+ }
+
+ public function getUserAgent(): ?string
+ {
+ return $this->userAgent;
+ }
+
+ /**
+ * Настройки обмена в base64
+ */
+ public function getData(): string
+ {
+ return $this->Data->getValue();
+ }
+
+ public function getDockind(): string
+ {
+ return $this->Data->getDockind();
+ }
+
+ /**
+ * Разобранные настройки обмена
+ */
+ public function getSettings(): Settings
+ {
+ $settings = new Settings();
+ $settings->mapFromXml(base64_decode($this->getData(), true) ?: throw new \UnexpectedValueException('Settings data is not valid base64.'));
+
+ return $settings;
+ }
+}
diff --git a/src/Objects/SuccessResultType.php b/src/Objects/SuccessResultType.php
index 3a32621..9738c3d 100644
--- a/src/Objects/SuccessResultType.php
+++ b/src/Objects/SuccessResultType.php
@@ -14,6 +14,8 @@ class SuccessResultType
protected ?Packet $GetPacketResponse = null;
+ protected ?GetSettingsResponseType $GetSettingsResponse = null;
+
/**
* @return \TTBooking\DirectBank\Objects\LogonResponseType|null
*/
@@ -36,4 +38,9 @@ public function getGetPacketResponse(): ?Packet
{
return $this->GetPacketResponse;
}
+
+ public function getGetSettingsResponse(): ?GetSettingsResponseType
+ {
+ return $this->GetSettingsResponse;
+ }
}
\ No newline at end of file
diff --git a/tests/ClientHttpTest.php b/tests/ClientHttpTest.php
index 844ff7a..cd019dd 100644
--- a/tests/ClientHttpTest.php
+++ b/tests/ClientHttpTest.php
@@ -7,6 +7,7 @@
use GuzzleHttp\Psr7\Response;
use PHPUnit\Framework\TestCase;
use Psr\Http\Message\RequestInterface;
+use TTBooking\DirectBank\Dictionary\ErrorCode;
use TTBooking\DirectBank\Exceptions\ClientException;
use TTBooking\DirectBank\Exceptions\OtpRequiredException;
use TTBooking\DirectBank\Exceptions\UnexpectedResponseException;
@@ -455,6 +456,80 @@ public function testCreateSessionStoresSessionId()
$this->assertSame('SID-1', $this->requests[1]->getHeaderLine('SID'));
}
+ public function testGetSettings()
+ {
+ $this->mock->append(
+ self::logon(),
+ new Response(200, [], file_get_contents(__DIR__ . '/Fixture/xml/1c/GetSettingsResponse.xml')),
+ );
+
+ $settings = $this->createClient(['customerId' => '0'])->getSettings('761700021132', '044525888', '40802810200000099888');
+
+ $this->assertSame('EFD857B5-7FA8-4195-8666-2CCADBC3C8DE', $settings->getId());
+ $this->assertSame('2806', $settings->getData()->getCustomerID());
+ $this->assertSame('https://dbogate.demobank.ru/', $settings->getData()->getBankServerAddress());
+ $this->assertSame('user_login', $settings->getData()->getLogon()->getLogin()->getUser());
+ $this->assertSame(['03', '05', '10', '11', '14', '30'], $settings->getData()->getDocKinds());
+
+ [$logon, $request] = $this->requests;
+
+ $this->assertStringEndsWith('/Logon', $logon->getUri()->getPath());
+ $this->assertSame('POST', $request->getMethod());
+ $this->assertStringEndsWith('/GetSettings', $request->getUri()->getPath());
+ $this->assertSame('0', $request->getHeaderLine('CustomerID'));
+ $this->assertSame('SID-1', $request->getHeaderLine('SID'));
+ $this->assertSame('761700021132', $request->getHeaderLine('Inn'));
+ $this->assertSame('044525888', $request->getHeaderLine('Bic'));
+ $this->assertSame('40802810200000099888', $request->getHeaderLine('Account'));
+ $this->assertSame('2.2.2', $request->getHeaderLine('APIVersion'));
+ $this->assertSame('2.2.2', $request->getHeaderLine('AvailableAPIVersion'));
+ $this->assertFalse($request->hasHeader('Authorization'));
+ $this->assertSame('', (string) $request->getBody());
+ }
+
+ public function testGetSettingsWithoutAccount()
+ {
+ $this->mock->append(new Response(200, [], file_get_contents(__DIR__ . '/Fixture/xml/1c/GetSettingsResponse.xml')));
+
+ $this->createClient(['sessionId' => 'SID-0', 'availableApiVersion' => null])->getSettings('7705260699', '044525888');
+
+ $this->assertFalse($this->requests[0]->hasHeader('Account'));
+ $this->assertFalse($this->requests[0]->hasHeader('AvailableAPIVersion'));
+ }
+
+ public function testGetSettingsError()
+ {
+ $this->mock->append(self::error('1012', 'Не удалось получить настройки обмена с банком'));
+
+ try {
+ $this->createClient(['sessionId' => 'SID-0'])->getSettings('7705260699', '044525888');
+ $this->fail('ClientException expected');
+ } catch (ClientException $e) {
+ $this->assertSame(ErrorCode::SETTINGS_UNAVAILABLE, $e->getBankCode());
+ }
+ }
+
+ public function invalidSettingsResponseProvider(): array
+ {
+ return [
+ 'no GetSettingsResponse' => [self::success('PACK-1')],
+ 'not base64' => [self::success('!!!')],
+ 'not settings' => [self::success('' . base64_encode('not xml') . '')],
+ ];
+ }
+
+ /**
+ * @dataProvider invalidSettingsResponseProvider
+ */
+ public function testGetSettingsUnexpectedResponse(Response $response)
+ {
+ $this->mock->append($response);
+
+ $this->expectException(UnexpectedResponseException::class);
+
+ $this->createClient(['sessionId' => 'SID-0'])->getSettings('7705260699', '044525888');
+ }
+
public function testBankErrorWithHttpErrorStatus()
{
$this->mock->append(self::error('1009', 'Ошибка приемного сервиса', 500));
diff --git a/tests/Fixture/xml/1c/GetSettingsResponse.xml b/tests/Fixture/xml/1c/GetSettingsResponse.xml
new file mode 100644
index 0000000..213a1ea
--- /dev/null
+++ b/tests/Fixture/xml/1c/GetSettingsResponse.xml
@@ -0,0 +1,26 @@
+
+
+
+
+ PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPFNldHRpbmdzIHhtbG5zPSJo
+dHRwOi8vZGlyZWN0YmFuay4xYy5ydS9YTUxTY2hlbWEiCiAgICB4bWxuczp4c2k9Imh0dHA6Ly93
+d3cudzMub3JnLzIwMDEvWE1MU2NoZW1hLWluc3RhbmNlIgogICAgeG1sbnM6eHNkPSJodHRwOi8v
+d3d3LnczLm9yZy8yMDAxL1hNTFNjaGVtYSIKICAgIGlkPSJFRkQ4NTdCNS03RkE4LTQxOTUtODY2
+Ni0yQ0NBREJDM0M4REUiCiAgICBmb3JtYXRWZXJzaW9uPSIyLjIuMSIKICAgIGNyZWF0aW9uRGF0
+ZT0iMjAxNi0wNC0yMlQwOTozODo1MSIgCiAgICB1c2VyQWdlbnQ9IkRlbW9CYW5rU2VydmljZSI+
+Cgk8U2VuZGVyIGJpYz0iMDQ0NTI1ODg4IiBuYW1lPSLQlNCV0JzQni3QkdCQ0J3QmiIgLz4KICAg
+IDxSZWNpcGllbnQgaWQ9IjI4MDYiIG5hbWU9ItCi0L7RgNCz0L7QstGL0Lkg0LTQvtC8INCa0L7Q
+vNC/0LvQtdC60YHQvdGL0LkiIGlubj0iNzcwNTI2MDY5OSIga3BwPSI3NzA1MDEwMDEiIC8+CiAg
+ICA8RGF0YT4KICAgICAgICA8Q3VzdG9tZXJJRD4yODA2PC9DdXN0b21lcklEPgogICAgICAgIDxC
+YW5rU2VydmVyQWRkcmVzcz5odHRwczovL2Rib2dhdGUuZGVtb2JhbmsucnUvPC9CYW5rU2VydmVy
+QWRkcmVzcz4KICAgICAgICA8Rm9ybWF0VmVyc2lvbj4yLjIuMTwvRm9ybWF0VmVyc2lvbj4KICAg
+ICAgICA8RW5jb2Rpbmc+VVRGLTg8L0VuY29kaW5nPgogICAgICAgIDxMb2dvbj4KICAgICAgICAg
+ICAgPExvZ2luPgogICAgICAgICAgICAgICAgPFVzZXI+dXNlcl9sb2dpbjwvVXNlcj4KICAgICAg
+ICAgICAgPC9Mb2dpbj4KICAgICAgICA8L0xvZ29uPgogICAgICAgIDxEb2N1bWVudCBkb2NLaW5k
+PSIwMyIgLz4KICAgICAgICA8RG9jdW1lbnQgZG9jS2luZD0iMDUiIC8+CiAgICAgICAgPERvY3Vt
+ZW50IGRvY0tpbmQ9IjEwIiAvPgogICAgICAgIDxEb2N1bWVudCBkb2NLaW5kPSIxMSIgLz4JCQog
+ICAgICAgIDxEb2N1bWVudCBkb2NLaW5kPSIxNCIgLz4KCQk8RG9jdW1lbnQgZG9jS2luZD0iMzAi
+IC8+CiAgICA8L0RhdGE+CjwvU2V0dGluZ3M+
+
+
+
diff --git a/tests/Fixture/xml/1c/readme.md b/tests/Fixture/xml/1c/readme.md
index cbd777b..1bea9d2 100644
--- a/tests/Fixture/xml/1c/readme.md
+++ b/tests/Fixture/xml/1c/readme.md
@@ -6,5 +6,6 @@
- Электронные документы — :
`CancelationRequest.xml`, `PayDocRu.xml`, `PayRequest.xml`, `Probe.xml`, `Settings.xml`, `Settings_Logon_Certificate.xml`,
`Statement.xml`, `StatementRequest.xml`, `StatusDocNotice.xml`, `StatusRequest.xml`
+- `GetSettingsResponse.xml` — пример ответа на GetSettings из
- `Packet.xml` — пример запроса SendPack из .
Подпись в примере обрезана и не является корректным base64, поэтому XSD файл не проходит.