diff --git a/composer.json b/composer.json index e5851363..cd10dd9a 100755 --- a/composer.json +++ b/composer.json @@ -52,4 +52,4 @@ }, "author": "PrestaShop", "license": "AFL-3.0" -} \ No newline at end of file +} diff --git a/controllers/front/notify.php b/controllers/front/notify.php index 584dc479..5bfd644d 100755 --- a/controllers/front/notify.php +++ b/controllers/front/notify.php @@ -75,13 +75,7 @@ public function postProcess() } if ($cart->orderExists()) { - if (method_exists('Order', 'getIdByCartId')) { - $orderId = Order::getIdByCartId($cartId); - } else { - // For PrestaShop 1.6 use the alternative method - $orderId = Order::getOrderByCartId($cartId); - } - + $orderId = $this->getOrderId($cartId); $order = new Order($orderId); $saferPayAuthorizedStatus = (int) Configuration::get(\Invertus\SaferPay\Config\SaferPayConfig::SAFERPAY_PAYMENT_AUTHORIZED); @@ -92,11 +86,11 @@ public function postProcess() } } + /** @var SaferPayOrderRepository $saferPayOrderRepository */ + $saferPayOrderRepository = $this->module->getService(SaferPayOrderRepository::class); + try { $assertResponseBody = $this->assertTransaction($cartId); - - /** @var SaferPayOrderRepository $saferPayOrderRepository */ - $saferPayOrderRepository = $this->module->getService(SaferPayOrderRepository::class); $saferPayOrderId = $saferPayOrderRepository->getIdByCartId($cartId); /** @var UpdateSaferPayOrderAction $updateSaferPayOrderAction */ @@ -104,12 +98,7 @@ public function postProcess() $updateSaferPayOrderAction->run(new SaferPayOrder($saferPayOrderId), self::SAFERPAY_ORDER_AUTHORIZE_ACTION); // If order does not exist but assertion is valid that means order authorized or captured. - if (method_exists('Order', 'getIdByCartId')) { - $orderId = Order::getIdByCartId($cartId); - } else { - // For PrestaShop 1.6 use the alternative method - $orderId = Order::getOrderByCartId($cartId); - } + $orderId = $this->getOrderId($cartId); if (!$orderId) { /** @var CheckoutProcessor $checkoutProcessor **/ $checkoutProcessor = $this->module->getService(CheckoutProcessor::class); @@ -153,8 +142,9 @@ public function postProcess() //NOTE to get latest information possible and not override new information. $order = new Order($orderId); + $paymentMethod = $assertResponseBody->getPaymentMeans()->getBrand()->getPaymentMethod(); - if ((int) Configuration::get(SaferPayConfig::PAYMENT_BEHAVIOR) === SaferPayConfig::DEFAULT_PAYMENT_BEHAVIOR_CAPTURE && + if (SaferPayConfig::supportsOrderCapture($paymentMethod) && (int) Configuration::get(SaferPayConfig::PAYMENT_BEHAVIOR) === SaferPayConfig::DEFAULT_PAYMENT_BEHAVIOR_CAPTURE && $assertResponseBody->getTransaction()->getStatus() !== TransactionStatus::CAPTURED ) { /** @var SaferPayOrderStatusService $orderStatusService */ @@ -162,6 +152,24 @@ public function postProcess() $orderStatusService->capture($order); } } catch (Exception $e) { + // this might be executed after pending transaction is declined (e.g. with accountToAccount payment) + if (!isset($order)) { + $orderId = $this->getOrderId($cartId); + $order = new Order($orderId); + } + + $saferPayOrderId = $saferPayOrderRepository->getIdByOrderId($order->id); + $saferPayOrder = new SaferPayOrder($saferPayOrderId); + + if ($order->id && $saferPayOrder->id) { + // assuming order transaction was declined + $order->setCurrentState(_SAFERPAY_PAYMENT_AUTHORIZATION_FAILED_); + $order->update(); + $saferPayOrder->authorized = false; + $saferPayOrder->pending = false; + $saferPayOrder->update(); + } + PrestaShopLogger::addLog( sprintf( '%s has caught an error: %s', @@ -187,6 +195,21 @@ private function assertTransaction($cartId) { return $transactionAssert->assert($cartId); } + /** + * @param int $cartId + * + * @return bool|int + */ + private function getOrderId($cartId) + { + if (method_exists('Order', 'getIdByCartId')) { + return Order::getIdByCartId($cartId); + } else { + // For PrestaShop 1.6 use the alternative method + return Order::getOrderByCartId($cartId); + } + } + protected function displayMaintenancePage() { return true; diff --git a/controllers/front/return.php b/controllers/front/return.php index f8e34029..f90c9855 100755 --- a/controllers/front/return.php +++ b/controllers/front/return.php @@ -172,8 +172,9 @@ public function postProcess() true )); } + $paymentMethod = $response->getPaymentMeans()->getBrand()->getPaymentMethod(); - if ((int) Configuration::get(SaferPayConfig::PAYMENT_BEHAVIOR) === SaferPayConfig::DEFAULT_PAYMENT_BEHAVIOR_CAPTURE && + if (SaferPayConfig::supportsOrderCapture($paymentMethod) && (int) Configuration::get(SaferPayConfig::PAYMENT_BEHAVIOR) === SaferPayConfig::DEFAULT_PAYMENT_BEHAVIOR_CAPTURE && $response->getTransaction()->getStatus() !== TransactionStatus::CAPTURED ) { $orderStatusService->capture(new Order($orderId)); diff --git a/saferpay.config.php b/saferpay.config.php index 94c75db3..b3c3553e 100755 --- a/saferpay.config.php +++ b/saferpay.config.php @@ -35,6 +35,10 @@ /** @var URL to module IMG files directory */ define('_SAFERPAY_PAYMENT_AUTHORIZED_', Configuration::get(SaferPayConfig::SAFERPAY_PAYMENT_AUTHORIZED)); } +if (!defined('_SAFERPAY_PAYMENT_PENDING_')) { + /** @var URL to module IMG files directory */ + define('_SAFERPAY_PAYMENT_PENDING_', Configuration::get(SaferPayConfig::SAFERPAY_PAYMENT_PENDING)); +} if (!defined('_SAFERPAY_PAYMENT_REJECTED_')) { /** @var URL to module IMG files directory */ define('_SAFERPAY_PAYMENT_REJECTED_', Configuration::get(SaferPayConfig::SAFERPAY_PAYMENT_REJECTED)); diff --git a/saferpayofficial.php b/saferpayofficial.php index c94e6f0b..a89a33e1 100755 --- a/saferpayofficial.php +++ b/saferpayofficial.php @@ -42,7 +42,7 @@ public function __construct($name = null) { $this->name = 'saferpayofficial'; $this->author = 'Invertus'; - $this->version = '1.2.2'; + $this->version = '1.2.3'; $this->module_key = '3d3506c3e184a1fe63b936b82bda1bdf'; $this->displayName = 'SaferpayOfficial'; $this->description = 'Saferpay Payment module'; @@ -116,6 +116,28 @@ public function getService($service) return $containerProvider->getService($service); } + public function hookDisplayOrderConfirmation($params) + { + if (empty($params['order'])) { + return ''; + } + + /** @var Order $psOrder */ + $psOrder = $params['order']; + + /** @var \Invertus\SaferPay\Repository\SaferPayOrderRepository $repository */ + $repository = $this->getService(\Invertus\SaferPay\Repository\SaferPayOrderRepository::class); + + $sfOrder = $repository->getByOrderId((int) $psOrder->id); + if (!$sfOrder->pending) { + return ''; + } + + //@todo: translate and move to template if needed when requirements are clear + return 'Your payment is still being processed by your bank. This can take up to 5 days (120 hours). Once we receive the final status, we will notify you immediately. +Thank you for your patience!'; + } + public function hookActionObjectOrderPaymentAddAfter($params) { if (!isset($params['object'])) { diff --git a/src/Api/Request/AssertService.php b/src/Api/Request/AssertService.php index c430dbce..d1297a41 100755 --- a/src/Api/Request/AssertService.php +++ b/src/Api/Request/AssertService.php @@ -29,6 +29,7 @@ use Invertus\SaferPay\DTO\Response\Assert\AssertBody; use Invertus\SaferPay\EntityBuilder\SaferPayAssertBuilder; use Invertus\SaferPay\Exception\Api\SaferPayApiException; +use Invertus\SaferPay\Exception\Api\TransactionDeclinedException; use Invertus\SaferPay\Service\Response\AssertResponseObjectCreator; use SaferPayOrder; diff --git a/src/Config/SaferPayConfig.php b/src/Config/SaferPayConfig.php index f04b7112..37f6393d 100755 --- a/src/Config/SaferPayConfig.php +++ b/src/Config/SaferPayConfig.php @@ -50,7 +50,7 @@ class SaferPayConfig const CONFIGURATION_NAME = 'SAFERPAY_CONFIGURATION_NAME'; const CSS_FILE = 'SAFERPAY_CSS_FILE'; const TEST_SUFFIX = '_TEST'; - const API_VERSION = 1.37; + const API_VERSION = '1.40'; const PAYMENT_METHODS = [ self::PAYMENT_ALIPAY, self::PAYMENT_AMEX, @@ -77,6 +77,8 @@ class SaferPayConfig self::PAYMENT_APPLEPAY, self::PAYMENT_KLARNA, self::PAYMENT_WLCRYPTOPAYMENTS, + self::PAYMENT_WECHATPAY, + self::PAYMENT_ACCOUNTTOACCOUNT, ]; const PAYMENT_ALIPAY = 'ALIPAY'; @@ -111,6 +113,7 @@ class SaferPayConfig const PAYMENT_PAYCONIQ = 'PAYCONIQ'; const PAYMENT_CARD = 'CARD'; const PAYMENT_POSTFINANCE_PAY = 'POSTFINANCEPAY'; + const PAYMENT_WECHATPAY = 'WECHATPAY'; const WALLET_PAYMENT_METHODS = [ self::PAYMENT_APPLEPAY, @@ -141,6 +144,7 @@ class SaferPayConfig 'Payconiq' => self::PAYMENT_PAYCONIQ, 'Card' => self::PAYMENT_CARD, 'PostFinancePay' => self::PAYMENT_POSTFINANCE_PAY, + 'WeChatPay' => self::PAYMENT_WECHATPAY, ]; const FIELD_SUPPORTED_PAYMENT_METHODS = [ @@ -152,6 +156,7 @@ class SaferPayConfig self::PAYMENT_DINERS, self::PAYMENT_JCB, self::PAYMENT_MYONE, + self::PAYMENT_WECHATPAY, ]; const WLCRYPTOPAYMENTS_SUPPORTED_CURRENCIES = [ @@ -214,6 +219,7 @@ class SaferPayConfig const SAFERPAY_PAYMENT_COMPLETED = 'SAFERPAY_PAYMENT_COMPLETED'; const SAFERPAY_PAYMENT_AUTHORIZED = 'SAFERPAY_PAYMENT_AUTHORIZED'; + const SAFERPAY_PAYMENT_PENDING = 'SAFERPAY_PAYMENT_PENDING'; const SAFERPAY_PAYMENT_REJECTED = 'SAFERPAY_PAYMENT_REJECTED'; const SAFERPAY_PAYMENT_AWAITING = 'SAFERPAY_PAYMENT_AWAITING'; const SAFERPAY_PAYMENT_REFUNDED = 'SAFERPAY_PAYMENT_REFUNDED'; @@ -262,6 +268,29 @@ class SaferPayConfig const PAYMENT_BEHAVIOR_WITHOUT_3D_CANCEL = 0; const PAYMENT_BEHAVIOR_WITHOUT_3D_AUTHORIZE = 1; + public static function supportsOrderCapture(string $paymentMethod): bool + { + //payments that DOES NOT SUPPORT capture + $unsupportedCapturePayments = [ + self::PAYMENT_WECHATPAY, + self::PAYMENT_ACCOUNTTOACCOUNT, + ]; + + return !in_array($paymentMethod, $unsupportedCapturePayments); + } + + public static function supportsOrderCancel(string $paymentMethod): bool + { + //payments that DOES NOT SUPPORT order cancel + $unsupportedCancelPayments = [ + self::PAYMENT_WECHATPAY, + self::PAYMENT_ACCOUNTTOACCOUNT, + ]; + + return !in_array($paymentMethod, $unsupportedCancelPayments); + } + + public static function getConfigSuffix() { if (Configuration::get(self::TEST_MODE)) { @@ -333,8 +362,8 @@ public static function getBaseUrl() public static function getDefaultConfiguration() { return [ - RequestHeader::SPEC_VERSION => '1.37', - RequestHeader::SPEC_REFUND_VERSION => '1.37', + RequestHeader::SPEC_VERSION => SaferPayConfig::API_VERSION, + RequestHeader::SPEC_REFUND_VERSION => SaferPayConfig::API_VERSION, RequestHeader::RETRY_INDICATOR => 0, SaferPayConfig::PAYMENT_BEHAVIOR => 1, SaferPayConfig::PAYMENT_BEHAVIOR_WITHOUT_3D => 1, diff --git a/src/Core/Payment/DTO/CheckoutData.php b/src/Core/Payment/DTO/CheckoutData.php index 7281cfe9..7a48165d 100644 --- a/src/Core/Payment/DTO/CheckoutData.php +++ b/src/Core/Payment/DTO/CheckoutData.php @@ -60,8 +60,8 @@ public function __construct( $this->fieldToken = $fieldToken; $this->successController = $successController; $this->isTransaction = $isTransaction; - $this->createAfterAuthorization = Configuration::get(SaferPayConfig::SAFERPAY_ORDER_CREATION_AFTER_AUTHORIZATION); $this->isAuthorizedOrder = false; + $this->setCreateAfterAuthorization($paymentMethod); } public static function create( @@ -184,4 +184,24 @@ public function setOrderStatus($status) { $this->status = $status; } -} \ No newline at end of file + + /** + * @param string $paymentMethod + * + * @return void + */ + private function setCreateAfterAuthorization($paymentMethod) + { + $methodsToForceBeforeAuthorization = [ + SaferPayConfig::PAYMENT_ACCOUNTTOACCOUNT, + ]; + + if (in_array($paymentMethod, $methodsToForceBeforeAuthorization, true)) { + $this->createAfterAuthorization = false; + + return; + } + + $this->createAfterAuthorization = Configuration::get(SaferPayConfig::SAFERPAY_ORDER_CREATION_AFTER_AUTHORIZATION); + } +} diff --git a/src/Entity/SaferPayOrder.php b/src/Entity/SaferPayOrder.php index 2bac92c9..81bd09e0 100755 --- a/src/Entity/SaferPayOrder.php +++ b/src/Entity/SaferPayOrder.php @@ -90,6 +90,11 @@ class SaferPayOrder extends ObjectModel */ public $authorized; + /** + * @var bool + */ + public $pending; + /** * @var array */ @@ -109,6 +114,7 @@ class SaferPayOrder extends ObjectModel 'canceled' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'], 'refunded' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'], 'authorized' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'], + 'pending' => ['type' => self::TYPE_BOOL, 'validate' => 'isBool'], ], ]; } diff --git a/src/EntityBuilder/SaferPayOrderBuilder.php b/src/EntityBuilder/SaferPayOrderBuilder.php index 4018db98..cca57f36 100755 --- a/src/EntityBuilder/SaferPayOrderBuilder.php +++ b/src/EntityBuilder/SaferPayOrderBuilder.php @@ -34,7 +34,6 @@ class SaferPayOrderBuilder { - //TODO to pass $body as InitializeBody. public function create($body, $cartId, $customerId, $isTransaction) { if (method_exists('Order', 'getIdByCartId')) { @@ -51,6 +50,7 @@ public function create($body, $cartId, $customerId, $isTransaction) $saferPayOrder->id_customer = $customerId; $saferPayOrder->redirect_url = $this->getRedirectionUrl($body); $saferPayOrder->is_transaction = $isTransaction; + $saferPayOrder->add(); return $saferPayOrder; diff --git a/src/Install/Installer.php b/src/Install/Installer.php index 8e5c9534..81e21ca8 100755 --- a/src/Install/Installer.php +++ b/src/Install/Installer.php @@ -96,6 +96,7 @@ private function registerHooks() $this->module->registerHook('actionAdminControllerSetMedia'); $this->module->registerHook('actionOrderHistoryAddAfter'); $this->module->registerHook('actionObjectOrderPaymentAddAfter'); + $this->module->registerHook('displayOrderConfirmation'); } private function installConfiguration() @@ -321,6 +322,20 @@ private function installOrderRefundTable() ); } + public function createPendingOrderStatus() + { + return $this->createOrderStatus( + SaferPayConfig::SAFERPAY_PAYMENT_PENDING, + 'Payment pending by Saferpay', + '#ec730a', + false, + true, + false, + false, + true + ); + } + public function createAllOrderStatus() { $success = true; @@ -343,6 +358,7 @@ public function createAllOrderStatus() true, true ); + $success &= $this->createPendingOrderStatus(); $success &= $this->createOrderStatus( SaferPayConfig::SAFERPAY_PAYMENT_REJECTED, 'Payment rejected by Saferpay', diff --git a/src/Presenter/AssertPresenter.php b/src/Presenter/AssertPresenter.php index f10ebabd..2e46e42a 100755 --- a/src/Presenter/AssertPresenter.php +++ b/src/Presenter/AssertPresenter.php @@ -23,6 +23,7 @@ namespace Invertus\SaferPay\Presenter; +use Invertus\SaferPay\Config\SaferPayConfig; use SaferPayAssert; use SaferPayOfficial; @@ -46,6 +47,8 @@ public function __construct(SaferPayOfficial $saferPay) public function present(SaferPayAssert $assert) { + $paymentMethod = $assert->payment_method; + return [ 'authAmount' => $assert->amount, 'transactionAuth' => $assert->authorized ? @@ -58,10 +61,12 @@ public function present(SaferPayAssert $assert) 'currency' => $assert->currency_code, 'transactionUncertain' => $assert->uncertain, 'brand' => $assert->brand, - 'paymentMethod' => $assert->payment_method, + 'paymentMethod' => $paymentMethod, 'transactionPaid' => $assert->status, 'merchantReference' => $assert->merchant_reference, 'paymentId' => $assert->payment_id, + 'supportsOrderCapture' => SaferPayConfig::supportsOrderCapture($paymentMethod), + 'supportsOrderCancel' => SaferPayConfig::supportsOrderCancel($paymentMethod), 'acceptance' => '????', 'liability_entity' => $assert->liability_entity, 'cardNumber' => $assert->card_number, diff --git a/src/Processor/CheckoutProcessor.php b/src/Processor/CheckoutProcessor.php index eac6cc4a..5b6912f1 100644 --- a/src/Processor/CheckoutProcessor.php +++ b/src/Processor/CheckoutProcessor.php @@ -37,6 +37,7 @@ use Invertus\SaferPay\Repository\SaferPayOrderRepository; use Invertus\SaferPay\Service\SaferPayInitialize; use Order; +use PrestaShop\PrestaShop\Adapter\Entity\PrestaShopLogger; use PrestaShopException; use SaferPayOrder; @@ -203,13 +204,15 @@ private function processAuthorizedOrder(CheckoutData $data, Cart $cart) } $saferPayOrder->id_order = $order->id; - if ($data->getOrderStatus() === 'AUTHORIZED') { $order->setCurrentState(_SAFERPAY_PAYMENT_AUTHORIZED_); $saferPayOrder->authorized = 1; } elseif ($data->getOrderStatus() === 'CAPTURED') { $order->setCurrentState(_SAFERPAY_PAYMENT_COMPLETED_); $saferPayOrder->captured = 1; + } elseif ($data->getOrderStatus() === 'PENDING') { + $order->setCurrentState(_SAFERPAY_PAYMENT_PENDING_); + $saferPayOrder->pending = 1; } $saferPayOrder->update(); diff --git a/src/Repository/SaferPayOrderRepository.php b/src/Repository/SaferPayOrderRepository.php index ae51aa3f..b9d262e8 100755 --- a/src/Repository/SaferPayOrderRepository.php +++ b/src/Repository/SaferPayOrderRepository.php @@ -25,6 +25,7 @@ use Db; use DbQuery; +use SaferPayOrder; if (!defined('_PS_VERSION_')) { exit; @@ -32,6 +33,17 @@ class SaferPayOrderRepository { + + /** + * @param int $orderId + * + * @return SaferPayOrder + */ + public function getByOrderId($orderId) + { + return new SaferPayOrder($this->getIdByOrderId($orderId)); + } + public function getIdByOrderId($orderId) { $query = new DbQuery(); diff --git a/src/Service/LegacyTranslator.php b/src/Service/LegacyTranslator.php index 96a0e1b8..5c53b5d2 100755 --- a/src/Service/LegacyTranslator.php +++ b/src/Service/LegacyTranslator.php @@ -81,6 +81,7 @@ private function getTranslations() SaferPayConfig::PAYMENT_PAYCONIQ => $this->module->l('Payconiq', self::FILE_NAME), SaferPayConfig::PAYMENT_CARD => $this->module->l('Card', self::FILE_NAME), SaferPayConfig::PAYMENT_POSTFINANCE_PAY => $this->module->l('PostFinancePay', self::FILE_NAME), + SaferPayConfig::PAYMENT_WECHATPAY => $this->module->l('WeChatPay', self::FILE_NAME), ]; } } diff --git a/upgrade/install-1.2.3.php b/upgrade/install-1.2.3.php new file mode 100644 index 00000000..b5ce3a0b --- /dev/null +++ b/upgrade/install-1.2.3.php @@ -0,0 +1,41 @@ + + *@copyright SIX Payment Services + *@license SIX Payment Services + */ + +use Invertus\SaferPay\Config\SaferPayConfig; +use Invertus\SaferPay\DTO\Request\RequestHeader; + +if (!defined('_PS_VERSION_')) { + exit; +} + +function upgrade_module_1_2_3(SaferPayOfficial $module) +{ + $installer = new \Invertus\SaferPay\Install\Installer($module); + + return + $installer->createPendingOrderStatus() && + Db::getInstance()->execute('ALTER TABLE ' . _DB_PREFIX_ . 'saferpay_order ADD COLUMN `pending` TINYINT(1) DEFAULT 0') && + $module->registerHook('displayOrderConfirmation') && + Configuration::updateValue(RequestHeader::SPEC_VERSION, SaferPayConfig::API_VERSION) && + Configuration::updateValue(RequestHeader::SPEC_REFUND_VERSION, SaferPayConfig::API_VERSION); +} diff --git a/views/templates/hook/admin/saferpay_order.tpl b/views/templates/hook/admin/saferpay_order.tpl index a8b8251d..fff4f2b2 100755 --- a/views/templates/hook/admin/saferpay_order.tpl +++ b/views/templates/hook/admin/saferpay_order.tpl @@ -54,10 +54,14 @@ {/if} {elseif $transactionPaid == 'AUTHORIZED'} - - + {if $supportsOrderCapture} + + {/if} + {if $supportsOrderCancel} + + {/if} {/if}