diff --git a/config/config/sylius_fixtures.php b/config/config/sylius_fixtures.php index 6764e2c7..8aa6e48e 100644 --- a/config/config/sylius_fixtures.php +++ b/config/config/sylius_fixtures.php @@ -4,6 +4,7 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator; +use CommerceWeavers\SyliusTpayPlugin\Tpay\PayGroup; use CommerceWeavers\SyliusTpayPlugin\Tpay\PaymentType; use Symfony\Config\SyliusFixturesConfig; 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 08cfe426..cbad6d2e 100644 --- a/config/services/form.php +++ b/config/services/form.php @@ -5,7 +5,6 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator; use CommerceWeavers\SyliusTpayPlugin\Form\DataTransformer\CardTypeDataTransformer; -use CommerceWeavers\SyliusTpayPlugin\Form\DataTransformer\VisaMobilePhoneDataTransformer; use CommerceWeavers\SyliusTpayPlugin\Form\EventListener\DecryptGatewayConfigListener; use CommerceWeavers\SyliusTpayPlugin\Form\EventListener\EncryptGatewayConfigListener; use CommerceWeavers\SyliusTpayPlugin\Form\EventListener\PreventSavingEmptyPasswordFieldsListener; @@ -17,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..02537818 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,27 @@ ->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.gateway_config'), + 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/src/ContextProvider/BankListContextProvider.php b/src/ContextProvider/BankListContextProvider.php index d7d195a4..c0af5cef 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,12 +12,14 @@ final class BankListContextProvider implements ContextProviderInterface { public function __construct( - private readonly TpayApiBankListProviderInterface $bankListProvider, + private readonly ValidTpayChannelListProviderInterface $validatedTpayApiBankListProvider, ) { } public function provide(array $templateContext, TemplateBlock $templateBlock): array { + // TODO this is runned few many times when on checkout/complete/choice-item + // propably there it should not be runned totaly but on summary step only if (isset($templateContext['order'])) { /** @var OrderInterface $order */ $order = $templateContext['order']; @@ -27,7 +29,7 @@ public function provide(array $templateContext, TemplateBlock $templateBlock): a } } - $templateContext['banks'] = $this->bankListProvider->provide(); + $templateContext['banks'] = $this->validatedTpayApiBankListProvider->provide(); return $templateContext; } diff --git a/src/Form/Type/TpayPaymentDetailsType.php b/src/Form/Type/TpayPaymentDetailsType.php index 885c38b3..43b765dd 100644 --- a/src/Form/Type/TpayPaymentDetailsType.php +++ b/src/Form/Type/TpayPaymentDetailsType.php @@ -5,6 +5,7 @@ namespace CommerceWeavers\SyliusTpayPlugin\Form\Type; use CommerceWeavers\SyliusTpayPlugin\Validator\Constraint\EncodedGooglePayToken; +use CommerceWeavers\SyliusTpayPlugin\Validator\Constraint\ValidTpayApiChannel; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\HiddenType; use Symfony\Component\Form\Extension\Core\Type\TelType; @@ -70,9 +71,14 @@ public function buildForm(FormBuilderInterface $builder, array $options): void ) ->add( 'tpay_channel_id', + HiddenType::class, [ 'property_path' => '[tpay_channel_id]', + 'validation_groups' => ['sylius_checkout_complete'], + 'constraints' => [ + new ValidTpayApiChannel(groups: ['sylius_checkout_complete']), + ], ], ) ->add( diff --git a/src/Tpay/Provider/TpayApiBankListProvider.php b/src/Tpay/Provider/AvailableTpayChannelListProvider.php similarity index 72% rename from src/Tpay/Provider/TpayApiBankListProvider.php rename to src/Tpay/Provider/AvailableTpayChannelListProvider.php index 4ed0111b..4a482f6b 100644 --- a/src/Tpay/Provider/TpayApiBankListProvider.php +++ b/src/Tpay/Provider/AvailableTpayChannelListProvider.php @@ -6,7 +6,7 @@ use CommerceWeavers\SyliusTpayPlugin\Tpay\Resolver\TpayTransactionChannelResolverInterface; -final class TpayApiBankListProvider implements TpayApiBankListProviderInterface +final class AvailableTpayChannelListProvider implements AvailableTpayChannelListProviderInterface { public function __construct( private readonly TpayTransactionChannelResolverInterface $channelResolver, @@ -30,11 +30,7 @@ public function provide(): array * } */ return array_filter($result, static function (array $channel) { - return - ($channel['instantRedirection'] ?? false) === true && - ($channel['onlinePayment'] ?? false) === true && - ($channel['available'] ?? false) === true - ; + return ($channel['available'] ?? false) === true; }); } } diff --git a/src/Tpay/Provider/TpayApiBankListProviderInterface.php b/src/Tpay/Provider/AvailableTpayChannelListProviderInterface.php similarity index 89% rename from src/Tpay/Provider/TpayApiBankListProviderInterface.php rename to src/Tpay/Provider/AvailableTpayChannelListProviderInterface.php index b8b1fa94..f81a8499 100644 --- a/src/Tpay/Provider/TpayApiBankListProviderInterface.php +++ b/src/Tpay/Provider/AvailableTpayChannelListProviderInterface.php @@ -4,7 +4,7 @@ namespace CommerceWeavers\SyliusTpayPlugin\Tpay\Provider; -interface TpayApiBankListProviderInterface +interface AvailableTpayChannelListProviderInterface { /** @return array{ * id: string, diff --git a/src/Tpay/Provider/ValidTpayChannelListProvider.php b/src/Tpay/Provider/ValidTpayChannelListProvider.php new file mode 100644 index 00000000..46d593cf --- /dev/null +++ b/src/Tpay/Provider/ValidTpayChannelListProvider.php @@ -0,0 +1,78 @@ +availableTpayApiBankListProvider->provide(); + + /** @var GatewayConfigInterface[] $tpayGatewayConfigs */ + $tpayGatewayConfigs = $this->gatewayRepository->findBy(['gatewayName' => 'tpay']); + + Assert::notEmpty($tpayGatewayConfigs, 'There is no gateway config of Tpay type available'); + + if (count($tpayGatewayConfigs) === 1 && + $tpayGatewayConfigs[0]->getConfig()['type'] === PaymentType::PAY_BY_LINK + ) { + return $availableChannels; + } + + $paymentMethodsToRemoveByGroupId = []; + $hasPblPaymentAvailable = false; + foreach ($tpayGatewayConfigs as $tpayGatewayConfig) { + // cached doctrine values are encrypted hence need for decrypt + $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, + default => null, + }; + } + + if (!$hasPblPaymentAvailable) { + throw new UnableToGetBankListException( + 'Bank list cannot be retrieved if there is no payment method with PayByLink type configured' + ); + } + + foreach ($availableChannels as $availableChannel) { + $channelId = (int) $availableChannel['id']; + if (in_array($channelId, $paymentMethodsToRemoveByGroupId, true)) { + unset($availableChannels[$channelId]); + } + } + + return $availableChannels; + } +} 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/Unit/ContextProvider/BankListContextProviderTest.php b/tests/Unit/ContextProvider/BankListContextProviderTest.php index 0c3e0a30..df18e6a8 100644 --- a/tests/Unit/ContextProvider/BankListContextProviderTest.php +++ b/tests/Unit/ContextProvider/BankListContextProviderTest.php @@ -5,7 +5,7 @@ namespace Tests\CommerceWeavers\SyliusTpayPlugin\Unit\ContextProvider; use CommerceWeavers\SyliusTpayPlugin\ContextProvider\BankListContextProvider; -use CommerceWeavers\SyliusTpayPlugin\Tpay\Provider\TpayApiBankListProviderInterface; +use CommerceWeavers\SyliusTpayPlugin\Tpay\Provider\AvailableTpayChannelListProviderInterface; use PHPUnit\Framework\TestCase; use Prophecy\PhpUnit\ProphecyTrait; use Prophecy\Prophecy\ObjectProphecy; @@ -17,10 +17,10 @@ class BankListContextProviderTest extends TestCase { use ProphecyTrait; - private TpayApiBankListProviderInterface|ObjectProphecy $bankListProvider; + private AvailableTpayChannelListProviderInterface|ObjectProphecy $bankListProvider; protected function setUp(): void { - $this->bankListProvider = $this->prophesize(TpayApiBankListProviderInterface::class); + $this->bankListProvider = $this->prophesize(AvailableTpayChannelListProviderInterface::class); } public function test_it_does_not_support_some_template_event_and_pay_by_link_template_name(): void 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/ValidatedTpayApiBankListProviderTest.php b/tests/Unit/Tpay/Provider/ValidatedTpayApiBankListProviderTest.php new file mode 100644 index 00000000..bd858c0b --- /dev/null +++ b/tests/Unit/Tpay/Provider/ValidatedTpayApiBankListProviderTest.php @@ -0,0 +1,144 @@ + [ + 'id' => '1', + 'name' => 'some bank', + 'available' => true, + ], + '103' => [ + 'id' => '103', + 'name' => 'card payment', + 'available' => true, + ], + '150' => [ + 'id' => '150', + 'name' => 'BLIK', + 'available' => true, + ], + '170' => [ + 'id' => '170', + 'name' => 'Apple Pay', + 'available' => true, + ], + '166' => [ + 'id' => '166', + 'name' => 'Google Pay', + 'available' => true, + ], + '171' => [ + 'id' => '171', + 'name' => 'Visa mobile', + 'available' => true, + ], + ]; + + private AvailableTpayChannelListProviderInterface|ObjectProphecy $availableTpayApiBankListProvider; + + private RepositoryInterface|ObjectProphecy $paymentMethodRepository; + + protected function setUp(): void + { + $this->availableTpayApiBankListProvider = $this->prophesize(AvailableTpayChannelListProviderInterface::class); + $this->paymentMethodRepository = $this->prophesize(RepositoryInterface::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 of Tpay type available'); + + $this->availableTpayApiBankListProvider->provide()->willReturn(self::BANK_LIST); + + $this->paymentMethodRepository + ->findBy(['gatewayConfig.gateway_name' => 'tpay']) + ->willReturn(new ArrayCollection([])) + ; + + $this->createTestSubject()->provide(); + } + + public function test_it_returns_all_available_payments_if_only_tpay_payment_method_is_pbl(): void + { + $tpayPblPaymentMethod = $this->prophesize(PaymentMethodInterface::class); + $tpayPblGatewayConfig = $this->prophesize(GatewayConfigInterface::class); + $tpayPblGatewayConfigConfig = [ + 'type' => 'pay_by_link', + ]; + $tpayPblGatewayConfig->getConfig()->willReturn($tpayPblGatewayConfigConfig); + $tpayPblPaymentMethod->getGatewayConfig()->willReturn($tpayPblGatewayConfig->reveal()); + + $this->availableTpayApiBankListProvider->provide()->willReturn(self::BANK_LIST); + + $this->paymentMethodRepository + ->findBy(['gatewayConfig.gateway_name' => 'tpay']) + ->willReturn(new ArrayCollection([$tpayPblPaymentMethod->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(GatewayConfigInterface::class); + $tpayPblGatewayConfigConfig = [ + 'type' => 'pay_by_link', + ]; + $tpayPblGatewayConfig->getConfig()->willReturn($tpayPblGatewayConfigConfig); + $tpayPblPaymentMethod->getGatewayConfig()->willReturn($tpayPblGatewayConfig->reveal()); + + $anotherTpayPblPaymentMethod = $this->prophesize(PaymentMethodInterface::class); + $anotherTpayPblGatewayConfig = $this->prophesize(GatewayConfigInterface::class); + $anotherTpayPblGatewayConfigConfig = [ + 'type' => 'visa_mobile', + ]; + $anotherTpayPblGatewayConfig->getConfig()->willReturn($anotherTpayPblGatewayConfigConfig); + $anotherTpayPblPaymentMethod->getGatewayConfig()->willReturn($anotherTpayPblGatewayConfig->reveal()); + + $this->availableTpayApiBankListProvider->provide()->willReturn(self::BANK_LIST); + + $this->paymentMethodRepository + ->findBy(['gatewayConfig.gateway_name' => 'tpay']) + ->willReturn(new ArrayCollection([ + $tpayPblPaymentMethod->reveal(), + $anotherTpayPblPaymentMethod->reveal(), + ])) + ; + + $result = $this->createTestSubject()->provide(); + + $expected = self::BANK_LIST; + unset($expected['171']); + + $this->assertSame($expected, $result); + } + + private function createTestSubject(): ValidTpayChannelListProvider + { + return new ValidTpayChannelListProvider( + $this->availableTpayApiBankListProvider->reveal(), + $this->paymentMethodRepository->reveal(), + ); + } +}