diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php
index ee2ef05e..26d3a1eb 100644
--- a/.php-cs-fixer.php
+++ b/.php-cs-fixer.php
@@ -17,6 +17,7 @@
->in(__DIR__ . '/src/Services/CRM/Timeline/')
->in(__DIR__ . '/src/Services/Entity/Section/')
->in(__DIR__ . '/src/Services/Department/')
+ ->in(__DIR__ . '/src/Services/Paysystem/')
->in(__DIR__ . '/src/Services/Sale/')
->in(__DIR__ . '/src/Services/Task/')
->in(__DIR__ . '/src/Services/Sale/')
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7af2a5bb..79e23652 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,27 @@
### Added
+- Added service `Services\Paysystem\Handler\Service\Handler` with support methods,
+ see [pay_system.handler.* methods](https://github.com/bitrix24/b24phpsdk/issues/260):
+ - `add` adds a payment system handler
+ - `update` updates a payment system handler
+ - `list` returns a list of payment system handlers
+ - `delete` deletes a payment system handler
+- Added service `Services\Paysystem\Service\Paysystem` with support methods,
+ see [sale.paysystem.* methods](https://github.com/bitrix24/b24phpsdk/issues/260):
+ - `add` adds a payment system
+ - `update` updates a payment system
+ - `get` returns a payment system by its identifier
+ - `list` returns a list of payment systems
+ - `delete` deletes a payment system
+ - `payPayment` pays a payment
+ - `payInvoice` pays an invoice (legacy version)
+- Added service `Services\Paysystem\Settings\Service\Settings` with support methods,
+ see [sale.paysystem.settings.* methods](https://github.com/bitrix24/b24phpsdk/issues/260):
+ - `get` returns the settings of the payment system
+ - `update` updates the payment system settings
+ - `getForPayment` returns the payment system settings for a specific payment
+ - `getForInvoice` returns the payment system settings for a specific invoice (legacy version)
- Added service `Services\Sale\Shipment\Service\Shipment` with support methods,
see [sale.shipment.* methods](https://github.com/bitrix24/b24phpsdk/issues/250):
- `add` adds a shipment
diff --git a/Makefile b/Makefile
index 7e2eb9b6..6a5e3a2d 100644
--- a/Makefile
+++ b/Makefile
@@ -51,6 +51,7 @@ help:
@echo ""
@echo "test-unit - run unit tests"
@echo "test-integration-sale-basket-property - run BasketProperty integration tests"
+ @echo "test-integration-scope-paysystem - run Payment System integration tests"
@echo "test-integration-sale-payment-item-basket - run PaymentItemBasket integration tests"
@echo "test-integration-sale-payment-item-shipment - run PaymentItemShipment integration tests"
@echo "test-integration-sale-property-relation - run PropertyRelation integration tests"
@@ -163,6 +164,18 @@ test-integration-scope-im:
test-integration-scope-placement:
docker-compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_scope_placement
+.PHONY: test-integration-scope-paysystem
+test-integration-scope-paysystem:
+ docker-compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_scope_paysystem
+
+.PHONY: test-integration-paysystem-service
+test-integration-paysystem-service:
+ docker-compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_paysystem_service
+
+.PHONY: test-integration-paysystem-settings
+test-integration-paysystem-settings:
+ docker-compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_paysystem_settings
+
.PHONY: test-integration-scope-im-open-lines
test-integration-scope-im-open-lines:
docker-compose run --rm php-cli vendor/bin/phpunit --testsuite integration_tests_scope_im_open_lines
diff --git a/phpstan.neon.dist b/phpstan.neon.dist
index 3bead767..c4d6b7ba 100644
--- a/phpstan.neon.dist
+++ b/phpstan.neon.dist
@@ -10,6 +10,7 @@ parameters:
- tests/Integration/Services/Catalog
- tests/Integration/Services/IMOpenLines
- tests/Integration/Services/Main
+ - tests/Integration/Services/Paysystem
- tests/Integration/Services/Placement
- tests/Integration/Services/CRM/Address
- tests/Integration/Services/CRM/Deal/Service/DealRecurringTest.php
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 812551f3..c6db4cb1 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -28,6 +28,15 @@
./tests/Integration/Services/Placement/
+
+ ./tests/Integration/Services/Paysystem/
+
+
+ ./tests/Integration/Services/Paysystem/Service/
+
+
+ ./tests/Integration/Services/Paysystem/Settings/
+
./tests/Integration/Services/User/
diff --git a/rector.php b/rector.php
index 4b458e5f..892855ab 100644
--- a/rector.php
+++ b/rector.php
@@ -34,6 +34,8 @@
__DIR__ . '/tests/Integration/Services/CRM/Address',
__DIR__ . '/src/Services/Main',
__DIR__ . '/tests/Integration/Services/Main',
+ __DIR__ . '/src/Services/Paysystem',
+ __DIR__ . '/tests/Integration/Services/Paysystem',
__DIR__ . '/src/Services/Placement',
__DIR__ . '/tests/Integration/Services/Placement',
__DIR__ . '/src/Services/CRM/Deal',
diff --git a/src/Services/Paysystem/Handler/Result/HandlerItemResult.php b/src/Services/Paysystem/Handler/Result/HandlerItemResult.php
new file mode 100644
index 00000000..b6b770c0
--- /dev/null
+++ b/src/Services/Paysystem/Handler/Result/HandlerItemResult.php
@@ -0,0 +1,29 @@
+
+ *
+ * For the full copyright and license information, please view the MIT-LICENSE.txt
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace Bitrix24\SDK\Services\Paysystem\Handler\Result;
+
+use Bitrix24\SDK\Core\Result\AbstractItem;
+
+/**
+ * Class HandlerItemResult
+ *
+ * @property-read int $ID Payment system handler identifier
+ * @property-read string $NAME Name of the REST handler
+ * @property-read string $CODE Code of the REST handler
+ * @property-read int $SORT Sorting order
+ * @property-read array $SETTINGS Handler settings containing currency, client type, form/checkout/iframe data, and codes
+ */
+class HandlerItemResult extends AbstractItem
+{
+}
diff --git a/src/Services/Paysystem/Handler/Result/HandlersResult.php b/src/Services/Paysystem/Handler/Result/HandlersResult.php
new file mode 100644
index 00000000..77edb07f
--- /dev/null
+++ b/src/Services/Paysystem/Handler/Result/HandlersResult.php
@@ -0,0 +1,37 @@
+
+ *
+ * For the full copyright and license information, please view the MIT-LICENSE.txt
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace Bitrix24\SDK\Services\Paysystem\Handler\Result;
+
+use Bitrix24\SDK\Core\Exceptions\BaseException;
+use Bitrix24\SDK\Core\Result\AbstractResult;
+
+/**
+ * List of payment system handlers result for sale.paysystem.handler.list
+ */
+class HandlersResult extends AbstractResult
+{
+ /**
+ * @return HandlerItemResult[]
+ * @throws BaseException
+ */
+ public function getHandlers(): array
+ {
+ $items = [];
+ foreach ($this->getCoreResponse()->getResponseData()->getResult() as $item) {
+ $items[] = new HandlerItemResult($item);
+ }
+
+ return $items;
+ }
+}
diff --git a/src/Services/Paysystem/Handler/Service/Handler.php b/src/Services/Paysystem/Handler/Service/Handler.php
new file mode 100644
index 00000000..6e1025b4
--- /dev/null
+++ b/src/Services/Paysystem/Handler/Service/Handler.php
@@ -0,0 +1,136 @@
+
+ *
+ * For the full copyright and license information, please view the MIT-LICENSE.txt
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace Bitrix24\SDK\Services\Paysystem\Handler\Service;
+
+use Bitrix24\SDK\Attributes\ApiEndpointMetadata;
+use Bitrix24\SDK\Attributes\ApiServiceMetadata;
+use Bitrix24\SDK\Core\Contracts\CoreInterface;
+use Bitrix24\SDK\Core\Credentials\Scope;
+use Bitrix24\SDK\Core\Exceptions\BaseException;
+use Bitrix24\SDK\Core\Exceptions\TransportException;
+use Bitrix24\SDK\Core\Result\AddedItemResult;
+use Bitrix24\SDK\Core\Result\DeletedItemResult;
+use Bitrix24\SDK\Core\Result\UpdatedItemResult;
+use Bitrix24\SDK\Services\AbstractService;
+use Bitrix24\SDK\Services\Paysystem\Handler\Result\HandlersResult;
+use Psr\Log\LoggerInterface;
+
+#[ApiServiceMetadata(new Scope(['pay_system']))]
+class Handler extends AbstractService
+{
+ public function __construct(CoreInterface $core, LoggerInterface $logger)
+ {
+ parent::__construct($core, $logger);
+ }
+
+ /**
+ * Adds a REST handler for the payment system.
+ *
+ * @link https://apidocs.bitrix24.com/api-reference/pay-system/sale-pay-system-handler-add.html
+ *
+ * @param string $name Name of the REST handler
+ * @param string $code Code of the REST handler. Must be unique among all handlers
+ * @param array $settings Handler settings
+ * @param int $sort Sorting. Default is 100
+ *
+ * @throws BaseException
+ * @throws TransportException
+ */
+ #[ApiEndpointMetadata(
+ 'sale.paysystem.handler.add',
+ 'https://apidocs.bitrix24.com/api-reference/pay-system/sale-pay-system-handler-add.html',
+ 'Adds a REST handler for the payment system.'
+ )]
+ public function add(string $name, string $code, array $settings, int $sort = 100): AddedItemResult
+ {
+ return new AddedItemResult(
+ $this->core->call('sale.paysystem.handler.add', [
+ 'NAME' => $name,
+ 'CODE' => $code,
+ 'SETTINGS' => $settings,
+ 'SORT' => $sort,
+ ])
+ );
+ }
+
+ /**
+ * Updates a REST handler for the payment system.
+ *
+ * @link https://apidocs.bitrix24.com/api-reference/pay-system/sale-pay-system-handler-update.html
+ *
+ * @param int $id Identifier of the REST handler
+ * @param array $fields Set of values for updating
+ *
+ * @throws BaseException
+ * @throws TransportException
+ */
+ #[ApiEndpointMetadata(
+ 'sale.paysystem.handler.update',
+ 'https://apidocs.bitrix24.com/api-reference/pay-system/sale-pay-system-handler-update.html',
+ 'Updates a REST handler for the payment system.'
+ )]
+ public function update(int $id, array $fields): UpdatedItemResult
+ {
+ return new UpdatedItemResult(
+ $this->core->call('sale.paysystem.handler.update', [
+ 'ID' => $id,
+ 'FIELDS' => $fields,
+ ])
+ );
+ }
+
+ /**
+ * Returns a list of REST handlers for the payment system.
+ *
+ * @link https://apidocs.bitrix24.com/api-reference/pay-system/sale-pay-system-handler-list.html
+ *
+ * @throws BaseException
+ * @throws TransportException
+ */
+ #[ApiEndpointMetadata(
+ 'sale.paysystem.handler.list',
+ 'https://apidocs.bitrix24.com/api-reference/pay-system/sale-pay-system-handler-list.html',
+ 'Returns a list of REST handlers for the payment system.'
+ )]
+ public function list(): HandlersResult
+ {
+ return new HandlersResult(
+ $this->core->call('sale.paysystem.handler.list')
+ );
+ }
+
+ /**
+ * Deletes a REST handler for the payment system.
+ *
+ * @link https://apidocs.bitrix24.com/api-reference/pay-system/sale-pay-system-handler-delete.html
+ *
+ * @param int $id Identifier of the REST handler
+ *
+ * @throws BaseException
+ * @throws TransportException
+ */
+ #[ApiEndpointMetadata(
+ 'sale.paysystem.handler.delete',
+ 'https://apidocs.bitrix24.com/api-reference/pay-system/sale-pay-system-handler-delete.html',
+ 'Deletes a REST handler for the payment system.'
+ )]
+ public function delete(int $id): DeletedItemResult
+ {
+ return new DeletedItemResult(
+ $this->core->call('sale.paysystem.handler.delete', [
+ 'ID' => $id,
+ ])
+ );
+ }
+}
diff --git a/src/Services/Paysystem/PaysystemServiceBuilder.php b/src/Services/Paysystem/PaysystemServiceBuilder.php
new file mode 100644
index 00000000..442f1ec1
--- /dev/null
+++ b/src/Services/Paysystem/PaysystemServiceBuilder.php
@@ -0,0 +1,69 @@
+
+ *
+ * For the full copyright and license information, please view the MIT-LICENSE.txt
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace Bitrix24\SDK\Services\Paysystem;
+
+use Bitrix24\SDK\Attributes\ApiServiceBuilderMetadata;
+use Bitrix24\SDK\Core\Credentials\Scope;
+use Bitrix24\SDK\Services\AbstractServiceBuilder;
+use Bitrix24\SDK\Services\Paysystem\Handler\Service\Handler;
+
+/**
+ * Class PaysystemServiceBuilder
+ *
+ * @package Bitrix24\SDK\Services\Paysystem
+ */
+#[ApiServiceBuilderMetadata(new Scope(['pay_system']))]
+class PaysystemServiceBuilder extends AbstractServiceBuilder
+{
+ /**
+ * Payment system handlers service (sale.paysystem.handler.*)
+ */
+ public function handler(): Handler
+ {
+ if (!isset($this->serviceCache[__METHOD__])) {
+ $this->serviceCache[__METHOD__] = new Handler($this->core, $this->log);
+ }
+
+ return $this->serviceCache[__METHOD__];
+ }
+
+ /**
+ * Payment systems service (sale.paysystem.*)
+ */
+ public function paysystem(): Service\Paysystem
+ {
+ if (!isset($this->serviceCache[__METHOD__])) {
+ $this->serviceCache[__METHOD__] = new Service\Paysystem(
+ new Service\Batch($this->batch, $this->log),
+ $this->core,
+ $this->log
+ );
+ }
+
+ return $this->serviceCache[__METHOD__];
+ }
+
+ /**
+ * Payment system settings service (sale.paysystem.settings.*)
+ */
+ public function settings(): Settings\Service\Settings
+ {
+ if (!isset($this->serviceCache[__METHOD__])) {
+ $this->serviceCache[__METHOD__] = new Settings\Service\Settings($this->core, $this->log);
+ }
+
+ return $this->serviceCache[__METHOD__];
+ }
+
+}
diff --git a/src/Services/Paysystem/Result/PaymentResult.php b/src/Services/Paysystem/Result/PaymentResult.php
new file mode 100644
index 00000000..2d55fb90
--- /dev/null
+++ b/src/Services/Paysystem/Result/PaymentResult.php
@@ -0,0 +1,35 @@
+
+ *
+ * For the full copyright and license information, please view the MIT-LICENSE.txt
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace Bitrix24\SDK\Services\Paysystem\Result;
+
+use Bitrix24\SDK\Core\Exceptions\BaseException;
+use Bitrix24\SDK\Core\Result\AbstractResult;
+
+/**
+ * Class PaymentResult
+ *
+ * @package Bitrix24\SDK\Services\Paysystem\Result
+ */
+class PaymentResult extends AbstractResult
+{
+ /**
+ * Returns the payment operation result
+ *
+ * @throws BaseException
+ */
+ public function isSuccess(): bool
+ {
+ return (bool)$this->getCoreResponse()->getResponseData()->getResult()[0];
+ }
+}
diff --git a/src/Services/Paysystem/Result/PaysystemItemResult.php b/src/Services/Paysystem/Result/PaysystemItemResult.php
new file mode 100644
index 00000000..bbef6f4a
--- /dev/null
+++ b/src/Services/Paysystem/Result/PaysystemItemResult.php
@@ -0,0 +1,43 @@
+
+ *
+ * For the full copyright and license information, please view the MIT-LICENSE.txt
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace Bitrix24\SDK\Services\Paysystem\Result;
+
+use Bitrix24\SDK\Core\Result\AbstractItem;
+
+/**
+ * Class PaysystemItemResult
+ *
+ * @property-read int|null $ID Payment system identifier
+ * @property-read string|null $NAME Payment system name
+ * @property-read string|null $CODE Payment system code
+ * @property-read string|null $DESCRIPTION Payment system description
+ * @property-read string|null $ACTIVE Payment system activity status (Y/N)
+ * @property-read int|null $SORT Sorting order
+ * @property-read string|null $PS_MODE Payment system mode
+ * @property-read string|null $ACTION_FILE Action file path
+ * @property-read string|null $RESULT_FILE Result file path
+ * @property-read string|null $NEW_WINDOW Open in new window flag (Y/N)
+ * @property-read string|null $HAVE_PAYMENT Has payment flag (Y/N)
+ * @property-read string|null $HAVE_ACTION Has action flag (Y/N)
+ * @property-read string|null $HAVE_RESULT Has result flag (Y/N)
+ * @property-read string|null $HAVE_PREPAY Has prepayment flag (Y/N)
+ * @property-read string|null $HAVE_PRICE Has price flag (Y/N)
+ * @property-read string|null $CURRENCY Currency code
+ * @property-read array|null $LOGOTIP Payment system logo
+ * @property-read string|null $XML_ID External identifier
+ * @property-read array|null $SETTINGS Payment system settings
+ */
+class PaysystemItemResult extends AbstractItem
+{
+}
diff --git a/src/Services/Paysystem/Result/PaysystemsResult.php b/src/Services/Paysystem/Result/PaysystemsResult.php
new file mode 100644
index 00000000..4ec7a612
--- /dev/null
+++ b/src/Services/Paysystem/Result/PaysystemsResult.php
@@ -0,0 +1,39 @@
+
+ *
+ * For the full copyright and license information, please view the MIT-LICENSE.txt
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace Bitrix24\SDK\Services\Paysystem\Result;
+
+use Bitrix24\SDK\Core\Exceptions\BaseException;
+use Bitrix24\SDK\Core\Result\AbstractResult;
+
+/**
+ * Class PaysystemsResult
+ *
+ * @package Bitrix24\SDK\Services\Paysystem\Result
+ */
+class PaysystemsResult extends AbstractResult
+{
+ /**
+ * @return PaysystemItemResult[]
+ * @throws BaseException
+ */
+ public function getPaysystems(): array
+ {
+ $paysystems = [];
+ foreach ($this->getCoreResponse()->getResponseData()->getResult() as $item) {
+ $paysystems[] = new PaysystemItemResult($item);
+ }
+
+ return $paysystems;
+ }
+}
diff --git a/src/Services/Paysystem/Service/Batch.php b/src/Services/Paysystem/Service/Batch.php
new file mode 100644
index 00000000..3c709237
--- /dev/null
+++ b/src/Services/Paysystem/Service/Batch.php
@@ -0,0 +1,95 @@
+
+ *
+ * For the full copyright and license information, please view the MIT-LICENSE.txt
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace Bitrix24\SDK\Services\Paysystem\Service;
+
+use Bitrix24\SDK\Attributes\ApiBatchMethodMetadata;
+use Bitrix24\SDK\Attributes\ApiBatchServiceMetadata;
+use Bitrix24\SDK\Core\Credentials\Scope;
+use Bitrix24\SDK\Core\Exceptions\BaseException;
+use Bitrix24\SDK\Core\Result\AddedItemBatchResult;
+use Bitrix24\SDK\Core\Result\DeletedItemBatchResult;
+use Bitrix24\SDK\Core\Result\UpdatedItemBatchResult;
+use Bitrix24\SDK\Services\AbstractBatchService;
+use Bitrix24\SDK\Services\Paysystem\Result\PaysystemItemResult;
+use Generator;
+
+#[ApiBatchServiceMetadata(new Scope(['pay_system']))]
+class Batch extends AbstractBatchService
+{
+ /**
+ * Batch add method for payment systems
+ *
+ * @param array $paysystems Array of payment system data
+ *
+ * @return Generator
+ *
+ * @throws BaseException
+ */
+ #[ApiBatchMethodMetadata(
+ 'sale.paysystem.add',
+ 'https://apidocs.bitrix24.com/api-reference/pay-system/sale-pay-system-add.html',
+ 'Adds payment systems.'
+ )]
+ public function add(array $paysystems): Generator
+ {
+ foreach ($this->batch->addEntityItems('sale.paysystem.add', $paysystems) as $key => $item) {
+ yield $key => new AddedItemBatchResult($item);
+ }
+ }
+
+ /**
+ * Batch update method for payment systems
+ *
+ * Update elements in array with structure
+ * element_id => [ // PS item id
+ * 'fields' => [] // PS item fields to update
+ * ]
+ *
+ * @return Generator
+ *
+ * @throws BaseException
+ */
+ #[ApiBatchMethodMetadata(
+ 'sale.paysystem.update',
+ 'https://apidocs.bitrix24.com/api-reference/pay-system/sale-pay-system-update.html',
+ 'Updates payment systems.'
+ )]
+ public function update(array $paysystems): Generator
+ {
+ foreach ($this->batch->updateEntityItems('sale.paysystem.update', $paysystems) as $key => $item) {
+ yield $key => new UpdatedItemBatchResult($item);
+ }
+ }
+
+ /**
+ * Batch delete method for payment systems
+ *
+ * @param array $paysystemIds Array of payment system identifiers
+ *
+ * @return Generator
+ *
+ * @throws BaseException
+ */
+ #[ApiBatchMethodMetadata(
+ 'sale.paysystem.delete',
+ 'https://apidocs.bitrix24.com/api-reference/pay-system/sale-pay-system-delete.html',
+ 'Deletes payment systems.'
+ )]
+ public function delete(array $paysystemIds): Generator
+ {
+ foreach ($this->batch->deleteEntityItems('sale.paysystem.delete', $paysystemIds) as $key => $item) {
+ yield $key => new DeletedItemBatchResult($item);
+ }
+ }
+}
diff --git a/src/Services/Paysystem/Service/Paysystem.php b/src/Services/Paysystem/Service/Paysystem.php
new file mode 100644
index 00000000..90621d55
--- /dev/null
+++ b/src/Services/Paysystem/Service/Paysystem.php
@@ -0,0 +1,198 @@
+
+ *
+ * For the full copyright and license information, please view the MIT-LICENSE.txt
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace Bitrix24\SDK\Services\Paysystem\Service;
+
+use Bitrix24\SDK\Attributes\ApiEndpointMetadata;
+use Bitrix24\SDK\Attributes\ApiServiceMetadata;
+use Bitrix24\SDK\Core\Contracts\CoreInterface;
+use Bitrix24\SDK\Core\Credentials\Scope;
+use Bitrix24\SDK\Core\Exceptions\BaseException;
+use Bitrix24\SDK\Core\Exceptions\TransportException;
+use Bitrix24\SDK\Core\Result\AddedItemResult;
+use Bitrix24\SDK\Core\Result\DeletedItemResult;
+use Bitrix24\SDK\Core\Result\UpdatedItemResult;
+use Bitrix24\SDK\Services\AbstractService;
+use Bitrix24\SDK\Services\Paysystem\Result\PaymentResult;
+use Bitrix24\SDK\Services\Paysystem\Result\PaysystemsResult;
+use Psr\Log\LoggerInterface;
+
+#[ApiServiceMetadata(new Scope(['pay_system']))]
+class Paysystem extends AbstractService
+{
+ /**
+ * Paysystem constructor.
+ */
+ public function __construct(public Batch $batch, CoreInterface $core, LoggerInterface $logger)
+ {
+ parent::__construct($core, $logger);
+ }
+
+ /**
+ * Adds a payment system.
+ *
+ * @link https://apidocs.bitrix24.com/api-reference/pay-system/sale-pay-system-add.html
+ *
+ * @param array $fields Field values for creating a payment system
+ *
+ * @throws BaseException
+ * @throws TransportException
+ */
+ #[ApiEndpointMetadata(
+ 'sale.paysystem.add',
+ 'https://apidocs.bitrix24.com/api-reference/pay-system/sale-pay-system-add.html',
+ 'Adds a payment system.'
+ )]
+ public function add(array $fields): AddedItemResult
+ {
+ return new AddedItemResult(
+ $this->core->call('sale.paysystem.add', $fields)
+ );
+ }
+
+ /**
+ * Modifies a payment system.
+ *
+ * @link https://apidocs.bitrix24.com/api-reference/pay-system/sale-pay-system-update.html
+ *
+ * @param int $id Payment system identifier
+ * @param array $fields Field values for update
+ *
+ * @throws BaseException
+ * @throws TransportException
+ */
+ #[ApiEndpointMetadata(
+ 'sale.paysystem.update',
+ 'https://apidocs.bitrix24.com/api-reference/pay-system/sale-pay-system-update.html',
+ 'Modifies a payment system.'
+ )]
+ public function update(int $id, array $fields): UpdatedItemResult
+ {
+ return new UpdatedItemResult(
+ $this->core->call('sale.paysystem.update', [
+ 'ID' => $id,
+ 'FIELDS' => $fields,
+ ])
+ );
+ } /**
+ * Returns a list of payment systems.
+ *
+ * @link https://apidocs.bitrix24.com/api-reference/pay-system/sale-pay-system-list.html
+ *
+ * @param array $select Fields to select
+ * @param array $filter Filter criteria
+ * @param array $order Sort order
+ *
+ * @throws BaseException
+ * @throws TransportException
+ */
+ #[ApiEndpointMetadata(
+ 'sale.paysystem.list',
+ 'https://apidocs.bitrix24.com/api-reference/pay-system/sale-pay-system-list.html',
+ 'Returns a list of payment systems.'
+ )]
+ public function list(array $select = [], array $filter = [], array $order = []): PaysystemsResult
+ {
+ return new PaysystemsResult(
+ $this->core->call('sale.paysystem.list', [
+ 'SELECT' => $select,
+ 'FILTER' => $filter,
+ 'ORDER' => $order,
+ ])
+ );
+ }
+
+ /**
+ * Deletes a payment system.
+ *
+ * @link https://apidocs.bitrix24.com/api-reference/pay-system/sale-pay-system-delete.html
+ *
+ * @param int $id Payment system identifier
+ *
+ * @throws BaseException
+ * @throws TransportException
+ */
+ #[ApiEndpointMetadata(
+ 'sale.paysystem.delete',
+ 'https://apidocs.bitrix24.com/api-reference/pay-system/sale-pay-system-delete.html',
+ 'Deletes a payment system.'
+ )]
+ public function delete(int $id): DeletedItemResult
+ {
+ return new DeletedItemResult(
+ $this->core->call('sale.paysystem.delete', [
+ 'ID' => $id,
+ ])
+ );
+ }
+
+ /**
+ * Pay for an order through a specific payment system.
+ *
+ * @link https://apidocs.bitrix24.com/api-reference/pay-system/sale-pay-system-pay-payment.html
+ *
+ * @param int $paymentId Payment identifier
+ * @param int $paySystemId Payment system identifier
+ *
+ * @throws BaseException
+ * @throws TransportException
+ */
+ #[ApiEndpointMetadata(
+ 'sale.paysystem.pay.payment',
+ 'https://apidocs.bitrix24.com/api-reference/pay-system/sale-pay-system-pay-payment.html',
+ 'Pay for an order through a specific payment system.'
+ )]
+ public function payPayment(int $paymentId, int $paySystemId): PaymentResult
+ {
+ return new PaymentResult(
+ $this->core->call('sale.paysystem.pay.payment', [
+ 'PAYMENT_ID' => $paymentId,
+ 'PAY_SYSTEM_ID' => $paySystemId,
+ ])
+ );
+ }
+
+ /**
+ * Pay an invoice through a specific payment system.
+ *
+ * @link https://apidocs.bitrix24.com/api-reference/pay-system/sale-pay-system-pay-invoice.html
+ *
+ * @param int $invoiceId Identifier of the old version invoice
+ * @param int|null $paySystemId Identifier of the payment system
+ * @param string|null $bxRestHandler Symbolic identifier of the payment system's REST handler
+ *
+ * @throws BaseException
+ * @throws TransportException
+ */
+ #[ApiEndpointMetadata(
+ 'sale.paysystem.pay.invoice',
+ 'https://apidocs.bitrix24.com/api-reference/pay-system/sale-pay-system-pay-invoice.html',
+ 'Pay an invoice through a specific payment system.'
+ )]
+ public function payInvoice(int $invoiceId, ?int $paySystemId = null, ?string $bxRestHandler = null): PaymentResult
+ {
+ $params = ['INVOICE_ID' => $invoiceId];
+
+ if ($paySystemId !== null) {
+ $params['PAY_SYSTEM_ID'] = $paySystemId;
+ }
+
+ if ($bxRestHandler !== null) {
+ $params['BX_REST_HANDLER'] = $bxRestHandler;
+ }
+
+ return new PaymentResult(
+ $this->core->call('sale.paysystem.pay.invoice', $params)
+ );
+ }
+}
diff --git a/src/Services/Paysystem/Settings/Result/SettingsItemResult.php b/src/Services/Paysystem/Settings/Result/SettingsItemResult.php
new file mode 100644
index 00000000..49231dd7
--- /dev/null
+++ b/src/Services/Paysystem/Settings/Result/SettingsItemResult.php
@@ -0,0 +1,40 @@
+
+ *
+ * For the full copyright and license information, please view the MIT-LICENSE.txt
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace Bitrix24\SDK\Services\Paysystem\Settings\Result;
+
+use Bitrix24\SDK\Core\Result\AbstractItem;
+
+/**
+ * Class SettingsItemResult
+ *
+ * Represents payment system settings data. The structure of the settings is defined
+ * when adding the payment system handler in the method sale.paysystem.handler.add
+ * under the CODES key of the SETTINGS parameter.
+ *
+ * The keys of the result object are the parameter codes specified when adding the handler,
+ * and the values are the parameter values: either filled in manually by the user when
+ * creating the payment system or specified when adding the payment system via
+ * sale.paysystem.add or specified when executing the method sale.paysystem.settings.update.
+ *
+ * Common setting properties (examples from documentation):
+ * @property-read string|null $REST_SERVICE_ID_IFRAME Service ID for iframe integration
+ * @property-read string|null $REST_SERVICE_KEY_IFRAME Service key for iframe integration
+ * @property-read string|null $PS_WORK_MODE_IFRAME Payment system work mode (e.g., "REGULAR")
+ *
+ * Note: The actual properties depend on the specific payment system handler configuration
+ * and may vary for different payment systems.
+ */
+class SettingsItemResult extends AbstractItem
+{
+}
diff --git a/src/Services/Paysystem/Settings/Service/Settings.php b/src/Services/Paysystem/Settings/Service/Settings.php
new file mode 100644
index 00000000..a1ed7666
--- /dev/null
+++ b/src/Services/Paysystem/Settings/Service/Settings.php
@@ -0,0 +1,166 @@
+
+ *
+ * For the full copyright and license information, please view the MIT-LICENSE.txt
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace Bitrix24\SDK\Services\Paysystem\Settings\Service;
+
+use Bitrix24\SDK\Attributes\ApiEndpointMetadata;
+use Bitrix24\SDK\Attributes\ApiServiceMetadata;
+use Bitrix24\SDK\Core\Contracts\CoreInterface;
+use Bitrix24\SDK\Core\Credentials\Scope;
+use Bitrix24\SDK\Core\Exceptions\BaseException;
+use Bitrix24\SDK\Core\Exceptions\TransportException;
+use Bitrix24\SDK\Core\Result\UpdatedItemResult;
+use Bitrix24\SDK\Services\AbstractService;
+use Bitrix24\SDK\Services\Paysystem\Settings\Result\SettingsItemResult;
+use Psr\Log\LoggerInterface;
+
+#[ApiServiceMetadata(new Scope(['pay_system']))]
+class Settings extends AbstractService
+{
+ /**
+ * Returns the settings of the payment system.
+ *
+ * @link https://apidocs.bitrix24.com/api-reference/pay-system/sale-pay-system-settings-get.html
+ *
+ * @param int $paySystemId Payment system identifier
+ * @param int $personTypeId Payer type identifier (pass 0 to get default settings)
+ *
+ *
+ * @throws BaseException
+ * @throws TransportException
+ */
+ #[ApiEndpointMetadata(
+ 'sale.paysystem.settings.get',
+ 'https://apidocs.bitrix24.com/api-reference/pay-system/sale-pay-system-settings-get.html',
+ 'Returns the settings of the payment system'
+ )]
+ public function get(int $paySystemId, int $personTypeId): SettingsItemResult
+ {
+ return new SettingsItemResult(
+ $this->core->call(
+ 'sale.paysystem.settings.get',
+ [
+ 'ID' => $paySystemId,
+ 'PERSON_TYPE_ID' => $personTypeId,
+ ]
+ )->getResponseData()->getResult()
+ );
+ }
+
+ /**
+ * Updates the payment system settings.
+ *
+ * @link https://apidocs.bitrix24.com/api-reference/pay-system/sale-pay-system-settings-update.html
+ *
+ * @param int $paySystemId Payment system identifier
+ * @param array $settings Settings to be updated. Each setting should have structure:
+ * ['PARAMETER_NAME' => ['TYPE' => 'VALUE', 'VALUE' => 'parameter_value']]
+ * @param int|null $personTypeId Payer type identifier (optional)
+ *
+ *
+ * @throws BaseException
+ * @throws TransportException
+ */
+ #[ApiEndpointMetadata(
+ 'sale.paysystem.settings.update',
+ 'https://apidocs.bitrix24.com/api-reference/pay-system/sale-pay-system-settings-update.html',
+ 'Updates the payment system settings'
+ )]
+ public function update(int $paySystemId, array $settings, ?int $personTypeId = null): UpdatedItemResult
+ {
+ $params = [
+ 'ID' => $paySystemId,
+ 'SETTINGS' => $settings,
+ ];
+
+ if ($personTypeId !== null) {
+ $params['PERSON_TYPE_ID'] = $personTypeId;
+ }
+
+ return new UpdatedItemResult(
+ $this->core->call('sale.paysystem.settings.update', $params)
+ );
+ }
+
+ /**
+ * Returns the payment system settings for a specific payment.
+ *
+ * @link https://apidocs.bitrix24.com/api-reference/pay-system/sale-pay-system-settings-payment-get.html
+ *
+ * @param int $paymentId Payment identifier
+ * @param int $paySystemId Payment system identifier
+ *
+ *
+ * @throws BaseException
+ * @throws TransportException
+ */
+ #[ApiEndpointMetadata(
+ 'sale.paysystem.settings.payment.get',
+ 'https://apidocs.bitrix24.com/api-reference/pay-system/sale-pay-system-settings-payment-get.html',
+ 'Returns the payment system settings for a specific payment'
+ )]
+ public function getForPayment(int $paymentId, int $paySystemId): SettingsItemResult
+ {
+ return new SettingsItemResult(
+ $this->core->call(
+ 'sale.paysystem.settings.payment.get',
+ [
+ 'PAYMENT_ID' => $paymentId,
+ 'PAY_SYSTEM_ID' => $paySystemId,
+ ]
+ )->getResponseData()->getResult()
+ );
+ }
+
+ /**
+ * Returns the payment system settings for a specific invoice (legacy version).
+ *
+ * @link https://apidocs.bitrix24.com/api-reference/pay-system/sale-pay-system-settings-invoice-get.html
+ *
+ * @param int $invoiceId Legacy invoice identifier
+ * @param int|null $paySystemId Payment system identifier (optional if bxRestHandler is provided)
+ * @param string|null $bxRestHandler Symbolic identifier of the payment system REST handler (optional if paySystemId is provided)
+ *
+ *
+ * @throws BaseException
+ * @throws TransportException
+ */
+ #[ApiEndpointMetadata(
+ 'sale.paysystem.settings.invoice.get',
+ 'https://apidocs.bitrix24.com/api-reference/pay-system/sale-pay-system-settings-invoice-get.html',
+ 'Returns the payment system settings for a specific invoice (legacy version)'
+ )]
+ public function getForInvoice(int $invoiceId, ?int $paySystemId = null, ?string $bxRestHandler = null): SettingsItemResult
+ {
+ if ($paySystemId === null && $bxRestHandler === null) {
+ throw new \InvalidArgumentException('Either paySystemId or bxRestHandler parameter must be provided');
+ }
+
+ $params = ['INVOICE_ID' => $invoiceId];
+
+ if ($paySystemId !== null) {
+ $params['PAY_SYSTEM_ID'] = $paySystemId;
+ }
+
+ if ($bxRestHandler !== null) {
+ $params['BX_REST_HANDLER'] = $bxRestHandler;
+ }
+
+ return new SettingsItemResult(
+ $this->core->call(
+ 'sale.paysystem.settings.invoice.get',
+ $params
+ )->getResponseData()->getResult()
+ );
+ }
+}
diff --git a/src/Services/ServiceBuilder.php b/src/Services/ServiceBuilder.php
index c03dcd9d..064ca371 100644
--- a/src/Services/ServiceBuilder.php
+++ b/src/Services/ServiceBuilder.php
@@ -32,6 +32,7 @@
use Bitrix24\SDK\Services\Placement\PlacementServiceBuilder;
use Bitrix24\SDK\Services\Workflows\WorkflowsServiceBuilder;
use Bitrix24\SDK\Services\Sale\SaleServiceBuilder;
+use Bitrix24\SDK\Services\Paysystem\PaysystemServiceBuilder;
use Psr\Log\LoggerInterface;
class ServiceBuilder extends AbstractServiceBuilder
@@ -205,6 +206,20 @@ public function getPlacementScope(): PlacementServiceBuilder
return $this->serviceCache[__METHOD__];
}
+ public function getPaysystemScope(): PaysystemServiceBuilder
+ {
+ if (!isset($this->serviceCache[__METHOD__])) {
+ $this->serviceCache[__METHOD__] = new PaysystemServiceBuilder(
+ $this->core,
+ $this->batch,
+ $this->bulkItemsReader,
+ $this->log
+ );
+ }
+
+ return $this->serviceCache[__METHOD__];
+ }
+
public function getCatalogScope(): CatalogServiceBuilder
{
if (!isset($this->serviceCache[__METHOD__])) {
diff --git a/tests/Integration/Services/Paysystem/Handler/Service/HandlerTest.php b/tests/Integration/Services/Paysystem/Handler/Service/HandlerTest.php
new file mode 100644
index 00000000..cafcb781
--- /dev/null
+++ b/tests/Integration/Services/Paysystem/Handler/Service/HandlerTest.php
@@ -0,0 +1,177 @@
+
+ *
+ * For the full copyright and license information, please view the MIT-LICENSE.txt
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace Bitrix24\SDK\Tests\Integration\Services\Paysystem\Handler\Service;
+
+use Bitrix24\SDK\Core\Exceptions\BaseException;
+use Bitrix24\SDK\Core\Exceptions\TransportException;
+use Bitrix24\SDK\Services\Paysystem\Handler\Service\Handler;
+use Bitrix24\SDK\Tests\Integration\Fabric;
+use PHPUnit\Framework\Attributes\CoversMethod;
+use PHPUnit\Framework\TestCase;
+
+/**
+ * Class HandlerTest
+ *
+ * @package Bitrix24\SDK\Tests\Integration\Services\Paysystem\Handler\Service
+ */
+#[CoversMethod(Handler::class, 'add')]
+#[CoversMethod(Handler::class, 'update')]
+#[CoversMethod(Handler::class, 'list')]
+#[CoversMethod(Handler::class, 'delete')]
+#[\PHPUnit\Framework\Attributes\CoversClass(\Bitrix24\SDK\Services\Paysystem\Handler\Service\Handler::class)]
+class HandlerTest extends TestCase
+{
+ protected Handler $handlerService;
+
+ protected function setUp(): void
+ {
+ $this->handlerService = Fabric::getServiceBuilder()->getPaysystemScope()->handler();
+ }
+
+ /**
+ * Get default handler settings for tests
+ */
+ private function getDefaultHandlerSettings(): array
+ {
+ return [
+ 'CURRENCY' => ['USD'],
+ 'CLIENT_TYPE' => 'b2c',
+ 'FORM_DATA' => [
+ 'ACTION_URI' => 'https://example.com/payment_form.php',
+ 'METHOD' => 'POST',
+ 'FIELDS' => [
+ 'paymentId' => [
+ 'CODE' => 'PAYMENT_ID',
+ 'VISIBLE' => 'Y'
+ ]
+ ]
+ ],
+ 'CODES' => [
+ 'PAYMENT_ID' => [
+ 'NAME' => 'Payment ID',
+ 'DESCRIPTION' => 'Payment identifier',
+ 'SORT' => '100',
+ 'GROUP' => 'PAYMENT',
+ 'DEFAULT' => [
+ 'PROVIDER_KEY' => 'PAYMENT',
+ 'PROVIDER_VALUE' => 'ACCOUNT_NUMBER'
+ ]
+ ]
+ ]
+ ];
+ }
+
+ /**
+ * @throws BaseException
+ * @throws TransportException
+ */
+ public function testAdd(): void
+ {
+ // Create a payment system handler
+ $handlerName = 'Test Handler ' . time();
+ $handlerCode = 'test_handler_' . time();
+ $handlerSettings = $this->getDefaultHandlerSettings();
+
+ $addedItemResult = $this->handlerService->add($handlerName, $handlerCode, $handlerSettings);
+ $handlerId = $addedItemResult->getId();
+
+ self::assertGreaterThan(0, $handlerId);
+
+ // Clean up
+ $this->handlerService->delete($handlerId);
+ }
+
+ /**
+ * @throws BaseException
+ * @throws TransportException
+ */
+ public function testUpdate(): void
+ {
+ // Create a handler first
+ $handlerName = 'Test Handler ' . time();
+ $handlerCode = 'test_handler_' . time();
+ $handlerSettings = $this->getDefaultHandlerSettings();
+
+ $addedItemResult = $this->handlerService->add($handlerName, $handlerCode, $handlerSettings);
+ $handlerId = $addedItemResult->getId();
+
+ // Update the handler
+ $updateFields = [
+ 'NAME' => 'Updated Test Handler ' . time(),
+ 'SORT' => 200
+ ];
+
+ $updatedItemResult = $this->handlerService->update($handlerId, $updateFields);
+ self::assertTrue($updatedItemResult->isSuccess());
+
+ // Clean up
+ $this->handlerService->delete($handlerId);
+ }
+
+ /**
+ * @throws BaseException
+ * @throws TransportException
+ */
+ public function testList(): void
+ {
+ // Create a handler first
+ $handlerName = 'Test Handler ' . time();
+ $handlerCode = 'test_handler_' . time();
+ $handlerSettings = $this->getDefaultHandlerSettings();
+
+ $addedItemResult = $this->handlerService->add($handlerName, $handlerCode, $handlerSettings);
+ $handlerId = $addedItemResult->getId();
+
+ // Test list functionality
+ $handlersResult = $this->handlerService->list();
+ $handlers = $handlersResult->getHandlers();
+
+ self::assertIsArray($handlers);
+
+ // Find our created handler
+ $foundHandler = null;
+ foreach ($handlers as $handler) {
+ if ($handler->ID === (string)$handlerId) {
+ $foundHandler = $handler;
+ break;
+ }
+ }
+
+ self::assertNotNull($foundHandler);
+ self::assertEquals($handlerName, $foundHandler->NAME);
+ self::assertEquals($handlerCode, $foundHandler->CODE);
+
+ // Clean up
+ $this->handlerService->delete($handlerId);
+ }
+
+ /**
+ * @throws BaseException
+ * @throws TransportException
+ */
+ public function testDelete(): void
+ {
+ // Create a handler first
+ $handlerName = 'Test Handler ' . time();
+ $handlerCode = 'test_handler_' . time();
+ $handlerSettings = $this->getDefaultHandlerSettings();
+
+ $addedItemResult = $this->handlerService->add($handlerName, $handlerCode, $handlerSettings);
+ $handlerId = $addedItemResult->getId();
+
+ // Delete the handler
+ $deletedItemResult = $this->handlerService->delete($handlerId);
+ self::assertTrue($deletedItemResult->isSuccess());
+ }
+}
\ No newline at end of file
diff --git a/tests/Integration/Services/Paysystem/Service/PaysystemBatchTest.php b/tests/Integration/Services/Paysystem/Service/PaysystemBatchTest.php
new file mode 100644
index 00000000..d8a07ca6
--- /dev/null
+++ b/tests/Integration/Services/Paysystem/Service/PaysystemBatchTest.php
@@ -0,0 +1,315 @@
+
+ *
+ * For the full copyright and license information, please view the MIT-LICENSE.txt
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace Bitrix24\SDK\Tests\Integration\Services\Paysystem\Service;
+
+use Bitrix24\SDK\Core\Exceptions\BaseException;
+use Bitrix24\SDK\Core\Exceptions\TransportException;
+use Bitrix24\SDK\Services\Paysystem\Service\Paysystem;
+use Bitrix24\SDK\Tests\Integration\Fabric;
+use PHPUnit\Framework\Attributes\CoversClass;
+use PHPUnit\Framework\TestCase;
+
+/**
+ * Class PaysystemBatchTest
+ *
+ * Integration tests for Payment System batch operations
+ *
+ * @package Bitrix24\SDK\Tests\Integration\Services\Paysystem\Service
+ */
+#[CoversClass(\Bitrix24\SDK\Services\Paysystem\Service\Batch::class)]
+class PaysystemBatchTest extends TestCase
+{
+ private const TEST_SEGMENT_ELEMENTS_COUNT = 50;
+
+ protected Paysystem $paysystemService;
+
+ /**
+ * Get or create a person type ID for tests
+ */
+ private function getPersonTypeId(): int
+ {
+ $personTypeService = Fabric::getServiceBuilder()->getSaleScope()->personType();
+ $personTypesResult = $personTypeService->list();
+
+ if ($personTypesResult->getPersonTypes() !== []) {
+ return $personTypesResult->getPersonTypes()[0]->id;
+ }
+
+ $addedPersonTypeResult = $personTypeService->add(['name' => 'Test Person Type ' . time()]);
+ return $addedPersonTypeResult->getId();
+ }
+
+ /**
+ * Create a test handler for payment system
+ */
+ private function createTestHandler(): string
+ {
+ $handlerService = Fabric::getServiceBuilder()->getPaysystemScope()->handler();
+ $handlerName = 'Test Handler ' . time();
+ $handlerCode = 'test_handler_' . time();
+ $handlerSettings = [
+ 'CURRENCY' => ['USD'],
+ 'CLIENT_TYPE' => 'b2c',
+ 'FORM_DATA' => [
+ 'ACTION_URI' => 'https://example.com/payment_form.php',
+ 'METHOD' => 'POST',
+ 'FIELDS' => [
+ 'paymentId' => [
+ 'CODE' => 'PAYMENT_ID',
+ 'VISIBLE' => 'Y'
+ ]
+ ]
+ ],
+ 'CODES' => [
+ 'PAYMENT_ID' => [
+ 'NAME' => 'Payment ID',
+ 'DESCRIPTION' => 'Payment identifier',
+ 'SORT' => '100',
+ 'GROUP' => 'PAYMENT',
+ 'DEFAULT' => [
+ 'PROVIDER_KEY' => 'PAYMENT',
+ 'PROVIDER_VALUE' => 'ACCOUNT_NUMBER'
+ ]
+ ]
+ ]
+ ];
+
+ $handlerService->add($handlerName, $handlerCode, $handlerSettings);
+ return $handlerCode;
+ }
+
+ /**
+ * Delete test handler
+ */
+ private function deleteTestHandlerByCode(string $handlerCode): void
+ {
+ try {
+ $handlerService = Fabric::getServiceBuilder()->getPaysystemScope()->handler();
+ $handlers = $handlerService->list();
+ foreach ($handlers->getHandlers() as $handlerItemResult) {
+ if ($handlerItemResult->CODE === $handlerCode) {
+ $handlerService->delete(intval($handlerItemResult->ID));
+ break;
+ }
+ }
+ } catch (BaseException $baseException) {
+ // Log the error but don't fail the test if handler deletion fails
+ // This is cleanup code, so failures should not break tests
+ error_log(sprintf('Warning: Failed to delete test handler %s: ', $handlerCode) . $baseException->getMessage());
+ }
+ }
+
+ /**
+ * Test batch add payment systems
+ *
+ * @throws BaseException
+ * @throws TransportException
+ */
+ public function testBatchAdd(): void
+ {
+ $handlerCode = $this->createTestHandler();
+ $personTypeId = $this->getPersonTypeId();
+ $paysystems = [];
+ $timestamp = time();
+
+ for ($i = 1; $i <= 10; $i++) {
+ $paysystems[] = [
+ 'NAME' => 'Batch Test Payment System ' . $i . ' ' . $timestamp,
+ 'DESCRIPTION' => 'Batch test payment system ' . $i,
+ 'PERSON_TYPE_ID' => $personTypeId,
+ 'BX_REST_HANDLER' => $handlerCode,
+ 'ACTIVE' => 'Y',
+ 'ENTITY_REGISTRY_TYPE' => 'ORDER',
+ 'NEW_WINDOW' => 'N',
+ 'XML_ID' => 'test_batch_ps_' . $i . '_' . $timestamp
+ ];
+ }
+
+ $cnt = 0;
+ $createdIds = [];
+ foreach ($this->paysystemService->batch->add($paysystems) as $item) {
+ $cnt++;
+ $paysystemId = $item->getId();
+ self::assertGreaterThanOrEqual(1, $paysystemId);
+ $createdIds[] = $paysystemId;
+ }
+
+ self::assertEquals(count($paysystems), $cnt);
+
+ // Clean up created payment systems
+ foreach ($createdIds as $createdId) {
+ $this->paysystemService->delete($createdId);
+ }
+
+ $this->deleteTestHandlerByCode($handlerCode);
+ }
+
+ /**
+ * Test batch update and delete payment systems
+ *
+ * @throws BaseException
+ * @throws TransportException
+ */
+ public function testBatchUpdate(): void
+ {
+ $handlerCode = $this->createTestHandler();
+ $personTypeId = $this->getPersonTypeId();
+
+ // Add payment systems first
+ $paysystems = [];
+ $timestamp = time();
+
+ for ($i = 1; $i <= self::TEST_SEGMENT_ELEMENTS_COUNT; $i++) {
+ $paysystems[] = [
+ 'NAME' => 'Batch Update Test PS ' . $i . ' ' . $timestamp,
+ 'DESCRIPTION' => 'Original description ' . $i,
+ 'PERSON_TYPE_ID' => $personTypeId,
+ 'BX_REST_HANDLER' => $handlerCode,
+ 'ACTIVE' => 'Y',
+ 'ENTITY_REGISTRY_TYPE' => 'ORDER',
+ 'NEW_WINDOW' => 'N',
+ 'XML_ID' => 'batch_update_test_' . $i . '_' . $timestamp
+ ];
+ }
+
+ $cnt = 0;
+ $paysystemIds = [];
+ foreach ($this->paysystemService->batch->add($paysystems) as $item) {
+ $cnt++;
+ $paysystemIds[] = $item->getId();
+ }
+
+ self::assertEquals(count($paysystems), $cnt);
+
+ // Generate update data
+ $updatePaysystemData = [];
+ foreach ($paysystemIds as $index => $id) {
+ $updatePaysystemData[$id] = [
+ 'fields' => [
+ 'NAME' => 'Updated Payment System ' . ($index + 1) . ' ' . $timestamp,
+ 'DESCRIPTION' => 'Updated description ' . ($index + 1),
+ 'SORT' => 200 + $index
+ ],
+ ];
+ }
+
+ // Update payment systems in batch mode
+ $cnt = 0;
+ foreach ($this->paysystemService->batch->update($updatePaysystemData) as $item) {
+ $cnt++;
+ $this->assertTrue($item->isSuccess());
+ }
+
+ self::assertEquals(count($paysystems), $cnt);
+
+ // Delete payment systems in batch mode
+ $cnt = 0;
+ foreach ($this->paysystemService->batch->delete($paysystemIds) as $item) {
+ $cnt++;
+ $this->assertTrue($item->isSuccess());
+ }
+
+ self::assertEquals(count($paysystems), $cnt);
+
+ // Clean up handler
+ $this->deleteTestHandlerByCode($handlerCode);
+ }
+
+ /**
+ * Test batch delete payment systems
+ *
+ * @throws BaseException
+ * @throws TransportException
+ */
+ public function testBatchDelete(): void
+ {
+ $handlerCode = $this->createTestHandler();
+ $personTypeId = $this->getPersonTypeId();
+
+ // Add some payment systems first
+ $paysystems = [];
+ $timestamp = time();
+
+ for ($i = 1; $i <= 5; $i++) {
+ $paysystems[] = [
+ 'NAME' => 'Batch Delete Test PS ' . $i . ' ' . $timestamp,
+ 'DESCRIPTION' => 'Test payment system for batch deletion ' . $i,
+ 'PERSON_TYPE_ID' => $personTypeId,
+ 'BX_REST_HANDLER' => $handlerCode,
+ 'ACTIVE' => 'Y',
+ 'ENTITY_REGISTRY_TYPE' => 'ORDER',
+ 'NEW_WINDOW' => 'N',
+ 'XML_ID' => 'batch_delete_test_' . $i . '_' . $timestamp
+ ];
+ }
+
+ $paysystemIds = [];
+ foreach ($this->paysystemService->batch->add($paysystems) as $item) {
+ $paysystemIds[] = $item->getId();
+ }
+
+ //echo "\nPaysystem Ids:\n";
+ //print_r($paysystemIds);
+
+ // Delete payment systems in batch
+ $cnt = 0;
+ foreach ($this->paysystemService->batch->delete($paysystemIds) as $item) {
+ $cnt++;
+ $this->assertTrue($item->isSuccess());
+ }
+
+ echo "\nCount Ids:\n";
+ print_r($cnt);
+
+ self::assertEquals(count($paysystems), $cnt);
+
+ // Clean up handler
+ $this->deleteTestHandlerByCode($handlerCode);
+ }
+
+ protected function setUp(): void
+ {
+ $this->paysystemService = Fabric::getServiceBuilder()->getPaysystemScope()->paysystem();
+ }
+
+ protected function tearDown(): void
+ {
+ // Additional cleanup: remove any remaining test handlers that might have been left
+ $this->cleanupTestHandlers();
+ }
+
+ /**
+ * Clean up any test handlers that might be left over
+ */
+ private function cleanupTestHandlers(): void
+ {
+ try {
+ $handlerService = Fabric::getServiceBuilder()->getPaysystemScope()->handler();
+ $handlers = $handlerService->list();
+ foreach ($handlers->getHandlers() as $handlerItemResult) {
+ if (str_contains($handlerItemResult->CODE, 'test_handler_')) {
+ try {
+ $handlerService->delete(intval($handlerItemResult->ID));
+ } catch (BaseException $e) {
+ // Ignore individual deletion errors
+ error_log(sprintf('Warning: Failed to cleanup test handler %s: ', $handlerItemResult->CODE) . $e->getMessage());
+ }
+ }
+ }
+ } catch (BaseException $baseException) {
+ // Ignore general cleanup errors
+ error_log("Warning: Failed to list handlers during cleanup: " . $baseException->getMessage());
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/Integration/Services/Paysystem/Service/PaysystemTest.php b/tests/Integration/Services/Paysystem/Service/PaysystemTest.php
new file mode 100644
index 00000000..5bdf555a
--- /dev/null
+++ b/tests/Integration/Services/Paysystem/Service/PaysystemTest.php
@@ -0,0 +1,477 @@
+
+ *
+ * For the full copyright and license information, please view the MIT-LICENSE.txt
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace Bitrix24\SDK\Tests\Integration\Services\Paysystem\Service;
+
+use Bitrix24\SDK\Core\Exceptions\BaseException;
+use Bitrix24\SDK\Core\Exceptions\TransportException;
+use Bitrix24\SDK\Services\Paysystem\Result\PaysystemItemResult;
+use Bitrix24\SDK\Services\Paysystem\Service\Paysystem;
+use Bitrix24\SDK\Tests\CustomAssertions\CustomBitrix24Assertions;
+use Bitrix24\SDK\Tests\Integration\Fabric;
+use PHPUnit\Framework\Attributes\CoversClass;
+use PHPUnit\Framework\Attributes\CoversMethod;
+use PHPUnit\Framework\TestCase;
+use Bitrix24\SDK\Core;
+
+/**
+ * Class PaysystemTest
+ *
+ * Integration tests for Payment System service
+ *
+ * @package Bitrix24\SDK\Tests\Integration\Services\Paysystem\Service
+ */
+#[CoversClass(Paysystem::class)]
+#[CoversMethod(Paysystem::class, 'add')]
+#[CoversMethod(Paysystem::class, 'delete')]
+#[CoversMethod(Paysystem::class, 'list')]
+#[CoversMethod(Paysystem::class, 'update')]
+#[CoversMethod(Paysystem::class, 'payPayment')]
+class PaysystemTest extends TestCase
+{
+ use CustomBitrix24Assertions;
+
+ private Paysystem $paysystemService;
+
+ /**
+ * Get or create a person type ID for tests
+ *
+ * @throws BaseException
+ * @throws TransportException
+ */
+ private function getPersonTypeId(): int
+ {
+ $personTypeService = Fabric::getServiceBuilder()->getSaleScope()->personType();
+ $personTypesResult = $personTypeService->list();
+
+ return $personTypesResult->getPersonTypes()[0]->id;
+ }
+
+ /**
+ * Helper method to create a test order
+ *
+ * @return int Order ID
+ * @throws BaseException
+ * @throws TransportException
+ */
+ private function createTestOrder(int $personTypeId): int
+ {
+ $orderService = Fabric::getServiceBuilder()->getSaleScope()->order();
+ $orderFields = [
+ 'lid' => 's1',
+ 'personTypeId' => $personTypeId,
+ 'currency' => 'USD',
+ 'price' => 100.00
+ ];
+
+ return $orderService->add($orderFields)->getId();
+ }
+
+ /**
+ * Helper method to create a test payment
+ *
+ * @return int Payment ID
+ * @throws BaseException
+ * @throws TransportException
+ */
+ private function createTestPayment(int $orderId, int $paySystemId): int
+ {
+ $paymentService = Fabric::getServiceBuilder()->getSaleScope()->payment();
+ $paymentFields = [
+ 'orderId' => $orderId,
+ 'paySystemId' => $paySystemId,
+ 'sum' => 100.00,
+ 'currency' => 'USD'
+ ];
+
+ return $paymentService->add($paymentFields)->getId();
+ }
+
+ /**
+ * Helper method to delete a test order
+ */
+ private function deleteTestOrder(int $id): void
+ {
+ try {
+ $orderService = Fabric::getServiceBuilder()->getSaleScope()->order();
+ $orderService->delete($id);
+ } catch (\Exception) {
+ // Ignore if order doesn't exist
+ }
+ }
+
+ /**
+ * Helper method to delete a test payment
+ */
+ private function deleteTestPayment(int $id): void
+ {
+ try {
+ $paymentService = Fabric::getServiceBuilder()->getSaleScope()->payment();
+ $paymentService->delete($id);
+ } catch (\Exception) {
+ // Ignore if payment doesn't exist
+ }
+ }
+
+ /**
+ * Create a test handler for payment system
+ *
+ * @return string Handler CODE
+ * @throws BaseException
+ * @throws TransportException
+ */
+ private function createTestHandler(): string
+ {
+ $handlerService = Fabric::getServiceBuilder()->getPaysystemScope()->handler();
+
+ $handlerName = 'Test Handler ' . time();
+ $handlerCode = 'test_handler_' . time();
+ $handlerSettings = [
+ 'CURRENCY' => ['USD'],
+ 'CLIENT_TYPE' => 'b2b',
+ 'FORM_DATA' => [
+ 'ACTION_URI' => 'https://example.com/payment_form.php',
+ 'METHOD' => 'POST',
+ 'FIELDS' => [
+ 'paymentId' => [
+ 'CODE' => 'PAYMENT_ID',
+ 'VISIBLE' => 'Y'
+ ]
+ ]
+ ],
+ 'CODES' => [
+ 'PAYMENT_ID' => [
+ 'NAME' => 'Payment ID',
+ 'DESCRIPTION' => 'Payment identifier',
+ 'SORT' => '100',
+ 'GROUP' => 'PAYMENT',
+ 'DEFAULT' => [
+ 'PROVIDER_KEY' => 'PAYMENT',
+ 'PROVIDER_VALUE' => 'ACCOUNT_NUMBER'
+ ]
+ ]
+ ]
+ ];
+
+ $handlerService->add($handlerName, $handlerCode, $handlerSettings);
+ // Return the CODE, not the ID
+ return $handlerCode;
+ }
+
+ /**
+ * Delete test handler by code
+ *
+ * @throws BaseException
+ * @throws TransportException
+ */
+ private function deleteTestHandlerByCode(string $handlerCode): void
+ {
+ try {
+ $handlerService = Fabric::getServiceBuilder()->getPaysystemScope()->handler();
+ // We need to get the handler ID first to delete it
+ $handlers = $handlerService->list();
+ foreach ($handlers->getHandlers() as $handlerItemResult) {
+ if ($handlerItemResult->CODE === $handlerCode) {
+ $handlerService->delete(intval($handlerItemResult->ID));
+ break;
+ }
+ }
+ } catch (BaseException $baseException) {
+ // Log the error but don't fail the test if handler deletion fails
+ // This is cleanup code, so failures should not break tests
+ error_log(sprintf('Warning: Failed to delete test handler %s: ', $handlerCode) . $baseException->getMessage());
+ }
+ }
+
+ /**
+ * Test add payment system
+ *
+ * @throws BaseException
+ * @throws TransportException
+ */
+ public function testAdd(): void
+ {
+ $personTypeId = $this->getPersonTypeId();
+ $handlerCode = $this->createTestHandler();
+
+ $name = 'Test Payment System ' . time();
+
+ $addedItemResult = $this->paysystemService->add([
+ 'NAME' => $name,
+ 'DESCRIPTION' => 'Test payment system description',
+ 'PERSON_TYPE_ID' => $personTypeId,
+ 'BX_REST_HANDLER' => $handlerCode,
+ 'ACTIVE' => 'Y',
+ 'ENTITY_REGISTRY_TYPE' => 'ORDER',
+ 'NEW_WINDOW' => 'N',
+ 'XML_ID' => 'test_ps_' . time()
+ ]);
+
+ self::assertGreaterThanOrEqual(1, $addedItemResult->getId());
+
+ // Clean up: first delete payment system, then handler
+ $this->paysystemService->delete($addedItemResult->getId());
+ $this->deleteTestHandlerByCode($handlerCode);
+ }
+
+ /**
+ * Test delete payment system
+ *
+ * @throws BaseException
+ * @throws TransportException
+ */
+ public function testDelete(): void
+ {
+ $personTypeId = $this->getPersonTypeId();
+ $handlerCode = $this->createTestHandler();
+
+ $name = 'Test Payment System to Delete ' . time();
+
+ $addedItemResult = $this->paysystemService->add([
+ 'NAME' => $name,
+ 'DESCRIPTION' => 'Test payment system for deletion',
+ 'PERSON_TYPE_ID' => $personTypeId,
+ 'BX_REST_HANDLER' => $handlerCode,
+ 'ACTIVE' => 'Y',
+ 'ENTITY_REGISTRY_TYPE' => 'ORDER'
+ ]);
+
+ $deletedItemResult = $this->paysystemService->delete($addedItemResult->getId());
+ self::assertTrue($deletedItemResult->isSuccess());
+
+ $this->deleteTestHandlerByCode($handlerCode);
+ }
+
+ /**
+ * Test list payment systems
+ *
+ * @throws BaseException
+ * @throws TransportException
+ */
+ public function testList(): void
+ {
+ $personTypeId = $this->getPersonTypeId();
+ $handlerCode = $this->createTestHandler();
+
+ // Create a test payment system first
+ $name = 'Test Payment System for List ' . time();
+
+ $addedItemResult = $this->paysystemService->add([
+ 'NAME' => $name,
+ 'DESCRIPTION' => 'Test payment system for listing',
+ 'PERSON_TYPE_ID' => $personTypeId,
+ 'BX_REST_HANDLER' => $handlerCode,
+ 'ACTIVE' => 'Y',
+ 'ENTITY_REGISTRY_TYPE' => 'ORDER'
+ ]);
+
+ $paysystemsResult = $this->paysystemService->list(
+ ['ID', 'NAME', 'ACTIVE'],
+ [],
+ ['ID' => 'ASC']
+ );
+
+ self::assertGreaterThanOrEqual(1, count($paysystemsResult->getPaysystems()));
+
+ foreach ($paysystemsResult->getPaysystems() as $paysystemItemResult) {
+ self::assertInstanceOf(PaysystemItemResult::class, $paysystemItemResult);
+ self::assertNotNull($paysystemItemResult->ID);
+ self::assertNotNull($paysystemItemResult->NAME);
+ }
+
+ // Clean up
+ $this->paysystemService->delete($addedItemResult->getId());
+ $this->deleteTestHandlerByCode($handlerCode);
+ }
+
+ /**
+ * Test update payment system
+ *
+ * @throws BaseException
+ * @throws TransportException
+ */
+ public function testUpdate(): void
+ {
+ $handlerCode = $this->createTestHandler();
+ $personTypeId = $this->getPersonTypeId();
+
+ // Create a payment system first
+ $addedItemResult = $this->paysystemService->add([
+ 'NAME' => 'Payment System for Update Test ' . time(),
+ 'PERSON_TYPE_ID' => $personTypeId,
+ 'BX_REST_HANDLER' => $handlerCode,
+ 'ENTITY_REGISTRY_TYPE' => 'ORDER'
+ ]);
+
+ $paysystemId = $addedItemResult->getId();
+ $newName = 'Updated Payment System ' . time();
+
+ // Update the payment system
+ $updatedItemResult = $this->paysystemService->update($paysystemId, [
+ 'NAME' => $newName
+ ]);
+
+ self::assertTrue($updatedItemResult->isSuccess());
+
+ // Verify the update by listing and finding our payment system
+ $paysystemsResult = $this->paysystemService->list(['ID', 'NAME'], ['ID' => $paysystemId]);
+ $paysystems = $paysystemsResult->getPaysystems();
+ self::assertCount(1, $paysystems);
+ self::assertEquals($newName, $paysystems[0]->NAME);
+
+ // Clean up
+ $this->paysystemService->delete($paysystemId);
+ $this->deleteTestHandlerByCode($handlerCode);
+ }
+
+ /**
+ * Test list with filter
+ *
+ * @throws BaseException
+ * @throws TransportException
+ */
+ public function testListWithFilter(): void
+ {
+ $handlerCode = $this->createTestHandler();
+ $personTypeId = $this->getPersonTypeId();
+ $testName = 'Test Payment System for List Filter ' . time();
+
+ // Add a test payment system
+ $addedItemResult = $this->paysystemService->add([
+ 'NAME' => $testName,
+ 'PERSON_TYPE_ID' => $personTypeId,
+ 'BX_REST_HANDLER' => $handlerCode,
+ 'ENTITY_REGISTRY_TYPE' => 'ORDER'
+ ]);
+
+ $paysystemId = $addedItemResult->getId();
+
+ // Test list with filter
+ $paysystemsResult = $this->paysystemService->list(['ID', 'NAME'], ['NAME' => $testName]);
+ $paysystems = $paysystemsResult->getPaysystems();
+
+ self::assertGreaterThanOrEqual(1, count($paysystems));
+ self::assertEquals($testName, $paysystems[0]->NAME);
+
+ // Clean up
+ $this->paysystemService->delete($paysystemId);
+ $this->deleteTestHandlerByCode($handlerCode);
+ }
+
+ /**
+ * Test list returns payment systems correctly
+ *
+ * @throws BaseException
+ * @throws TransportException
+ */
+ public function testListCount(): void
+ {
+ $handlerCode = $this->createTestHandler();
+ $personTypeId = $this->getPersonTypeId();
+
+ // Add a test payment system
+ $addedItemResult = $this->paysystemService->add([
+ 'NAME' => 'Test Payment System for Count ' . time(),
+ 'PERSON_TYPE_ID' => $personTypeId,
+ 'BX_REST_HANDLER' => $handlerCode,
+ 'ENTITY_REGISTRY_TYPE' => 'ORDER'
+ ]);
+
+ $paysystemId = $addedItemResult->getId();
+
+ // Test list returns at least one result
+ $paysystemsResult = $this->paysystemService->list();
+ self::assertGreaterThanOrEqual(1, count($paysystemsResult->getPaysystems()));
+
+ // Clean up
+ $this->paysystemService->delete($paysystemId);
+ $this->deleteTestHandlerByCode($handlerCode);
+ }
+
+ /**
+ * Test pay payment through specific payment system
+ *
+ * @throws BaseException
+ * @throws TransportException
+ */
+ public function testPayPayment(): void
+ {
+ $handlerCode = $this->createTestHandler();
+ $personTypeId = $this->getPersonTypeId();
+
+ // Create a test payment system
+ $addedItemResult = $this->paysystemService->add([
+ 'NAME' => 'Test Payment System for Payment ' . time(),
+ 'PERSON_TYPE_ID' => $personTypeId,
+ 'BX_REST_HANDLER' => $handlerCode,
+ 'ENTITY_REGISTRY_TYPE' => 'ORDER',
+ 'ACTIVE' => 'Y'
+ ]);
+
+ $paysystemId = $addedItemResult->getId();
+
+ // Create a test order
+ $orderId = $this->createTestOrder($personTypeId);
+
+ // Create a test payment
+ $paymentId = $this->createTestPayment($orderId, $paysystemId);
+
+ // Test the payPayment method
+ $paymentResult = $this->paysystemService->payPayment($paymentId, $paysystemId);
+
+ // Verify that payment result is returned (can be true or false depending on payment system configuration)
+ self::assertIsBool($paymentResult->isSuccess());
+
+ // Clean up in reverse order
+ $this->deleteTestPayment($paymentId);
+ $this->deleteTestOrder($orderId);
+ $this->paysystemService->delete($paysystemId);
+ $this->deleteTestHandlerByCode($handlerCode);
+ }
+
+
+ protected function setUp(): void
+ {
+ $this->paysystemService = Fabric::getServiceBuilder()->getPaysystemScope()->paysystem();
+ }
+
+ protected function tearDown(): void
+ {
+ // Additional cleanup: remove any remaining test handlers that might have been left
+ $this->cleanupTestHandlers();
+ }
+
+ /**
+ * Clean up any test handlers that might be left over
+ */
+ private function cleanupTestHandlers(): void
+ {
+ try {
+ $handlerService = Fabric::getServiceBuilder()->getPaysystemScope()->handler();
+ $handlers = $handlerService->list();
+ foreach ($handlers->getHandlers() as $handlerItemResult) {
+ if (str_contains($handlerItemResult->CODE, 'test_handler_')) {
+ try {
+ $handlerService->delete(intval($handlerItemResult->ID));
+ } catch (BaseException $e) {
+ // Ignore individual deletion errors
+ error_log(sprintf('Warning: Failed to cleanup test handler %s: ', $handlerItemResult->CODE) . $e->getMessage());
+ }
+ }
+ }
+ } catch (BaseException $baseException) {
+ // Ignore general cleanup errors
+ error_log("Warning: Failed to list handlers during cleanup: " . $baseException->getMessage());
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/Integration/Services/Paysystem/Settings/Service/SettingsTest.php b/tests/Integration/Services/Paysystem/Settings/Service/SettingsTest.php
new file mode 100644
index 00000000..ef87a07d
--- /dev/null
+++ b/tests/Integration/Services/Paysystem/Settings/Service/SettingsTest.php
@@ -0,0 +1,384 @@
+
+ *
+ * For the full copyright and license information, please view the MIT-LICENSE.txt
+ * file that was distributed with this source code.
+ */
+
+declare(strict_types=1);
+
+namespace Bitrix24\SDK\Tests\Integration\Services\Paysystem\Settings\Service;
+
+use Bitrix24\SDK\Core\Exceptions\BaseException;
+use Bitrix24\SDK\Core\Exceptions\TransportException;
+use Bitrix24\SDK\Services\Paysystem\Settings\Result\SettingsItemResult;
+use Bitrix24\SDK\Services\Paysystem\Settings\Service\Settings;
+use Bitrix24\SDK\Tests\CustomAssertions\CustomBitrix24Assertions;
+use Bitrix24\SDK\Tests\Integration\Fabric;
+use PHPUnit\Framework\Attributes\CoversClass;
+use PHPUnit\Framework\Attributes\CoversMethod;
+use PHPUnit\Framework\TestCase;
+
+/**
+ * Class SettingsTest
+ *
+ * Integration tests for Payment System Settings service
+ *
+ * @package Bitrix24\SDK\Tests\Integration\Services\Paysystem\Settings\Service
+ */
+#[CoversClass(Settings::class)]
+#[CoversMethod(Settings::class, 'get')]
+#[CoversMethod(Settings::class, 'update')]
+#[CoversMethod(Settings::class, 'getForPayment')]
+class SettingsTest extends TestCase
+{
+ use CustomBitrix24Assertions;
+
+ private Settings $settingsService;
+
+ private array $testDataCleanup = [];
+
+ /**
+ * Get or create a person type ID for tests
+ *
+ * @throws BaseException
+ * @throws TransportException
+ */
+ private function getPersonTypeId(): int
+ {
+ $personTypeService = Fabric::getServiceBuilder()->getSaleScope()->personType();
+ $personTypesResult = $personTypeService->list();
+
+ return $personTypesResult->getPersonTypes()[0]->id;
+ }
+
+ /**
+ * Create a test handler for payment system
+ *
+ * @return string Handler CODE
+ * @throws BaseException
+ * @throws TransportException
+ */
+ private function createTestHandler(): string
+ {
+ $handlerService = Fabric::getServiceBuilder()->getPaysystemScope()->handler();
+
+ $handlerName = 'Test Settings Handler ' . time();
+ $handlerCode = 'test_settings_handler_' . time();
+ $handlerSettings = [
+ 'CURRENCY' => ['USD'],
+ 'CLIENT_TYPE' => 'b2b',
+ 'FORM_DATA' => [
+ 'ACTION_URI' => 'https://example.com/payment_form.php',
+ 'METHOD' => 'POST',
+ 'FIELDS' => [
+ 'paymentId' => [
+ 'CODE' => 'PAYMENT_ID',
+ 'VISIBLE' => 'Y'
+ ]
+ ]
+ ],
+ 'CODES' => [
+ 'TEST_API_KEY' => [
+ 'NAME' => 'Test API Key',
+ 'DESCRIPTION' => 'API key for testing',
+ 'SORT' => '100',
+ 'GROUP' => 'CONNECT',
+ 'DEFAULT' => [
+ 'PROVIDER_KEY' => 'VALUE',
+ 'PROVIDER_VALUE' => ''
+ ]
+ ],
+ 'TEST_MERCHANT_ID' => [
+ 'NAME' => 'Test Merchant ID',
+ 'DESCRIPTION' => 'Merchant identifier for testing',
+ 'SORT' => '200',
+ 'GROUP' => 'CONNECT',
+ 'DEFAULT' => [
+ 'PROVIDER_KEY' => 'VALUE',
+ 'PROVIDER_VALUE' => ''
+ ]
+ ],
+ 'TEST_MODE' => [
+ 'NAME' => 'Test Mode',
+ 'DESCRIPTION' => 'Test or production mode',
+ 'SORT' => '300',
+ 'GROUP' => 'GENERAL',
+ 'DEFAULT' => [
+ 'PROVIDER_KEY' => 'VALUE',
+ 'PROVIDER_VALUE' => 'TEST'
+ ]
+ ]
+ ]
+ ];
+
+ $handlerService->add($handlerName, $handlerCode, $handlerSettings);
+ $this->testDataCleanup['handler_code'] = $handlerCode;
+
+ return $handlerCode;
+ }
+
+ /**
+ * Create a test payment system
+ *
+ * @return int Payment system ID
+ * @throws BaseException
+ * @throws TransportException
+ */
+ private function createTestPaymentSystem(string $handlerCode): int
+ {
+ $paysystemService = Fabric::getServiceBuilder()->getPaysystemScope()->paysystem();
+ $personTypeId = $this->getPersonTypeId();
+
+ $name = 'Test Payment System for Settings ' . time();
+
+ $addedItemResult = $paysystemService->add([
+ 'NAME' => $name,
+ 'DESCRIPTION' => 'Test payment system for settings testing',
+ 'PERSON_TYPE_ID' => $personTypeId,
+ 'BX_REST_HANDLER' => $handlerCode,
+ 'ACTIVE' => 'Y',
+ 'ENTITY_REGISTRY_TYPE' => 'ORDER',
+ 'NEW_WINDOW' => 'N',
+ 'XML_ID' => 'test_settings_ps_' . time()
+ ]);
+
+ $paySystemId = $addedItemResult->getId();
+ $this->testDataCleanup['paysystem_id'] = $paySystemId;
+
+ return $paySystemId;
+ }
+
+ /**
+ * Helper method to create a test order
+ *
+ * @return int Order ID
+ * @throws BaseException
+ * @throws TransportException
+ */
+ private function createTestOrder(int $personTypeId): int
+ {
+ $orderService = Fabric::getServiceBuilder()->getSaleScope()->order();
+ $orderFields = [
+ 'lid' => 's1',
+ 'personTypeId' => $personTypeId,
+ 'currency' => 'USD',
+ 'price' => 100.00
+ ];
+
+ $orderId = $orderService->add($orderFields)->getId();
+ $this->testDataCleanup['order_id'] = $orderId;
+
+ return $orderId;
+ }
+
+ /**
+ * Helper method to create a test payment
+ *
+ * @return int Payment ID
+ * @throws BaseException
+ * @throws TransportException
+ */
+ private function createTestPayment(int $orderId, int $paySystemId): int
+ {
+ $paymentService = Fabric::getServiceBuilder()->getSaleScope()->payment();
+ $paymentFields = [
+ 'orderId' => $orderId,
+ 'paySystemId' => $paySystemId,
+ 'sum' => 100.00,
+ 'currency' => 'USD'
+ ];
+
+ $paymentId = $paymentService->add($paymentFields)->getId();
+ $this->testDataCleanup['payment_id'] = $paymentId;
+
+ return $paymentId;
+ }
+
+ /**
+ * Test get payment system settings
+ *
+ * @throws BaseException
+ * @throws TransportException
+ */
+ public function testGet(): void
+ {
+ $handlerCode = $this->createTestHandler();
+ $paySystemId = $this->createTestPaymentSystem($handlerCode);
+ $personTypeId = $this->getPersonTypeId();
+
+ $settingsItemResult = $this->settingsService->get($paySystemId, $personTypeId);
+
+ self::assertInstanceOf(SettingsItemResult::class, $settingsItemResult);
+ // The settings should contain the default values from the handler
+ $settings = iterator_to_array($settingsItemResult->getIterator());
+ self::assertIsArray($settings);
+
+ // Check that we can access settings as properties
+ self::assertNotNull($settingsItemResult->TEST_MODE ?? null);
+ }
+
+ /**
+ * Test update payment system settings
+ *
+ * @throws BaseException
+ * @throws TransportException
+ */
+ public function testUpdate(): void
+ {
+ $handlerCode = $this->createTestHandler();
+ $paySystemId = $this->createTestPaymentSystem($handlerCode);
+ $personTypeId = $this->getPersonTypeId();
+
+ // First, get current settings to ensure we have a baseline
+ $this->settingsService->get($paySystemId, $personTypeId);
+
+ // Update settings
+ $newSettings = [
+ 'TEST_API_KEY' => [
+ 'TYPE' => 'VALUE',
+ 'VALUE' => 'test_api_key_' . time()
+ ],
+ 'TEST_MERCHANT_ID' => [
+ 'TYPE' => 'VALUE',
+ 'VALUE' => 'test_merchant_' . time()
+ ]
+ ];
+
+ $updatedItemResult = $this->settingsService->update($paySystemId, $newSettings, $personTypeId);
+ self::assertTrue($updatedItemResult->isSuccess());
+
+ // Verify settings were updated
+ $settingsItemResult = $this->settingsService->get($paySystemId, $personTypeId);
+
+ // Debug: check what we actually got
+ $settingsArray = iterator_to_array($settingsItemResult->getIterator());
+
+ // Check that settings exist and have correct values
+ self::assertArrayHasKey('TEST_API_KEY', $settingsArray);
+ self::assertArrayHasKey('TEST_MERCHANT_ID', $settingsArray);
+
+ // Compare values directly from array
+ self::assertEquals($newSettings['TEST_API_KEY']['VALUE'], $settingsArray['TEST_API_KEY']['VALUE']);
+ self::assertEquals($newSettings['TEST_MERCHANT_ID']['VALUE'], $settingsArray['TEST_MERCHANT_ID']['VALUE']);
+ }
+
+ /**
+ * Test get payment system settings for specific payment
+ *
+ * @throws BaseException
+ * @throws TransportException
+ */
+ public function testGetForPayment(): void
+ {
+ $handlerCode = $this->createTestHandler();
+ $paySystemId = $this->createTestPaymentSystem($handlerCode);
+ $personTypeId = $this->getPersonTypeId();
+
+ // Create test order and payment
+ $orderId = $this->createTestOrder($personTypeId);
+ $paymentId = $this->createTestPayment($orderId, $paySystemId);
+
+ $settingsItemResult = $this->settingsService->getForPayment($paymentId, $paySystemId);
+
+ self::assertInstanceOf(SettingsItemResult::class, $settingsItemResult);
+
+ // The settings should contain the values from the payment system
+ $settings = iterator_to_array($settingsItemResult->getIterator());
+ self::assertIsArray($settings);
+
+ // Check that we can access settings as properties
+ self::assertNotNull($settingsItemResult->TEST_MODE ?? null);
+ }
+
+ /**
+ * Test get with default person type (ID = 0)
+ *
+ * @throws BaseException
+ * @throws TransportException
+ */
+ public function testGetWithDefaultPersonType(): void
+ {
+ $handlerCode = $this->createTestHandler();
+ $paySystemId = $this->createTestPaymentSystem($handlerCode);
+
+ $settingsItemResult = $this->settingsService->get($paySystemId, 0);
+
+ self::assertInstanceOf(SettingsItemResult::class, $settingsItemResult);
+ $settings = iterator_to_array($settingsItemResult->getIterator());
+ self::assertIsArray($settings);
+ }
+
+ protected function setUp(): void
+ {
+ $this->settingsService = Fabric::getServiceBuilder()->getPaysystemScope()->settings();
+ $this->testDataCleanup = [];
+ }
+
+ protected function tearDown(): void
+ {
+ // Clean up test data in reverse order of creation
+ $this->cleanupTestData();
+ }
+
+ /**
+ * Clean up test data created during tests
+ */
+ private function cleanupTestData(): void
+ {
+ try {
+ // Delete payment first (if exists)
+ if (isset($this->testDataCleanup['payment_id'])) {
+ $paymentService = Fabric::getServiceBuilder()->getSaleScope()->payment();
+ try {
+ $paymentService->delete($this->testDataCleanup['payment_id']);
+ } catch (\Exception $e) {
+ error_log("Warning: Failed to delete test payment: " . $e->getMessage());
+ }
+ }
+
+ // Delete order (if exists)
+ if (isset($this->testDataCleanup['order_id'])) {
+ $orderService = Fabric::getServiceBuilder()->getSaleScope()->order();
+ try {
+ $orderService->delete($this->testDataCleanup['order_id']);
+ } catch (\Exception $e) {
+ error_log("Warning: Failed to delete test order: " . $e->getMessage());
+ }
+ }
+
+ // Delete payment system (if exists)
+ if (isset($this->testDataCleanup['paysystem_id'])) {
+ $paysystemService = Fabric::getServiceBuilder()->getPaysystemScope()->paysystem();
+ try {
+ $paysystemService->delete($this->testDataCleanup['paysystem_id']);
+ } catch (\Exception $e) {
+ error_log("Warning: Failed to delete test payment system: " . $e->getMessage());
+ }
+ }
+
+ // Delete handler last (if exists)
+ if (isset($this->testDataCleanup['handler_code'])) {
+ $handlerService = Fabric::getServiceBuilder()->getPaysystemScope()->handler();
+ try {
+ // We need to get the handler ID first to delete it
+ $handlers = $handlerService->list();
+ foreach ($handlers->getHandlers() as $handlerItemResult) {
+ if ($handlerItemResult->CODE === $this->testDataCleanup['handler_code']) {
+ $handlerService->delete(intval($handlerItemResult->ID));
+ break;
+ }
+ }
+ } catch (\Exception $e) {
+ error_log("Warning: Failed to delete test handler: " . $e->getMessage());
+ }
+ }
+ } catch (\Exception $exception) {
+ error_log("Warning: General cleanup error: " . $exception->getMessage());
+ }
+ }
+}
\ No newline at end of file