diff --git a/config/services/context_provider.php b/config/services/context_provider.php index 47641bb9..05bb58a2 100644 --- a/config/services/context_provider.php +++ b/config/services/context_provider.php @@ -12,7 +12,7 @@ $services->set(BankListContextProvider::class) ->args([ - service('commerce_weavers_sylius_tpay.tpay.provider.tpay_api_bank_list'), + service('commerce_weavers_sylius_tpay.tpay.provider.validated_tpay_api_bank_list'), ]) ->tag('sylius.ui.template_event.context_provider') ; diff --git a/config/services/form.php b/config/services/form.php index c1831e4a..4b33b162 100644 --- a/config/services/form.php +++ b/config/services/form.php @@ -4,9 +4,7 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator; -use CommerceWeavers\SyliusTpayPlugin\EventListener\AddCreditCardToAccountMenuEventListener; use CommerceWeavers\SyliusTpayPlugin\Form\DataTransformer\CardTypeDataTransformer; -use CommerceWeavers\SyliusTpayPlugin\Form\DataTransformer\VisaMobilePhoneDataTransformer; use CommerceWeavers\SyliusTpayPlugin\Form\EventListener\AddSavedCreditCardsListener; use CommerceWeavers\SyliusTpayPlugin\Form\EventListener\DecryptGatewayConfigListener; use CommerceWeavers\SyliusTpayPlugin\Form\EventListener\EncryptGatewayConfigListener; @@ -18,7 +16,7 @@ use CommerceWeavers\SyliusTpayPlugin\Form\Type\TpayPaymentDetailsType; use CommerceWeavers\SyliusTpayPlugin\Payum\Factory\TpayGatewayFactory; -return function(ContainerConfigurator $container): void { +return static function(ContainerConfigurator $container): void { $services = $container->services(); $services->set(CompleteTypeExtension::class) diff --git a/config/services/tpay.php b/config/services/tpay.php index 1145c420..09f57274 100644 --- a/config/services/tpay.php +++ b/config/services/tpay.php @@ -19,8 +19,9 @@ use CommerceWeavers\SyliusTpayPlugin\Tpay\Factory\CreateRedirectBasedPaymentPayloadFactoryInterface; use CommerceWeavers\SyliusTpayPlugin\Tpay\Factory\CreateVisaMobilePaymentPayloadFactory; use CommerceWeavers\SyliusTpayPlugin\Tpay\Factory\CreateVisaMobilePaymentPayloadFactoryInterface; -use CommerceWeavers\SyliusTpayPlugin\Tpay\Provider\TpayApiBankListProvider; -use CommerceWeavers\SyliusTpayPlugin\Tpay\Provider\TpayApiBankListProviderInterface; +use CommerceWeavers\SyliusTpayPlugin\Tpay\Provider\AvailableTpayChannelListProvider; +use CommerceWeavers\SyliusTpayPlugin\Tpay\Provider\AvailableTpayChannelListProviderInterface; +use CommerceWeavers\SyliusTpayPlugin\Tpay\Provider\ValidTpayChannelListProvider; use CommerceWeavers\SyliusTpayPlugin\Tpay\Resolver\CachedTpayTransactionChannelResolver; use CommerceWeavers\SyliusTpayPlugin\Tpay\Resolver\TpayTransactionChannelResolver; use CommerceWeavers\SyliusTpayPlugin\Tpay\Security\Notification\Factory\BasicPaymentFactory; @@ -149,18 +150,28 @@ ->alias(SignatureVerifierInterface::class, 'commerce_weavers_sylius_tpay.tpay.security.notification.verifier.signature') ; - $services->set('commerce_weavers_sylius_tpay.tpay.provider.tpay_api_bank_list', TpayApiBankListProvider::class) + $services->set('commerce_weavers_sylius_tpay.tpay.provider.available_tpay_api_bank_list', AvailableTpayChannelListProvider::class) ->args([ service('commerce_weavers_sylius_tpay.tpay.resolver.tpay_transaction_channel_resolver'), ]) - ->alias(TpayApiBankListProviderInterface::class, 'commerce_weavers_sylius_tpay.tpay.provider.tpay_api_bank_list') + ->alias(AvailableTpayChannelListProviderInterface::class, 'commerce_weavers_sylius_tpay.tpay.provider.available_tpay_api_bank_list') + ; + + $services->set('commerce_weavers_sylius_tpay.tpay.provider.validated_tpay_api_bank_list', ValidTpayChannelListProvider::class) + ->args([ + service('commerce_weavers_sylius_tpay.tpay.provider.available_tpay_api_bank_list'), + service('sylius.repository.payment_method'), + service('sylius.context.channel'), + service('payum.dynamic_gateways.cypher') + ]) + ->alias(AvailableTpayChannelListProviderInterface::class, 'commerce_weavers_sylius_tpay.tpay.provider.validated_tpay_api_bank_list') ; $services->set('commerce_weavers_sylius_tpay.tpay.resolver.tpay_transaction_channel_resolver', TpayTransactionChannelResolver::class) ->args([ service('payum'), ]) - ->alias(TpayApiBankListProviderInterface::class, 'commerce_weavers_sylius_tpay.tpay.resolver.tpay_transaction_channel_resolver') + ->alias(AvailableTpayChannelListProviderInterface::class, 'commerce_weavers_sylius_tpay.tpay.resolver.tpay_transaction_channel_resolver') ; $services->set('commerce_weavers_sylius_tpay.tpay.resolver.cached_tpay_transaction_channel_resolver', CachedTpayTransactionChannelResolver::class) diff --git a/config/services/validator.php b/config/services/validator.php index baebd1cd..b0028ecb 100644 --- a/config/services/validator.php +++ b/config/services/validator.php @@ -6,6 +6,7 @@ use CommerceWeavers\SyliusTpayPlugin\Validator\Constraint\EncodedGooglePayTokenValidator; use CommerceWeavers\SyliusTpayPlugin\Validator\Constraint\ForAuthorizedUserOnlyValidator; +use CommerceWeavers\SyliusTpayPlugin\Validator\Constraint\ValidTpayChannelValidator; return static function(ContainerConfigurator $container): void { $services = $container->services(); @@ -20,4 +21,11 @@ ]) ->tag('validator.constraint_validator') ; + + $services->set('commerce_weavers_sylius_tpay.validator.constraint.valid_tpay_channel', ValidTpayChannelValidator::class) + ->args([ + service('commerce_weavers_sylius_tpay.tpay.provider.validated_tpay_api_bank_list'), + ]) + ->tag('validator.constraint_validator') + ; }; diff --git a/phpstan.neon b/phpstan.neon index b0ac1fd7..0a867b4a 100644 --- a/phpstan.neon +++ b/phpstan.neon @@ -38,3 +38,4 @@ parameters: - '#Comparison operation "<=" between int<1, max> and \(array\|float\|int\) results in an error\.#' - '#Parameter \#1 \$request \(CommerceWeavers\\SyliusTpayPlugin\\Payum\\Request\\Api\\InitializeApplePayPayment\) of method CommerceWeavers\\SyliusTpayPlugin\\Payum\\Action\\Api\\InitializeApplePayPaymentAction::doExecute\(\) should be contravariant with parameter \$request \(Payum\\Core\\Request\\Generic\) of method CommerceWeavers\\SyliusTpayPlugin\\Payum\\Action\\Api\\BasePaymentAwareAction::doExecute\(\)#' - '#Parameter \#1 \$request \(CommerceWeavers\\SyliusTpayPlugin\\Payum\\Request\\Api\\Notify\) of method CommerceWeavers\\SyliusTpayPlugin\\Payum\\Action\\Api\\NotifyAction::doExecute\(\) should be contravariant with parameter \$request \(Payum\\Core\\Request\\Generic\) of method CommerceWeavers\\SyliusTpayPlugin\\Payum\\Action\\Api\\BasePaymentAwareAction::doExecute\(\)#' + - '#Parameter \#2 \$callback of function array_filter expects \(callable\(array\|bool\|object\|string\): bool\)\|null, Closure\(array\): bool given#' diff --git a/src/ContextProvider/BankListContextProvider.php b/src/ContextProvider/BankListContextProvider.php index d7d195a4..7bea178c 100644 --- a/src/ContextProvider/BankListContextProvider.php +++ b/src/ContextProvider/BankListContextProvider.php @@ -4,7 +4,7 @@ namespace CommerceWeavers\SyliusTpayPlugin\ContextProvider; -use CommerceWeavers\SyliusTpayPlugin\Tpay\Provider\TpayApiBankListProviderInterface; +use CommerceWeavers\SyliusTpayPlugin\Tpay\Provider\ValidTpayChannelListProviderInterface; use Sylius\Bundle\UiBundle\ContextProvider\ContextProviderInterface; use Sylius\Bundle\UiBundle\Registry\TemplateBlock; use Sylius\Component\Core\Model\OrderInterface; @@ -12,7 +12,7 @@ final class BankListContextProvider implements ContextProviderInterface { public function __construct( - private readonly TpayApiBankListProviderInterface $bankListProvider, + private readonly ValidTpayChannelListProviderInterface $validTpayChannelListProvider, ) { } @@ -27,7 +27,7 @@ public function provide(array $templateContext, TemplateBlock $templateBlock): a } } - $templateContext['banks'] = $this->bankListProvider->provide(); + $templateContext['banks'] = $this->validTpayChannelListProvider->provide(); return $templateContext; } diff --git a/src/Form/Type/TpayPaymentDetailsType.php b/src/Form/Type/TpayPaymentDetailsType.php index e52a2568..6bb28935 100644 --- a/src/Form/Type/TpayPaymentDetailsType.php +++ b/src/Form/Type/TpayPaymentDetailsType.php @@ -6,6 +6,7 @@ use CommerceWeavers\SyliusTpayPlugin\Repository\CreditCardRepositoryInterface; use CommerceWeavers\SyliusTpayPlugin\Validator\Constraint\EncodedGooglePayToken; +use CommerceWeavers\SyliusTpayPlugin\Validator\Constraint\ValidTpayChannel; use Sylius\Component\Core\Model\CustomerInterface; use Sylius\Component\Core\Model\OrderInterface; use Sylius\Component\Core\Model\ShopUserInterface; @@ -87,6 +88,10 @@ public function buildForm(FormBuilderInterface $builder, array $options): void HiddenType::class, [ 'property_path' => '[tpay_channel_id]', + 'validation_groups' => ['sylius_checkout_complete'], + 'constraints' => [ + new ValidTpayChannel(groups: ['sylius_checkout_complete']), + ], ], ) ->add( diff --git a/src/Repository/FindByChannelWithGatewayConfigTrait.php b/src/Repository/FindByChannelWithGatewayConfigTrait.php new file mode 100644 index 00000000..8459ffc4 --- /dev/null +++ b/src/Repository/FindByChannelWithGatewayConfigTrait.php @@ -0,0 +1,30 @@ +createQueryBuilder('o') + ->addSelect('gatewayConfig') + ->innerJoin('o.gatewayConfig', 'gatewayConfig', Join::WITH, 'gatewayConfig.gatewayName = :gatewayName') + ->where('o.enabled = :enabled') + ->andWhere(':channel MEMBER OF o.channels') + ->setParameter('enabled', true) + ->setParameter('channel', $channel) + ->setParameter('gatewayName', $gatewayConfigName) + ->getQuery() + ->getResult() + ; + } +} diff --git a/src/Repository/FindByChannelWithGatewayConfigTraitInterface.php b/src/Repository/FindByChannelWithGatewayConfigTraitInterface.php new file mode 100644 index 00000000..7ca1f239 --- /dev/null +++ b/src/Repository/FindByChannelWithGatewayConfigTraitInterface.php @@ -0,0 +1,13 @@ +availableTpayApiBankListProvider->provide(); + + /** @var PaymentMethodInterface[] $paymentMethods */ + $paymentMethods = $this->paymentMethodRepository->findByChannelAndGatewayConfigNameWithGatewayConfig( + $this->channelContext->getChannel(), + 'tpay', + ); + + Assert::notEmpty($paymentMethods, 'There is no payment method of Tpay type available'); + + $paymentMethodsToRemoveByGroupId = []; + $hasPblPaymentAvailable = false; + foreach ($paymentMethods as $paymentMethod) { + /** @var (GatewayConfigInterface&CryptedInterface)|null $tpayGatewayConfig */ + $tpayGatewayConfig = $paymentMethod->getGatewayConfig(); + + if (null === $tpayGatewayConfig) { + continue; + } + + $tpayGatewayConfig->decrypt($this->cypher); + $config = $tpayGatewayConfig->getConfig(); + + if (!array_key_exists('type', $config)) { + continue; + } + + if ($hasPblPaymentAvailable === false && $config['type'] === PaymentType::PAY_BY_LINK) { + $hasPblPaymentAvailable = true; + } + + match ($config['type']) { + PaymentType::VISA_MOBILE => $paymentMethodsToRemoveByGroupId[] = PayGroup::VISA_MOBILE, + PaymentType::APPLE_PAY => $paymentMethodsToRemoveByGroupId[] = PayGroup::APPLE_PAY, + PaymentType::GOOGLE_PAY => $paymentMethodsToRemoveByGroupId[] = PayGroup::GOOGLE_PAY, + PaymentType::BLIK => $paymentMethodsToRemoveByGroupId[] = PayGroup::BLIK, + PaymentType::CARD => $paymentMethodsToRemoveByGroupId[] = PayGroup::CARD, + default => null, + }; + } + + if (!$hasPblPaymentAvailable) { + throw new UnableToGetBankListException( + 'Bank list cannot be retrieved if there is no payment method with PayByLink type configured', + ); + } + + return array_filter($availableChannels, static function (array $channel) use ($paymentMethodsToRemoveByGroupId): bool { + $groupId = (int) $channel['groups'][0]['id']; + + return !in_array($groupId, $paymentMethodsToRemoveByGroupId, true); + }); + } +} diff --git a/src/Tpay/Provider/ValidTpayChannelListProviderInterface.php b/src/Tpay/Provider/ValidTpayChannelListProviderInterface.php new file mode 100644 index 00000000..cd4795cb --- /dev/null +++ b/src/Tpay/Provider/ValidTpayChannelListProviderInterface.php @@ -0,0 +1,10 @@ +validatedTpayApiBankListProvider->provide(); + + if (array_key_exists($value, $validChannels)) { + return; + } + + $this->context->buildViolation($constraint->message) + ->setCode($constraint::NOT_VALID_CHANNEL_ERROR) + ->addViolation() + ; + } +} diff --git a/tests/Application/config/packages/_sylius.yaml b/tests/Application/config/packages/_sylius.yaml index ce960b37..655cf95b 100644 --- a/tests/Application/config/packages/_sylius.yaml +++ b/tests/Application/config/packages/_sylius.yaml @@ -23,3 +23,9 @@ sylius_order: order: classes: model: App\Entity\Order + +sylius_payment: + resources: + payment_method: + classes: + repository: App\Repository\PaymentMethodRepository diff --git a/tests/Application/src/Repository/PaymentMethodRepository.php b/tests/Application/src/Repository/PaymentMethodRepository.php new file mode 100644 index 00000000..f62cd2c5 --- /dev/null +++ b/tests/Application/src/Repository/PaymentMethodRepository.php @@ -0,0 +1,13 @@ +bankListProvider = $this->prophesize(TpayApiBankListProviderInterface::class); + $this->validTpayChannelListProvider = $this->prophesize(ValidTpayChannelListProviderInterface::class); } public function test_it_does_not_support_some_template_event_and_pay_by_link_template_name(): void @@ -54,8 +54,8 @@ public function test_it_provides_banks_for_context_if_there_is_no_order_in_templ { $templateBlock = new TemplateBlock('pay_by_link', 'cw.tpay.shop.select_payment.choice_item_form', null, null, null, null); - $this->bankListProvider->provide()->shouldBeCalled(); - $this->bankListProvider->provide()->willReturn(['1' => 'some bank']); + $this->validTpayChannelListProvider->provide()->shouldBeCalled(); + $this->validTpayChannelListProvider->provide()->willReturn(['1' => 'some bank']); $context = $this->createTestObject()->provide( ['i_am_not_an_order' => 'some_context'], @@ -72,8 +72,8 @@ public function test_it_provides_nothing_new_for_context_if_order_has_no_payment $order = $this->prophesize(OrderInterface::class); $order->getLastPayment()->willReturn(null); - $this->bankListProvider->provide()->shouldNotBeCalled(); - $this->bankListProvider->provide()->willReturn(['1' => 'some bank']); + $this->validTpayChannelListProvider->provide()->shouldNotBeCalled(); + $this->validTpayChannelListProvider->provide()->willReturn(['1' => 'some bank']); $context = $this->createTestObject()->provide( ['order' => $order->reveal()], @@ -90,8 +90,8 @@ public function test_it_provides_bank_list_as_context_if_order_has_payment(): vo $payment = $this->prophesize(PaymentInterface::class); $order->getLastPayment()->willReturn($payment); - $this->bankListProvider->provide()->shouldBeCalled(); - $this->bankListProvider->provide()->willReturn(['1' => 'some bank']); + $this->validTpayChannelListProvider->provide()->shouldBeCalled(); + $this->validTpayChannelListProvider->provide()->willReturn(['1' => 'some bank']); $context = $this->createTestObject()->provide( ['order' => $order->reveal()], @@ -104,6 +104,6 @@ public function test_it_provides_bank_list_as_context_if_order_has_payment(): vo private function createTestObject(): BankListContextProvider { - return new BankListContextProvider($this->bankListProvider->reveal()); + return new BankListContextProvider($this->validTpayChannelListProvider->reveal()); } } diff --git a/tests/Unit/Tpay/Provider/AvailableTpayApiBankListProviderTest.php b/tests/Unit/Tpay/Provider/AvailableTpayApiBankListProviderTest.php new file mode 100644 index 00000000..0686cffd --- /dev/null +++ b/tests/Unit/Tpay/Provider/AvailableTpayApiBankListProviderTest.php @@ -0,0 +1,83 @@ +tpayTransactionChannelResolver = $this->prophesize(TpayTransactionChannelResolverInterface::class); + } + + public function test_it_does_not_provide_not_available_transaction_channels(): void + { + $correctChannel = [ + 'id' => '1', + 'instantRedirection' => true, + 'onlinePayment' => true, + 'available' => true, + ]; + + $incorrectChannel = [ + 'id' => '2', + 'instantRedirection' => true, + 'onlinePayment' => true, + 'available' => false, + ]; + + $channels = [ + '1' => $correctChannel, + '2' => $incorrectChannel, + ]; + + $this->tpayTransactionChannelResolver->resolve()->willReturn($channels); + + $this->assertSame( + ['1' => $correctChannel], + $this->createTestSubject()->provide() + ); + } + + public function test_it_provides_transaction_channels(): void + { + $channels = [ + '1' => [ + 'id' => '1', + 'instantRedirection' => true, + 'onlinePayment' => true, + 'available' => true, + ], + '2' => [ + 'id' => '2', + 'instantRedirection' => true, + 'onlinePayment' => true, + 'available' => true, + ], + ]; + + $this->tpayTransactionChannelResolver->resolve()->willReturn($channels); + + $this->assertSame( + $channels, + $this->createTestSubject()->provide() + ); + } + + + private function createTestSubject(): AvailableTpayChannelListProvider + { + return new AvailableTpayChannelListProvider($this->tpayTransactionChannelResolver->reveal()); + } +} diff --git a/tests/Unit/Tpay/Provider/ValidTpayChannelListProviderTest.php b/tests/Unit/Tpay/Provider/ValidTpayChannelListProviderTest.php new file mode 100644 index 00000000..744af090 --- /dev/null +++ b/tests/Unit/Tpay/Provider/ValidTpayChannelListProviderTest.php @@ -0,0 +1,256 @@ + [ + 'id' => '1', + 'name' => 'some bank', + 'available' => true, + 'groups' => [ + ['id' => '1'], + ], + ], + '2' => [ + 'id' => '2', + 'name' => 'card payment', + 'available' => true, + 'groups' => [ + ['id' => '103'], + ], + ], + '3' => [ + 'id' => '3', + 'name' => 'BLIK', + 'available' => true, + 'groups' => [ + ['id' => '150'], + ], + ], + '4' => [ + 'id' => '4', + 'name' => 'Apple Pay', + 'available' => true, + 'groups' => [ + ['id' => '170'], + ], + ], + '5' => [ + 'id' => '5', + 'name' => 'Google Pay', + 'available' => true, + 'groups' => [ + ['id' => '166'], + ], + ], + '6' => [ + 'id' => '6', + 'name' => 'Visa mobile', + 'available' => true, + 'groups' => [ + ['id' => '171'], + ], + ], + ]; + + private AvailableTpayChannelListProviderInterface|ObjectProphecy $availableTpayApiBankListProvider; + + private PaymentMethodRepositoryInterface|ObjectProphecy $paymentMethodRepository; + + private ChannelContextInterface|ObjectProphecy $channelContext; + + private CypherInterface|ObjectProphecy $cypher; + + protected function setUp(): void + { + $this->availableTpayApiBankListProvider = $this->prophesize(AvailableTpayChannelListProviderInterface::class); + $this->paymentMethodRepository = $this->prophesize(PaymentMethodRepositoryInterface::class); + $this->channelContext = $this->prophesize(ChannelContextInterface::class); + $this->cypher = $this->prophesize(CypherInterface::class); + } + + public function test_it_throws_exception_if_there_is_no_tpay_based_payment(): void + { + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('There is no payment method of Tpay type available'); + + $this->availableTpayApiBankListProvider->provide()->willReturn(self::BANK_LIST); + + $channel = $this->prophesize(ChannelInterface::class); + $this->channelContext->getChannel()->willReturn($channel->reveal()); + + $this->paymentMethodRepository->findByChannelAndGatewayConfigNameWithGatewayConfig( + $channel, + 'tpay' + )->willReturn([]); + + $this->createTestSubject()->provide(); + } + + public function test_it_throws_exception_if_there_is_no_gateway_config_that_is_pbl_type(): void + { + $this->expectException(UnableToGetBankListException::class); + $this->expectExceptionMessage('Bank list cannot be retrieved if there is no payment method with PayByLink type configured'); + + $notTpayBasedPaymentMethod = $this->prophesize(PaymentMethodInterface::class); + $notTpayBasedGatewayConfig = $this->prophesize(GatewayConfig::class); + $notTpayBasedGatewayConfigConfig = [ + 'type' => 'visa_mobile', + ]; + $notTpayBasedPaymentMethod->getGatewayConfig()->willReturn($notTpayBasedGatewayConfig); + + $this->availableTpayApiBankListProvider->provide()->willReturn(self::BANK_LIST); + + $channel = $this->prophesize(ChannelInterface::class); + $this->channelContext->getChannel()->willReturn($channel->reveal()); + + $notTpayBasedGatewayConfig->decrypt($this->cypher)->shouldBeCalled(); + $notTpayBasedGatewayConfig->getConfig()->willReturn($notTpayBasedGatewayConfigConfig); + + $this->paymentMethodRepository + ->findByChannelAndGatewayConfigNameWithGatewayConfig($channel, 'tpay') + ->willReturn([ + $notTpayBasedPaymentMethod + ]) + ; + + $this->createTestSubject()->provide(); + } + + public function test_it_returns_all_available_payments_if_only_tpay_payment_method_is_pbl(): void + { + $tpayBasedPaymentMethod = $this->prophesize(PaymentMethodInterface::class); + $tpayPblGatewayConfig = $this->prophesize(GatewayConfig::class); + $tpayPblGatewayConfigConfig = [ + 'type' => 'pay_by_link', + ]; + $tpayBasedPaymentMethod->getGatewayConfig()->willReturn($tpayPblGatewayConfig); + + $tpayPblGatewayConfig->decrypt($this->cypher)->shouldBeCalled(); + $tpayPblGatewayConfig->getConfig()->willReturn($tpayPblGatewayConfigConfig); + + $this->availableTpayApiBankListProvider->provide()->willReturn(self::BANK_LIST); + + $channel = $this->prophesize(ChannelInterface::class); + $this->channelContext->getChannel()->willReturn($channel->reveal()); + + $this->paymentMethodRepository + ->findByChannelAndGatewayConfigNameWithGatewayConfig($channel, 'tpay') + ->willReturn([ + $tpayBasedPaymentMethod->reveal(), + ]) + ; + + $result = $this->createTestSubject()->provide(); + + $this->assertSame(self::BANK_LIST, $result); + } + + public function test_it_returns_valid_payments_according_to_available_tpay_payment_methods(): void + { + $tpayPblPaymentMethod = $this->prophesize(PaymentMethodInterface::class); + $tpayPblGatewayConfig = $this->prophesize(GatewayConfig::class); + $tpayPblGatewayConfigConfig = [ + 'type' => 'pay_by_link', + ]; + $tpayPblPaymentMethod->getGatewayConfig()->willReturn($tpayPblGatewayConfig); + $tpayPblGatewayConfig->decrypt($this->cypher)->shouldBeCalled(); + $tpayPblGatewayConfig->getConfig()->willReturn($tpayPblGatewayConfigConfig); + + $anotherTpayPblPaymentMethod = $this->prophesize(PaymentMethodInterface::class); + $anotherTpayPblGatewayConfig = $this->prophesize(GatewayConfig::class); + $anotherTpayPblGatewayConfigConfig = [ + 'type' => 'visa_mobile', + ]; + $anotherTpayPblPaymentMethod->getGatewayConfig()->willReturn($anotherTpayPblGatewayConfig); + $anotherTpayPblGatewayConfig->decrypt($this->cypher)->shouldBeCalled(); + $anotherTpayPblGatewayConfig->getConfig()->willReturn($anotherTpayPblGatewayConfigConfig); + + $this->availableTpayApiBankListProvider->provide()->willReturn(self::BANK_LIST); + + $channel = $this->prophesize(ChannelInterface::class); + $this->channelContext->getChannel()->willReturn($channel->reveal()); + + $this->paymentMethodRepository + ->findByChannelAndGatewayConfigNameWithGatewayConfig($channel, 'tpay') + ->willReturn([ + $tpayPblPaymentMethod->reveal(), + $anotherTpayPblPaymentMethod->reveal(), + ]) + ; + + $result = $this->createTestSubject()->provide(); + + $expected = self::BANK_LIST; + unset($expected['6']); + + $this->assertSame($expected, $result); + } + + public function test_it_returns_valid_payments_even_if_gateway_config_lacks_type(): void + { + $tpayPblPaymentMethod = $this->prophesize(PaymentMethodInterface::class); + $tpayPblGatewayConfig = $this->prophesize(GatewayConfig::class); + $tpayPblGatewayConfigConfig = [ + 'type' => 'pay_by_link', + ]; + $tpayPblPaymentMethod->getGatewayConfig()->willReturn($tpayPblGatewayConfig); + $tpayPblGatewayConfig->decrypt($this->cypher)->shouldBeCalled(); + $tpayPblGatewayConfig->getConfig()->willReturn($tpayPblGatewayConfigConfig); + + $anotherTpayPblPaymentMethod = $this->prophesize(PaymentMethodInterface::class); + $anotherTpayPblGatewayConfig = $this->prophesize(GatewayConfig::class); + $anotherTpayPblGatewayConfigConfig = [ + 'i_have_no_type' => 'i_should_still_work', + ]; + $anotherTpayPblPaymentMethod->getGatewayConfig()->willReturn($anotherTpayPblGatewayConfig); + $anotherTpayPblGatewayConfig->decrypt($this->cypher)->shouldBeCalled(); + $anotherTpayPblGatewayConfig->getConfig()->willReturn($anotherTpayPblGatewayConfigConfig); + + $this->availableTpayApiBankListProvider->provide()->willReturn(self::BANK_LIST); + + $channel = $this->prophesize(ChannelInterface::class); + $this->channelContext->getChannel()->willReturn($channel->reveal()); + + $this->paymentMethodRepository + ->findByChannelAndGatewayConfigNameWithGatewayConfig($channel, 'tpay') + ->willReturn([ + $tpayPblPaymentMethod->reveal(), + $anotherTpayPblPaymentMethod->reveal(), + ]) + ; + + $result = $this->createTestSubject()->provide(); + + $this->assertSame(self::BANK_LIST, $result); + } + + private function createTestSubject(): ValidTpayChannelListProvider + { + return new ValidTpayChannelListProvider( + $this->availableTpayApiBankListProvider->reveal(), + $this->paymentMethodRepository->reveal(), + $this->channelContext->reveal(), + $this->cypher->reveal(), + ); + } +} diff --git a/tests/Unit/Validator/Constraint/ValidTpayChannelValidatorTest.php b/tests/Unit/Validator/Constraint/ValidTpayChannelValidatorTest.php new file mode 100644 index 00000000..0801493d --- /dev/null +++ b/tests/Unit/Validator/Constraint/ValidTpayChannelValidatorTest.php @@ -0,0 +1,91 @@ + [ + 'id' => '1', + 'name' => 'some bank', + 'available' => true, + 'groups' => [ + ['id' => '1'], + ], + ], + '2' => [ + 'id' => '2', + 'name' => 'card payment', + 'available' => true, + 'groups' => [ + ['id' => '103'], + ], + ], + ]; + + private ValidTpayChannelListProviderInterface|ObjectProphecy $validTpayChannelListProvider; + + protected function setUp(): void + { + $this->validTpayChannelListProvider = $this->prophesize(ValidTpayChannelListProviderInterface::class); + + parent::setUp(); + } + + public function test_it_throws_an_exception_if_a_value_is_not_a_string(): void + { + $this->expectException(InvalidArgumentException::class); + + $this->validator->validate(new \stdClass(), new ValidTpayChannel()); + } + + public function test_it_throws_an_exception_if_a_value_has_an_invalid_type(): void + { + $this->expectException(InvalidArgumentException::class); + + $this->validator->validate(1, new ValidTpayChannel()); + } + + public function test_it_throws_an_exception_if_a_constraint_has_an_invalid_type(): void + { + $this->expectException(InvalidArgumentException::class); + + $this->validator->validate( + '11', + $this->prophesize(Constraint::class)->reveal(), + ); + } + + public function test_it_builds_violation_if_tpay_channel_id_is_not_valid(): void + { + $this->validTpayChannelListProvider->provide()->willReturn(self::EXAMPLE_PROVIDE); + + $this->validator->validate( + '111', + new ValidTpayChannel(), + ); + + $this->buildViolation('commerce_weavers_sylius_tpay.shop.pay.tpay_channel.not_valid') + ->setCode('632f97f3-c302-409b-a321-ec078194302d') + ->assertRaised() + ; + } + + protected function createValidator(): ValidTpayChannelValidator + { + return new ValidTpayChannelValidator($this->validTpayChannelListProvider->reveal()); + } +} diff --git a/translations/validators.en.yaml b/translations/validators.en.yaml index e1ed8b28..61aa4f94 100644 --- a/translations/validators.en.yaml +++ b/translations/validators.en.yaml @@ -17,6 +17,8 @@ commerce_weavers_sylius_tpay: not_json_encoded: 'The Google Pay token must be a JSON object encoded with Base64.' encoded_card_data: required: 'The card data is required.' + tpay_channel: + not_valid: 'Selected payment channel is not valid anymore.' tpay_channel_id: length: 'Bank channel id cannot be empty.' required: 'Bank channel id is required.' diff --git a/translations/validators.pl.yaml b/translations/validators.pl.yaml index 01cd66df..477f5f44 100644 --- a/translations/validators.pl.yaml +++ b/translations/validators.pl.yaml @@ -17,6 +17,8 @@ commerce_weavers_sylius_tpay: not_json_encoded: 'Token Google Pay musi być obiektem JSON zakodowanym przez Base64.' encoded_card_data: required: 'Dane karty są wymagane.' + tpay_channel: + not_valid: 'Wybrany kanał płatności nie jest już dostępny.' tpay_channel_id: length: 'Id kanału banku nie może być puste.' required: 'Id kanału banku jest wymagane.'