From 36109263fd540b38310186a97e059edd31e324c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Chru=C5=9Bciel?= Date: Thu, 31 Oct 2024 23:38:41 +0100 Subject: [PATCH] Extract credit cards to separate form type and code cleanup --- config/services/form.php | 9 ++ config/services/payum/mapper.php | 18 --- src/Api/Command/PayByCardHandler.php | 2 +- .../AddSavedCreditCardsListener.php | 3 + src/Form/Type/TpayCreditCardChoiceType.php | 91 +++++++++++ src/Form/Type/TpayPaymentDetailsType.php | 46 +----- .../shop/paying_for_orders_by_card/cart.yml | 1 - .../Type/TpayCreditCardChoiceTypeTest.php | 150 ++++++++++++++++++ 8 files changed, 255 insertions(+), 65 deletions(-) create mode 100644 src/Form/Type/TpayCreditCardChoiceType.php create mode 100644 tests/Unit/Form/Type/TpayCreditCardChoiceTypeTest.php diff --git a/config/services/form.php b/config/services/form.php index 4b33b162..23cd335e 100644 --- a/config/services/form.php +++ b/config/services/form.php @@ -12,6 +12,7 @@ use CommerceWeavers\SyliusTpayPlugin\Form\Extension\CompleteTypeExtension; use CommerceWeavers\SyliusTpayPlugin\Form\Extension\PaymentTypeExtension; use CommerceWeavers\SyliusTpayPlugin\Form\Type\TpayCardType; +use CommerceWeavers\SyliusTpayPlugin\Form\Type\TpayCreditCardChoiceType; use CommerceWeavers\SyliusTpayPlugin\Form\Type\TpayGatewayConfigurationType; use CommerceWeavers\SyliusTpayPlugin\Form\Type\TpayPaymentDetailsType; use CommerceWeavers\SyliusTpayPlugin\Payum\Factory\TpayGatewayFactory; @@ -50,6 +51,13 @@ ->args([ service('commerce_weavers_sylius_tpay.form.event_listener.remove_unnecessary_payment_details_fields'), service('security.token_storage'), + ]) + ->tag('form.type') + ; + + $services->set(TpayCreditCardChoiceType::class) + ->args([ + service('security.token_storage'), service('translator'), service('commerce_weavers_sylius_tpay.repository.credit_card'), service('sylius.context.cart'), @@ -77,6 +85,7 @@ $services ->set('commerce_weavers_sylius_tpay.form.event_listener.add_saved_credit_cards', AddSavedCreditCardsListener::class) + ->deprecate('commerce-weavers/sylius-tpay-plugin', '2.0', 'The "%service_id%" service is deprecated and will be removed in the version 2.0 as it is considered daed code.') ->args([ service('security.token_storage'), service('translator'), diff --git a/config/services/payum/mapper.php b/config/services/payum/mapper.php index 8bd3ec26..e1cee2d4 100644 --- a/config/services/payum/mapper.php +++ b/config/services/payum/mapper.php @@ -4,25 +4,7 @@ namespace Symfony\Component\DependencyInjection\Loader\Configurator; -use CommerceWeavers\SyliusTpayPlugin\Payum\Action\Api\CreateApplePayTransactionAction; -use CommerceWeavers\SyliusTpayPlugin\Payum\Action\Api\CreateBlikLevelZeroTransactionAction; -use CommerceWeavers\SyliusTpayPlugin\Payum\Action\Api\CreateCardTransactionAction; -use CommerceWeavers\SyliusTpayPlugin\Payum\Action\Api\CreateGooglePayTransactionAction; -use CommerceWeavers\SyliusTpayPlugin\Payum\Action\Api\CreatePayByLinkTransactionAction; -use CommerceWeavers\SyliusTpayPlugin\Payum\Action\Api\CreateRedirectBasedTransactionAction; -use CommerceWeavers\SyliusTpayPlugin\Payum\Action\Api\CreateVisaMobileTransactionAction; -use CommerceWeavers\SyliusTpayPlugin\Payum\Action\Api\GetTpayTransactionsChannelsAction; -use CommerceWeavers\SyliusTpayPlugin\Payum\Action\Api\InitializeApplePayPaymentAction; -use CommerceWeavers\SyliusTpayPlugin\Payum\Action\Api\NotifyAction; -use CommerceWeavers\SyliusTpayPlugin\Payum\Action\Api\PayWithCardAction; -use CommerceWeavers\SyliusTpayPlugin\Payum\Action\Api\SaveCreditCardAction; -use CommerceWeavers\SyliusTpayPlugin\Payum\Action\CaptureAction; -use CommerceWeavers\SyliusTpayPlugin\Payum\Action\GetStatusAction; -use CommerceWeavers\SyliusTpayPlugin\Payum\Action\RefundAction; -use CommerceWeavers\SyliusTpayPlugin\Payum\Action\ResolveNextRouteAction; -use CommerceWeavers\SyliusTpayPlugin\Payum\Factory\TpayGatewayFactory; use CommerceWeavers\SyliusTpayPlugin\Payum\Mapper\PayWithCardActionPayloadMapper; -use CommerceWeavers\SyliusTpayPlugin\Payum\Mapper\PayWithCardActionPayloadMapperInterface; return static function(ContainerConfigurator $container): void { $services = $container->services(); diff --git a/src/Api/Command/PayByCardHandler.php b/src/Api/Command/PayByCardHandler.php index ccea37c7..67088548 100644 --- a/src/Api/Command/PayByCardHandler.php +++ b/src/Api/Command/PayByCardHandler.php @@ -21,7 +21,7 @@ public function __invoke(PayByCard $command): PayResult return $this->createResultFrom($payment); } - private function setTransactionData(PaymentInterface $payment, string $encodedCardData, bool $saveCard = false): void + private function setTransactionData(PaymentInterface $payment, string $encodedCardData, bool $saveCard): void { $paymentDetails = PaymentDetails::fromArray($payment->getDetails()); $paymentDetails->setEncodedCardData($encodedCardData); diff --git a/src/Form/EventListener/AddSavedCreditCardsListener.php b/src/Form/EventListener/AddSavedCreditCardsListener.php index 3ffa180a..fc65248f 100644 --- a/src/Form/EventListener/AddSavedCreditCardsListener.php +++ b/src/Form/EventListener/AddSavedCreditCardsListener.php @@ -16,6 +16,9 @@ use Symfony\Component\Translation\TranslatableMessage; use Symfony\Contracts\Translation\TranslatorInterface; +/** + * @deprecated Deprecated since CommerceWeavers/TpayPlugin 1.0.1, will be removed in 2.0.0. Use \CommerceWeavers\SyliusTpayPlugin\Form\Type\TpayCreditCardChoiceType instead. + */ final class AddSavedCreditCardsListener { public function __construct( diff --git a/src/Form/Type/TpayCreditCardChoiceType.php b/src/Form/Type/TpayCreditCardChoiceType.php new file mode 100644 index 00000000..5adf0871 --- /dev/null +++ b/src/Form/Type/TpayCreditCardChoiceType.php @@ -0,0 +1,91 @@ +tokenStorage->getToken(); + + $user = $token?->getUser(); + + if (!$user instanceof ShopUserInterface) { + return; + } + + /** @var OrderInterface $order */ + $order = $this->cartContext->getCart(); + $channel = $order->getChannel(); + /** @var CustomerInterface $customer */ + $customer = $user->getCustomer(); + + $creditCards = $this->creditCardRepository->findByCustomerAndChannel($customer, $channel); + + $choices = []; + + if (count($creditCards) !== 0) { + $choices = $this->mapChoices($creditCards); + } + + $resolver->setDefaults([ + 'choices' => $choices, + 'required' => false, + 'label' => 'commerce_weavers_sylius_tpay.shop.order_summary.card.use_saved_credit_card.label', + 'placeholder' => new TranslatableMessage('commerce_weavers_sylius_tpay.shop.credit_card.use_new_card'), + ]); + } + + public function getParent(): string + { + return ChoiceType::class; + } + + public function getBlockPrefix(): string + { + return 'cw_tpay_credit_card_choice'; + } + + private function mapChoices(array $creditCards): array + { + $choices = []; + + foreach ($creditCards as $creditCard) { + $stringifiedCard = $this->translator->trans( + 'commerce_weavers_sylius_tpay.shop.credit_card.card_selection_one_liner', + [ + '%brand%' => $creditCard->getBrand(), + '%tail%' => $creditCard->getTail(), + '%expires%' => $creditCard->getExpirationDate()->format('m-Y'), + ], + 'messages', + ); + + $choices[$stringifiedCard] = $creditCard->getId(); + } + + return $choices; + } +} diff --git a/src/Form/Type/TpayPaymentDetailsType.php b/src/Form/Type/TpayPaymentDetailsType.php index 6bb28935..c9f0ba0a 100644 --- a/src/Form/Type/TpayPaymentDetailsType.php +++ b/src/Form/Type/TpayPaymentDetailsType.php @@ -4,35 +4,25 @@ namespace CommerceWeavers\SyliusTpayPlugin\Form\Type; -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; -use Sylius\Component\Order\Context\CartContextInterface; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\Extension\Core\Type\CheckboxType; -use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\Extension\Core\Type\HiddenType; use Symfony\Component\Form\Extension\Core\Type\TelType; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormEvents; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; -use Symfony\Component\Translation\TranslatableMessage; use Symfony\Component\Validator\Constraints\Length; use Symfony\Component\Validator\Constraints\Regex; -use Symfony\Contracts\Translation\TranslatorInterface; final class TpayPaymentDetailsType extends AbstractType { public function __construct( private readonly object $removeUnnecessaryPaymentDetailsFieldsListener, private readonly TokenStorageInterface $tokenStorage, - private readonly TranslatorInterface $translator, - private readonly CreditCardRepositoryInterface $creditCardRepository, - private readonly CartContextInterface $cartContext, ) { } @@ -135,44 +125,10 @@ public function buildForm(FormBuilderInterface $builder, array $options): void ) ; - /** @var OrderInterface $order */ - $order = $this->cartContext->getCart(); - $channel = $order->getChannel(); - /** @var CustomerInterface $customer */ - $customer = $user->getCustomer(); - - $creditCards = $this->creditCardRepository->findByCustomerAndChannel($customer, $channel); - - if (count($creditCards) === 0) { - return; - } - - $choices = []; - - foreach ($creditCards as $creditCard) { - $stringifiedCard = $this->translator->trans( - 'commerce_weavers_sylius_tpay.shop.credit_card.card_selection_one_liner', - [ - '%brand%' => $creditCard->getBrand(), - '%tail%' => $creditCard->getTail(), - '%expires%' => $creditCard->getExpirationDate()->format('m-Y'), - ], - 'messages', - ); - - $choices[$stringifiedCard] = $creditCard->getId(); - } - $builder ->add( 'use_saved_credit_card', - ChoiceType::class, - [ - 'label' => 'commerce_weavers_sylius_tpay.shop.order_summary.card.use_saved_credit_card.label', - 'placeholder' => new TranslatableMessage('commerce_weavers_sylius_tpay.shop.credit_card.use_new_card'), - 'required' => false, - 'choices' => $choices, - ], + TpayCreditCardChoiceType::class, ) ; } diff --git a/tests/Api/DataFixtures/shop/paying_for_orders_by_card/cart.yml b/tests/Api/DataFixtures/shop/paying_for_orders_by_card/cart.yml index 33ca5ae2..65ab0b75 100644 --- a/tests/Api/DataFixtures/shop/paying_for_orders_by_card/cart.yml +++ b/tests/Api/DataFixtures/shop/paying_for_orders_by_card/cart.yml @@ -14,7 +14,6 @@ Sylius\Component\Core\Model\ShopUser: username: 'sylius@example.com' usernameCanonical: 'sylius@example.com' - Sylius\Component\Core\Model\Product: product_mug: code: 'MUG' diff --git a/tests/Unit/Form/Type/TpayCreditCardChoiceTypeTest.php b/tests/Unit/Form/Type/TpayCreditCardChoiceTypeTest.php new file mode 100644 index 00000000..f2403644 --- /dev/null +++ b/tests/Unit/Form/Type/TpayCreditCardChoiceTypeTest.php @@ -0,0 +1,150 @@ +tokenStorage = $this->prophesize(TokenStorageInterface::class); + $this->translator = $this->prophesize(TranslatorInterface::class); + $this->creditCardRepository = $this->prophesize(CreditCardRepositoryInterface::class); + $this->cartContext = $this->prophesize(CartContextInterface::class); + + parent::setUp(); + } + + protected function getExtensions(): array + { + $type = new TpayCreditCardChoiceType( + $this->tokenStorage->reveal(), + $this->translator->reveal(), + $this->creditCardRepository->reveal(), + $this->cartContext->reveal() + ); + + return [ + new PreloadedExtension([$type], []), + ]; + } + + public function test_it_configures_options_that_map_customer_credit_cards_to_description_id_map(): void + { + $token = $this->prophesize(TokenInterface::class); + $this->tokenStorage->getToken()->willReturn($token->reveal()); + + /** @var ShopUserInterface $user */ + $user = $this->prophesize(ShopUserInterface::class); + $token->getUser()->willReturn($user->reveal()); + + /** @var OrderInterface $order */ + $order = $this->prophesize(OrderInterface::class); + $this->cartContext->getCart()->willReturn($order->reveal()); + + /** @var ChannelInterface $channel */ + $channel = $this->prophesize(ChannelInterface::class); + $order->getChannel()->willReturn($channel->reveal()); + + /** @var CustomerInterface $customer */ + $customer = $this->prophesize(CustomerInterface::class); + $user->getCustomer()->willReturn($customer->reveal()); + + /** @var CreditCardInterface $creditCard */ + $creditCard = $this->prophesize(CreditCardInterface::class); + $creditCard->getId()->willReturn('f47ac10b-58cc-4372-a567-0e02b2c3d479'); + $creditCard->getTail()->willReturn('1234'); + $creditCard->getBrand()->willReturn('visa'); + $creditCard->getExpirationDate()->willReturn(new \DateTimeImmutable('2022-12-01')); + + $this->translator->trans( + 'commerce_weavers_sylius_tpay.shop.credit_card.card_selection_one_liner', + [ + '%brand%' => 'visa', + '%tail%' => '1234', + '%expires%' => '12-2022', + ], + 'messages' + )->willReturn('visa ending in 1234 expiring 12-2022'); + + $this->creditCardRepository->findByCustomerAndChannel($customer->reveal(), $channel->reveal())->willReturn([$creditCard->reveal()]); + + $resolver = new OptionsResolver(); + + $type = new TpayCreditCardChoiceType( + $this->tokenStorage->reveal(), + $this->translator->reveal(), + $this->creditCardRepository->reveal(), + $this->cartContext->reveal() + ); + $type->configureOptions($resolver); + + $options = $resolver->resolve(); + + $this->assertArrayHasKey('choices', $options); + $this->assertSame(['visa ending in 1234 expiring 12-2022' => 'f47ac10b-58cc-4372-a567-0e02b2c3d479'], $options['choices']); + } + + public function test_it_returns_empty_choices_when_no_credit_cards_are_found() + { + $token = $this->prophesize(TokenInterface::class); + $this->tokenStorage->getToken()->willReturn($token->reveal()); + + /** @var ShopUserInterface $user */ + $user = $this->prophesize(ShopUserInterface::class); + $token->getUser()->willReturn($user->reveal()); + + /** @var OrderInterface $order */ + $order = $this->prophesize(OrderInterface::class); + $this->cartContext->getCart()->willReturn($order->reveal()); + + /** @var ChannelInterface $channel */ + $channel = $this->prophesize(ChannelInterface::class); + $order->getChannel()->willReturn($channel->reveal()); + + /** @var CustomerInterface $customer */ + $customer = $this->prophesize(CustomerInterface::class); + $user->getCustomer()->willReturn($customer->reveal()); + + $this->creditCardRepository->findByCustomerAndChannel($customer->reveal(), $channel->reveal())->willReturn([]); + + $resolver = new OptionsResolver(); + $type = new TpayCreditCardChoiceType( + $this->tokenStorage->reveal(), + $this->translator->reveal(), + $this->creditCardRepository->reveal(), + $this->cartContext->reveal() + ); + $type->configureOptions($resolver); + + $options = $resolver->resolve(); + + $this->assertArrayHasKey('choices', $options); + $this->assertEmpty($options['choices']); + } +}