diff --git a/config/services/form.php b/config/services/form.php index e7f67ebd..c1831e4a 100644 --- a/config/services/form.php +++ b/config/services/form.php @@ -51,8 +51,10 @@ $services->set(TpayPaymentDetailsType::class) ->args([ service('commerce_weavers_sylius_tpay.form.event_listener.remove_unnecessary_payment_details_fields'), - service('commerce_weavers_sylius_tpay.form.event_listener.add_saved_credit_cards'), service('security.token_storage'), + service('translator'), + service('commerce_weavers_sylius_tpay.repository.credit_card'), + service('sylius.context.cart'), ]) ->tag('form.type') ; diff --git a/src/Entity/CreditCardInterface.php b/src/Entity/CreditCardInterface.php index 9c3f2f4c..9ec2543f 100644 --- a/src/Entity/CreditCardInterface.php +++ b/src/Entity/CreditCardInterface.php @@ -4,6 +4,7 @@ namespace CommerceWeavers\SyliusTpayPlugin\Entity; +use Sylius\Component\Core\Model\ChannelInterface; use Sylius\Component\Core\Model\CustomerInterface; use Sylius\Resource\Model\ResourceInterface; @@ -28,4 +29,8 @@ public function setExpirationDate(?\DateTimeInterface $expirationDate): void; public function getCustomer(): ?CustomerInterface; public function setCustomer(?CustomerInterface $customer): void; + + public function getChannel(): ?ChannelInterface; + + public function setChannel(?ChannelInterface $channel): void; } diff --git a/src/Form/EventListener/AddSavedCreditCardsListener.php b/src/Form/EventListener/AddSavedCreditCardsListener.php index def3769c..3ffa180a 100644 --- a/src/Form/EventListener/AddSavedCreditCardsListener.php +++ b/src/Form/EventListener/AddSavedCreditCardsListener.php @@ -28,6 +28,7 @@ public function __construct( public function __invoke(FormEvent $event): void { $form = $event->getForm(); + /** @var FormInterface $form */ $form = $form->getParent(); diff --git a/src/Form/Type/TpayPaymentDetailsType.php b/src/Form/Type/TpayPaymentDetailsType.php index af50841b..e52a2568 100644 --- a/src/Form/Type/TpayPaymentDetailsType.php +++ b/src/Form/Type/TpayPaymentDetailsType.php @@ -4,25 +4,34 @@ namespace CommerceWeavers\SyliusTpayPlugin\Form\Type; +use CommerceWeavers\SyliusTpayPlugin\Repository\CreditCardRepositoryInterface; use CommerceWeavers\SyliusTpayPlugin\Validator\Constraint\EncodedGooglePayToken; +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 object $addSavedCreditCardsListener, private readonly TokenStorageInterface $tokenStorage, + private readonly TranslatorInterface $translator, + private readonly CreditCardRepositoryInterface $creditCardRepository, + private readonly CartContextInterface $cartContext, ) { } @@ -107,24 +116,60 @@ public function buildForm(FormBuilderInterface $builder, array $options): void [$this->removeUnnecessaryPaymentDetailsFieldsListener, '__invoke'], ); - $builder->addEventListener( - FormEvents::PRE_SET_DATA, - [$this->addSavedCreditCardsListener, '__invoke'], - ); - $token = $this->tokenStorage->getToken(); $user = $token?->getUser(); if ($user instanceof ShopUserInterface) { $builder ->add( - 'saveCreditCardForLater', + 'save_credit_card_for_later', CheckboxType::class, [ 'label' => 'commerce_weavers_sylius_tpay.shop.order_summary.card.save_credit_card_for_later.label', ], ) ; + + /** @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, + ], + ) + ; } } } diff --git a/src/Model/PaymentDetails.php b/src/Model/PaymentDetails.php index 88251add..e404f25b 100644 --- a/src/Model/PaymentDetails.php +++ b/src/Model/PaymentDetails.php @@ -200,8 +200,7 @@ public function setTpayChannelId(?string $tpayChannelId): void public function getType(): string { return match (true) { - null !== $this->getEncodedCardData() => PaymentType::CARD, - null !== $this->getUseSavedCreditCard() => PaymentType::CARD, + null !== $this->getEncodedCardData(), null !== $this->getUseSavedCreditCard() => PaymentType::CARD, null !== $this->getBlikToken() => PaymentType::BLIK, null !== $this->getTpayChannelId() => PaymentType::PAY_BY_LINK, null !== $this->getGooglePayToken() => PaymentType::GOOGLE_PAY, diff --git a/src/Payum/Action/Api/SaveCreditCardAction.php b/src/Payum/Action/Api/SaveCreditCardAction.php index f8d73b46..1b3f4094 100644 --- a/src/Payum/Action/Api/SaveCreditCardAction.php +++ b/src/Payum/Action/Api/SaveCreditCardAction.php @@ -47,6 +47,7 @@ protected function doExecute(Generic $request, PaymentInterface $model, PaymentD Assert::isInstanceOf($customer, CustomerInterface::class); $creditCard->setCustomer($customer); + $creditCard->setChannel($order?->getChannel()); $expiryDate = $request->getTokenExpiryDate(); diff --git a/templates/shop/payment/_card.html.twig b/templates/shop/payment/_card.html.twig index 40172dae..e6d96148 100644 --- a/templates/shop/payment/_card.html.twig +++ b/templates/shop/payment/_card.html.twig @@ -2,12 +2,12 @@
- {% if form.tpay.useSavedCreditCard is defined %} + {% if form.tpay.use_saved_credit_card is defined %}
- {{ form_label(form.tpay.useSavedCreditCard) }} + {{ form_label(form.tpay.use_saved_credit_card) }}
- {{ form_widget(form.tpay.useSavedCreditCard, { attr: {'data-tpay-saved-card': ''}}) }} + {{ form_widget(form.tpay.use_saved_credit_card, { attr: {'data-tpay-saved-card': ''}}) }}
@@ -56,9 +56,9 @@
- {% if form.tpay.saveCreditCardForLater is defined %} + {% if form.tpay.save_credit_card_for_later is defined %}
- {{ form_row(form.tpay.saveCreditCardForLater) }} + {{ form_row(form.tpay.save_credit_card_for_later) }}
{% endif %} diff --git a/tests/Unit/Payum/Action/Api/SaveCreditCardActionTest.php b/tests/Unit/Payum/Action/Api/SaveCreditCardActionTest.php index b9059ffb..43eeb6dd 100644 --- a/tests/Unit/Payum/Action/Api/SaveCreditCardActionTest.php +++ b/tests/Unit/Payum/Action/Api/SaveCreditCardActionTest.php @@ -15,6 +15,7 @@ use PHPUnit\Framework\TestCase; use Prophecy\PhpUnit\ProphecyTrait; use Prophecy\Prophecy\ObjectProphecy; +use Sylius\Component\Core\Model\ChannelInterface; use Sylius\Component\Core\Model\CustomerInterface; use Sylius\Component\Core\Model\OrderInterface; use Sylius\Component\Core\Model\PaymentInterface; @@ -34,6 +35,8 @@ final class SaveCreditCardActionTest extends TestCase private CustomerInterface|ObjectProphecy $customer; + private ChannelInterface|ObjectProphecy $channel; + private TpayApi|ObjectProphecy $api; private BasicPaymentFactoryInterface|ObjectProphecy $factory; @@ -53,6 +56,9 @@ protected function setUp(): void $this->customer = $this->prophesize(CustomerInterface::class); $order->getCustomer()->willReturn($this->customer->reveal()); + $this->channel = $this->prophesize(ChannelInterface::class); + $order->getChannel()->willReturn($this->channel->reveal()); + $this->model = $this->prophesize(PaymentInterface::class); $this->model->getOrder()->willReturn($order->reveal()); $this->model->getDetails()->willReturn([]); @@ -92,6 +98,7 @@ public function test_it_saves_returned_credit_card(): void $creditCard->setToken('card_token')->shouldBeCalled(); $creditCard->setExpirationDate(new \DateTimeImmutable('01-11-2028'))->shouldBeCalled(); $creditCard->setCustomer($this->customer)->shouldBeCalled(); + $creditCard->setChannel($this->channel)->shouldBeCalled(); $this->repository->add($creditCard->reveal())->shouldBeCalled();