diff --git a/config.xml b/config.xml index debe51af4..34f80a6f7 100644 --- a/config.xml +++ b/config.xml @@ -2,7 +2,7 @@ ps_checkout - + diff --git a/config/common.yml b/config/common.yml index 05aa5b33f..9cf3d5fb4 100644 --- a/config/common.yml +++ b/config/common.yml @@ -454,6 +454,8 @@ services: public: true arguments: - "@ps_checkout.logger" + - '@PrestaShop\Module\PrestashopCheckout\Repository\PayPalOrderRepository' + - '@PrestaShop\Module\PrestashopCheckout\PayPal\PayPalConfiguration' PrestaShop\Module\PrestashopCheckout\PayPal\PaymentToken\PaymentMethodTokenService: class: 'PrestaShop\Module\PrestashopCheckout\PayPal\PaymentToken\PaymentMethodTokenService' diff --git a/controllers/front/payment.php b/controllers/front/payment.php index f2c8575b8..50c2fc0f6 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,12 @@ 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); + } + // no break default: break; } diff --git a/ps_checkout.php b/ps_checkout.php index 7d0de113e..c22939cbf 100755 --- a/ps_checkout.php +++ b/ps_checkout.php @@ -116,7 +116,7 @@ class Ps_checkout extends PaymentModule // Needed in order to retrieve the module version easier (in api call headers) than instanciate // the module each time to get the version - const VERSION = '8.4.3.0'; + const VERSION = '8.4.3.1'; const INTEGRATION_DATE = '2024-04-01'; @@ -137,7 +137,7 @@ public function __construct() // We cannot use the const VERSION because the const is not computed by addons marketplace // when the zip is uploaded - $this->version = '8.4.3.0'; + $this->version = '8.4.3.1'; $this->author = 'PrestaShop'; $this->currencies = true; $this->currencies_mode = 'checkbox'; @@ -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 df8bc537c..297dfe0d4 100644 --- a/src/Checkout/CheckoutChecker.php +++ b/src/Checkout/CheckoutChecker.php @@ -21,10 +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 Psr\Log\LoggerInterface; use Validate; @@ -34,13 +36,26 @@ class CheckoutChecker * @var LoggerInterface */ private $logger; + /** + * @var PayPalOrderRepository + */ + private $payPalOrderRepository; + /** + * @var PayPalConfiguration + */ + private $payPalConfiguration; /** * @param LoggerInterface $logger */ - public function __construct(LoggerInterface $logger) - { + public function __construct( + LoggerInterface $logger, + PayPalOrderRepository $payPalOrderRepository, + PayPalConfiguration $payPalConfiguration + ) { $this->logger = $logger; + $this->payPalOrderRepository = $payPalOrderRepository; + $this->payPalConfiguration = $payPalConfiguration; } /** @@ -57,6 +72,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)) { @@ -74,7 +91,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', @@ -90,9 +107,17 @@ 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')) { + 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); + try { + $payPalOrder->addTag(PayPalOrder::THREE_D_SECURE_NOT_REQUIRED); + $this->payPalOrderRepository->savePayPalOrder($payPalOrder); + } catch (PsCheckoutException $e) { + } + } break; } } diff --git a/src/Database/TableManager.php b/src/Database/TableManager.php index e721240e3..d84f7641c 100644 --- a/src/Database/TableManager.php +++ b/src/Database/TableManager.php @@ -46,120 +46,138 @@ public function __construct(\Db $db = null) */ public function createTable() { - $result = $this->db->execute(' - CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'pscheckout_order_matrice` ( - `id_order_matrice` int(10) unsigned NOT NULL AUTO_INCREMENT, - `id_order_prestashop` int(10) unsigned NOT NULL, - `id_order_paypal` varchar(20) NOT NULL, - PRIMARY KEY (`id_order_matrice`) - ) ENGINE=' . _MYSQL_ENGINE_ . ' DEFAULT CHARSET=UTF8; - ') && $this->db->execute(' - CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'pscheckout_cart` ( - `id_pscheckout_cart` int(10) unsigned NOT NULL AUTO_INCREMENT, - `id_cart` int unsigned NOT NULL, - `paypal_intent` varchar(20) DEFAULT "CAPTURE", - `paypal_order` varchar(20) NULL, - `paypal_status` varchar(30) NULL, - `paypal_funding` varchar(20) NULL, - `paypal_token` text DEFAULT NULL, - `paypal_token_expire` datetime NULL, - `paypal_authorization_expire` datetime NULL, - `environment` varchar(20) NULL, - `isExpressCheckout` tinyint(1) unsigned DEFAULT 0 NOT NULL, - `isHostedFields` tinyint(1) unsigned DEFAULT 0 NOT NULL, - `date_add` datetime NOT NULL, - `date_upd` datetime NOT NULL, - PRIMARY KEY (`id_pscheckout_cart`) - ) ENGINE=' . _MYSQL_ENGINE_ . ' DEFAULT CHARSET=UTF8; - ') && $this->db->execute(' - CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'pscheckout_funding_source` ( - `name` varchar(20) NOT NULL, - `active` tinyint(1) unsigned DEFAULT 0 NOT NULL, - `position` tinyint(2) unsigned NOT NULL, - `id_shop` int unsigned NOT NULL, - PRIMARY KEY (`name`, `id_shop`), - INDEX (`id_shop`) - ) ENGINE=' . _MYSQL_ENGINE_ . ' DEFAULT CHARSET=UTF8; - ') && $this->db->execute(' - CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'pscheckout_order` ( - `id` varchar(50) NOT NULL, - `id_cart` int unsigned NOT NULL, - `status` varchar(30) NOT NULL, - `intent` varchar(50) DEFAULT "CAPTURE", - `funding_source` varchar(50) NOT NULL, - `payment_source` text, - `environment` varchar(50) NOT NULL, - `is_card_fields` tinyint(1) NOT NULL, - `is_express_checkout` tinyint(1) NOT NULL, - `customer_intent` varchar(50), - `payment_token_id` varchar(50), - PRIMARY KEY (`id`) - ) ENGINE=' . _MYSQL_ENGINE_ . ' DEFAULT CHARSET=UTF8; - ') && $this->db->execute(' - CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'pscheckout_capture` ( - `id` varchar(50) NOT NULL, - `id_order` varchar(50) NOT NULL, - `status` varchar(30) NOT NULL, - `final_capture` tinyint(1) NOT NULL, - `created_at` varchar(50) NOT NULL, - `updated_at` varchar(50) NOT NULL, - `seller_protection` text, - `seller_receivable_breakdown` text, - PRIMARY KEY (`id`) - ) ENGINE=' . _MYSQL_ENGINE_ . ' DEFAULT CHARSET=UTF8; - ') && $this->db->execute(' - CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'pscheckout_refund` ( - `id` varchar(50) NOT NULL, - `id_order` varchar(50) NOT NULL, - `status` varchar(30) NOT NULL, - `invoice_id` varchar(50) NOT NULL, - `custom_id` varchar(50) NOT NULL, - `acquirer_reference_number` varchar(50) NOT NULL, - `seller_payable_breakdown` text, - `id_order_slip` INT UNSIGNED, - PRIMARY KEY (`id`) - ) ENGINE=' . _MYSQL_ENGINE_ . ' DEFAULT CHARSET=UTF8; - ') && $this->db->execute(' - CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'pscheckout_authorization` ( - `id` varchar(50) NOT NULL, - `id_order` varchar(50) NOT NULL, - `status` varchar(30) NOT NULL, - `expiration_time` varchar(50) NOT NULL, - `seller_protection` text, - PRIMARY KEY (`id`) - ) ENGINE=' . _MYSQL_ENGINE_ . ' DEFAULT CHARSET=UTF8; - ') && $this->db->execute(' - CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'pscheckout_purchase_unit` ( - `id_order` varchar(50) NOT NULL, - `checksum` varchar(50) NOT NULL, - `reference_id` varchar(50) NOT NULL, - `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` INT UNSIGNED AUTO_INCREMENT, - `token_id` varchar(50) NOT NULL, - `paypal_customer_id` varchar(50) NOT NULL, - `payment_source` varchar(50) NOT NULL, - `data` text NOT NULL, - `merchant_id` varchar(50) NOT NULL, - `status` varchar(50) NOT NULL, - `is_favorite` tinyint(1) unsigned DEFAULT 0 NOT NULL, - PRIMARY KEY (`id`), - UNIQUE KEY `token_id_merchant_id_paypal_customer_id` (`token_id`, `merchant_id`, `paypal_customer_id`) - ) ENGINE=' . _MYSQL_ENGINE_ . ' DEFAULT CHARSET=UTF8; - '); + $tables = [ + 'pscheckout_order_matrice' => ' + CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'pscheckout_order_matrice` ( + `id_order_matrice` int(10) unsigned NOT NULL AUTO_INCREMENT, + `id_order_prestashop` int(10) unsigned NOT NULL, + `id_order_paypal` varchar(20) NOT NULL, + PRIMARY KEY (`id_order_matrice`) + ) ENGINE=' . _MYSQL_ENGINE_ . ' DEFAULT CHARSET=UTF8; + ', + 'pscheckout_cart' => ' + CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'pscheckout_cart` ( + `id_pscheckout_cart` int(10) unsigned NOT NULL AUTO_INCREMENT, + `id_cart` int unsigned NOT NULL, + `paypal_intent` varchar(20) DEFAULT "CAPTURE", + `paypal_order` varchar(20) NULL, + `paypal_status` varchar(30) NULL, + `paypal_funding` varchar(20) NULL, + `paypal_token` text DEFAULT NULL, + `paypal_token_expire` datetime NULL, + `paypal_authorization_expire` datetime NULL, + `environment` varchar(20) NULL, + `isExpressCheckout` tinyint(1) unsigned DEFAULT 0 NOT NULL, + `isHostedFields` tinyint(1) unsigned DEFAULT 0 NOT NULL, + `date_add` datetime NOT NULL, + `date_upd` datetime NOT NULL, + PRIMARY KEY (`id_pscheckout_cart`) + ) ENGINE=' . _MYSQL_ENGINE_ . ' DEFAULT CHARSET=UTF8; + ', + 'pscheckout_funding_source' => ' + CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'pscheckout_funding_source` ( + `name` varchar(20) NOT NULL, + `active` tinyint(1) unsigned DEFAULT 0 NOT NULL, + `position` tinyint(2) unsigned NOT NULL, + `id_shop` int unsigned NOT NULL, + PRIMARY KEY (`name`, `id_shop`), + INDEX (`id_shop`) + ) ENGINE=' . _MYSQL_ENGINE_ . ' DEFAULT CHARSET=UTF8; + ', + 'pscheckout_order' => ' + CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'pscheckout_order` ( + `id` varchar(50) NOT NULL, + `id_cart` int unsigned NOT NULL, + `status` varchar(30) NOT NULL, + `intent` varchar(50) DEFAULT "CAPTURE", + `funding_source` varchar(50) NOT NULL, + `payment_source` text, + `environment` varchar(50) NOT NULL, + `is_card_fields` tinyint(1) NOT NULL, + `is_express_checkout` tinyint(1) NOT NULL, + `customer_intent` varchar(50), + `payment_token_id` varchar(50), + `tags` varchar(255) DEFAULT NULL, + PRIMARY KEY (`id`) + ) ENGINE=' . _MYSQL_ENGINE_ . ' DEFAULT CHARSET=UTF8; + ', + 'pscheckout_capture' => ' + CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'pscheckout_capture` ( + `id` varchar(50) NOT NULL, + `id_order` varchar(50) NOT NULL, + `status` varchar(30) NOT NULL, + `final_capture` tinyint(1) NOT NULL, + `created_at` varchar(50) NOT NULL, + `updated_at` varchar(50) NOT NULL, + `seller_protection` text, + `seller_receivable_breakdown` text, + PRIMARY KEY (`id`) + ) ENGINE=' . _MYSQL_ENGINE_ . ' DEFAULT CHARSET=UTF8; + ', + 'pscheckout_refund' => ' + CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'pscheckout_refund` ( + `id` varchar(50) NOT NULL, + `id_order` varchar(50) NOT NULL, + `status` varchar(30) NOT NULL, + `invoice_id` varchar(50) NOT NULL, + `custom_id` varchar(50) NOT NULL, + `acquirer_reference_number` varchar(50) NOT NULL, + `seller_payable_breakdown` text, + `id_order_slip` INT UNSIGNED, + PRIMARY KEY (`id`) + ) ENGINE=' . _MYSQL_ENGINE_ . ' DEFAULT CHARSET=UTF8; + ', + 'pscheckout_authorization' => ' + CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'pscheckout_authorization` ( + `id` varchar(50) NOT NULL, + `id_order` varchar(50) NOT NULL, + `status` varchar(30) NOT NULL, + `expiration_time` varchar(50) NOT NULL, + `seller_protection` text, + PRIMARY KEY (`id`) + ) ENGINE=' . _MYSQL_ENGINE_ . ' DEFAULT CHARSET=UTF8; + ', + 'pscheckout_purchase_unit' => ' + CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'pscheckout_purchase_unit` ( + `id_order` varchar(50) NOT NULL, + `checksum` varchar(50) NOT NULL, + `reference_id` varchar(50) NOT NULL, + `items` text, + PRIMARY KEY (`reference_id`, `id_order`) + ) ENGINE=' . _MYSQL_ENGINE_ . ' DEFAULT CHARSET=UTF8; + ', + 'pscheckout_customer' => ' + 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; + ', + 'pscheckout_payment_token' => ' + CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'pscheckout_payment_token` ( + `id` INT UNSIGNED AUTO_INCREMENT, + `token_id` varchar(50) NOT NULL, + `paypal_customer_id` varchar(50) NOT NULL, + `payment_source` varchar(50) NOT NULL, + `data` text NOT NULL, + `merchant_id` varchar(50) NOT NULL, + `status` varchar(50) NOT NULL, + `is_favorite` tinyint(1) unsigned DEFAULT 0 NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `token_id_merchant_id_paypal_customer_id` (`token_id`, `merchant_id`, `paypal_customer_id`) + ) ENGINE=' . _MYSQL_ENGINE_ . ' DEFAULT CHARSET=UTF8; + ', + ]; + + foreach ($tables as $tableName => $query) { + if (!$this->execute($query)) { + throw new \Exception(sprintf('Error while creating table %s', $tableName)); + } + } $this->checkTable(); - return (bool) $result; + return true; } /** @@ -169,7 +187,7 @@ public function createTable() */ public function dropTable() { - // Avoid to loose PayPal data if module is reset or uninstall + // Avoid to lose PayPal data if module is reset or uninstalled return true; } @@ -181,7 +199,7 @@ public function dropTable() */ public function populatePsCartFromOrderMatrice() { - return $this->db->execute(' + return $this->execute(' INSERT INTO `' . _DB_PREFIX_ . 'pscheckout_cart` (`id_cart`, `paypal_order`, `date_add`, `date_upd`) SELECT o.id_cart, om.id_order_paypal, o.date_add, o.date_upd FROM `' . _DB_PREFIX_ . 'pscheckout_order_matrice` AS om @@ -204,11 +222,11 @@ public function checkTable() foreach ($fields as $field) { $databaseFields[] = $field['Field']; if ($field['Field'] === 'paypal_token' && $field['Type'] !== 'text') { - $this->db->execute('ALTER TABLE `' . _DB_PREFIX_ . 'pscheckout_cart` CHANGE `paypal_token` `paypal_token` text DEFAULT NULL;'); + $this->execute('ALTER TABLE `' . _DB_PREFIX_ . 'pscheckout_cart` CHANGE `paypal_token` `paypal_token` text DEFAULT NULL;'); } if ($field['Field'] === 'paypal_status' && $field['Type'] !== 'varchar(30)') { - $this->db->execute('ALTER TABLE `' . _DB_PREFIX_ . 'pscheckout_cart` CHANGE `paypal_status` `paypal_status` varchar(30) NULL;'); + $this->execute('ALTER TABLE `' . _DB_PREFIX_ . 'pscheckout_cart` CHANGE `paypal_status` `paypal_status` varchar(30) NULL;'); } } } @@ -218,7 +236,24 @@ public function checkTable() $missingFields = array_diff($objectFields, $databaseFields); if (in_array('environment', $missingFields, true)) { - $this->db->execute('ALTER TABLE `' . _DB_PREFIX_ . 'pscheckout_cart` ADD COLUMN `environment` varchar(20) DEFAULT NULL;'); + $this->execute('ALTER TABLE `' . _DB_PREFIX_ . 'pscheckout_cart` ADD COLUMN `environment` varchar(20) DEFAULT NULL;'); + } + + $fields = $this->db->executeS('SHOW COLUMNS FROM `' . _DB_PREFIX_ . 'pscheckout_order`'); + + if (!empty($fields)) { + $field = array_filter($fields, function ($field) { + return $field['Field'] === 'tags'; + }); + + if (empty($field)) { + $this->execute('ALTER TABLE `' . _DB_PREFIX_ . 'pscheckout_order` ADD COLUMN `tags` varchar(255) DEFAULT NULL;'); + } } } + + private function execute($sql) + { + return $this->db->execute($sql, false); + } } diff --git a/src/PayPal/Order/Entity/PayPalOrder.php b/src/PayPal/Order/Entity/PayPalOrder.php index 3e367a65a..b725cd7d5 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,9 +75,25 @@ class PayPalOrder * @var PaymentTokenId|null */ private $paymentTokenId; - - public function __construct($id, $idCart, $intent, $fundingSource, $status, $paymentSource = [], $environment = 'LIVE', $isCardFields = false, $isExpressCheckout = false, $customerIntent = [], $paymentTokenId = null) - { + /** + * @var array + */ + private $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; @@ -88,6 +105,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 +335,32 @@ 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 9e1a9a076..b9b89c923 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 Ps_checkout; use PsCheckoutCart; @@ -42,6 +46,10 @@ class OrderPresenter * @var FundingSourceTranslationProvider */ private $fundingSourceTranslationProvider; + /** + * @var PayPalOrderRepository + */ + private $payPalOrderRepository; /** * @param Ps_checkout $module @@ -54,6 +62,7 @@ public function __construct(Ps_checkout $module, array $orderPayPal) /** @var FundingSourceTranslationProvider $fundingSourceTranslationProvider */ $fundingSourceTranslationProvider = $this->module->getService(FundingSourceTranslationProvider::class); $this->fundingSourceTranslationProvider = $fundingSourceTranslationProvider; + $this->payPalOrderRepository = $this->module->getService(PayPalOrderRepository::class); } /** @@ -65,6 +74,15 @@ public function present() return []; } + $threeDSNotRequired = false; + + try { + $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) { + } + $card3DSecure = new Card3DSecure(); return array_merge( @@ -73,6 +91,7 @@ public function present() 'intent' => $this->orderPayPal['intent'], 'status' => $this->getOrderStatus(), 'transactions' => $this->getTransactions(), + '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..0aff12909 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, @@ -101,19 +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 - ); + return $this->buildPayPalOrderFromQueryResult($queryResult); } /** @@ -139,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'], @@ -150,7 +149,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/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'; diff --git a/upgrade/upgrade-8.4.3.1.php b/upgrade/upgrade-8.4.3.1.php new file mode 100644 index 000000000..6cb295acd --- /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.3.1 + * + * @param Ps_checkout $module + * + * @return bool + */ +function upgrade_module_8_4_3_1($module) +{ + try { + $db = Db::getInstance(); + + $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); + + return false; + } + + return true; +} diff --git a/views/templates/admin/ajaxPayPalOrder.tpl b/views/templates/admin/ajaxPayPalOrder.tpl index 2f545646a..e01ac8a35 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'} @@ -112,6 +116,9 @@ {if $psCheckoutCart->paypal_funding === 'card' && !$orderPayPal.isLiabilityShifted}
+ {if $orderPayPal.is3DSNotRequired} + {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'}
{/if}