From 037b24be574780d97b2d8a99320fecaf2093a096 Mon Sep 17 00:00:00 2001 From: Matt75 <5262628+Matt75@users.noreply.github.com> Date: Mon, 15 Jan 2024 09:58:43 +0100 Subject: [PATCH 01/60] Replace client token by user id token --- controllers/front/token.php | 64 +++++++++++++++++++++++++++++++++++ src/Database/TableManager.php | 14 ++++++++ 2 files changed, 78 insertions(+) create mode 100644 controllers/front/token.php diff --git a/controllers/front/token.php b/controllers/front/token.php new file mode 100644 index 000000000..c6e69beb6 --- /dev/null +++ b/controllers/front/token.php @@ -0,0 +1,64 @@ + + * @copyright Since 2007 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0 + */ + +use PrestaShop\Module\PrestashopCheckout\CommandBus\CommandBusInterface; +use PrestaShop\Module\PrestashopCheckout\Controller\AbstractFrontController; +use PrestaShop\Module\PrestashopCheckout\PayPal\OAuth\Query\GetPayPalGetUserIdTokenQuery; +use PrestaShop\Module\PrestashopCheckout\PayPal\OAuth\Query\GetPayPalGetUserIdTokenQueryResult; +use Psr\Log\LoggerInterface; + +/** + * This controller receive ajax call to retrieve a PayPal User Id Token + */ +class Ps_CheckoutTokenModuleFrontController extends AbstractFrontController +{ + /** + * @see FrontController::postProcess() + */ + public function postProcess() + { + try { + /** @var CommandBusInterface $commandBus */ + $commandBus = $this->module->getService('ps_checkout.bus.command'); + /** @var GetPayPalGetUserIdTokenQueryResult $getPayPalGetUserIdTokenQueryResult */ + $getPayPalGetUserIdTokenQueryResult = $commandBus->handle(new GetPayPalGetUserIdTokenQuery($this->getCustomerId())); + + $this->exitWithResponse([ + 'status' => true, + 'httpCode' => 200, + 'body' => [ + 'token' => $getPayPalGetUserIdTokenQueryResult->getUserIdToken(), + ], + ]); + } catch (Exception $exception) { + /** @var LoggerInterface $logger */ + $logger = $this->module->getService('ps_checkout.logger'); + $logger->error( + sprintf( + 'TokenController exception %s : %s', + $exception->getCode(), + $exception->getMessage() + ) + ); + + $this->exitWithExceptionMessage($exception); + } + } +} diff --git a/src/Database/TableManager.php b/src/Database/TableManager.php index 966b5a28f..8c777da4f 100644 --- a/src/Database/TableManager.php +++ b/src/Database/TableManager.php @@ -147,6 +147,20 @@ public function createTable() `items` text, PRIMARY KEY (`reference_id`, `id_order`) ) ENGINE=' . _MYSQL_ENGINE_ . ' DEFAULT CHARSET=UTF8; + ') && $this->db->execute(' + CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'pscheckout_customer` ( + `id_customer` int unsigned NOT NULL, + `paypal_customer_id` varchar(50) NOT NULL, + PRIMARY KEY (`id_customer`, `paypal_customer_id`) + ) ENGINE=' . _MYSQL_ENGINE_ . ' DEFAULT CHARSET=UTF8; + ') && $this->db->execute(' + CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'pscheckout_payment_token` ( + `id` varchar(50) NOT NULL, + `paypal_customer_id` varchar(50) NOT NULL, + `payment_source` varchar(50) NOT NULL, + `data` text NOT NULL, + PRIMARY KEY (`id_customer`, `paypal_customer_id`) + ) ENGINE=' . _MYSQL_ENGINE_ . ' DEFAULT CHARSET=UTF8; '); $this->checkTable(); From f5ef9adbb0f386c9f3104364fb512ec67f5eb3f4 Mon Sep 17 00:00:00 2001 From: Matt75 <5262628+Matt75@users.noreply.github.com> Date: Mon, 15 Jan 2024 10:06:06 +0100 Subject: [PATCH 02/60] Implement payment method token --- .../Query/GetCustomerPaymentMethodTokensQueryHandler.php | 2 +- .../Query/GetCustomerPaymentMethodTokensQueryResult.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/PaymentMethodToken/Query/GetCustomerPaymentMethodTokensQueryHandler.php b/src/PaymentMethodToken/Query/GetCustomerPaymentMethodTokensQueryHandler.php index f839d6c69..9d90e9129 100644 --- a/src/PaymentMethodToken/Query/GetCustomerPaymentMethodTokensQueryHandler.php +++ b/src/PaymentMethodToken/Query/GetCustomerPaymentMethodTokensQueryHandler.php @@ -55,7 +55,7 @@ public function __construct(PayPalCustomerRepository $customerRepository, Paymen */ public function handle(GetCustomerPaymentMethodTokensQuery $query) { - $customerIdPayPal = $query->getCustomerId()->getValue() ? $this->customerRepository->findPayPalCustomerIdByCustomerId($query->getCustomerId()) : null; + $customerIdPayPal = $query->getCustomerId() ? $this->customerRepository->findPayPalCustomerIdByCustomerId($query->getCustomerId()) : null; $paymentTokens = $this->paymentMethodTokenRepository->findByCustomerId($customerIdPayPal, $query->getPageSize(), $query->getPageNumber()); if ($query->isTotalCountRequired()) { diff --git a/src/PaymentMethodToken/Query/GetCustomerPaymentMethodTokensQueryResult.php b/src/PaymentMethodToken/Query/GetCustomerPaymentMethodTokensQueryResult.php index eb7f871c4..6a6b7b7d3 100644 --- a/src/PaymentMethodToken/Query/GetCustomerPaymentMethodTokensQueryResult.php +++ b/src/PaymentMethodToken/Query/GetCustomerPaymentMethodTokensQueryResult.php @@ -48,7 +48,7 @@ class GetCustomerPaymentMethodTokensQueryResult * @param array $paymentTokens * @param CustomerId $customerId * @param int $totalItems - * @param int|null $totalPages + * @param int $totalPages */ public function __construct(array $paymentTokens, CustomerId $customerId, $totalItems, $totalPages) { From 3680039b243cf136bdbbde0f951e1ba97fd113e7 Mon Sep 17 00:00:00 2001 From: Bastien Tafforeau Date: Wed, 6 Mar 2024 16:06:39 +0100 Subject: [PATCH 03/60] Adding payment method token events for each webhook --- src/Dispatcher/OrderDispatcher.php | 15 ++++ .../Command/SavePaymentMethodTokenCommand.php | 85 +++++++++++++++++++ .../SavePaymentMethodTokenCommandHandler.php | 49 +++++++++++ .../Event/PaymentMethodTokenCreatedEvent.php | 26 ++++++ .../Event/PaymentMethodTokenDeletedEvent.php | 26 ++++++ ...ymentMethodTokenDeletionInitiatedEvent.php | 26 ++++++ .../Event/PaymentMethodTokenEvent.php | 45 ++++++++++ .../PaymentMethodTokenEventSubscriber.php | 77 +++++++++++++++++ .../PaymentMethodTokenRepository.php | 2 +- .../ValueObject/PaymentMethodTokenId.php | 2 +- 10 files changed, 351 insertions(+), 2 deletions(-) create mode 100644 src/PaymentMethodToken/Command/SavePaymentMethodTokenCommand.php create mode 100644 src/PaymentMethodToken/CommandHandler/SavePaymentMethodTokenCommandHandler.php create mode 100644 src/PaymentMethodToken/Event/PaymentMethodTokenCreatedEvent.php create mode 100644 src/PaymentMethodToken/Event/PaymentMethodTokenDeletedEvent.php create mode 100644 src/PaymentMethodToken/Event/PaymentMethodTokenDeletionInitiatedEvent.php create mode 100644 src/PaymentMethodToken/Event/PaymentMethodTokenEvent.php create mode 100644 src/PaymentMethodToken/EventSubscriber/PaymentMethodTokenEventSubscriber.php diff --git a/src/Dispatcher/OrderDispatcher.php b/src/Dispatcher/OrderDispatcher.php index b41330a0c..ad11d367d 100644 --- a/src/Dispatcher/OrderDispatcher.php +++ b/src/Dispatcher/OrderDispatcher.php @@ -24,6 +24,9 @@ use PrestaShop\Module\PrestashopCheckout\Event\EventDispatcherInterface; use PrestaShop\Module\PrestashopCheckout\Event\SymfonyEventDispatcherAdapter; use PrestaShop\Module\PrestashopCheckout\Exception\PsCheckoutException; +use PrestaShop\Module\PrestashopCheckout\PaymentMethodToken\Event\PaymentMethodTokenCreatedEvent; +use PrestaShop\Module\PrestashopCheckout\PaymentMethodToken\Event\PaymentMethodTokenDeletedEvent; +use PrestaShop\Module\PrestashopCheckout\PaymentMethodToken\Event\PaymentMethodTokenDeletionInitiatedEvent; use PrestaShop\Module\PrestashopCheckout\PayPal\Order\Event\PayPalOrderApprovalReversedEvent; use PrestaShop\Module\PrestashopCheckout\PayPal\Order\Event\PayPalOrderApprovedEvent; use PrestaShop\Module\PrestashopCheckout\PayPal\Order\Event\PayPalOrderCompletedEvent; @@ -48,6 +51,9 @@ class OrderDispatcher implements Dispatcher const PS_CHECKOUT_ORDER_APPROVED = 'CheckoutOrderApproved'; const PS_CHECKOUT_ORDER_COMPLETED = 'CheckoutOrderCompleted'; const PS_CHECKOUT_ORDER_APPROVAL_REVERSED = 'CheckoutPaymentApprovalReversed'; + const PS_CHECKOUT_VAULT_PAYMENT_TOKEN_CREATED = 'VaultPaymentTokenCreated'; + const PS_CHECKOUT_VAULT_PAYMENT_TOKEN_DELETED = 'VaultPaymentTokenDeleted'; + const PS_CHECKOUT_VAULT_PAYMENT_TOKEN_DELETION_INITIATED = 'VaultPaymentTokenDeletionInitiated'; /** * Dispatch the Event Type to manage the merchant status @@ -98,6 +104,15 @@ public function dispatchEventType($payload) case static::PS_CHECKOUT_ORDER_APPROVAL_REVERSED: $eventDispatcher->dispatch(new PayPalOrderApprovalReversedEvent($payload['orderId'], $payload['resource'])); break; + case static::PS_CHECKOUT_VAULT_PAYMENT_TOKEN_CREATED: + $eventDispatcher->dispatch(new PaymentMethodTokenCreatedEvent($payload['resource'])); + break; + case static::PS_CHECKOUT_VAULT_PAYMENT_TOKEN_DELETED: + $eventDispatcher->dispatch(new PaymentMethodTokenDeletedEvent($payload['resource'])); + break; + case static::PS_CHECKOUT_VAULT_PAYMENT_TOKEN_DELETION_INITIATED: + $eventDispatcher->dispatch(new PaymentMethodTokenDeletionInitiatedEvent($payload['resource'])); + break; default: $logger->warning( 'Unknown webhook, cannot be processed.', diff --git a/src/PaymentMethodToken/Command/SavePaymentMethodTokenCommand.php b/src/PaymentMethodToken/Command/SavePaymentMethodTokenCommand.php new file mode 100644 index 000000000..96ec60b02 --- /dev/null +++ b/src/PaymentMethodToken/Command/SavePaymentMethodTokenCommand.php @@ -0,0 +1,85 @@ + + * @copyright Since 2007 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0 + */ + +namespace PrestaShop\Module\PrestashopCheckout\PaymentMethodToken\Command; + +use PrestaShop\Module\PrestashopCheckout\PaymentMethodToken\ValueObject\PaymentMethodTokenId; +use PrestaShop\Module\PrestashopCheckout\PayPal\Customer\ValueObject\PayPalCustomerId; + +class SavePaymentMethodTokenCommand +{ + /** @var PaymentMethodTokenId */ + private $paymentMethodTokenId; + + /** @var PayPalCustomerId */ + private $paypalCustomerId; + + /** @var string */ + private $paymentSource; + + /** @var array */ + private $paymentMethodTokenData; + + /** + * @param PaymentMethodTokenId $paymentMethodTokenId + * @param PayPalCustomerId $paypalCustomerId + * @param string $paymentSource + * @param array $paymentMethodTokenData + */ + public function __construct($paymentMethodTokenId, $paypalCustomerId, $paymentSource, $paymentMethodTokenData) + { + $this->paymentMethodTokenId = $paymentMethodTokenId; + $this->paypalCustomerId = $paypalCustomerId; + $this->paymentSource = $paymentSource; + $this->paymentMethodTokenData = $paymentMethodTokenData; + } + + /** + * @return PaymentMethodTokenId + */ + public function getPaymentMethodTokenId() + { + return $this->paymentMethodTokenId; + } + + /** + * @return PayPalCustomerId + */ + public function getPaypalCustomerId() + { + return $this->paypalCustomerId; + } + + /** + * @return string + */ + public function getPaymentSource() + { + return $this->paymentSource; + } + + /** + * @return array + */ + public function getPaymentMethodTokenData() + { + return $this->paymentMethodTokenData; + } +} diff --git a/src/PaymentMethodToken/CommandHandler/SavePaymentMethodTokenCommandHandler.php b/src/PaymentMethodToken/CommandHandler/SavePaymentMethodTokenCommandHandler.php new file mode 100644 index 000000000..76fc2623b --- /dev/null +++ b/src/PaymentMethodToken/CommandHandler/SavePaymentMethodTokenCommandHandler.php @@ -0,0 +1,49 @@ + + * @copyright Since 2007 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0 + */ + +namespace PrestaShop\Module\PrestashopCheckout\PaymentMethodToken\CommandHandler; + +use Exception; +use PrestaShop\Module\PrestashopCheckout\PaymentMethodToken\Command\SavePaymentMethodTokenCommand; +use PrestaShop\Module\PrestashopCheckout\PaymentMethodToken\PaymentMethodTokenRepository; + +class SavePaymentMethodTokenCommandHandler +{ + /** @var PaymentMethodTokenRepository */ + private $paymentMethodTokenRepository; + + public function __construct(PaymentMethodTokenRepository $paymentMethodTokenRepository) + { + $this->paymentMethodTokenRepository = $paymentMethodTokenRepository; + } + + /** + * @throws Exception + */ + public function handle(SavePaymentMethodTokenCommand $savePaymentMethodTokenCommand) + { + $this->paymentMethodTokenRepository->save( + $savePaymentMethodTokenCommand->getPaymentMethodTokenId(), + $savePaymentMethodTokenCommand->getPaypalCustomerId(), + $savePaymentMethodTokenCommand->getPaymentSource(), + $savePaymentMethodTokenCommand->getPaymentMethodTokenData() + ); + } +} diff --git a/src/PaymentMethodToken/Event/PaymentMethodTokenCreatedEvent.php b/src/PaymentMethodToken/Event/PaymentMethodTokenCreatedEvent.php new file mode 100644 index 000000000..eec06ddd8 --- /dev/null +++ b/src/PaymentMethodToken/Event/PaymentMethodTokenCreatedEvent.php @@ -0,0 +1,26 @@ + + * @copyright Since 2007 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0 + */ + +namespace PrestaShop\Module\PrestashopCheckout\PaymentMethodToken\Event; + +class PaymentMethodTokenCreatedEvent extends PaymentMethodTokenEvent +{ + +} diff --git a/src/PaymentMethodToken/Event/PaymentMethodTokenDeletedEvent.php b/src/PaymentMethodToken/Event/PaymentMethodTokenDeletedEvent.php new file mode 100644 index 000000000..9903a3e1e --- /dev/null +++ b/src/PaymentMethodToken/Event/PaymentMethodTokenDeletedEvent.php @@ -0,0 +1,26 @@ + + * @copyright Since 2007 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0 + */ + +namespace PrestaShop\Module\PrestashopCheckout\PaymentMethodToken\Event; + +class PaymentMethodTokenDeletedEvent extends PaymentMethodTokenEvent +{ + +} diff --git a/src/PaymentMethodToken/Event/PaymentMethodTokenDeletionInitiatedEvent.php b/src/PaymentMethodToken/Event/PaymentMethodTokenDeletionInitiatedEvent.php new file mode 100644 index 000000000..06781ec97 --- /dev/null +++ b/src/PaymentMethodToken/Event/PaymentMethodTokenDeletionInitiatedEvent.php @@ -0,0 +1,26 @@ + + * @copyright Since 2007 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0 + */ + +namespace PrestaShop\Module\PrestashopCheckout\PaymentMethodToken\Event; + +class PaymentMethodTokenDeletionInitiatedEvent extends PaymentMethodTokenEvent +{ + +} diff --git a/src/PaymentMethodToken/Event/PaymentMethodTokenEvent.php b/src/PaymentMethodToken/Event/PaymentMethodTokenEvent.php new file mode 100644 index 000000000..bb9170c9b --- /dev/null +++ b/src/PaymentMethodToken/Event/PaymentMethodTokenEvent.php @@ -0,0 +1,45 @@ + + * @copyright Since 2007 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0 + */ + +namespace PrestaShop\Module\PrestashopCheckout\PaymentMethodToken\Event; + +use PrestaShop\Module\PrestashopCheckout\Event\Event; + +class PaymentMethodTokenEvent extends Event +{ + /** @var array */ + private $resource; + + /** + * @param array $resource + */ + public function __construct($resource) + { + $this->resource = $resource; + } + + /** + * @return array + */ + public function getResource() + { + return $this->resource; + } +} diff --git a/src/PaymentMethodToken/EventSubscriber/PaymentMethodTokenEventSubscriber.php b/src/PaymentMethodToken/EventSubscriber/PaymentMethodTokenEventSubscriber.php new file mode 100644 index 000000000..d7e25d7b1 --- /dev/null +++ b/src/PaymentMethodToken/EventSubscriber/PaymentMethodTokenEventSubscriber.php @@ -0,0 +1,77 @@ + + * @copyright Since 2007 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0 + */ + +namespace PrestaShop\Module\PrestashopCheckout\PaymentMethodToken\EventSubscriber; + +use PrestaShop\Module\PrestashopCheckout\CommandBus\CommandBusInterface; +use PrestaShop\Module\PrestashopCheckout\PaymentMethodToken\Command\SavePaymentMethodTokenCommand; +use PrestaShop\Module\PrestashopCheckout\PaymentMethodToken\Event\PaymentMethodTokenCreatedEvent; +use PrestaShop\Module\PrestashopCheckout\PaymentMethodToken\Event\PaymentMethodTokenDeletedEvent; +use PrestaShop\Module\PrestashopCheckout\PaymentMethodToken\Event\PaymentMethodTokenDeletionInitiatedEvent; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; +use Ps_checkout; + +class PaymentMethodTokenEventSubscriber implements EventSubscriberInterface +{ + /** @var Ps_checkout */ + private $module; + + /** @var CommandBusInterface */ + private $commandBus; + + public function __construct(Ps_checkout $module) + { + $this->module = $module; + $this->commandBus = $this->module->getService('ps_checkout.bus.command'); + } + + /** + * {@inheritdoc} + */ + public static function getSubscribedEvents() + { + return [ + PaymentMethodTokenCreatedEvent::class => [ + ['saveCreatedPaymentMethodToken'], + ], + PaymentMethodTokenDeletedEvent::class => [ + ['deletePaymentMethodToken'], + ], + PaymentMethodTokenDeletionInitiatedEvent::class => [ + [''], // No sé + ] + ]; + } + + public function saveCreatedPaymentMethodToken(PaymentMethodTokenCreatedEvent $event) + { + $this->commandBus->handle(new SavePaymentMethodTokenCommand( + $paymentMethodTokenId, + $paypalCustomerId, + $paymentSource, + $event->getResource() + )); + } + + public function deletePaymentMethodToken(PaymentMethodTokenDeletedEvent $event) + { + + } +} diff --git a/src/PaymentMethodToken/PaymentMethodTokenRepository.php b/src/PaymentMethodToken/PaymentMethodTokenRepository.php index 236955b4c..0853cb06c 100644 --- a/src/PaymentMethodToken/PaymentMethodTokenRepository.php +++ b/src/PaymentMethodToken/PaymentMethodTokenRepository.php @@ -24,7 +24,7 @@ use DbQuery; use Exception; use PrestaShop\Module\PrestashopCheckout\PayPal\Customer\ValueObject\PayPalCustomerId; -use PrestaShop\Module\PrestashopCheckout\Vault\ValueObject\PaymentMethodTokenId; +use PrestaShop\Module\PrestashopCheckout\PaymentMethodToken\ValueObject\PaymentMethodTokenId; class PaymentMethodTokenRepository { diff --git a/src/PaymentMethodToken/ValueObject/PaymentMethodTokenId.php b/src/PaymentMethodToken/ValueObject/PaymentMethodTokenId.php index d08c35dae..4464677e1 100644 --- a/src/PaymentMethodToken/ValueObject/PaymentMethodTokenId.php +++ b/src/PaymentMethodToken/ValueObject/PaymentMethodTokenId.php @@ -18,7 +18,7 @@ * @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0 */ -namespace PrestaShop\Module\PrestashopCheckout\Vault\ValueObject; +namespace PrestaShop\Module\PrestashopCheckout\PaymentMethodToken\ValueObject; use InvalidArgumentException; From fa194cb6732ff3b2aaf42e9e9bf4d06da29fe619 Mon Sep 17 00:00:00 2001 From: Laurynas Date: Fri, 8 Mar 2024 14:33:58 +0200 Subject: [PATCH 04/60] Added is_favorite column to token table --- src/Database/TableManager.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Database/TableManager.php b/src/Database/TableManager.php index 8c777da4f..7b34c0189 100644 --- a/src/Database/TableManager.php +++ b/src/Database/TableManager.php @@ -92,6 +92,7 @@ public function createTable() `paypal_customer_id` varchar(50) NOT NULL, `payment_source` varchar(50) NOT NULL, `data` text NOT NULL, + `is_favorite` tinyint(1) unsigned DEFAULT 0 NOT NULL, PRIMARY KEY (`id_customer`, `paypal_customer_id`) ) ENGINE=' . _MYSQL_ENGINE_ . ' DEFAULT CHARSET=UTF8; ') && $this->db->execute(' From 6ded1db93b50a59200d4a0d4c789909fc50e87b6 Mon Sep 17 00:00:00 2001 From: Laurynas Date: Fri, 8 Mar 2024 18:09:19 +0200 Subject: [PATCH 05/60] PaymentToken entity and repository --- config/common.yml | 10 ++ ps_checkout.php | 22 +++ src/Database/TableManager.php | 2 +- src/Entity/PaymentToken.php | 145 ++++++++++++++++++ src/FundingSource/FundingSourcePresenter.php | 32 ++++ src/FundingSource/FundingSourceProvider.php | 17 +- .../FundingSourceTranslationProvider.php | 12 ++ .../Event/PaymentMethodTokenCreatedEvent.php | 1 - .../Event/PaymentMethodTokenDeletedEvent.php | 1 - ...ymentMethodTokenDeletionInitiatedEvent.php | 1 - .../PaymentMethodTokenEventSubscriber.php | 5 +- .../PaymentMethodTokenRepository.php | 2 +- src/Repository/PaymentTokenRepository.php | 71 +++++++++ 13 files changed, 312 insertions(+), 9 deletions(-) create mode 100644 src/Entity/PaymentToken.php create mode 100644 src/Repository/PaymentTokenRepository.php diff --git a/config/common.yml b/config/common.yml index 27a4fb6e7..5f0492fff 100644 --- a/config/common.yml +++ b/config/common.yml @@ -329,6 +329,11 @@ services: arguments: - '@PrestaShop\Module\PrestashopCheckout\FundingSource\FundingSourceCollectionBuilder' - '@PrestaShop\Module\PrestashopCheckout\FundingSource\FundingSourcePresenter' + - '@PrestaShop\Module\PrestashopCheckout\Repository\PaymentTokenRepository' + + ps_checkout.repository.paypal.code: + class: 'PrestaShop\Module\PrestashopCheckout\Repository\PayPalCodeRepository' + public: true PrestaShop\Module\PrestashopCheckout\Validator\MerchantValidator: class: 'PrestaShop\Module\PrestashopCheckout\Validator\MerchantValidator' @@ -483,3 +488,8 @@ services: public: true arguments: - "@ps_checkout.http.client" + + PrestaShop\Module\PrestashopCheckout\Repository\PaymentTokenRepository: + class: 'PrestaShop\Module\PrestashopCheckout\Repository\PaymentTokenRepository' + arguments: + - '@ps_checkout.db' diff --git a/ps_checkout.php b/ps_checkout.php index 042896a80..39c795d0d 100755 --- a/ps_checkout.php +++ b/ps_checkout.php @@ -589,8 +589,30 @@ public function hookPaymentOptions(array $params) /** @var \PrestaShop\Module\PrestashopCheckout\PayPal\PayPalConfiguration $configurationPayPal */ $configurationPayPal = $this->getService(\PrestaShop\Module\PrestashopCheckout\PayPal\PayPalConfiguration::class); + /** @var PrestaShop\Module\PrestashopCheckout\Configuration\PrestaShopConfiguration $psConfiguration */ + $psConfiguration = $this->getService('ps_checkout.configuration'); + + $vaultingEnabled = $psConfiguration->get( + 'PS_CHECKOUT_VAULTING', + [ + 'id_shop' => \Context::getContext()->shop->id, + 'default' => '0', + ] + ); + $paymentOptions = []; + if ((int) $vaultingEnabled) { + foreach ($fundingSourceProvider->getSavedTokens($cart->id_customer) as $fundingSource) { + $paymentOption = new PrestaShop\PrestaShop\Core\Payment\PaymentOption(); + $paymentOption->setModuleName($this->name . '-' . $fundingSource->name); + $paymentOption->setCallToActionText($fundingSource->label); + $paymentOption->setBinary(false); + + $paymentOptions[] = $paymentOption; + } + } + foreach ($fundingSourceProvider->getAll() as $fundingSource) { $paymentOption = new PrestaShop\PrestaShop\Core\Payment\PaymentOption(); $paymentOption->setModuleName($this->name . '-' . $fundingSource->name); diff --git a/src/Database/TableManager.php b/src/Database/TableManager.php index 7b34c0189..ca9efcfb0 100644 --- a/src/Database/TableManager.php +++ b/src/Database/TableManager.php @@ -93,7 +93,7 @@ public function createTable() `payment_source` varchar(50) NOT NULL, `data` text NOT NULL, `is_favorite` tinyint(1) unsigned DEFAULT 0 NOT NULL, - PRIMARY KEY (`id_customer`, `paypal_customer_id`) + PRIMARY KEY (`id`, `paypal_customer_id`) ) ENGINE=' . _MYSQL_ENGINE_ . ' DEFAULT CHARSET=UTF8; ') && $this->db->execute(' CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'pscheckout_order` ( diff --git a/src/Entity/PaymentToken.php b/src/Entity/PaymentToken.php new file mode 100644 index 000000000..5cc22b5f4 --- /dev/null +++ b/src/Entity/PaymentToken.php @@ -0,0 +1,145 @@ +id = $id; + $this->payPalCustomerId = $payPalCustomerId; + $this->paymentSource = $paymentSource; + $this->data = $data; + $this->isFavorite = $isFavorite; + } + + /** + * @return string + */ + public function getId() + { + return $this->id; + } + + /** + * @param string $id + * + * @return PaymentToken + */ + public function setId($id) + { + $this->id = $id; + + return $this; + } + + /** + * @return string + */ + public function getPayPalCustomerId() + { + return $this->payPalCustomerId; + } + + /** + * @param string $payPalCustomerId + * + * @return PaymentToken + */ + public function setPayPalCustomerId($payPalCustomerId) + { + $this->payPalCustomerId = $payPalCustomerId; + + return $this; + } + + /** + * @return string + */ + public function getPaymentSource() + { + return $this->paymentSource; + } + + /** + * @param string $paymentSource + * + * @return PaymentToken + */ + public function setPaymentSource($paymentSource) + { + $this->paymentSource = $paymentSource; + + return $this; + } + + /** + * @return string + */ + public function getData() + { + return $this->data; + } + + /** + * @param string $data + * + * @return PaymentToken + */ + public function setData($data) + { + $this->data = $data; + + return $this; + } + + /** + * @return bool + */ + public function isFavorite() + { + return $this->isFavorite; + } + + /** + * @param bool $isFavorite + * + * @return PaymentToken + */ + public function setIsFavorite($isFavorite) + { + $this->isFavorite = $isFavorite; + + return $this; + } +} diff --git a/src/FundingSource/FundingSourcePresenter.php b/src/FundingSource/FundingSourcePresenter.php index db30b33a9..d99abdecc 100644 --- a/src/FundingSource/FundingSourcePresenter.php +++ b/src/FundingSource/FundingSourcePresenter.php @@ -20,6 +20,7 @@ namespace PrestaShop\Module\PrestashopCheckout\FundingSource; +use PrestaShop\Module\PrestashopCheckout\Entity\PaymentToken; use PrestaShop\Module\PrestashopCheckout\Repository\CountryRepository; class FundingSourcePresenter @@ -63,4 +64,35 @@ public function present($entity, $isAdmin) $entity->getIsToggleable() ); } + + /** + * @param PaymentToken $paymentToken + * + * @return FundingSource + */ + public function presentPaymentToken(PaymentToken $paymentToken) + { + $data = json_decode($paymentToken->getData(), true); + + if ($paymentToken->getPaymentSource() === 'card') { + $fundingSourceName = $this->translation->getVaultedPaymentMethodName( + true, + $data['payment_source']['card']['last_digits'] + ); + } else { + $fundingSourceName = $this->translation->getVaultedPaymentMethodName( + false, + $data['payment_source']['paypal']['email_address'] + ); + } + + return new FundingSource( + 'token-' . $paymentToken->getId(), + $fundingSourceName, + 0, + [], + true, + false + ); + } } diff --git a/src/FundingSource/FundingSourceProvider.php b/src/FundingSource/FundingSourceProvider.php index 62a594c0d..392e387a3 100644 --- a/src/FundingSource/FundingSourceProvider.php +++ b/src/FundingSource/FundingSourceProvider.php @@ -20,6 +20,8 @@ namespace PrestaShop\Module\PrestashopCheckout\FundingSource; +use PrestaShop\Module\PrestashopCheckout\Repository\PaymentTokenRepository; + class FundingSourceProvider { /** @@ -31,6 +33,10 @@ class FundingSourceProvider * @var FundingSourcePresenter */ private $presenter; + /** + * @var PaymentTokenRepository + */ + private $paymentTokenRepository; /** * @param FundingSourceCollectionBuilder $fundingSourceCollectionBuilder @@ -38,10 +44,12 @@ class FundingSourceProvider */ public function __construct( FundingSourceCollectionBuilder $fundingSourceCollectionBuilder, - FundingSourcePresenter $presenter + FundingSourcePresenter $presenter, + PaymentTokenRepository $paymentTokenRepository ) { $this->collection = new FundingSourceCollection($fundingSourceCollectionBuilder->create()); $this->presenter = $presenter; + $this->paymentTokenRepository = $paymentTokenRepository; } /** @@ -66,4 +74,11 @@ public function getAll($isAdmin = false) return $fundingSources; } + + public function getSavedTokens($customerId) + { + return array_map(function ($paymentToken) { + return $this->presenter->presentPaymentToken($paymentToken); + }, $this->paymentTokenRepository->getAllByCustomerId($customerId)); + } } diff --git a/src/FundingSource/FundingSourceTranslationProvider.php b/src/FundingSource/FundingSourceTranslationProvider.php index 8e83281c1..f2f88bb27 100644 --- a/src/FundingSource/FundingSourceTranslationProvider.php +++ b/src/FundingSource/FundingSourceTranslationProvider.php @@ -64,6 +64,8 @@ public function __construct(Module $module) 'maxima' => 'Maxima', 'mercadopago' => 'Mercado Pago', 'sepa' => 'SEPA', + 'token-card' => $module->l('Pay with card %s', 'fundingsourcetranslationprovider'), + 'token-account' => $module->l('Pay with PayPal Account %s', 'fundingsourcetranslationprovider'), ]; $payByTranslation = $module->l('Pay by %s', 'fundingsourcetranslationprovider'); @@ -98,6 +100,16 @@ public function getPaymentMethodName($fundingSource) return isset($this->fundingSourceNames[$fundingSource]) ? $this->fundingSourceNames[$fundingSource] : ''; } + /** + * @param bool $isCard + * + * @return string + */ + public function getVaultedPaymentMethodName($isCard, $data) + { + return str_replace('%s', $data, $this->fundingSourceNames['token-' . ($isCard ? 'card' : 'account')]); + } + /** * @return array */ diff --git a/src/PaymentMethodToken/Event/PaymentMethodTokenCreatedEvent.php b/src/PaymentMethodToken/Event/PaymentMethodTokenCreatedEvent.php index eec06ddd8..fc3c3f9a1 100644 --- a/src/PaymentMethodToken/Event/PaymentMethodTokenCreatedEvent.php +++ b/src/PaymentMethodToken/Event/PaymentMethodTokenCreatedEvent.php @@ -22,5 +22,4 @@ class PaymentMethodTokenCreatedEvent extends PaymentMethodTokenEvent { - } diff --git a/src/PaymentMethodToken/Event/PaymentMethodTokenDeletedEvent.php b/src/PaymentMethodToken/Event/PaymentMethodTokenDeletedEvent.php index 9903a3e1e..5c5a0f159 100644 --- a/src/PaymentMethodToken/Event/PaymentMethodTokenDeletedEvent.php +++ b/src/PaymentMethodToken/Event/PaymentMethodTokenDeletedEvent.php @@ -22,5 +22,4 @@ class PaymentMethodTokenDeletedEvent extends PaymentMethodTokenEvent { - } diff --git a/src/PaymentMethodToken/Event/PaymentMethodTokenDeletionInitiatedEvent.php b/src/PaymentMethodToken/Event/PaymentMethodTokenDeletionInitiatedEvent.php index 06781ec97..c96ad7424 100644 --- a/src/PaymentMethodToken/Event/PaymentMethodTokenDeletionInitiatedEvent.php +++ b/src/PaymentMethodToken/Event/PaymentMethodTokenDeletionInitiatedEvent.php @@ -22,5 +22,4 @@ class PaymentMethodTokenDeletionInitiatedEvent extends PaymentMethodTokenEvent { - } diff --git a/src/PaymentMethodToken/EventSubscriber/PaymentMethodTokenEventSubscriber.php b/src/PaymentMethodToken/EventSubscriber/PaymentMethodTokenEventSubscriber.php index d7e25d7b1..6b54eb832 100644 --- a/src/PaymentMethodToken/EventSubscriber/PaymentMethodTokenEventSubscriber.php +++ b/src/PaymentMethodToken/EventSubscriber/PaymentMethodTokenEventSubscriber.php @@ -25,8 +25,8 @@ use PrestaShop\Module\PrestashopCheckout\PaymentMethodToken\Event\PaymentMethodTokenCreatedEvent; use PrestaShop\Module\PrestashopCheckout\PaymentMethodToken\Event\PaymentMethodTokenDeletedEvent; use PrestaShop\Module\PrestashopCheckout\PaymentMethodToken\Event\PaymentMethodTokenDeletionInitiatedEvent; -use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Ps_checkout; +use Symfony\Component\EventDispatcher\EventSubscriberInterface; class PaymentMethodTokenEventSubscriber implements EventSubscriberInterface { @@ -56,7 +56,7 @@ public static function getSubscribedEvents() ], PaymentMethodTokenDeletionInitiatedEvent::class => [ [''], // No sé - ] + ], ]; } @@ -72,6 +72,5 @@ public function saveCreatedPaymentMethodToken(PaymentMethodTokenCreatedEvent $ev public function deletePaymentMethodToken(PaymentMethodTokenDeletedEvent $event) { - } } diff --git a/src/PaymentMethodToken/PaymentMethodTokenRepository.php b/src/PaymentMethodToken/PaymentMethodTokenRepository.php index 0853cb06c..8dd815cc2 100644 --- a/src/PaymentMethodToken/PaymentMethodTokenRepository.php +++ b/src/PaymentMethodToken/PaymentMethodTokenRepository.php @@ -23,8 +23,8 @@ use Db; use DbQuery; use Exception; -use PrestaShop\Module\PrestashopCheckout\PayPal\Customer\ValueObject\PayPalCustomerId; use PrestaShop\Module\PrestashopCheckout\PaymentMethodToken\ValueObject\PaymentMethodTokenId; +use PrestaShop\Module\PrestashopCheckout\PayPal\Customer\ValueObject\PayPalCustomerId; class PaymentMethodTokenRepository { diff --git a/src/Repository/PaymentTokenRepository.php b/src/Repository/PaymentTokenRepository.php new file mode 100644 index 000000000..479e5f3c8 --- /dev/null +++ b/src/Repository/PaymentTokenRepository.php @@ -0,0 +1,71 @@ +db = $db; + } + + public function save(PaymentToken $paymentToken) + { + return $this->db->insert( + PaymentToken::TABLE, + [ + 'id' => $paymentToken->getId(), + 'paypal_customer_id' => $paymentToken->getPayPalCustomerId(), + 'payment_source' => $paymentToken->getPaymentSource(), + 'data' => $paymentToken->getData(), + 'is_favorite' => $paymentToken->isFavorite(), + ], + false, + true, + Db::REPLACE + ); + } + + /** + * @param $customerId + * + * @return PaymentToken[] + * + * @throws PrestaShopDatabaseException + */ + public function getAllByCustomerId($customerId) + { + $query = new DbQuery(); + $query->select('t.`id`, t.`paypal_customer_id`, t.`payment_source`, t.`data`, t.`is_favorite`') + ->from(PaymentToken::TABLE, 't') + ->leftJoin('pscheckout_customer', 'c', 't.`paypal_customer_id` = c.`paypal_customer_id`') + ->where('c.`id_customer` =' . (int) $customerId) + ->orderBy('t.`is_favorite` DESC') + ->orderBy('t.`id` ASC'); + $queryResult = $this->db->executeS($query); + + if (!$queryResult) { + return []; + } + + return array_map(function ($paymentSource) { + return new PaymentToken( + $paymentSource['id'], + $paymentSource['paypal_customer_id'], + $paymentSource['payment_source'], + $paymentSource['data'], + (bool) $paymentSource['is_favorite'] + ); + }, $queryResult); + } +} From 72ee1dfe31e77b420bb3ae78d4df4f7baa06b18f Mon Sep 17 00:00:00 2001 From: Laurynas Date: Wed, 13 Mar 2024 14:11:50 +0200 Subject: [PATCH 06/60] Added new JS component for vaulted payment methods --- .../1_6/payment-options.component.js | 3 +- .../1_7/payment-options.component.js | 3 +- .../common/payment-option.component.js | 18 ++- .../common/payment-token.component.js | 131 ++++++++++++++++++ _dev/js/front/src/service/paypal.service.js | 9 +- ps_checkout.php | 13 +- src/FundingSource/FundingSourceProvider.php | 6 + 7 files changed, 173 insertions(+), 10 deletions(-) create mode 100644 _dev/js/front/src/components/common/payment-token.component.js diff --git a/_dev/js/front/src/components/1_6/payment-options.component.js b/_dev/js/front/src/components/1_6/payment-options.component.js index 076078574..2f86e9f65 100644 --- a/_dev/js/front/src/components/1_6/payment-options.component.js +++ b/_dev/js/front/src/components/1_6/payment-options.component.js @@ -78,7 +78,8 @@ export class PaymentOptionsComponent extends BaseComponent { if ( this.config.expressCheckout.active && 'ps_checkout-' + this.payPalService.getFundingSource() !== - HTMLListenerElements[index].button.dataset.moduleName + HTMLListenerElements[index].button.dataset.moduleName && + this.payPalService.getOrderId() ) { this.psCheckoutApi .postCancelOrder({ diff --git a/_dev/js/front/src/components/1_7/payment-options.component.js b/_dev/js/front/src/components/1_7/payment-options.component.js index a4d47d0bd..0eba9aebf 100644 --- a/_dev/js/front/src/components/1_7/payment-options.component.js +++ b/_dev/js/front/src/components/1_7/payment-options.component.js @@ -69,7 +69,8 @@ export class PaymentOptionsComponent extends BaseComponent { if ( this.config.expressCheckout.active && 'ps_checkout-' + this.payPalService.getFundingSource() !== - radio.dataset.moduleName + radio.dataset.moduleName && + this.payPalService.getOrderId() ) { this.psCheckoutApi .postCancelOrder({ diff --git a/_dev/js/front/src/components/common/payment-option.component.js b/_dev/js/front/src/components/common/payment-option.component.js index ea41ae465..741f5e9ce 100644 --- a/_dev/js/front/src/components/common/payment-option.component.js +++ b/_dev/js/front/src/components/common/payment-option.component.js @@ -23,6 +23,7 @@ import { SmartButtonComponent } from './smart-button.component'; import { PaymentFieldsComponent } from "./payment-fields.component"; import {CardFieldsComponent} from "./card-fields.component"; import {PS_VERSION_1_6} from "../../constants/ps-version.constants"; +import {PaymentTokenComponent} from "./payment-token.component"; /** * @typedef PaymentOptionComponentProps @@ -128,11 +129,13 @@ export class PaymentOptionComponent extends BaseComponent { } } - this.children.Marker = this.marker = new MarkComponent(this.app, { - fundingSource: this.props.fundingSource, + if (!this.props.fundingSource.name.includes('token')) { + this.children.Marker = this.marker = new MarkComponent(this.app, { + fundingSource: this.props.fundingSource, - HTMLElement: this.data.HTMLElementMarker - }).render(); + HTMLElement: this.data.HTMLElementMarker + }).render(); + } } renderPaymentFields() { @@ -170,7 +173,12 @@ export class PaymentOptionComponent extends BaseComponent { this.data.HTMLElementCardFields.style.display = 'none'; } - if (this.data.HTMLElementCardFields && isCardFieldsEligible && isCardFieldsAvailable) { + if (this.props.fundingSource.name.includes('token')) { + this.children.paymentToken = new PaymentTokenComponent(this.app, { + fundingSource: this.props.fundingSource, + HTMLElement: this.data.HTMLElementSmartButton + }).render(); + } else if (this.data.HTMLElementCardFields && isCardFieldsEligible && isCardFieldsAvailable) { this.data.HTMLElementCardFields.style.display = ''; this.children.cardFields = new CardFieldsComponent(this.app, { fundingSource: this.props.fundingSource, diff --git a/_dev/js/front/src/components/common/payment-token.component.js b/_dev/js/front/src/components/common/payment-token.component.js new file mode 100644 index 000000000..eb56eb7c4 --- /dev/null +++ b/_dev/js/front/src/components/common/payment-token.component.js @@ -0,0 +1,131 @@ +/** + * Copyright since 2007 PrestaShop SA and Contributors + * PrestaShop is an International Registered Trademark & Property of PrestaShop SA + * + * NOTICE OF LICENSE + * + * This source file is subject to the Academic Free License 3.0 (AFL-3.0) + * that is bundled with this package in the file LICENSE.md. + * It is also available through the world-wide-web at this URL: + * https://opensource.org/licenses/AFL-3.0 + * If you did not receive a copy of the license and are unable to + * obtain it through the world-wide-web, please send an email + * to license@prestashop.com so we can send you a copy immediately. + * + * @author PrestaShop SA + * @copyright Since 2007 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0) + */ +import { BaseComponent } from '../../core/dependency-injection/base.component'; + +/** + * @typedef PaymentTokenComponentProps + * + * @param {string} fundingSource.name + * + * @param {HTMLElement} [HTMLElement] + * @param {HTMLElement} HTMLElementWrapper + */ + +export class PaymentTokenComponent extends BaseComponent { + static Inject = { + config: 'PsCheckoutConfig', + payPalService: 'PayPalService', + psCheckoutApi: 'PsCheckoutApi', + querySelectorService: 'QuerySelectorService', + }; + + created() { + this.data.name = this.props.fundingSource.name; + this.data.orderId = this.payPalService.getOrderId(); + + this.data.HTMLElement = this.props.HTMLElement; + + this.data.conditions = this.app.root.children.conditionsCheckbox; + this.data.loader = this.app.root.children.loader; + this.data.notification = this.app.root.children.notification; + this.data.HTMLElementBaseButton = + this.querySelectorService.getBasePaymentConfirmation(); + this.data.HTMLElementButton = null; + this.data.HTMLElementButtonWrapper = this.getButtonWrapper(); + } + + getButtonWrapper() { + const buttonWrapper = `.ps_checkout-button[data-funding-source=${this.data.name}]`; + return document.querySelector(buttonWrapper); + } + + handleError(error) { + this.data.loader.hide(); + this.data.notification.showError(error.message); + this.data.HTMLElementButton.removeAttribute('disabled'); + } + + submitOrder() { + this.createOrder().then(() => this.validateOrder()); + } + + createOrder() { + return this.psCheckoutApi.postCreateOrder( + { + fundingSource: this.data.name + } + ) + .then((data) => { + this.data.orderId = data; + this.validateOrder(); + }) + .catch((error) => this.handleError(error)); + } + + validateOrder() { + this.psCheckoutApi.postValidateOrder( + { + orderID: this.data.orderId, + fundingSource: this.data.name, + } + ).then(() => { + this.data.loader.hide(); + this.data.HTMLElementButton.removeAttribute('disabled'); + }).catch((error) => this.handleError(error)); + } + + + renderButton() { + this.data.HTMLElementButton = + this.data.HTMLElementBaseButton.cloneNode(true); + + this.data.HTMLElementButtonWrapper.append(this.data.HTMLElementButton); + this.data.HTMLElementButton.classList.remove('disabled'); + this.data.HTMLElementButton.style.display = ''; + this.data.HTMLElementButton.disabled = !this.isSubmittable(); + + this.data.conditions && + this.data.conditions.onChange(() => { + // In some PS versions, the handler fails to disable the button because of the timing. + setTimeout(() => { + this.data.HTMLElementButton.disabled = !this.isSubmittable(); + }, 0); + }); + + this.data.HTMLElementButton.addEventListener('click', (event) => { + event.preventDefault(); + + this.data.loader.show(); + this.data.HTMLElementButton.setAttribute('disabled', ''); + + this.submitOrder(); + }); + } + + + + isSubmittable() { + return this.data.conditions ? this.data.conditions.isChecked() : false; + } + + render() { + this.renderButton(); + return this; + } +} diff --git a/_dev/js/front/src/service/paypal.service.js b/_dev/js/front/src/service/paypal.service.js index b488599f5..56c4fe060 100644 --- a/_dev/js/front/src/service/paypal.service.js +++ b/_dev/js/front/src/service/paypal.service.js @@ -375,11 +375,11 @@ export class PayPalService extends BaseClass { this.configPrestaShop.fundingSourcesSorted || paypalFundingSources ) .filter( - (fundingSource) => paypalFundingSources.indexOf(fundingSource) >= 0 + (fundingSource) => paypalFundingSources.indexOf(fundingSource) >= 0 || fundingSource.includes('token') ) .map((fundingSource) => ({ name: fundingSource, - mark: this.sdk.Marks({ fundingSource }) + mark: fundingSource.includes('token') ? null : this.sdk.Marks({ fundingSource }) })) .filter((fundingSource) => { if ( @@ -391,6 +391,11 @@ export class PayPalService extends BaseClass { 'Card Fields (CCF) eligibility is declined. Switching to PayPal branded card fields (SCF)' ); } + + if (fundingSource.name.includes('token')) { + return true; + } + console.log(fundingSource.name, fundingSource.mark.isEligible()); return fundingSource.mark.isEligible(); diff --git a/ps_checkout.php b/ps_checkout.php index 39c795d0d..d5b62c580 100755 --- a/ps_checkout.php +++ b/ps_checkout.php @@ -607,7 +607,9 @@ public function hookPaymentOptions(array $params) $paymentOption = new PrestaShop\PrestaShop\Core\Payment\PaymentOption(); $paymentOption->setModuleName($this->name . '-' . $fundingSource->name); $paymentOption->setCallToActionText($fundingSource->label); - $paymentOption->setBinary(false); + $paymentOption->setBinary(true); + $paymentOption->setLogo($this->getPathUri() . 'views/img/tail-spin.svg'); + $paymentOption->setAdditionalInformation('THIS IS VAULTED PAYMENT METHOD'); $paymentOptions[] = $paymentOption; } @@ -921,6 +923,11 @@ public function hookActionFrontControllerSetMedia() $payWithTranslations = []; $isCardAvailable = false; + foreach ($fundingSourceProvider->getSavedTokens(\Context::getContext()->customer->id) as $fundingSource) { + $fundingSourcesSorted[] = $fundingSource->name; + $payWithTranslations[$fundingSource->name] = $fundingSource->label; + } + foreach ($fundingSourceProvider->getAll() as $fundingSource) { $fundingSourcesSorted[] = $fundingSource->name; $payWithTranslations[$fundingSource->name] = $fundingSource->label; @@ -1443,6 +1450,10 @@ public function hookDisplayPaymentByBinaries(array $params) $fundingSourceProvider = $this->getService(\PrestaShop\Module\PrestashopCheckout\FundingSource\FundingSourceProvider::class); $paymentOptions = []; + foreach ($fundingSourceProvider->getSavedTokens($cart->id_customer) as $fundingSource) { + $paymentOptions[] = $fundingSource->name; + } + foreach ($fundingSourceProvider->getAll() as $fundingSource) { $paymentOptions[] = $fundingSource->name; } diff --git a/src/FundingSource/FundingSourceProvider.php b/src/FundingSource/FundingSourceProvider.php index 392e387a3..a8248b018 100644 --- a/src/FundingSource/FundingSourceProvider.php +++ b/src/FundingSource/FundingSourceProvider.php @@ -41,6 +41,7 @@ class FundingSourceProvider /** * @param FundingSourceCollectionBuilder $fundingSourceCollectionBuilder * @param FundingSourcePresenter $presenter + * @param PaymentTokenRepository $paymentTokenRepository */ public function __construct( FundingSourceCollectionBuilder $fundingSourceCollectionBuilder, @@ -75,6 +76,11 @@ public function getAll($isAdmin = false) return $fundingSources; } + /** + * @param $customerId + * @return FundingSource[] + * @throws \PrestaShopDatabaseException + */ public function getSavedTokens($customerId) { return array_map(function ($paymentToken) { From 0b15f00b5416000f13775b83c14a68995e97ec2f Mon Sep 17 00:00:00 2001 From: Laurynas Date: Wed, 13 Mar 2024 15:38:33 +0200 Subject: [PATCH 07/60] Added correct payment logo display --- ps_checkout.php | 4 ++-- src/FundingSource/FundingSource.php | 9 ++++++++- src/FundingSource/FundingSourcePresenter.php | 3 ++- src/FundingSource/FundingSourceProvider.php | 4 ++-- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/ps_checkout.php b/ps_checkout.php index d5b62c580..07a2643b5 100755 --- a/ps_checkout.php +++ b/ps_checkout.php @@ -595,7 +595,7 @@ public function hookPaymentOptions(array $params) $vaultingEnabled = $psConfiguration->get( 'PS_CHECKOUT_VAULTING', [ - 'id_shop' => \Context::getContext()->shop->id, + 'id_shop' => (int) $cart->id_shop, 'default' => '0', ] ); @@ -608,7 +608,7 @@ public function hookPaymentOptions(array $params) $paymentOption->setModuleName($this->name . '-' . $fundingSource->name); $paymentOption->setCallToActionText($fundingSource->label); $paymentOption->setBinary(true); - $paymentOption->setLogo($this->getPathUri() . 'views/img/tail-spin.svg'); + $paymentOption->setLogo($this->getPathUri() . 'views/img/' . $fundingSource->paymentSource.'.svg'); $paymentOption->setAdditionalInformation('THIS IS VAULTED PAYMENT METHOD'); $paymentOptions[] = $paymentOption; diff --git a/src/FundingSource/FundingSource.php b/src/FundingSource/FundingSource.php index 547722237..e5a52882f 100644 --- a/src/FundingSource/FundingSource.php +++ b/src/FundingSource/FundingSource.php @@ -52,6 +52,11 @@ class FundingSource */ public $isToggleable; + /** + * @var string|null + */ + public $paymentSource; + /** * @param string $name * @param string $label @@ -59,8 +64,9 @@ class FundingSource * @param array $countries * @param bool $isEnabled * @param bool $isToggleable + * @param string|null $paymentSource */ - public function __construct($name, $label, $position, $countries, $isEnabled, $isToggleable) + public function __construct($name, $label, $position, $countries, $isEnabled, $isToggleable, $paymentSource = null) { $this->name = $name; $this->label = $label; @@ -68,5 +74,6 @@ public function __construct($name, $label, $position, $countries, $isEnabled, $i $this->countries = $countries; $this->isEnabled = $isEnabled; $this->isToggleable = $isToggleable; + $this->paymentSource = $paymentSource; } } diff --git a/src/FundingSource/FundingSourcePresenter.php b/src/FundingSource/FundingSourcePresenter.php index d99abdecc..978d425b8 100644 --- a/src/FundingSource/FundingSourcePresenter.php +++ b/src/FundingSource/FundingSourcePresenter.php @@ -92,7 +92,8 @@ public function presentPaymentToken(PaymentToken $paymentToken) 0, [], true, - false + false, + $paymentToken->getPaymentSource() ); } } diff --git a/src/FundingSource/FundingSourceProvider.php b/src/FundingSource/FundingSourceProvider.php index a8248b018..a8660f881 100644 --- a/src/FundingSource/FundingSourceProvider.php +++ b/src/FundingSource/FundingSourceProvider.php @@ -77,7 +77,7 @@ public function getAll($isAdmin = false) } /** - * @param $customerId + * @param int $customerId * @return FundingSource[] * @throws \PrestaShopDatabaseException */ @@ -85,6 +85,6 @@ public function getSavedTokens($customerId) { return array_map(function ($paymentToken) { return $this->presenter->presentPaymentToken($paymentToken); - }, $this->paymentTokenRepository->getAllByCustomerId($customerId)); + }, $this->paymentTokenRepository->getAllByCustomerId((int) $customerId)); } } From feaf7adba8948bdcf01595e29ec86e088703e8e6 Mon Sep 17 00:00:00 2001 From: Laurynas Date: Wed, 13 Mar 2024 17:37:22 +0200 Subject: [PATCH 08/60] Added custom marks for vaulted payment methods --- .../js/front/src/components/common/marker.component.js | 2 +- .../src/components/common/payment-option.component.js | 10 ++++------ .../src/components/common/payment-token.component.js | 6 +++++- _dev/js/front/src/config/ps-checkout.config.js | 3 ++- ps_checkout.php | 4 +++- 5 files changed, 15 insertions(+), 10 deletions(-) diff --git a/_dev/js/front/src/components/common/marker.component.js b/_dev/js/front/src/components/common/marker.component.js index 07bc38efc..ac0a0da07 100644 --- a/_dev/js/front/src/components/common/marker.component.js +++ b/_dev/js/front/src/components/common/marker.component.js @@ -48,7 +48,7 @@ export class MarkComponent extends BaseComponent { const src = this.config.customMark[this.data.name]; let logoList = []; - if (this.config.cardSupportedBrands && this.config.cardLogoBrands) { + if (this.data.name === 'card' && this.config.cardSupportedBrands && this.config.cardLogoBrands) { this.config.cardSupportedBrands.forEach(brand => { if (this.config.cardLogoBrands[brand]) { let customMarkImg = document.createElement('img'); diff --git a/_dev/js/front/src/components/common/payment-option.component.js b/_dev/js/front/src/components/common/payment-option.component.js index 741f5e9ce..6706bc4d4 100644 --- a/_dev/js/front/src/components/common/payment-option.component.js +++ b/_dev/js/front/src/components/common/payment-option.component.js @@ -129,13 +129,11 @@ export class PaymentOptionComponent extends BaseComponent { } } - if (!this.props.fundingSource.name.includes('token')) { - this.children.Marker = this.marker = new MarkComponent(this.app, { - fundingSource: this.props.fundingSource, + this.children.Marker = this.marker = new MarkComponent(this.app, { + fundingSource: this.props.fundingSource, - HTMLElement: this.data.HTMLElementMarker - }).render(); - } + HTMLElement: this.data.HTMLElementMarker + }).render(); } renderPaymentFields() { diff --git a/_dev/js/front/src/components/common/payment-token.component.js b/_dev/js/front/src/components/common/payment-token.component.js index eb56eb7c4..7fadcdb6b 100644 --- a/_dev/js/front/src/components/common/payment-token.component.js +++ b/_dev/js/front/src/components/common/payment-token.component.js @@ -48,6 +48,9 @@ export class PaymentTokenComponent extends BaseComponent { this.querySelectorService.getBasePaymentConfirmation(); this.data.HTMLElementButton = null; this.data.HTMLElementButtonWrapper = this.getButtonWrapper(); + + this.data.fundingSource = this.data.name.split('-')[0]; + this.data.tokenId = this.data.name.split('-')[1]; } getButtonWrapper() { @@ -68,7 +71,8 @@ export class PaymentTokenComponent extends BaseComponent { createOrder() { return this.psCheckoutApi.postCreateOrder( { - fundingSource: this.data.name + fundingSource: this.data.fundingSource, + tokenId: this.data.tokenId } ) .then((data) => { diff --git a/_dev/js/front/src/config/ps-checkout.config.js b/_dev/js/front/src/config/ps-checkout.config.js index d8c7de27e..ed748ccad 100644 --- a/_dev/js/front/src/config/ps-checkout.config.js +++ b/_dev/js/front/src/config/ps-checkout.config.js @@ -47,7 +47,8 @@ export const PsCheckoutConfig = { loaderImage: window.ps_checkoutLoaderImage, customMark: { - card: window.ps_checkoutCardFundingSourceImg + card: window.ps_checkoutCardFundingSourceImg, + ...window.ps_checkoutVaultedPaymentMarks }, autoRenderDisabled: window.ps_checkoutAutoRenderDisabled, diff --git a/ps_checkout.php b/ps_checkout.php index 07a2643b5..32b5891f6 100755 --- a/ps_checkout.php +++ b/ps_checkout.php @@ -608,7 +608,6 @@ public function hookPaymentOptions(array $params) $paymentOption->setModuleName($this->name . '-' . $fundingSource->name); $paymentOption->setCallToActionText($fundingSource->label); $paymentOption->setBinary(true); - $paymentOption->setLogo($this->getPathUri() . 'views/img/' . $fundingSource->paymentSource.'.svg'); $paymentOption->setAdditionalInformation('THIS IS VAULTED PAYMENT METHOD'); $paymentOptions[] = $paymentOption; @@ -922,10 +921,12 @@ public function hookActionFrontControllerSetMedia() $fundingSourcesSorted = []; $payWithTranslations = []; $isCardAvailable = false; + $vaultedPaymentMarks = []; foreach ($fundingSourceProvider->getSavedTokens(\Context::getContext()->customer->id) as $fundingSource) { $fundingSourcesSorted[] = $fundingSource->name; $payWithTranslations[$fundingSource->name] = $fundingSource->label; + $vaultedPaymentMarks[$fundingSource->name] = $this->getPathUri() .'views/img/' . $fundingSource->paymentSource .'.svg'; } foreach ($fundingSourceProvider->getAll() as $fundingSource) { @@ -965,6 +966,7 @@ public function hookActionFrontControllerSetMedia() $this->name . 'LoaderImage' => $this->getPathUri() . 'views/img/loader.svg', $this->name . 'PayPalButtonConfiguration' => $payPalConfiguration->getButtonConfiguration(), $this->name . 'CardFundingSourceImg' => Media::getMediaPath(_PS_MODULE_DIR_ . $this->name . '/views/img/payment-cards.png'), + $this->name . 'VaultedPaymentMarks' => $vaultedPaymentMarks, $this->name . 'CardLogos' => [ 'AMEX' => Media::getMediaPath(_PS_MODULE_DIR_ . $this->name . '/views/img/amex.svg'), 'CB_NATIONALE' => Media::getMediaPath(_PS_MODULE_DIR_ . $this->name . '/views/img/cb.svg'), From 6eb6b1e1602e6aecd502f72cb2a7e287526659a1 Mon Sep 17 00:00:00 2001 From: Laurynas Date: Wed, 13 Mar 2024 17:57:40 +0200 Subject: [PATCH 09/60] Moved vaulting config check to fundingSourceProvider --- config/common.yml | 4 ++- ps_checkout.php | 26 +++++---------- src/FundingSource/FundingSourceProvider.php | 35 ++++++++++++++++++--- 3 files changed, 42 insertions(+), 23 deletions(-) diff --git a/config/common.yml b/config/common.yml index 5f0492fff..62dba6b39 100644 --- a/config/common.yml +++ b/config/common.yml @@ -126,11 +126,11 @@ services: class: 'PrestaShop\Module\PrestashopCheckout\PayPal\Payment\Capture\EventSubscriber\PayPalCaptureEventSubscriber' public: true arguments: + - '@ps_checkout.module' - '@PrestaShop\Module\PrestashopCheckout\Order\Service\CheckOrderAmount' - "@ps_checkout.cache.paypal.capture" - "@ps_checkout.cache.paypal.order" - '@PrestaShop\Module\PrestashopCheckout\Order\State\Service\OrderStateMapper' - - "@ps_checkout.bus.command" PrestaShop\Module\PrestashopCheckout\PayPal\Payment\Refund\EventSubscriber\PayPalRefundEventSubscriber: class: 'PrestaShop\Module\PrestashopCheckout\PayPal\Payment\Refund\EventSubscriber\PayPalRefundEventSubscriber' @@ -330,6 +330,8 @@ services: - '@PrestaShop\Module\PrestashopCheckout\FundingSource\FundingSourceCollectionBuilder' - '@PrestaShop\Module\PrestashopCheckout\FundingSource\FundingSourcePresenter' - '@PrestaShop\Module\PrestashopCheckout\Repository\PaymentTokenRepository' + - '@PrestaShop\Module\PrestashopCheckout\Configuration\PrestaShopConfiguration' + - '@PrestaShop\Module\PrestashopCheckout\Context\PrestaShopContext' ps_checkout.repository.paypal.code: class: 'PrestaShop\Module\PrestashopCheckout\Repository\PayPalCodeRepository' diff --git a/ps_checkout.php b/ps_checkout.php index 32b5891f6..4c14cc3f4 100755 --- a/ps_checkout.php +++ b/ps_checkout.php @@ -592,26 +592,16 @@ public function hookPaymentOptions(array $params) /** @var PrestaShop\Module\PrestashopCheckout\Configuration\PrestaShopConfiguration $psConfiguration */ $psConfiguration = $this->getService('ps_checkout.configuration'); - $vaultingEnabled = $psConfiguration->get( - 'PS_CHECKOUT_VAULTING', - [ - 'id_shop' => (int) $cart->id_shop, - 'default' => '0', - ] - ); - $paymentOptions = []; - if ((int) $vaultingEnabled) { - foreach ($fundingSourceProvider->getSavedTokens($cart->id_customer) as $fundingSource) { - $paymentOption = new PrestaShop\PrestaShop\Core\Payment\PaymentOption(); - $paymentOption->setModuleName($this->name . '-' . $fundingSource->name); - $paymentOption->setCallToActionText($fundingSource->label); - $paymentOption->setBinary(true); - $paymentOption->setAdditionalInformation('THIS IS VAULTED PAYMENT METHOD'); + foreach ($fundingSourceProvider->getSavedTokens($cart->id_customer) as $fundingSource) { + $paymentOption = new PrestaShop\PrestaShop\Core\Payment\PaymentOption(); + $paymentOption->setModuleName($this->name . '-' . $fundingSource->name); + $paymentOption->setCallToActionText($fundingSource->label); + $paymentOption->setBinary(true); + $paymentOption->setAdditionalInformation('THIS IS VAULTED PAYMENT METHOD'); - $paymentOptions[] = $paymentOption; - } + $paymentOptions[] = $paymentOption; } foreach ($fundingSourceProvider->getAll() as $fundingSource) { @@ -923,7 +913,7 @@ public function hookActionFrontControllerSetMedia() $isCardAvailable = false; $vaultedPaymentMarks = []; - foreach ($fundingSourceProvider->getSavedTokens(\Context::getContext()->customer->id) as $fundingSource) { + foreach ($fundingSourceProvider->getSavedTokens($this->context->customer->id) as $fundingSource) { $fundingSourcesSorted[] = $fundingSource->name; $payWithTranslations[$fundingSource->name] = $fundingSource->label; $vaultedPaymentMarks[$fundingSource->name] = $this->getPathUri() .'views/img/' . $fundingSource->paymentSource .'.svg'; diff --git a/src/FundingSource/FundingSourceProvider.php b/src/FundingSource/FundingSourceProvider.php index a8660f881..cbfac4bb9 100644 --- a/src/FundingSource/FundingSourceProvider.php +++ b/src/FundingSource/FundingSourceProvider.php @@ -20,6 +20,8 @@ namespace PrestaShop\Module\PrestashopCheckout\FundingSource; +use PrestaShop\Module\PrestashopCheckout\Configuration\PrestaShopConfiguration; +use PrestaShop\Module\PrestashopCheckout\Context\PrestaShopContext; use PrestaShop\Module\PrestashopCheckout\Repository\PaymentTokenRepository; class FundingSourceProvider @@ -37,20 +39,33 @@ class FundingSourceProvider * @var PaymentTokenRepository */ private $paymentTokenRepository; + /** + * @var PrestaShopConfiguration + */ + private $prestaShopConfiguration; + /** + * @var PrestaShopContext + */ + private $prestaShopContext; /** * @param FundingSourceCollectionBuilder $fundingSourceCollectionBuilder * @param FundingSourcePresenter $presenter * @param PaymentTokenRepository $paymentTokenRepository + * @param PrestaShopConfiguration $prestaShopConfiguration */ public function __construct( FundingSourceCollectionBuilder $fundingSourceCollectionBuilder, FundingSourcePresenter $presenter, - PaymentTokenRepository $paymentTokenRepository + PaymentTokenRepository $paymentTokenRepository, + PrestaShopConfiguration $prestaShopConfiguration, + PrestaShopContext $prestaShopContext ) { $this->collection = new FundingSourceCollection($fundingSourceCollectionBuilder->create()); $this->presenter = $presenter; $this->paymentTokenRepository = $paymentTokenRepository; + $this->prestaShopConfiguration = $prestaShopConfiguration; + $this->prestaShopContext = $prestaShopContext; } /** @@ -83,8 +98,20 @@ public function getAll($isAdmin = false) */ public function getSavedTokens($customerId) { - return array_map(function ($paymentToken) { - return $this->presenter->presentPaymentToken($paymentToken); - }, $this->paymentTokenRepository->getAllByCustomerId((int) $customerId)); + $vaultingEnabled = $this->prestaShopConfiguration->get( + 'PS_CHECKOUT_VAULTING', + [ + 'id_shop' => (int) $this->prestaShopContext->getShopId(), + 'default' => '0', + ] + ); + + if ((int) $customerId && (int) $vaultingEnabled) { + return array_map(function ($paymentToken) { + return $this->presenter->presentPaymentToken($paymentToken); + }, $this->paymentTokenRepository->getAllByCustomerId((int)$customerId)); + } + + return []; } } From 646f7d4907139de4e61e356a02ad8cdfd3fa1c26 Mon Sep 17 00:00:00 2001 From: Laurynas Date: Wed, 13 Mar 2024 17:58:09 +0200 Subject: [PATCH 10/60] CS fix --- ps_checkout.php | 2 +- src/Entity/PaymentToken.php | 19 +++++++++++++++++++ src/FundingSource/FundingSourceProvider.php | 4 +++- src/Repository/PaymentTokenRepository.php | 19 +++++++++++++++++++ 4 files changed, 42 insertions(+), 2 deletions(-) diff --git a/ps_checkout.php b/ps_checkout.php index 4c14cc3f4..f4f0bba46 100755 --- a/ps_checkout.php +++ b/ps_checkout.php @@ -916,7 +916,7 @@ public function hookActionFrontControllerSetMedia() foreach ($fundingSourceProvider->getSavedTokens($this->context->customer->id) as $fundingSource) { $fundingSourcesSorted[] = $fundingSource->name; $payWithTranslations[$fundingSource->name] = $fundingSource->label; - $vaultedPaymentMarks[$fundingSource->name] = $this->getPathUri() .'views/img/' . $fundingSource->paymentSource .'.svg'; + $vaultedPaymentMarks[$fundingSource->name] = $this->getPathUri() . 'views/img/' . $fundingSource->paymentSource . '.svg'; } foreach ($fundingSourceProvider->getAll() as $fundingSource) { diff --git a/src/Entity/PaymentToken.php b/src/Entity/PaymentToken.php index 5cc22b5f4..c1c4146bc 100644 --- a/src/Entity/PaymentToken.php +++ b/src/Entity/PaymentToken.php @@ -1,4 +1,23 @@ + * @copyright Since 2007 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0 + */ + namespace PrestaShop\Module\PrestashopCheckout\Entity; diff --git a/src/FundingSource/FundingSourceProvider.php b/src/FundingSource/FundingSourceProvider.php index cbfac4bb9..968817cb0 100644 --- a/src/FundingSource/FundingSourceProvider.php +++ b/src/FundingSource/FundingSourceProvider.php @@ -93,7 +93,9 @@ public function getAll($isAdmin = false) /** * @param int $customerId + * * @return FundingSource[] + * * @throws \PrestaShopDatabaseException */ public function getSavedTokens($customerId) @@ -109,7 +111,7 @@ public function getSavedTokens($customerId) if ((int) $customerId && (int) $vaultingEnabled) { return array_map(function ($paymentToken) { return $this->presenter->presentPaymentToken($paymentToken); - }, $this->paymentTokenRepository->getAllByCustomerId((int)$customerId)); + }, $this->paymentTokenRepository->getAllByCustomerId((int) $customerId)); } return []; diff --git a/src/Repository/PaymentTokenRepository.php b/src/Repository/PaymentTokenRepository.php index 479e5f3c8..75485eb81 100644 --- a/src/Repository/PaymentTokenRepository.php +++ b/src/Repository/PaymentTokenRepository.php @@ -1,4 +1,23 @@ + * @copyright Since 2007 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0 + */ + namespace PrestaShop\Module\PrestashopCheckout\Repository; From d1654f74bbe7fc1f1d1c3821122da24c4a09842d Mon Sep 17 00:00:00 2001 From: Laurynas Date: Thu, 14 Mar 2024 17:48:14 +0200 Subject: [PATCH 11/60] Added forms for vaulting payments and setting as favorite --- .../common/card-fields.component.js | 13 ++++++ .../common/payment-token.component.js | 28 ++++++----- .../common/smart-button.component.js | 14 ++++++ ps_checkout.php | 24 +++++++++- views/templates/hook/partials/cardFields.tpl | 7 +++ .../hook/partials/vaultPaymentFields.tpl | 46 +++++++++++++++++++ .../hook/partials/vaultPaymentForm.tpl | 33 +++++++++++++ .../hook/partials/vaultTokenForm.tpl | 38 +++++++++++++++ 8 files changed, 189 insertions(+), 14 deletions(-) create mode 100644 views/templates/hook/partials/vaultPaymentFields.tpl create mode 100644 views/templates/hook/partials/vaultPaymentForm.tpl create mode 100644 views/templates/hook/partials/vaultTokenForm.tpl diff --git a/_dev/js/front/src/components/common/card-fields.component.js b/_dev/js/front/src/components/common/card-fields.component.js index 07cbbdf03..67bf71e51 100644 --- a/_dev/js/front/src/components/common/card-fields.component.js +++ b/_dev/js/front/src/components/common/card-fields.component.js @@ -186,6 +186,18 @@ export class CardFieldsComponent extends BaseComponent { this.toggleCardFieldsErrors(); } + getVaultFormData() { + if (this.data.HTMLElementCardForm) { + const formData = new FormData(this.data.HTMLElementCardForm); + return { + vault: formData.get(`ps_checkout-vault-payment-${this.data.name}`) === '1', + favorite: formData.get(`ps_checkout-favorite-payment-${this.data.name}`) === '1' + }; + + } + return {}; + } + renderPayPalCardFields() { this.data.HTMLElementCardForm.classList.toggle('loading', true); @@ -223,6 +235,7 @@ export class CardFieldsComponent extends BaseComponent { return this.psCheckoutApi .postCreateOrder({ + ...this.getVaultFormData(), ...data, fundingSource: this.data.name, isHostedFields: true diff --git a/_dev/js/front/src/components/common/payment-token.component.js b/_dev/js/front/src/components/common/payment-token.component.js index 7fadcdb6b..c0264b911 100644 --- a/_dev/js/front/src/components/common/payment-token.component.js +++ b/_dev/js/front/src/components/common/payment-token.component.js @@ -44,13 +44,9 @@ export class PaymentTokenComponent extends BaseComponent { this.data.conditions = this.app.root.children.conditionsCheckbox; this.data.loader = this.app.root.children.loader; this.data.notification = this.app.root.children.notification; - this.data.HTMLElementBaseButton = - this.querySelectorService.getBasePaymentConfirmation(); + this.data.HTMLElementBaseButton = this.querySelectorService.getBasePaymentConfirmation(); this.data.HTMLElementButton = null; this.data.HTMLElementButtonWrapper = this.getButtonWrapper(); - - this.data.fundingSource = this.data.name.split('-')[0]; - this.data.tokenId = this.data.name.split('-')[1]; } getButtonWrapper() { @@ -68,13 +64,22 @@ export class PaymentTokenComponent extends BaseComponent { this.createOrder().then(() => this.validateOrder()); } + getVaultFormData() { + const form = document.querySelector(`form#ps_checkout-vault-token-form-${this.data.name}`); + if (form) { + const formData = new FormData(form); + return { + favorite: formData.get(`ps_checkout-favorite-payment-${this.data.name}`) === '1', + fundingSource: formData.get(`ps_checkout-funding-source-${this.data.name}`), + vaultId: formData.get(`ps_checkout-vault-id-${this.data.name}`) + }; + + } + return {}; + } + createOrder() { - return this.psCheckoutApi.postCreateOrder( - { - fundingSource: this.data.fundingSource, - tokenId: this.data.tokenId - } - ) + return this.psCheckoutApi.postCreateOrder(this.getVaultFormData()) .then((data) => { this.data.orderId = data; this.validateOrder(); @@ -94,7 +99,6 @@ export class PaymentTokenComponent extends BaseComponent { }).catch((error) => this.handleError(error)); } - renderButton() { this.data.HTMLElementButton = this.data.HTMLElementBaseButton.cloneNode(true); diff --git a/_dev/js/front/src/components/common/smart-button.component.js b/_dev/js/front/src/components/common/smart-button.component.js index 146ea21de..715bbbe50 100644 --- a/_dev/js/front/src/components/common/smart-button.component.js +++ b/_dev/js/front/src/components/common/smart-button.component.js @@ -158,6 +158,7 @@ export class SmartButtonComponent extends BaseComponent { createOrder: (data) => { return this.psCheckoutApi .postCreateOrder({ + ...this.getVaultFormData(), ...data, fundingSource: this.data.name, isExpressCheckout: this.config.expressCheckout.active @@ -177,6 +178,19 @@ export class SmartButtonComponent extends BaseComponent { .render(buttonSelector); } + getVaultFormData() { + const form = document.querySelector(`form#ps_checkout-vault-payment-form-${this.data.name}`); + if (form) { + const formData = new FormData(form); + return { + vault: formData.get(`ps_checkout-vault-payment-${this.data.name}`) === '1', + favorite: formData.get(`ps_checkout-favorite-payment-${this.data.name}`) === '1' + }; + + } + return {}; + } + handleError(error) { let errorMessage = error; diff --git a/ps_checkout.php b/ps_checkout.php index f4f0bba46..b174f044e 100755 --- a/ps_checkout.php +++ b/ps_checkout.php @@ -594,12 +594,26 @@ public function hookPaymentOptions(array $params) $paymentOptions = []; + $vaultingEnabled = (bool) $psConfiguration->get( + 'PS_CHECKOUT_VAULTING', + [ + 'id_shop' => (int) $cart->id_shop, + 'default' => '0', + ] + ); + foreach ($fundingSourceProvider->getSavedTokens($cart->id_customer) as $fundingSource) { $paymentOption = new PrestaShop\PrestaShop\Core\Payment\PaymentOption(); $paymentOption->setModuleName($this->name . '-' . $fundingSource->name); $paymentOption->setCallToActionText($fundingSource->label); $paymentOption->setBinary(true); - $paymentOption->setAdditionalInformation('THIS IS VAULTED PAYMENT METHOD'); + + $this->context->smarty->assign([ + 'fundingSource' => $fundingSource->name, + 'paymentSource' => $fundingSource->paymentSource, + 'vaultId' => explode('-', $fundingSource->name)[1] + ]); + $paymentOption->setForm($this->context->smarty->fetch('module:ps_checkout/views/templates/hook/partials/vaultTokenForm.tpl')); $paymentOptions[] = $paymentOption; } @@ -609,10 +623,16 @@ public function hookPaymentOptions(array $params) $paymentOption->setModuleName($this->name . '-' . $fundingSource->name); $paymentOption->setCallToActionText($fundingSource->label); $paymentOption->setBinary(true); + $this->context->smarty->assign([ + 'vaultingEnabled' => $vaultingEnabled, + 'fundingSource' => $fundingSource->name + ]); if ('card' === $fundingSource->name && $configurationPayPal->isHostedFieldsEnabled() && in_array($configurationPayPal->getCardHostedFieldsStatus(), ['SUBSCRIBED', 'LIMITED'], true)) { - $this->context->smarty->assign('modulePath', $this->getPathUri()); + $this->context->smarty->assign('modulePath',$this->getPathUri()); $paymentOption->setForm($this->context->smarty->fetch('module:ps_checkout/views/templates/hook/partials/cardFields.tpl')); + } else if (in_array($fundingSource->name, ['paypal', /*'venmo'*/])) { + $paymentOption->setForm($this->context->smarty->fetch('module:ps_checkout/views/templates/hook/partials/vaultPaymentForm.tpl')); } $paymentOptions[] = $paymentOption; diff --git a/views/templates/hook/partials/cardFields.tpl b/views/templates/hook/partials/cardFields.tpl index 0443c5718..5625c05f1 100644 --- a/views/templates/hook/partials/cardFields.tpl +++ b/views/templates/hook/partials/cardFields.tpl @@ -26,6 +26,8 @@ * * Script tags will be removed and some HTML5 element can cause an Exception due to DOMDocument class *} +
+
@@ -60,9 +62,14 @@
+ {if $vaultingEnabled} + {include file='module:ps_checkout/views/templates/hook/partials/vaultPaymentFields.tpl' fundingSource='card'} + {/if}
+ +