From 24447d435442bf91579b11b997fbaead07ec9bad Mon Sep 17 00:00:00 2001 From: L3RAZ Date: Mon, 20 Jan 2025 16:09:30 +0200 Subject: [PATCH 1/7] Added new 3DS status in admin order page --- classes/PsCheckoutCart.php | 39 ++++++++++++++++++++ config/common.yml | 2 ++ src/Checkout/CheckoutChecker.php | 24 +++++++++++-- src/Presenter/Order/OrderPresenter.php | 9 +++++ upgrade/upgrade-8.4.3.1.php | 44 +++++++++++++++++++++++ views/templates/admin/ajaxPayPalOrder.tpl | 6 +++- 6 files changed, 120 insertions(+), 4 deletions(-) create mode 100644 upgrade/upgrade-8.4.3.1.php diff --git a/classes/PsCheckoutCart.php b/classes/PsCheckoutCart.php index 376c355b6..e1d53d915 100755 --- a/classes/PsCheckoutCart.php +++ b/classes/PsCheckoutCart.php @@ -29,6 +29,7 @@ class PsCheckoutCart extends ObjectModel const STATUS_APPROVAL_REVERSED = 'REVERSED'; const STATUS_PENDING_APPROVAL = 'PENDING_APPROVAL'; const STATUS_PARTIALLY_COMPLETED = 'PARTIALLY_COMPLETED'; + const THREE_D_SECURE_NOT_REQUIRED = 'THREE_D_SECURE_NOT_REQUIRED'; /** * @var int|null Cart Identifier @@ -75,6 +76,8 @@ class PsCheckoutCart extends ObjectModel */ public $environment = 'LIVE'; + public $additional_tags = ''; + /** * @var bool */ @@ -157,6 +160,12 @@ class PsCheckoutCart extends ObjectModel 'allow_null' => true, 'required' => false, ], + 'additional_tags' => [ + 'type' => self::TYPE_STRING, + 'validate' => 'isGenericName', + 'allow_null' => true, + 'required' => false, + ], 'isExpressCheckout' => [ 'type' => self::TYPE_BOOL, 'validate' => 'isBool', @@ -329,4 +338,34 @@ public function isOrderAvailable() && in_array($this->paypal_status, [PsCheckoutCart::STATUS_CREATED, PsCheckoutCart::STATUS_APPROVED, PsCheckoutCart::STATUS_PAYER_ACTION_REQUIRED], true) && !$this->isPayPalOrderExpired(); } + + /** + * @return string[] + */ + public function getAdditionalTags() + { + return explode(',', (string) $this->additional_tags); + } + + /** + * @param array $tags + * + * @return void + */ + public function setAdditionalTags(array $tags) + { + $this->additional_tags = implode(',', $tags); + } + + /** + * @param string $tag + * + * @return void + */ + public function addAdditionalTag($tag) + { + $tags = $this->getAdditionalTags(); + $tags[] = $tag; + $this->setAdditionalTags(array_unique($tags)); + } } diff --git a/config/common.yml b/config/common.yml index 05aa5b33f..11fddcd7b 100644 --- a/config/common.yml +++ b/config/common.yml @@ -454,6 +454,8 @@ services: public: true arguments: - "@ps_checkout.logger" + - '@PrestaShop\Module\PrestashopCheckout\Repository\PsCheckoutCartRepository' + - '@PrestaShop\Module\PrestashopCheckout\PayPal\PayPalConfiguration' PrestaShop\Module\PrestashopCheckout\PayPal\PaymentToken\PaymentMethodTokenService: class: 'PrestaShop\Module\PrestashopCheckout\PayPal\PaymentToken\PaymentMethodTokenService' diff --git a/src/Checkout/CheckoutChecker.php b/src/Checkout/CheckoutChecker.php index df8bc537c..afca2f94b 100644 --- a/src/Checkout/CheckoutChecker.php +++ b/src/Checkout/CheckoutChecker.php @@ -25,6 +25,9 @@ use Customer; use PrestaShop\Module\PrestashopCheckout\Exception\PsCheckoutException; use PrestaShop\Module\PrestashopCheckout\PayPal\Card3DSecure; +use PrestaShop\Module\PrestashopCheckout\PayPal\PayPalConfiguration; +use PrestaShop\Module\PrestashopCheckout\Repository\PsCheckoutCartRepository; +use PsCheckoutCart; use Psr\Log\LoggerInterface; use Validate; @@ -35,12 +38,23 @@ class CheckoutChecker */ private $logger; + /** + * @var PsCheckoutCartRepository + */ + private $psCheckoutCartRepository; + /** + * @var PayPalConfiguration + */ + private $payPalConfiguration; + /** * @param LoggerInterface $logger */ - public function __construct(LoggerInterface $logger) + public function __construct(LoggerInterface $logger, PsCheckoutCartRepository $psCheckoutCartRepository, PayPalConfiguration $payPalConfiguration) { $this->logger = $logger; + $this->psCheckoutCartRepository = $psCheckoutCartRepository; + $this->payPalConfiguration = $payPalConfiguration; } /** @@ -90,8 +104,12 @@ public function continueWithAuthorization($cartId, $orderPayPal) case Card3DSecure::RETRY: throw new PsCheckoutException('Card Strong Customer Authentication must be retried.', PsCheckoutException::PAYPAL_PAYMENT_CARD_SCA_UNKNOWN); case Card3DSecure::NO_DECISION: - if (Configuration::get('PS_CHECKOUT_LIABILITY_SHIFT_REQ')) { - throw new PsCheckoutException('No liability shift to card issuer', PsCheckoutException::PAYPAL_PAYMENT_CARD_SCA_UNKNOWN); + if ($this->payPalConfiguration->getHostedFieldsContingencies() === 'SCA_WHEN_REQUIRED') { + $psCheckoutCart = $this->psCheckoutCartRepository->findOneByCartId($cartId); + if ($psCheckoutCart ) { + $psCheckoutCart->addAdditionalTag(PsCheckoutCart::THREE_D_SECURE_NOT_REQUIRED); + $psCheckoutCart->save(); + } } break; } diff --git a/src/Presenter/Order/OrderPresenter.php b/src/Presenter/Order/OrderPresenter.php index 9e1a9a076..9d4665cb2 100644 --- a/src/Presenter/Order/OrderPresenter.php +++ b/src/Presenter/Order/OrderPresenter.php @@ -24,6 +24,7 @@ use PrestaShop\Module\PrestashopCheckout\PayPal\Card3DSecure; use PrestaShop\Module\PrestashopCheckout\Presenter\Date\DatePresenter; use PrestaShop\Module\PrestashopCheckout\Provider\PaymentMethodLogoProvider; +use PrestaShop\Module\PrestashopCheckout\Repository\PsCheckoutCartRepository; use Ps_checkout; use PsCheckoutCart; @@ -42,6 +43,10 @@ class OrderPresenter * @var FundingSourceTranslationProvider */ private $fundingSourceTranslationProvider; + /** + * @var PsCheckoutCartRepository + */ + private $psCheckoutCartRepository; /** * @param Ps_checkout $module @@ -54,6 +59,7 @@ public function __construct(Ps_checkout $module, array $orderPayPal) /** @var FundingSourceTranslationProvider $fundingSourceTranslationProvider */ $fundingSourceTranslationProvider = $this->module->getService(FundingSourceTranslationProvider::class); $this->fundingSourceTranslationProvider = $fundingSourceTranslationProvider; + $this->psCheckoutCartRepository = $this->module->getService(PsCheckoutCartRepository::class); } /** @@ -65,6 +71,8 @@ public function present() return []; } + $psCheckoutCart = $this->psCheckoutCartRepository->findOneByPayPalOrderId($this->orderPayPal['id']); + $card3DSecure = new Card3DSecure(); return array_merge( @@ -73,6 +81,7 @@ public function present() 'intent' => $this->orderPayPal['intent'], 'status' => $this->getOrderStatus(), 'transactions' => $this->getTransactions(), + 'is3DSNotRequired' => in_array(PsCheckoutCart::THREE_D_SECURE_NOT_REQUIRED, $psCheckoutCart->getAdditionalTags()), 'is3DSecureAvailable' => $card3DSecure->is3DSecureAvailable($this->orderPayPal), 'isLiabilityShifted' => $card3DSecure->isLiabilityShifted($this->orderPayPal), 'paymentSource' => $this->getPaymentSourceName($this->orderPayPal), diff --git a/upgrade/upgrade-8.4.3.1.php b/upgrade/upgrade-8.4.3.1.php new file mode 100644 index 000000000..9182cc467 --- /dev/null +++ b/upgrade/upgrade-8.4.3.1.php @@ -0,0 +1,44 @@ + + * @copyright Since 2007 PrestaShop SA and Contributors + * @license https://opensource.org/licenses/AFL-3.0 Academic Free License version 3.0 + */ +if (!defined('_PS_VERSION_')) { + exit; +} + +/** + * Update main function for module version 8.4.2.0 + * + * @param Ps_checkout $module + * + * @return bool + */ +function upgrade_module_8_4_2_0($module) +{ + try { + $db = Db::getInstance(); + + $db->execute('ALTER TABLE `' . _DB_PREFIX_ . 'pscheckout_cart` ADD COLUMN `additional_tags` varchar(255) DEFAULT NULL;'); + } catch (Exception $exception) { + PrestaShopLogger::addLog($exception->getMessage(), 4, 1, 'Module', $module->id); + + return false; + } + + return true; +} diff --git a/views/templates/admin/ajaxPayPalOrder.tpl b/views/templates/admin/ajaxPayPalOrder.tpl index 2f545646a..8d23c593f 100755 --- a/views/templates/admin/ajaxPayPalOrder.tpl +++ b/views/templates/admin/ajaxPayPalOrder.tpl @@ -82,7 +82,11 @@ {if $psCheckoutCart->paypal_funding === 'card'}
{l s='3D Secure' mod='ps_checkout'}
- {if $orderPayPal.is3DSecureAvailable && $orderPayPal.isLiabilityShifted} + {if $orderPayPal.is3DSNotRequired} + + {l s='Not required' mod='ps_checkout'} + + {elseif $orderPayPal.is3DSecureAvailable && $orderPayPal.isLiabilityShifted} {l s='Success' mod='ps_checkout'} From 00c35fc7ab8c24c9322aa85758c202f437a0fbd0 Mon Sep 17 00:00:00 2001 From: L3RAZ Date: Tue, 21 Jan 2025 13:31:20 +0200 Subject: [PATCH 2/7] Moved logic to PayPalOrder object --- classes/PsCheckoutCart.php | 39 ------------------------ config/common.yml | 2 +- src/Checkout/CheckoutChecker.php | 25 +++++++++------ src/PayPal/Order/Entity/PayPalOrder.php | 37 +++++++++++++++++++++- src/Presenter/Order/OrderPresenter.php | 19 +++++++++--- src/Repository/PayPalOrderRepository.php | 7 +++-- upgrade/upgrade-8.4.3.1.php | 2 +- 7 files changed, 72 insertions(+), 59 deletions(-) diff --git a/classes/PsCheckoutCart.php b/classes/PsCheckoutCart.php index e1d53d915..376c355b6 100755 --- a/classes/PsCheckoutCart.php +++ b/classes/PsCheckoutCart.php @@ -29,7 +29,6 @@ class PsCheckoutCart extends ObjectModel const STATUS_APPROVAL_REVERSED = 'REVERSED'; const STATUS_PENDING_APPROVAL = 'PENDING_APPROVAL'; const STATUS_PARTIALLY_COMPLETED = 'PARTIALLY_COMPLETED'; - const THREE_D_SECURE_NOT_REQUIRED = 'THREE_D_SECURE_NOT_REQUIRED'; /** * @var int|null Cart Identifier @@ -76,8 +75,6 @@ class PsCheckoutCart extends ObjectModel */ public $environment = 'LIVE'; - public $additional_tags = ''; - /** * @var bool */ @@ -160,12 +157,6 @@ class PsCheckoutCart extends ObjectModel 'allow_null' => true, 'required' => false, ], - 'additional_tags' => [ - 'type' => self::TYPE_STRING, - 'validate' => 'isGenericName', - 'allow_null' => true, - 'required' => false, - ], 'isExpressCheckout' => [ 'type' => self::TYPE_BOOL, 'validate' => 'isBool', @@ -338,34 +329,4 @@ public function isOrderAvailable() && in_array($this->paypal_status, [PsCheckoutCart::STATUS_CREATED, PsCheckoutCart::STATUS_APPROVED, PsCheckoutCart::STATUS_PAYER_ACTION_REQUIRED], true) && !$this->isPayPalOrderExpired(); } - - /** - * @return string[] - */ - public function getAdditionalTags() - { - return explode(',', (string) $this->additional_tags); - } - - /** - * @param array $tags - * - * @return void - */ - public function setAdditionalTags(array $tags) - { - $this->additional_tags = implode(',', $tags); - } - - /** - * @param string $tag - * - * @return void - */ - public function addAdditionalTag($tag) - { - $tags = $this->getAdditionalTags(); - $tags[] = $tag; - $this->setAdditionalTags(array_unique($tags)); - } } diff --git a/config/common.yml b/config/common.yml index 11fddcd7b..9cf3d5fb4 100644 --- a/config/common.yml +++ b/config/common.yml @@ -454,7 +454,7 @@ services: public: true arguments: - "@ps_checkout.logger" - - '@PrestaShop\Module\PrestashopCheckout\Repository\PsCheckoutCartRepository' + - '@PrestaShop\Module\PrestashopCheckout\Repository\PayPalOrderRepository' - '@PrestaShop\Module\PrestashopCheckout\PayPal\PayPalConfiguration' PrestaShop\Module\PrestashopCheckout\PayPal\PaymentToken\PaymentMethodTokenService: diff --git a/src/Checkout/CheckoutChecker.php b/src/Checkout/CheckoutChecker.php index afca2f94b..6a28b4086 100644 --- a/src/Checkout/CheckoutChecker.php +++ b/src/Checkout/CheckoutChecker.php @@ -25,7 +25,9 @@ use Customer; use PrestaShop\Module\PrestashopCheckout\Exception\PsCheckoutException; use PrestaShop\Module\PrestashopCheckout\PayPal\Card3DSecure; +use PrestaShop\Module\PrestashopCheckout\PayPal\Order\Entity\PayPalOrder; use PrestaShop\Module\PrestashopCheckout\PayPal\PayPalConfiguration; +use PrestaShop\Module\PrestashopCheckout\Repository\PayPalOrderRepository; use PrestaShop\Module\PrestashopCheckout\Repository\PsCheckoutCartRepository; use PsCheckoutCart; use Psr\Log\LoggerInterface; @@ -37,23 +39,26 @@ class CheckoutChecker * @var LoggerInterface */ private $logger; - /** - * @var PsCheckoutCartRepository + * @var PayPalOrderRepository */ - private $psCheckoutCartRepository; + private $payPalOrderRepository; /** * @var PayPalConfiguration */ private $payPalConfiguration; + /** * @param LoggerInterface $logger */ - public function __construct(LoggerInterface $logger, PsCheckoutCartRepository $psCheckoutCartRepository, PayPalConfiguration $payPalConfiguration) - { + public function __construct( + LoggerInterface $logger, + PayPalOrderRepository $payPalOrderRepository, + PayPalConfiguration $payPalConfiguration + ) { $this->logger = $logger; - $this->psCheckoutCartRepository = $psCheckoutCartRepository; + $this->payPalOrderRepository = $payPalOrderRepository; $this->payPalConfiguration = $payPalConfiguration; } @@ -105,10 +110,10 @@ public function continueWithAuthorization($cartId, $orderPayPal) throw new PsCheckoutException('Card Strong Customer Authentication must be retried.', PsCheckoutException::PAYPAL_PAYMENT_CARD_SCA_UNKNOWN); case Card3DSecure::NO_DECISION: if ($this->payPalConfiguration->getHostedFieldsContingencies() === 'SCA_WHEN_REQUIRED') { - $psCheckoutCart = $this->psCheckoutCartRepository->findOneByCartId($cartId); - if ($psCheckoutCart ) { - $psCheckoutCart->addAdditionalTag(PsCheckoutCart::THREE_D_SECURE_NOT_REQUIRED); - $psCheckoutCart->save(); + $payPalOrder = $this->payPalOrderRepository->getPayPalOrderByCartId($cartId); + if ($payPalOrder) { + $payPalOrder->addTag(PayPalOrder::THREE_D_SECURE_NOT_REQUIRED); + $this->payPalOrderRepository->savePayPalOrder($payPalOrder); } } break; diff --git a/src/PayPal/Order/Entity/PayPalOrder.php b/src/PayPal/Order/Entity/PayPalOrder.php index 3e367a65a..53c3c86d7 100644 --- a/src/PayPal/Order/Entity/PayPalOrder.php +++ b/src/PayPal/Order/Entity/PayPalOrder.php @@ -29,6 +29,7 @@ class PayPalOrder const CUSTOMER_INTENT_VAULT = 'VAULT'; const CUSTOMER_INTENT_FAVORITE = 'FAVORITE'; const CUSTOMER_INTENT_USES_VAULTING = 'USES_VAULTING'; + const THREE_D_SECURE_NOT_REQUIRED = '3DS_NOT_REQUIRED'; /** * @var PayPalOrderId @@ -74,8 +75,12 @@ class PayPalOrder * @var PaymentTokenId|null */ private $paymentTokenId; + /** + * @var array + */ + private $tags; - public function __construct($id, $idCart, $intent, $fundingSource, $status, $paymentSource = [], $environment = 'LIVE', $isCardFields = false, $isExpressCheckout = false, $customerIntent = [], $paymentTokenId = null) + public function __construct($id, $idCart, $intent, $fundingSource, $status, $paymentSource = [], $environment = 'LIVE', $isCardFields = false, $isExpressCheckout = false, $customerIntent = [], $paymentTokenId = null, $tags = []) { $this->id = new PayPalOrderId($id); $this->idCart = $idCart; @@ -88,6 +93,7 @@ public function __construct($id, $idCart, $intent, $fundingSource, $status, $pay $this->isExpressCheckout = (bool) $isExpressCheckout; $this->customerIntent = $customerIntent; $this->paymentTokenId = $paymentTokenId; + $this->tags = $tags; } /** @@ -317,4 +323,33 @@ public function setPaymentTokenId($paymentTokenId) { $this->paymentTokenId = $paymentTokenId; } + + + /** + * @return string[] + */ + public function getTags() + { + return $this->tags; + } + + /** + * @param array $tags + * + * @return void + */ + public function setTags(array $tags) + { + $this->tags = $tags; + } + + /** + * @param string $tag + * + * @return void + */ + public function addTag($tag) + { + $this->tags[] = $tag; + } } diff --git a/src/Presenter/Order/OrderPresenter.php b/src/Presenter/Order/OrderPresenter.php index 9d4665cb2..2ffabc89b 100644 --- a/src/Presenter/Order/OrderPresenter.php +++ b/src/Presenter/Order/OrderPresenter.php @@ -20,10 +20,14 @@ namespace PrestaShop\Module\PrestashopCheckout\Presenter\Order; +use PrestaShop\Module\PrestashopCheckout\Exception\PsCheckoutException; use PrestaShop\Module\PrestashopCheckout\FundingSource\FundingSourceTranslationProvider; use PrestaShop\Module\PrestashopCheckout\PayPal\Card3DSecure; +use PrestaShop\Module\PrestashopCheckout\PayPal\Order\Entity\PayPalOrder; +use PrestaShop\Module\PrestashopCheckout\PayPal\Order\ValueObject\PayPalOrderId; use PrestaShop\Module\PrestashopCheckout\Presenter\Date\DatePresenter; use PrestaShop\Module\PrestashopCheckout\Provider\PaymentMethodLogoProvider; +use PrestaShop\Module\PrestashopCheckout\Repository\PayPalOrderRepository; use PrestaShop\Module\PrestashopCheckout\Repository\PsCheckoutCartRepository; use Ps_checkout; use PsCheckoutCart; @@ -44,9 +48,9 @@ class OrderPresenter */ private $fundingSourceTranslationProvider; /** - * @var PsCheckoutCartRepository + * @var PayPalOrderRepository */ - private $psCheckoutCartRepository; + private $payPalOrderRepository; /** * @param Ps_checkout $module @@ -59,7 +63,7 @@ public function __construct(Ps_checkout $module, array $orderPayPal) /** @var FundingSourceTranslationProvider $fundingSourceTranslationProvider */ $fundingSourceTranslationProvider = $this->module->getService(FundingSourceTranslationProvider::class); $this->fundingSourceTranslationProvider = $fundingSourceTranslationProvider; - $this->psCheckoutCartRepository = $this->module->getService(PsCheckoutCartRepository::class); + $this->payPalOrderRepository = $this->module->getService(PayPalOrderRepository::class); } /** @@ -71,7 +75,12 @@ public function present() return []; } - $psCheckoutCart = $this->psCheckoutCartRepository->findOneByPayPalOrderId($this->orderPayPal['id']); + $threeDSNotRequired = false; + + try { + $payPalOrder = $this->payPalOrderRepository->getPayPalOrderById(new PayPalOrderId($this->orderPayPal['id'])); + $threeDSNotRequired = in_array(PayPalOrder::THREE_D_SECURE_NOT_REQUIRED, $payPalOrder->getTags()); + } catch (PsCheckoutException $e) {} $card3DSecure = new Card3DSecure(); @@ -81,7 +90,7 @@ public function present() 'intent' => $this->orderPayPal['intent'], 'status' => $this->getOrderStatus(), 'transactions' => $this->getTransactions(), - 'is3DSNotRequired' => in_array(PsCheckoutCart::THREE_D_SECURE_NOT_REQUIRED, $psCheckoutCart->getAdditionalTags()), + 'is3DSNotRequired' => $threeDSNotRequired, 'is3DSecureAvailable' => $card3DSecure->is3DSecureAvailable($this->orderPayPal), 'isLiabilityShifted' => $card3DSecure->isLiabilityShifted($this->orderPayPal), 'paymentSource' => $this->getPaymentSourceName($this->orderPayPal), diff --git a/src/Repository/PayPalOrderRepository.php b/src/Repository/PayPalOrderRepository.php index 405d43e64..9ec2b4c52 100644 --- a/src/Repository/PayPalOrderRepository.php +++ b/src/Repository/PayPalOrderRepository.php @@ -68,6 +68,7 @@ public function savePayPalOrder(PayPalOrder $payPalOrder) 'is_express_checkout' => (int) $payPalOrder->isExpressCheckout(), 'customer_intent' => pSQL(implode(',', $payPalOrder->getCustomerIntent())), 'payment_token_id' => $payPalOrder->getPaymentTokenId() ? pSQL($payPalOrder->getPaymentTokenId()->getValue()) : null, + 'tags' => pSQL(implode(',', $payPalOrder->getTags())), ], false, true, @@ -112,7 +113,8 @@ public function getPayPalOrderById(PayPalOrderId $payPalOrderId) $queryResult['is_card_fields'], $queryResult['is_express_checkout'], explode(',', $queryResult['customer_intent']), - $queryResult['payment_token_id'] ? new PaymentTokenId($queryResult['payment_token_id']) : null + $queryResult['payment_token_id'] ? new PaymentTokenId($queryResult['payment_token_id']) : null, + explode(',', $queryResult['tags']) ); } @@ -150,7 +152,8 @@ public function getPayPalOrderByCartId($cartId) $queryResult['is_card_fields'], $queryResult['is_express_checkout'], explode(',', $queryResult['customer_intent']), - $queryResult['payment_token_id'] ? new PaymentTokenId($queryResult['payment_token_id']) : null + $queryResult['payment_token_id'] ? new PaymentTokenId($queryResult['payment_token_id']) : null, + explode(',', $queryResult['tags']) ); } diff --git a/upgrade/upgrade-8.4.3.1.php b/upgrade/upgrade-8.4.3.1.php index 9182cc467..8b72a53bd 100644 --- a/upgrade/upgrade-8.4.3.1.php +++ b/upgrade/upgrade-8.4.3.1.php @@ -33,7 +33,7 @@ function upgrade_module_8_4_2_0($module) try { $db = Db::getInstance(); - $db->execute('ALTER TABLE `' . _DB_PREFIX_ . 'pscheckout_cart` ADD COLUMN `additional_tags` varchar(255) DEFAULT NULL;'); + $db->execute('ALTER TABLE `' . _DB_PREFIX_ . 'pscheckout_order` ADD COLUMN `tags` varchar(255) DEFAULT NULL;'); } catch (Exception $exception) { PrestaShopLogger::addLog($exception->getMessage(), 4, 1, 'Module', $module->id); From 0f69d5cc0661f656e3073bbf2ccaf6bf08abb5d0 Mon Sep 17 00:00:00 2001 From: L3RAZ Date: Tue, 21 Jan 2025 13:54:20 +0200 Subject: [PATCH 3/7] Added contingencies check to payment controller --- controllers/front/payment.php | 8 ++++++++ src/Checkout/CheckoutChecker.php | 16 +++++++++------- 2 files changed, 17 insertions(+), 7 deletions(-) diff --git a/controllers/front/payment.php b/controllers/front/payment.php index f2c8575b8..dd5d48367 100644 --- a/controllers/front/payment.php +++ b/controllers/front/payment.php @@ -29,6 +29,7 @@ use PrestaShop\Module\PrestashopCheckout\PayPal\Order\Query\GetPayPalOrderForCheckoutCompletedQuery; use PrestaShop\Module\PrestashopCheckout\PayPal\Order\Query\GetPayPalOrderForCheckoutCompletedQueryResult; use PrestaShop\Module\PrestashopCheckout\PayPal\Order\ValueObject\PayPalOrderId; +use PrestaShop\Module\PrestashopCheckout\PayPal\PayPalConfiguration; use PrestaShop\Module\PrestashopCheckout\Repository\PaymentTokenRepository; use PrestaShop\Module\PrestashopCheckout\Repository\PayPalOrderRepository; @@ -87,6 +88,8 @@ public function postProcess() $this->commandBus = $this->module->getService('ps_checkout.bus.command'); + /** @var PayPalConfiguration $payPalConfiguration */ + $payPalConfiguration = $this->module->getService(PayPalConfiguration::class); /** @var PayPalOrderRepository $payPalOrderRepository */ $payPalOrderRepository = $this->module->getService(PayPalOrderRepository::class); /** @var Psr\SimpleCache\CacheInterface $payPalOrderCache */ @@ -136,6 +139,11 @@ public function postProcess() $this->createOrder($payPalOrderFromCache, $payPalOrder); break; case Card3DSecure::NO_DECISION: + if ($payPalConfiguration->getHostedFieldsContingencies() === 'SCA_WHEN_REQUIRED') { + $this->commandBus->handle(new CapturePayPalOrderCommand($orderId, array_keys($payPalOrderFromCache['payment_source'])[0])); + $payPalOrderFromCache = $payPalOrderCache->get($orderId); + $this->createOrder($payPalOrderFromCache, $payPalOrder); + } default: break; } diff --git a/src/Checkout/CheckoutChecker.php b/src/Checkout/CheckoutChecker.php index 6a28b4086..efd71a034 100644 --- a/src/Checkout/CheckoutChecker.php +++ b/src/Checkout/CheckoutChecker.php @@ -21,15 +21,12 @@ namespace PrestaShop\Module\PrestashopCheckout\Checkout; use Cart; -use Configuration; use Customer; use PrestaShop\Module\PrestashopCheckout\Exception\PsCheckoutException; use PrestaShop\Module\PrestashopCheckout\PayPal\Card3DSecure; use PrestaShop\Module\PrestashopCheckout\PayPal\Order\Entity\PayPalOrder; use PrestaShop\Module\PrestashopCheckout\PayPal\PayPalConfiguration; use PrestaShop\Module\PrestashopCheckout\Repository\PayPalOrderRepository; -use PrestaShop\Module\PrestashopCheckout\Repository\PsCheckoutCartRepository; -use PsCheckoutCart; use Psr\Log\LoggerInterface; use Validate; @@ -76,6 +73,8 @@ public function continueWithAuthorization($cartId, $orderPayPal) throw new PsCheckoutException(sprintf('PayPal Order %s is already captured', $orderPayPal['id']), PsCheckoutException::PAYPAL_ORDER_ALREADY_CAPTURED); } + $contingencies = $this->payPalConfiguration->getHostedFieldsContingencies(); + $paymentSource = isset($orderPayPal['payment_source']) ? key($orderPayPal['payment_source']) : ''; if (in_array($paymentSource, ['google_pay', 'card'], true)) { @@ -93,7 +92,7 @@ public function continueWithAuthorization($cartId, $orderPayPal) (string) Card3DSecure::RETRY, ], [ - Configuration::get('PS_CHECKOUT_LIABILITY_SHIFT_REQ') ? 'Rejected, no liability shift' : 'Proceed, without liability shift', + $contingencies === 'SCA_ALWAYS' ? 'Rejected, no liability shift' : 'Proceed, without liability shift', 'Proceed, liability shift is possible', 'Rejected', 'Retry, ask customer to retry', @@ -109,12 +108,15 @@ public function continueWithAuthorization($cartId, $orderPayPal) case Card3DSecure::RETRY: throw new PsCheckoutException('Card Strong Customer Authentication must be retried.', PsCheckoutException::PAYPAL_PAYMENT_CARD_SCA_UNKNOWN); case Card3DSecure::NO_DECISION: - if ($this->payPalConfiguration->getHostedFieldsContingencies() === 'SCA_WHEN_REQUIRED') { + if ($contingencies === 'SCA_ALWAYS') { + throw new PsCheckoutException('No liability shift to card issuer', PsCheckoutException::PAYPAL_PAYMENT_CARD_SCA_UNKNOWN); + } + if ($contingencies === 'SCA_WHEN_REQUIRED') { $payPalOrder = $this->payPalOrderRepository->getPayPalOrderByCartId($cartId); - if ($payPalOrder) { + try { $payPalOrder->addTag(PayPalOrder::THREE_D_SECURE_NOT_REQUIRED); $this->payPalOrderRepository->savePayPalOrder($payPalOrder); - } + } catch (PsCheckoutException $e) {} } break; } From d1552df75e4a122528a328b389ce3284a65cc719 Mon Sep 17 00:00:00 2001 From: L3RAZ Date: Tue, 21 Jan 2025 16:26:30 +0200 Subject: [PATCH 4/7] Added additional text to order page --- src/Presenter/Order/OrderPresenter.php | 1 + views/templates/admin/ajaxPayPalOrder.tpl | 3 +++ 2 files changed, 4 insertions(+) diff --git a/src/Presenter/Order/OrderPresenter.php b/src/Presenter/Order/OrderPresenter.php index 2ffabc89b..0e2646a30 100644 --- a/src/Presenter/Order/OrderPresenter.php +++ b/src/Presenter/Order/OrderPresenter.php @@ -82,6 +82,7 @@ public function present() $threeDSNotRequired = in_array(PayPalOrder::THREE_D_SECURE_NOT_REQUIRED, $payPalOrder->getTags()); } catch (PsCheckoutException $e) {} + $card3DSecure = new Card3DSecure(); return array_merge( diff --git a/views/templates/admin/ajaxPayPalOrder.tpl b/views/templates/admin/ajaxPayPalOrder.tpl index 8d23c593f..c467d1f4c 100755 --- a/views/templates/admin/ajaxPayPalOrder.tpl +++ b/views/templates/admin/ajaxPayPalOrder.tpl @@ -116,6 +116,9 @@ {if $psCheckoutCart->paypal_funding === 'card' && !$orderPayPal.isLiabilityShifted}
+ {if $orderPayPal.is3DSNotRequired} + {l s='Your 3D Secure settings for this transaction was SCA_WHEN_REQUIRED and the current transaction doesn’t require it.' mod='ps_checkout'} + {/if} {l s='The bank issuer declined the liability shift. We advice you not to honor the order immediately, wait a few days in case of chargeback and contact the consumer to ensure authenticity of the transaction. For this type of cases we also recommend to consider Chargeback protection.' mod='ps_checkout'}
{/if} From 627963c0e2ddb572a101de7100cddf3c7e76aadc Mon Sep 17 00:00:00 2001 From: L3RAZ Date: Wed, 22 Jan 2025 10:47:37 +0200 Subject: [PATCH 5/7] CS fix --- controllers/front/payment.php | 1 + ps_checkout.php | 2 +- src/Checkout/CheckoutChecker.php | 4 ++-- src/PayPal/Order/Entity/PayPalOrder.php | 1 - src/Presenter/Order/OrderPresenter.php | 5 ++--- translations/en.php | 2 +- translations/it.php | 2 +- 7 files changed, 8 insertions(+), 9 deletions(-) diff --git a/controllers/front/payment.php b/controllers/front/payment.php index dd5d48367..50c2fc0f6 100644 --- a/controllers/front/payment.php +++ b/controllers/front/payment.php @@ -144,6 +144,7 @@ public function postProcess() $payPalOrderFromCache = $payPalOrderCache->get($orderId); $this->createOrder($payPalOrderFromCache, $payPalOrder); } + // no break default: break; } diff --git a/ps_checkout.php b/ps_checkout.php index 7d0de113e..89c446021 100755 --- a/ps_checkout.php +++ b/ps_checkout.php @@ -1123,7 +1123,7 @@ public function hookActionFrontControllerSetMedia() 'checkout.payment.token.delete.modal.confirm-button' => $this->l('Delete payment method'), 'checkout.payment.loader.processing-request' => $this->l('Please wait, we are processing your request'), 'APPLE_PAY_MERCHANT_SESSION_VALIDATION_ERROR' => $this->l('We’re unable to process your Apple Pay payment at the moment. This could be due to an issue verifying the payment setup for this website. Please try again later or choose a different payment method.'), - 'APPROVE_APPLE_PAY_VALIDATION_ERROR' => $this->l('We encountered an issue while processing your Apple Pay payment. Please verify your order details and try again, or use a different payment method.') + 'APPROVE_APPLE_PAY_VALIDATION_ERROR' => $this->l('We encountered an issue while processing your Apple Pay payment. Please verify your order details and try again, or use a different payment method.'), ], ]); diff --git a/src/Checkout/CheckoutChecker.php b/src/Checkout/CheckoutChecker.php index efd71a034..297dfe0d4 100644 --- a/src/Checkout/CheckoutChecker.php +++ b/src/Checkout/CheckoutChecker.php @@ -45,7 +45,6 @@ class CheckoutChecker */ private $payPalConfiguration; - /** * @param LoggerInterface $logger */ @@ -116,7 +115,8 @@ public function continueWithAuthorization($cartId, $orderPayPal) try { $payPalOrder->addTag(PayPalOrder::THREE_D_SECURE_NOT_REQUIRED); $this->payPalOrderRepository->savePayPalOrder($payPalOrder); - } catch (PsCheckoutException $e) {} + } catch (PsCheckoutException $e) { + } } break; } diff --git a/src/PayPal/Order/Entity/PayPalOrder.php b/src/PayPal/Order/Entity/PayPalOrder.php index 53c3c86d7..35caa1424 100644 --- a/src/PayPal/Order/Entity/PayPalOrder.php +++ b/src/PayPal/Order/Entity/PayPalOrder.php @@ -324,7 +324,6 @@ public function setPaymentTokenId($paymentTokenId) $this->paymentTokenId = $paymentTokenId; } - /** * @return string[] */ diff --git a/src/Presenter/Order/OrderPresenter.php b/src/Presenter/Order/OrderPresenter.php index 0e2646a30..c34be0e5d 100644 --- a/src/Presenter/Order/OrderPresenter.php +++ b/src/Presenter/Order/OrderPresenter.php @@ -28,7 +28,6 @@ use PrestaShop\Module\PrestashopCheckout\Presenter\Date\DatePresenter; use PrestaShop\Module\PrestashopCheckout\Provider\PaymentMethodLogoProvider; use PrestaShop\Module\PrestashopCheckout\Repository\PayPalOrderRepository; -use PrestaShop\Module\PrestashopCheckout\Repository\PsCheckoutCartRepository; use Ps_checkout; use PsCheckoutCart; @@ -80,8 +79,8 @@ public function present() try { $payPalOrder = $this->payPalOrderRepository->getPayPalOrderById(new PayPalOrderId($this->orderPayPal['id'])); $threeDSNotRequired = in_array(PayPalOrder::THREE_D_SECURE_NOT_REQUIRED, $payPalOrder->getTags()); - } catch (PsCheckoutException $e) {} - + } catch (PsCheckoutException $e) { + } $card3DSecure = new Card3DSecure(); diff --git a/translations/en.php b/translations/en.php index 8ddbcca0f..e3d15c604 100644 --- a/translations/en.php +++ b/translations/en.php @@ -674,4 +674,4 @@ $_MODULE['<{ps_checkout}prestashop>vaulttokenform_0db443e338b016aae2b24ce0be40beda'] = 'Make this my preferred payment method'; $_MODULE['<{ps_checkout}prestashop>vaulttokenform_6c493105989f12b2da5d1f770ac779f9'] = 'This payment method has been saved to your account.'; $_MODULE['<{ps_checkout}prestashop>vaulttokenform_ca3e15a8a9c7a796a1893277bf48b8ef'] = 'This payment method has been saved to your account and defined as favorite for future purchases.'; -$_MODULE['<{ps_checkout}prestashop>vaulttokenform_f2a6c498fb90ee345d997f888fce3b18'] = 'Delete'; \ No newline at end of file +$_MODULE['<{ps_checkout}prestashop>vaulttokenform_f2a6c498fb90ee345d997f888fce3b18'] = 'Delete'; diff --git a/translations/it.php b/translations/it.php index 208215d27..2544d89ab 100644 --- a/translations/it.php +++ b/translations/it.php @@ -593,4 +593,4 @@ $_MODULE['<{ps_checkout}prestashop>validate_ee02bd0a1dffdbc6ee993d07fa3997b7'] = 'Un cliente ha riscontrato un errore durante il pagamento'; $_MODULE['<{ps_checkout}prestashop>validate_fb970bdd962fa85ffc3459b4d5312b10'] = 'Transazione non riuscita. Riprova con un\'altra carta.'; $_MODULE['<{ps_checkout}prestashop>validate_fdef7819688bef617b84868f7713f883'] = 'Questo messaggio viene inviato automaticamente dal modulo PrestaShop Checkout'; -$_MODULE['<{ps_checkout}prestashop>validate_ff74fb308795919496b905a116ae1da6'] = 'Questo metodo di pagamento non è disponibile'; \ No newline at end of file +$_MODULE['<{ps_checkout}prestashop>validate_ff74fb308795919496b905a116ae1da6'] = 'Questo metodo di pagamento non è disponibile'; From ed92e2789f39625def8b117b4e05668b023f5e72 Mon Sep 17 00:00:00 2001 From: L3RAZ Date: Thu, 23 Jan 2025 13:35:30 +0200 Subject: [PATCH 6/7] Code review fixes --- src/PayPal/Order/Entity/PayPalOrder.php | 16 +++++++++++++-- src/Presenter/Order/OrderPresenter.php | 3 ++- src/Repository/PayPalOrderRepository.php | 25 +++++++++++------------- 3 files changed, 27 insertions(+), 17 deletions(-) diff --git a/src/PayPal/Order/Entity/PayPalOrder.php b/src/PayPal/Order/Entity/PayPalOrder.php index 35caa1424..b725cd7d5 100644 --- a/src/PayPal/Order/Entity/PayPalOrder.php +++ b/src/PayPal/Order/Entity/PayPalOrder.php @@ -80,8 +80,20 @@ class PayPalOrder */ private $tags; - public function __construct($id, $idCart, $intent, $fundingSource, $status, $paymentSource = [], $environment = 'LIVE', $isCardFields = false, $isExpressCheckout = false, $customerIntent = [], $paymentTokenId = null, $tags = []) - { + public function __construct( + $id, + $idCart, + $intent, + $fundingSource, + $status, + $paymentSource = [], + $environment = 'LIVE', + $isCardFields = false, + $isExpressCheckout = false, + $customerIntent = [], + $paymentTokenId = null, + $tags = [] + ) { $this->id = new PayPalOrderId($id); $this->idCart = $idCart; $this->intent = $intent; diff --git a/src/Presenter/Order/OrderPresenter.php b/src/Presenter/Order/OrderPresenter.php index c34be0e5d..b9b89c923 100644 --- a/src/Presenter/Order/OrderPresenter.php +++ b/src/Presenter/Order/OrderPresenter.php @@ -77,7 +77,8 @@ public function present() $threeDSNotRequired = false; try { - $payPalOrder = $this->payPalOrderRepository->getPayPalOrderById(new PayPalOrderId($this->orderPayPal['id'])); + $payPalOrderId = new PayPalOrderId($this->orderPayPal['id']); + $payPalOrder = $this->payPalOrderRepository->getPayPalOrderById($payPalOrderId); $threeDSNotRequired = in_array(PayPalOrder::THREE_D_SECURE_NOT_REQUIRED, $payPalOrder->getTags()); } catch (PsCheckoutException $e) { } diff --git a/src/Repository/PayPalOrderRepository.php b/src/Repository/PayPalOrderRepository.php index 9ec2b4c52..0aff12909 100644 --- a/src/Repository/PayPalOrderRepository.php +++ b/src/Repository/PayPalOrderRepository.php @@ -102,20 +102,7 @@ public function getPayPalOrderById(PayPalOrderId $payPalOrderId) throw new PsCheckoutException('PayPal Order not found'); } - return new PayPalOrder( - $queryResult['id'], - (int) $queryResult['id_cart'], - $queryResult['intent'], - $queryResult['funding_source'], - $queryResult['status'], - json_decode($queryResult['payment_source'], true), - $queryResult['environment'], - $queryResult['is_card_fields'], - $queryResult['is_express_checkout'], - explode(',', $queryResult['customer_intent']), - $queryResult['payment_token_id'] ? new PaymentTokenId($queryResult['payment_token_id']) : null, - explode(',', $queryResult['tags']) - ); + return $this->buildPayPalOrderFromQueryResult($queryResult); } /** @@ -141,6 +128,16 @@ public function getPayPalOrderByCartId($cartId) throw new PsCheckoutException('PayPal Order not found'); } + return $this->buildPayPalOrderFromQueryResult($queryResult); + } + + /** + * @param array $queryResult + * + * @return PayPalOrder + */ + private function buildPayPalOrderFromQueryResult($queryResult) + { return new PayPalOrder( $queryResult['id'], (int) $queryResult['id_cart'], From 44e59fbd1b3f345ed670e1baebb43a8ebd28ceb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Laurynas=20=C5=A0edys?= Date: Thu, 23 Jan 2025 14:27:18 +0200 Subject: [PATCH 7/7] Wording change Co-authored-by: Matthias RAIGNE <5262628+Matt75@users.noreply.github.com> --- views/templates/admin/ajaxPayPalOrder.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/views/templates/admin/ajaxPayPalOrder.tpl b/views/templates/admin/ajaxPayPalOrder.tpl index c467d1f4c..e01ac8a35 100755 --- a/views/templates/admin/ajaxPayPalOrder.tpl +++ b/views/templates/admin/ajaxPayPalOrder.tpl @@ -117,7 +117,7 @@ {if $psCheckoutCart->paypal_funding === 'card' && !$orderPayPal.isLiabilityShifted}
{if $orderPayPal.is3DSNotRequired} - {l s='Your 3D Secure settings for this transaction was SCA_WHEN_REQUIRED and the current transaction doesn’t require it.' mod='ps_checkout'} + {l s='Your 3D Secure settings for this transaction were set to "Strong Customer Authentication (SCA) when required", but the current transaction does not require it as per the regulation.' mod='ps_checkout'} {/if} {l s='The bank issuer declined the liability shift. We advice you not to honor the order immediately, wait a few days in case of chargeback and contact the consumer to ensure authenticity of the transaction. For this type of cases we also recommend to consider Chargeback protection.' mod='ps_checkout'}