diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 000000000..56630629e --- /dev/null +++ b/.editorconfig @@ -0,0 +1,19 @@ +# EditorConfig is awesome: http://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = space +indent_size = 2 + +[*.php] +indent_size = 4 + +[*.md] +trim_trailing_whitespace = false \ No newline at end of file diff --git a/.gitignore b/.gitignore index 90cda2985..10494e978 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,5 @@ build/ .codecept.phar /translations/ var/ -config.xml \ No newline at end of file +config.xml +mails/ \ No newline at end of file diff --git a/README.md b/README.md index 017e63864..9330dce69 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ Download and install the Mollie PrestaShop payment module and start receiving on ## Maintained by [Invertus](https://www.invertus.eu/). ## -## Compatible with PrestaShop 1.5 to 1.7 ## +## Compatible with PrestaShop 1.6 to 1.7 ## ** Please note: ** FTP access to your web server could be required for the installation of this module. If you have no experience with this, then leave the installation of this module to your website developer or server administrator. @@ -35,17 +35,17 @@ If you have no experience with this, then leave the installation of this module ## Installation PrestaShop 1.7 ## * Download the latest version of the module (the '.zip' file) via the [Releases page](https://github.com/mollie/Prestashop/releases) which is compatible -with PrestaShop 1.5 to 1.7. +with PrestaShop 1.6 to 1.7. * Go to the administration panel of your PrestaShop webshop * In your administration panel, select the 'Modules' tab and then choose 'upload a module' at the top right of your screen * Select 'select file' and then upload the '.zip' file which you downloaded earlier * After the module has been installed, choose 'configure' * Enter your _API-key_ and save the data. -## Installation PrestaShop 1.5 and 1.6 ## +## Installation PrestaShop 1.6 ## * Download the latest version of the module (the '.zip' file) via the [Releases-page](https://github.com/mollie/Prestashop/releases) which is compatible -with PrestaShop 1.5 to 1.7. +with PrestaShop 1.6 to 1.7. * Go to the administration panel of your PrestaShop webshop * In your administration panel, select the 'Modules' tab and then choose 'upload a module' at the top right of your screen * Select 'select file' and then upload the '.zip' file which you downloaded earlier @@ -55,7 +55,7 @@ with PrestaShop 1.5 to 1.7. ## or ## * Download the latest version of the module (the '.zip' file) via the [Releases-page](https://github.com/mollie/Prestashop/releases) which is compatible -with PrestaShop 1.5 to 1.7. +with PrestaShop 1.6 to 1.7. * Extract the archive * Copy the `mollie` folder to the` modules` folder via FTP in your PrestaShop installation * Go to the tab 'Modules' in your administration panel and choose 'Payments and Gateways' in the 'Categories' list. diff --git a/config.xml b/config.xml index e3171d13f..fd6abc8e5 100644 --- a/config.xml +++ b/config.xml @@ -2,7 +2,7 @@ mollie - + diff --git a/config/config.yml b/config/config.yml index 2d2bf9290..2997dad1a 100644 --- a/config/config.yml +++ b/config/config.yml @@ -3,6 +3,9 @@ imports: - { resource: service.yml } - { resource: repository.yml } - { resource: context.yml } + - { resource: factory.yml } + - { resource: presenter.yml } + - { resource: validator.yml } services: mollie: @@ -14,6 +17,7 @@ services: Mollie\Install\Installer: arguments: - '@mollie' + - '@Mollie\Service\imageService' Mollie\Install\Uninstall: arguments: diff --git a/config/factory.yml b/config/factory.yml new file mode 100644 index 000000000..00d3cbe53 --- /dev/null +++ b/config/factory.yml @@ -0,0 +1,2 @@ +services: + Mollie\Factory\CustomerFactory: diff --git a/config/presenter.yml b/config/presenter.yml new file mode 100644 index 000000000..8633dc5ed --- /dev/null +++ b/config/presenter.yml @@ -0,0 +1,4 @@ +services: + Mollie\Presenter\OrderListActionBuilder: + arguments: + - '@mollie' \ No newline at end of file diff --git a/config/repository.yml b/config/repository.yml index e3b7afe23..494944ea1 100644 --- a/config/repository.yml +++ b/config/repository.yml @@ -12,3 +12,7 @@ services: Mollie\Repository\MolCarrierInformationRepository: Mollie\Repository\ModuleRepository: + + Mollie\Repository\PendingOrderCartRepository: + arguments: + - 'MolPendingOrderCart' \ No newline at end of file diff --git a/config/service.yml b/config/service.yml index d53c7443f..9970c6a76 100644 --- a/config/service.yml +++ b/config/service.yml @@ -7,6 +7,7 @@ services: - '@Mollie\Repository\PaymentMethodRepository' - '@Mollie\Repository\MethodCountryRepository' - '@Mollie\Service\CartLinesService' + - '@Mollie\Service\PaymentsTranslationService' Mollie\Service\ApiService: arguments: @@ -19,10 +20,6 @@ services: - '@Mollie\Service\ApiService' - '@Mollie\Repository\CountryRepository' - Mollie\Service\UrlPathService: - arguments: - - '@mollie' - Mollie\Service\IssuerService: arguments: - '@mollie' @@ -43,9 +40,13 @@ services: Mollie\Service\CarrierService: Mollie\Service\OrderStatusService: + arguments: + - '@Mollie\Service\MailService' Mollie\Service\ErrorDisplayService: + Mollie\Service\imageService: + Mollie\Service\CountryService: arguments: - '@mollie' @@ -86,4 +87,48 @@ services: arguments: - '@mollie' - Mollie\Service\CartDuplicationService: \ No newline at end of file + Mollie\Service\CartDuplicationService: + + Mollie\Service\OrderCartAssociationService: + arguments: + - '@Mollie\Service\CartDuplicationService' + + Mollie\Service\MemorizeCartService: + arguments: + - '@Mollie\Service\OrderCartAssociationService' + + Mollie\Service\RepeatOrderLinkFactory: + + Mollie\Logger\PrestaLogger: + + Psr\Log\LoggerInterface: '@Mollie\Logger\PrestaLogger' + + Mollie\Service\RestorePendingCartService: + arguments: + - '@Mollie\Repository\PendingOrderCartRepository' + + Mollie\Service\TransactionService: + arguments: + - '@mollie' + - '@Mollie\Repository\PaymentMethodRepository' + - '@Mollie\Service\OrderStatusService' + + Mollie\Service\MolliePaymentMailService: + arguments: + - '@mollie' + - '@Mollie\Repository\PaymentMethodRepository' + - '@Mollie\Service\MailService' + + Mollie\Service\MailService: + arguments: + - '@mollie' + + Mollie\Service\PaymentsTranslationService: + - '@mollie' + - '@Mollie\Service\LanguageService' + + Mollie\Service\PaymentReturnService: + - '@mollie' + - '@Mollie\Service\CartDuplicationService' + - '@Mollie\Repository\PaymentMethodRepository' + - '@Mollie\Service\RepeatOrderLinkFactory' diff --git a/config/validator.yml b/config/validator.yml new file mode 100644 index 000000000..421215710 --- /dev/null +++ b/config/validator.yml @@ -0,0 +1,2 @@ +services: + Mollie\Validator\OrderConfMailValidator: diff --git a/controllers/admin/AdminMollieAjaxController.php b/controllers/admin/AdminMollieAjaxController.php new file mode 100644 index 000000000..03306e193 --- /dev/null +++ b/controllers/admin/AdminMollieAjaxController.php @@ -0,0 +1,64 @@ +togglePaymentMethod(); + break; + case 'resendPaymentMail': + $this->resendPaymentMail(); + break; + default: + break; + } + } + + private function togglePaymentMethod() + { + $paymentMethod = Tools::getValue('paymentMethod'); + $paymentStatus = Tools::getValue('status'); + + /** @var PaymentMethodRepository $paymentMethodRepo */ + $paymentMethodRepo = $this->module->getContainer(PaymentMethodRepository::class); + $methodId = $paymentMethodRepo->getPaymentMethodIdByMethodId($paymentMethod); + $method = new MolPaymentMethod($methodId); + switch ($paymentStatus) { + case 'deactivate': + $method->enabled = 0; + break; + case 'activate': + $method->enabled = 1; + break; + } + $method->update(); + + $this->ajaxDie(json_encode( + [ + 'success' => true, + 'paymentStatus' => $method->enabled + ] + )); + } + + private function resendPaymentMail() + { + $orderId = Tools::getValue('id_order'); + + /** @var MolliePaymentMailService $molliePaymentMailService */ + $molliePaymentMailService = $this->module->getContainer(MolliePaymentMailService::class); + + $response = $molliePaymentMailService->sendSecondChanceMail($orderId); + + $this->ajaxDie(json_encode($response)); + } +} \ No newline at end of file diff --git a/controllers/admin/AdminMollieModuleController.php b/controllers/admin/AdminMollieModuleController.php new file mode 100644 index 000000000..db751c8cf --- /dev/null +++ b/controllers/admin/AdminMollieModuleController.php @@ -0,0 +1,6 @@ +createOrder($method, $apiPayment, $cart->id, $originalAmount, $customer->secure_key, $orderReference); $orderReference = isset($apiPayment->metadata->order_reference) ? pSQL($apiPayment->metadata->order_reference) : ''; - // Store payment linked to cart - //todo: test + $orderId = Order::getOrderByCartId($cart->id); + $order = new Order($orderId); if ($apiPayment->method !== PaymentMethod::BANKTRANSFER) { try { Db::getInstance()->insert( 'mollie_payments', array( 'cart_id' => (int) $cart->id, + 'order_id' => (int) $order->id, 'method' => pSQL($apiPayment->method), 'transaction_id' => pSQL($apiPayment->id), 'order_reference' => pSQL($orderReference), @@ -171,80 +175,12 @@ public function initContent() } } - $status = $apiPayment->status; - if (!isset(Mollie\Config\Config::getStatuses()[$apiPayment->status])) { - $status = 'open'; - } - - $paymentStatus = (int) Mollie\Config\Config::getStatuses()[$status]; - - if ($paymentStatus < 1) { - $paymentStatus = Configuration::get(Mollie\Config\Config::STATUS_MOLLIE_AWAITING); - } - -// if ($apiPayment->method === PaymentMethod::BANKTRANSFER) { -// // Set the `banktransfer` details -// if ($apiPayment instanceof MollieOrderAlias) { -// // If this is an order, take the first payment -// $apiPayment = $apiPayment->payments(); -// $apiPayment = $apiPayment[0]; -// } -// -// $details = $apiPayment->details->transferReference; -// $address = "IBAN: {$apiPayment->details->bankAccount} / BIC: {$apiPayment->details->bankBic}"; -// -// $extraVars = array( -// '{bankwire_owner}' => 'Stichting Mollie Payments', -// '{bankwire_details}' => $details, -// '{bankwire_address}' => $address, -// ); -// -// $this->module->currentOrderReference = $orderReference; -// $this->module->validateOrder( -// (int) $cart->id, -// $paymentStatus, -// $originalAmount, -// isset(Mollie\Config\Config::$methods[$apiPayment->method]) ? Mollie\Config\Config::$methods[$apiPayment->method] : $this->module->name, -// null, -// $extraVars, -// null, -// false, -// $customer->secure_key -// ); -// -// $orderId = Order::getOrderByCartId((int) $cart->id); -// -// try { -// Db::getInstance()->insert( -// 'mollie_payments', -// array( -// 'cart_id' => (int) $cart->id, -// 'order_id' => (int) $orderId, -// 'order_reference' => pSQL($orderReference), -// 'method' => pSQL($apiPayment->method), -// 'transaction_id' => pSQL($apiPayment->id), -// 'bank_status' => PaymentStatus::STATUS_OPEN, -// 'created_at' => array('type' => 'sql', 'value' => 'NOW()'), -// ) -// ); -// } catch (PrestaShopDatabaseException $e) { -// $paymentMethodRepo->tryAddOrderReferenceColumn(); -// $redirectLink = $this->context->link->getPageLink( -// 'order', -// true, -// null, -// [ -// 'step' => 1, -// ] -// ); -// $this->errors[] = $this->l('Failed to validate order', 'payment'); -// $this->redirectWithNotifications($redirectLink); -// } -// } - - // Go to payment url - Tools::redirect($apiPayment->getCheckoutUrl()); + if($apiPayment->getCheckoutUrl() !== null){ + Tools::redirect($apiPayment->getCheckoutUrl()); + } else { + Tools::redirect($apiPayment->redirectUrl); + } } /** @@ -425,5 +361,10 @@ private function createOrder($method, $apiPayment, $cartId, $originalAmount, $se $order->total_paid = $totalPrice->toPrecision(2); $order->reference = $orderReference; $order->update(); + + /** @var MemorizeCartService $memorizeCart */ + $memorizeCart = $this->module->getContainer(MemorizeCartService::class); + + $memorizeCart->memorizeCart($order); } } diff --git a/controllers/front/return.php b/controllers/front/return.php index 7847b9587..77d9c3add 100644 --- a/controllers/front/return.php +++ b/controllers/front/return.php @@ -33,14 +33,16 @@ * @codingStandardsIgnoreStart */ -use _PhpScoper5eddef0da618a\Mollie\Api\Types\OrderStatus; use _PhpScoper5eddef0da618a\Mollie\Api\Types\PaymentMethod; use _PhpScoper5eddef0da618a\Mollie\Api\Types\PaymentStatus; -use Mollie\Config\Config; use Mollie\Controller\AbstractMollieController; +use Mollie\Factory\CustomerFactory; use Mollie\Repository\PaymentMethodRepository; -use Mollie\Service\CartDuplicationService; -use Mollie\Service\OrderStatusService; +use Mollie\Service\RestorePendingCartService; +use Mollie\Utility\PaymentMethodUtility; +use Mollie\Utility\TransactionUtility; +use Mollie\Utility\ArrayUtility; +use Mollie\Service\PaymentReturnService; use PrestaShop\PrestaShop\Adapter\CoreException; if (!defined('_PS_VERSION_')) { @@ -57,15 +59,11 @@ */ class MollieReturnModuleFrontController extends AbstractMollieController { - const PENDING = 1; - const DONE = 2; + + const FILE_NAME = 'return'; /** @var bool $ssl */ public $ssl = true; - /** @var bool $display_column_left */ - public $display_column_left = false; - /** @var bool $display_column_left */ - public $display_column_right = false; /** * Unset the cart id from cookie if the order exists @@ -98,6 +96,12 @@ public function init() */ public function initContent() { + $customerId = Tools::getValue('customerId'); + $customerSecureKey = Tools::getValue('key'); + + /** @var CustomerFactory $customerFactory */ + $customerFactory = $this->module->getContainer(CustomerFactory::class); + $this->context = $customerFactory->recreateFromRequest($customerId, $customerSecureKey, $this->context); if (Tools::getValue('ajax')) { $this->processAjax(); exit; @@ -161,7 +165,13 @@ public function initContent() $this->context->link->getModuleLink( $this->module->name, 'return', - ['ajax' => 1, 'action' => 'getStatus', 'transaction_id' => $data['mollie_info']['transaction_id']], + [ + 'ajax' => 1, + 'action' => 'getStatus', + 'transaction_id' => $data['mollie_info']['transaction_id'], + 'key' => $this->context->customer->secure_key, + 'customerId' => $this->context->customer->id + ], true ) ); @@ -254,12 +264,11 @@ protected function processGetStatus() ])); } - if (!Tools::isSubmit('module')) { $_GET['module'] = $this->module->name; } - $isOrder = Tools::substr($transactionId, 0, 3) === 'ord'; + $isOrder = TransactionUtility::isOrderTransaction($transactionId); if ($isOrder) { $transaction = $this->module->api->orders->get($transactionId, ['embed' => 'payments']); } else { @@ -267,151 +276,58 @@ protected function processGetStatus() } $orderStatus = $transaction->status; - if ($orderStatus === 'created' && $isOrder) { - foreach ($transaction->payments() as $payment) { - $orderStatus = $payment->status; - } + + if ($transaction->resource === "order") { + $payments = ArrayUtility::getLastElement($transaction->_embedded->payments); + $orderStatus = $payments->status; } + + $notSuccessfulPaymentMessage = $this->module->l('Your payment was not successful, please try again.', self::FILE_NAME); + $paymentMethod = PaymentMethodUtility::getPaymentMethodName($transaction->method); + + /** @var PaymentReturnService $paymentReturnService */ + $paymentReturnService = $this->module->getContainer(PaymentReturnService::class); + $stockManagement = Configuration::get('PS_STOCK_MANAGEMENT'); switch ($orderStatus) { - case PaymentStatus::STATUS_EXPIRED: - case PaymentStatus::STATUS_FAILED: - case PaymentStatus::STATUS_CANCELED: - /** @var CartDuplicationService $cartDuplicationService */ - $cartDuplicationService = $this->module->getContainer(CartDuplicationService::class); - $cartDuplicationService->restoreCart($order->id_cart); - - $this->warning[] = $this->module->l('Your payment was not successful, please try again.'); - - $this->context->cookie->mollie_payment_canceled_error = - json_encode($this->warning); - - $this->updateTransactions($transactionId, $orderId, $orderStatus, $dbPayment['method']); - - if (!Config::isVersion17()) { - $orderLink = $this->context->link->getPageLink( - 'order', - true, - null - ); - } else { - $orderLink = $this->context->link->getPageLink( - 'cart', - null, - $this->context->language->id, - [ - 'action' => 'show', - ], - false, - null, - false - - ); - } - - die(json_encode([ - 'success' => true, - 'status' => static::DONE, - 'response' => json_encode($transaction), - 'href' => $orderLink - - ])); + case PaymentStatus::STATUS_OPEN: + case PaymentStatus::STATUS_PENDING: + $response = $paymentReturnService->handlePendingStatus( + $order, + $transaction, + $orderStatus, + $paymentMethod, + $stockManagement + ); + break; case PaymentStatus::STATUS_AUTHORIZED: case PaymentStatus::STATUS_PAID: - case OrderStatus::STATUS_COMPLETED: - $status = static::DONE; - $orderDetails = $order->getOrderDetailList(); - /** @var OrderDetail $detail */ - foreach ($orderDetails as $detail) { - $orderDetail = new OrderDetail($detail['id_order_detail']); - if ( - Configuration::get('PS_STOCK_MANAGEMENT') && - ($orderDetail->getStockState() || $orderDetail->product_quantity_in_stock < 0) - ) { - $orderStatus = Mollie\Config\Config::STATUS_PAID_ON_BACKORDER; - break; - } - } + $response = $paymentReturnService->handlePaidStatus( + $order, + $transaction, + $paymentMethod, + $stockManagement + ); break; - default: - $status = static::PENDING; - $orderDetails = $order->getOrderDetailList(); - /** @var OrderDetail $detail */ - foreach ($orderDetails as $detail) { - $orderDetail = new OrderDetail($detail['id_order_detail']); - if ( - Configuration::get('PS_STOCK_MANAGEMENT') && - ($orderDetail->getStockState() || $orderDetail->product_quantity_in_stock < 0) - ) { - $orderStatus = Mollie\Config\Config::STATUS_PENDING_ON_BACKORDER; - break; - } - } + case PaymentStatus::STATUS_EXPIRED: + case PaymentStatus::STATUS_CANCELED: + case PaymentStatus::STATUS_FAILED: + $this->setWarning($notSuccessfulPaymentMessage); + /** @var RestorePendingCartService $restorePendingCart */ + $restorePendingCart = $this->module->getContainer(RestorePendingCartService::class); + $restorePendingCart->restore($order); + + $response = $paymentReturnService->handleFailedStatus($order, $transaction, $orderStatus, $paymentMethod); break; } - $this->updateTransactions($transactionId, $orderId, $orderStatus, $dbPayment['method']); - - $successUrl = $this->context->link->getPageLink( - 'order-confirmation', - true, - null, - [ - 'id_cart' => (int)$cart->id, - 'id_module' => (int)$this->module->id, - 'id_order' => (int)version_compare(_PS_VERSION_, '1.7.1.0', '>=') - ? Order::getIdByCartId((int)$cart->id) - : Order::getOrderByCartId((int)$cart->id), - 'key' => $cart->secure_key, - ] - ); - - die(json_encode([ - 'success' => true, - 'status' => $status, - 'response' => json_encode($transaction), - 'href' => $successUrl - ])); + die(json_encode($response)); } - protected function savePaymentStatus($transactionId, $status, $orderId) + private function setWarning($message) { - try { - return Db::getInstance()->update( - 'mollie_payments', - [ - 'updated_at' => ['type' => 'sql', 'value' => 'NOW()'], - 'bank_status' => pSQL($status), - 'order_id' => (int)$orderId, - ], - '`transaction_id` = \'' . pSQL($transactionId) . '\'' - ); - } catch (Exception $e) { - /** @var PaymentMethodRepository $paymentMethodRepo */ - $paymentMethodRepo = $this->module->getContainer(PaymentMethodRepository::class); - $paymentMethodRepo->tryAddOrderReferenceColumn(); - throw $e; - } - } + $this->warning[] = $message; - protected function updateTransactions($transactionId, $orderId, $orderStatus, $paymentMethod) - { - /** @var OrderStatusService $orderStatusService */ - $orderStatusService = $this->module->getContainer(OrderStatusService::class); - - $orderStatusId = (int)Mollie\Config\Config::getStatuses()[$orderStatus]; - $this->savePaymentStatus($transactionId, $orderStatus, $orderId); - $transactionInfo = [ - 'transactionId' => $transactionId, - 'paymentMethod' => $paymentMethod, - ]; - $orderStatusService->setOrderStatus($orderId, $orderStatusId, null, [], $transactionInfo); - - $orderPayments = OrderPayment::getByOrderId($orderId); - /** @var OrderPayment $orderPayment */ - foreach ($orderPayments as $orderPayment) { - $orderPayment->transaction_id = $transactionId; - $orderPayment->payment_method = $paymentMethod; - $orderPayment->update(); - } + $this->context->cookie->mollie_payment_canceled_error = + json_encode($this->warning); } } diff --git a/controllers/front/webhook.php b/controllers/front/webhook.php index 021ffeee3..1a70a9b1a 100644 --- a/controllers/front/webhook.php +++ b/controllers/front/webhook.php @@ -34,15 +34,15 @@ */ use _PhpScoper5eddef0da618a\Mollie\Api\Exceptions\ApiException; -use _PhpScoper5eddef0da618a\Mollie\Api\Resources\Payment; use _PhpScoper5eddef0da618a\Mollie\Api\Resources\Payment as MolliePaymentAlias; use _PhpScoper5eddef0da618a\Mollie\Api\Resources\Order as MollieOrderAlias; -use _PhpScoper5eddef0da618a\Mollie\Api\Resources\ResourceFactory; use _PhpScoper5eddef0da618a\Mollie\Api\Types\PaymentMethod; -use _PhpScoper5eddef0da618a\Mollie\Api\Types\PaymentStatus; use _PhpScoper5eddef0da618a\Mollie\Api\Types\RefundStatus; +use Mollie\Config\Config; use Mollie\Repository\PaymentMethodRepository; use Mollie\Service\OrderStatusService; +use Mollie\Utility\OrderStatusUtility; +use Mollie\Utility\TransactionUtility; use PrestaShop\PrestaShop\Adapter\CoreException; if (!defined('_PS_VERSION_')) { @@ -114,7 +114,7 @@ protected function executeWebhook() } $transactionId = Tools::getValue('id'); - if (Tools::substr($transactionId, 0, 3) === 'ord') { + if (TransactionUtility::isOrderTransaction($transactionId)) { $payment = $this->processTransaction($this->module->api->orders->get($transactionId, array('embed' => 'payments'))); } else { $payment = $this->processTransaction($this->module->api->payments->get($transactionId)); @@ -151,7 +151,7 @@ public function processTransaction($transaction) // Ensure that we are dealing with a Payment object, in case of transaction ID or Payment object w/ Order ID, convert if ($transaction instanceof MolliePaymentAlias) { - if (!empty($transaction->orderId) && Tools::substr($transaction->orderId, 0, 3) === 'ord') { + if (!empty($transaction->orderId) && TransactionUtility::isOrderTransaction($transaction->orderId)) { // Part of order $transaction = $this->module->api->orders->get($transaction->orderId, array('embed' => 'payments')); } else { @@ -160,54 +160,8 @@ public function processTransaction($transaction) } } - if (!isset($apiPayment)) { - $apiPayments = array(); - /** @var MollieOrderAlias $transaction */ - foreach ($transaction->_embedded->payments as $embeddedPayment) { - $apiPayment = ResourceFactory::createFromApiResult($embeddedPayment, new Payment($this->module->api)); - $apiPayments[] = $apiPayment; - unset($apiPayment); - } - if (count($apiPayments) === 1) { - $apiPayment = $apiPayments[0]; - } else { - // In case of multiple payments, the one with the paid status is leading - foreach ($apiPayments as $payment) { - if (in_array($payment->status, array(PaymentStatus::STATUS_PAID, PaymentStatus::STATUS_AUTHORIZED))) { - $apiPayment = $payment; - break; - } - } - - // No paid/authorized payments found, looking for payments with a final status - if (!isset($apiPayment)) { - foreach ($apiPayments as $payment) { - if (in_array($payment->status, array( - PaymentStatus::STATUS_CANCELED, - PaymentStatus::STATUS_FAILED, - PaymentStatus::STATUS_EXPIRED, - ))) { - $apiPayment = $payment; - break; - } - } - } - - // In case there is no final payments, we are going to look for any pending payments - if (!isset($apiPayment)) { - foreach ($apiPayments as $payment) { - if (in_array($payment->status, array( - PaymentStatus::STATUS_PENDING, - ))) { - $apiPayment = $payment; - break; - } - } - } - } - if (isset($apiPayment)) { - $apiPayment->metadata = $transaction->metadata; - } + if (!empty($transaction->id) && TransactionUtility::isOrderTransaction(($transaction->id))) { + $apiPayment = $this->module->api->orders->get($transaction->id, array('embed' => 'payments')); } if (!isset($apiPayment)) { @@ -233,38 +187,82 @@ public function processTransaction($transaction) '`transaction_id` = \'' . pSQL($transaction->id) . '\'' ); - if ($apiPayment->metadata->cart_id) { - if ($apiPayment->hasRefunds() || $apiPayment->hasChargebacks()) { - if (isset($apiPayment->settlementAmount->value, $apiPayment->amountRefunded->value) - && (float) $apiPayment->amountRefunded->value >= (float) $apiPayment->settlementAmount->value - ) { - $orderStatusService->setOrderStatus($orderId, RefundStatus::STATUS_REFUNDED); - } else { - $orderStatusService->setOrderStatus($orderId, Mollie\Config\Config::PARTIAL_REFUND_CODE); + switch ($transaction->resource) { + case Mollie\Config\Config::MOLLIE_API_STATUS_PAYMENT: + if ($apiPayment->metadata->cart_id) { + if ($apiPayment->hasRefunds() || $apiPayment->hasChargebacks()) { + if (isset($apiPayment->settlementAmount->value, $apiPayment->amountRefunded->value) + && (float)$apiPayment->amountRefunded->value >= (float)$apiPayment->settlementAmount->value + ) { + $orderStatusService->setOrderStatus($orderId, RefundStatus::STATUS_REFUNDED); + } else { + $orderStatusService->setOrderStatus($orderId, Mollie\Config\Config::PARTIAL_REFUND_CODE); + } + } elseif ($psPayment['method'] === PaymentMethod::BANKTRANSFER + ) { + $order = new Order($orderId); + $order->payment = isset(Mollie\Config\Config::$methods[$apiPayment->method]) + ? Mollie\Config\Config::$methods[$apiPayment->method] + : $this->module->displayName; + $order->update(); + + $orderStatusService->setOrderStatus($orderId, $apiPayment->status); + } elseif ($psPayment['method'] !== PaymentMethod::BANKTRANSFER + && ($apiPayment->isPaid() || $apiPayment->isAuthorized() || $apiPayment->isExpired()) + && Tools::encrypt($cart->secure_key) === $apiPayment->metadata->secure_key + ) { + $paymentStatus = (int)Mollie\Config\Config::getStatuses()[$apiPayment->status]; + + /** @var OrderStatusService $orderStatusService */ + $orderStatusService = $this->module->getContainer(OrderStatusService::class); + $orderStatusService->setOrderStatus($orderId, $paymentStatus); + + $orderId = Order::getOrderByCartId((int)$apiPayment->metadata->cart_id); + } } - } elseif ($psPayment['method'] === PaymentMethod::BANKTRANSFER - && $psPayment['bank_status'] === PaymentStatus::STATUS_OPEN - && $apiPayment->status === PaymentStatus::STATUS_PAID - ) { - $order = new Order($orderId); - $order->payment = isset(Mollie\Config\Config::$methods[$apiPayment->method]) - ? Mollie\Config\Config::$methods[$apiPayment->method] - : $this->module->displayName; - $order->update(); - - $orderStatusService->setOrderStatus($orderId, $apiPayment->status); - } elseif ($psPayment['method'] !== PaymentMethod::BANKTRANSFER - && ($apiPayment->isPaid() || $apiPayment->isAuthorized() || $apiPayment->isExpired()) - && Tools::encrypt($cart->secure_key) === $apiPayment->metadata->secure_key - ) { - $paymentStatus = (int) Mollie\Config\Config::getStatuses()[$apiPayment->status]; - - /** @var OrderStatusService $orderStatusService */ - $orderStatusService = $this->module->getContainer(OrderStatusService::class); - $orderStatusService->setOrderStatus($orderId, $paymentStatus); - - $orderId = Order::getOrderByCartId((int) $apiPayment->metadata->cart_id); - } + break; + case Mollie\Config\Config::MOLLIE_API_STATUS_ORDER: + if ($apiPayment->metadata->cart_id) { + /** todo: investigate if banktransfer logic is needed here */ + if ($psPayment['method'] === PaymentMethod::BANKTRANSFER + ) { + $order = new Order($orderId); + $order->payment = isset(Mollie\Config\Config::$methods[$apiPayment->method]) + ? Mollie\Config\Config::$methods[$apiPayment->method] + : $this->module->displayName; + $order->update(); + + $orderStatusService->setOrderStatus($orderId, $apiPayment->status); + } elseif ($psPayment['method'] !== PaymentMethod::BANKTRANSFER + && Tools::encrypt($cart->secure_key) === $apiPayment->metadata->secure_key + && $apiPayment->status === \_PhpScoper5eddef0da618a\Mollie\Api\Types\OrderStatus::STATUS_CREATED + ) { + $orderPayments = $apiPayment->payments(); + $paymentStatus = \_PhpScoper5eddef0da618a\Mollie\Api\Types\OrderStatus::STATUS_CREATED; + foreach ($orderPayments as $orderPayment) { + $paymentStatus = $orderPayment->status; + } + $paymentStatus = (int)Mollie\Config\Config::getStatuses()[$paymentStatus]; + + /** @var OrderStatusService $orderStatusService */ + $orderStatusService = $this->module->getContainer(OrderStatusService::class); + $orderStatusService->setOrderStatus($orderId, $paymentStatus); + + $orderId = Order::getOrderByCartId((int)$apiPayment->metadata->cart_id); + } elseif ($psPayment['method'] !== PaymentMethod::BANKTRANSFER + && Tools::encrypt($cart->secure_key) === $apiPayment->metadata->secure_key + ) { + $status = OrderStatusUtility::transformPaymentStatusToRefunded($apiPayment); + $paymentStatus = (int) Config::getStatuses()[$status]; + + /** @var OrderStatusService $orderStatusService */ + $orderStatusService = $this->module->getContainer(OrderStatusService::class); + $orderStatusService->setOrderStatus($orderId, $paymentStatus); + + $orderId = Order::getOrderByCartId((int)$apiPayment->metadata->cart_id); + } + } + break; } // Store status in database diff --git a/mails/en/mollie_payment.html b/mails/en/mollie_payment.html new file mode 100644 index 000000000..85287d26b --- /dev/null +++ b/mails/en/mollie_payment.html @@ -0,0 +1,816 @@ + + + + + Payment + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
 
+ +
+ + + +
+ + + + +
+ + + + + + +
+ +
+ + + + +
+ + + + + + +
+ + + +
+
+
+ +
+
+ + + + + +
+ + + + + + +
+ +
+ + + + + + +
+ + + + + + + +
+
+ Hi {firstName} {lastName}, +
+
+
+
+ +
+
+ +
+ + + + + + +
+ +
+ + + + +
+

+

+ +
+
+ +
+
+ + + + +
+ + + + + + +
+ +
+ + + + + + +
+ + + + +
+
+ Thank you for shopping with {shop_name}! +
+
+
+
+ +
+
+ + + + +
+ + + + + + +
+ +
+ + + + + + +
+ + + + +
+
+ You can now finish your order by clicking on: here. +
+
+
+
+ +
+
+ + + + +
+ + + + + +
+ + + + + + + + + + + + + +
+ + + + +
+ + + + + + +
+ +
+ + + + +
+ +
+
+ +
+
+ + + + + + + + diff --git a/mails/en/mollie_payment.txt b/mails/en/mollie_payment.txt new file mode 100644 index 000000000..f35168132 --- /dev/null +++ b/mails/en/mollie_payment.txt @@ -0,0 +1,7 @@ +Hi {firstName} {lastName}, + +Thank you for shopping with {shop_name}! + +You can now finish your order by clicking on: [here]({checkoutUrl}). + +[{shop_name}]({shop_url}) \ No newline at end of file diff --git a/mollie.php b/mollie.php index 16f499e68..4dd8e63a0 100644 --- a/mollie.php +++ b/mollie.php @@ -32,6 +32,7 @@ * @link https://www.mollie.nl * @codingStandardsIgnoreStart */ + if (!include_once(dirname(__FILE__) . '/vendor/autoload.php')) { return; } @@ -47,6 +48,7 @@ if (!include_once(dirname(__FILE__) . '/vendor/guzzlehttp/psr7/src/functions_include.php')) { return; } + /** * Class Mollie * @@ -75,6 +77,10 @@ class Mollie extends PaymentModule const ADDONS = false; const SUPPORTED_PHP_VERSION = '5.6'; + + const ADMIN_MOLLIE_CONTROLLER = 'AdminMollieModuleController'; + const ADMIN_MOLLIE_AJAX_CONTROLLER = 'AdminMollieAjaxController'; + /** * Mollie constructor. * @@ -86,14 +92,14 @@ public function __construct() { $this->name = 'mollie'; $this->tab = 'payments_gateways'; - $this->version = '4.0.6'; + $this->version = '4.0.7'; $this->author = 'Mollie B.V.'; $this->need_instance = 1; $this->bootstrap = true; $this->module_key = 'a48b2f8918358bcbe6436414f48d8915'; parent::__construct(); - $this->ps_versions_compliancy = array('min' => '1.6.1.0', 'max' => _PS_VERSION_); + $this->ps_versions_compliancy = ['min' => '1.6.1.0', 'max' => _PS_VERSION_]; $this->displayName = $this->l('Mollie'); $this->description = $this->l('Mollie Payments'); @@ -191,6 +197,7 @@ public function getContext() { return $this->context; } + public function getIdentifier() { return $this->identifier; @@ -311,6 +318,7 @@ public function getContent() 'profile_id_message' => $this->l('Wrong profile ID'), 'profile_id_message_empty' => $this->l('Profile ID cannot be empty'), 'payment_api' => Mollie\Config\Config::MOLLIE_PAYMENTS_API, + 'ajaxUrl' => $this->context->link->getAdminLink('AdminMollieAjax'), ]); $this->context->controller->addJS($this->getPathUri() . 'views/js/method_countries.js'); $this->context->controller->addJS($this->getPathUri() . 'views/js/validation.js'); @@ -334,8 +342,9 @@ public function getContent() /** * @param string $str - * * @return string + * @deprecated + * */ public function lang($str) { @@ -375,7 +384,7 @@ protected function getUpdateMessage($url) 'this_version' => $this->version, 'release_version' => $latestVersion, ]); - $updateMessage = $this->smarty->fetch(_PS_MODULE_DIR_.'mollie/views/templates/admin/new_release.tpl'); + $updateMessage = $this->smarty->fetch(_PS_MODULE_DIR_ . 'mollie/views/templates/admin/new_release.tpl'); } } else { $updateMessage = $this->l('Warning: Update xml file from github follows an unexpected format.'); @@ -455,6 +464,29 @@ public function hookActionFrontControllerSetMedia() } } + /** + * Add custom JS && CSS to admin controllers + */ + public function hookActionAdminControllerSetMedia() + { + $currentController = Tools::getValue('controller'); + + if ('AdminOrders' === $currentController) { + Media::addJsDef([ + 'mollieHookAjaxUrl' => $this->context->link->getAdminLink('AdminMollieAjax'), + ]); + $this->context->controller->addCSS($this->getPathUri() . 'views/css/admin/order-list.css'); + $this->context->controller->addJS($this->getPathUri() . 'views/js/admin/order_list.js'); + + if (Tools::isSubmit('addorder')) { + Media::addJsDef([ + 'molliePendingStatus' => Configuration::get(\Mollie\Config\Config::STATUS_MOLLIE_AWAITING), + ]); + $this->context->controller->addJS($this->getPathUri() . 'views/js/admin/order_add.js'); + } + } + } + /** * @throws PrestaShopException * @throws SmartyException @@ -469,6 +501,9 @@ public function hookDisplayBackOfficeHeader() 'mollieCheckMethods' => time() > ((int)Configuration::get(Mollie\Config\Config::MOLLIE_METHODS_LAST_CHECK) + Mollie\Config\Config::MOLLIE_METHODS_CHECK_INTERVAL), ]); $html .= $this->display(__FILE__, 'views/templates/admin/ordergrid.tpl'); + if (Tools::isSubmit('addorder')) { + $html .= $this->display($this->getPathUri(), 'views/templates/admin/email_checkbox.tpl'); + } } return $html; @@ -626,7 +661,7 @@ public function hookDisplayPaymentEU() if (!in_array($iso, Mollie\Config\Config::$methodCurrencies[$method['id_method']])) { continue; } - $images = json_decode($method['images_json'],true); + $images = json_decode($method['images_json'], true); $paymentOptions[] = [ 'cta_text' => $this->lang($method['method_name']), 'logo' => Configuration::get(Mollie\Config\Config::MOLLIE_IMAGES) === Mollie\Config\Config::LOGOS_NORMAL @@ -996,7 +1031,7 @@ public function displayAjaxMollieOrderInfo() $input = @json_decode(Tools::file_get_contents('php://input'), true); $adminOrdersController = new AdminOrdersController(); - return $orderInfoService->displayMollieOrderInfo($input, $adminOrdersController->id); + return $orderInfoService->displayMollieOrderInfo($input, $adminOrdersController->id); } /** @@ -1100,15 +1135,18 @@ public function hookActionEmailSendBefore($params) } $cart = new Cart($params['cart']->id); - if (Order::getByCartId($cart->id)->module !== $this->name) { + $orderId = Order::getOrderByCartId($cart->id); + $order = new Order($orderId); + if ($order === null || $order->module !== $this->name) { return true; } if ($params['template'] === 'order_conf') { - if (Configuration::get(\Mollie\Config\Config::MOLLIE_SEND_ORDER_CONFIRMATION)) { - return true; - } - return false; + /** @var \Mollie\Validator\OrderConfMailValidator $orderConfValidator */ + $orderConfValidator = $this->getContainer(\Mollie\Validator\OrderConfMailValidator::class); + $sendOrderConfStatus = Configuration::get(\Mollie\Config\Config::MOLLIE_SEND_ORDER_CONFIRMATION); + + return $orderConfValidator->validateOrderConfMailSend($sendOrderConfStatus, $order->current_state); } if ($params['template'] === 'order_conf' || @@ -1129,7 +1167,8 @@ public function hookActionEmailSendBefore($params) $params['template'] === 'outofstock' || $params['template'] === 'bankwire' || $params['template'] === 'refund') { - $order = Order::getByCartId($cart->id); + $orderId = Order::getOrderByCartId($cart->id); + $order = new Order($orderId); if (!$order) { return true; } @@ -1174,11 +1213,130 @@ public function hookDisplayPDFInvoice($params) } + /** + * @return array + */ + public function getTabs() + { + return [ + [ + 'name' => $this->name, + 'class_name' => self::ADMIN_MOLLIE_CONTROLLER, + 'ParentClassName' => 'AdminParentShipping', + 'parent' => 'AdminParentShipping' + ], + [ + 'name' => $this->l('AJAX', __CLASS__), + 'class_name' => self::ADMIN_MOLLIE_AJAX_CONTROLLER, + 'ParentClassName' => self::ADMIN_MOLLIE_CONTROLLER, + 'parent' => self::ADMIN_MOLLIE_CONTROLLER, + 'module_tab' => true, + 'visible' => false, + ], + ]; + } + + public function hookActionAdminOrdersListingFieldsModifier($params) + { + if (isset($params['select'])) { + $params['select'] .= ' ,mol.`transaction_id`'; + } + if (isset($params['join'])) { + $params['join'] .= ' LEFT JOIN `' . _DB_PREFIX_ . 'mollie_payments` mol ON mol.`order_reference` = a.`reference`'; + } + $params['fields']['order_id'] = [ + 'title' => $this->l('Resend payment link'), + 'align' => 'text-center', + 'class' => 'fixed-width-xs', + 'orderby' => false, + 'search' => false, + 'remove_onclick' => true, + 'callback_object' => 'mollie', + 'callback' => 'resendOrderPaymentLink' + ]; + } + + public function hookActionValidateOrder($params) + { + if ($this->context->controller instanceof AdminOrdersControllerCore && + $params["order"]->module === $this->name + ) { + $cartId = $params["cart"]->id; + $totalPaid = strval($params["order"]->total_paid); + $currency = $params["currency"]->iso_code; + $customerKey = $params["customer"]->secure_key; + $orderReference = $params["order"]->reference; + $orderPayment = $params["order"]->payment; + $orderId = $params["order"]->id; + + /** @var \Mollie\Service\PaymentMethodService $paymentMethodService */ + $paymentMethodService = $this->getContainer(\Mollie\Service\PaymentMethodService::class); + $paymentMethodObj = new MolPaymentMethod(); + $paymentData = $paymentMethodService->getPaymentData( + $totalPaid, + $currency, + '', + null, + $cartId, + $customerKey, + $paymentMethodObj, + false, + $orderReference + ); + + $newPayment = $this->api->payments->create($paymentData); + + /** @var \Mollie\Repository\PaymentMethodRepository $paymentMethodRepository */ + $paymentMethodRepository = $this->getContainer(\Mollie\Repository\PaymentMethodRepository::class); + $paymentMethodRepository->addOpenStatusPayment( + $cartId, + $orderPayment, + $newPayment->id, + $orderId, + $orderReference + ); + + $sendMolliePaymentMail = Tools::getValue('mollie-email-send'); + if ($sendMolliePaymentMail === 'on') { + /** @var \Mollie\Service\MolliePaymentMailService $molliePaymentMailService */ + $molliePaymentMailService = $this->getContainer(\Mollie\Service\MolliePaymentMailService::class); + $molliePaymentMailService->sendSecondChanceMail($orderId); + } + } + } + + /** + * @param $idOrder + * @return string + * @throws Exception + */ + public static function resendOrderPaymentLink($orderId) + { + $module = Module::getInstanceByName('mollie'); + /** @var \Mollie\Repository\PaymentMethodRepository $molliePaymentRepo */ + $molliePaymentRepo = $module->getContainer(\Mollie\Repository\PaymentMethodRepository::class); + $molPayment = $molliePaymentRepo->getPaymentBy('order_id', $orderId); + if (\Mollie\Utility\MollieStatusUtility::isPaymentFinished($molPayment['bank_status'])) { + return false; + } + + $mollie = Module::getInstanceByName('mollie'); + + /** @var \Mollie\Presenter\OrderListActionBuilder $orderListActionBuilder */ + $orderListActionBuilder = $mollie->getContainer(\Mollie\Presenter\OrderListActionBuilder::class); + + return $orderListActionBuilder->buildOrderPaymentResendButton($mollie->smarty, $orderId); + } + private function setApiKey() { + if ($this->api) { + return; + } /** @var \Mollie\Service\ApiService $apiService */ $apiService = $this->getContainer(\Mollie\Service\ApiService::class); try { + $this->api = $apiService->setApiKey(Configuration::get(Mollie\Config\Config::MOLLIE_API_KEY), $this->version); } catch (_PhpScoper5eddef0da618a\Mollie\Api\Exceptions\IncompatiblePlatform $e) { PrestaShopLogger::addLog(__METHOD__ . ' - System incompatible: ' . $e->getMessage(), Mollie\Config\Config::CRASH); diff --git a/sql/install.php b/sql/install.php index a9630a73b..677ffdcde 100644 --- a/sql/install.php +++ b/sql/install.php @@ -82,6 +82,21 @@ `custom_url` VARCHAR(255) ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;'; +$sql[] = 'CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.'mol_excluded_country` ( + `id_mol_country` INT(64) NOT NULL PRIMARY KEY AUTO_INCREMENT, + `id_method` VARCHAR(64), + `id_country` INT(64), + `all_countries` tinyint + ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;'; + +$sql[] = ' + CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.'mol_pending_order_cart` ( + `id_mol_pending_order_cart` INT(64) NOT NULL PRIMARY KEY AUTO_INCREMENT, + `order_id` INT(64) NOT NULL, + `cart_id` INT(64) NOT NULL + ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8; +'; + foreach ($sql as $query) { if (Db::getInstance()->execute($query) == false) { return false; diff --git a/sql/uninstall.php b/sql/uninstall.php index b1aaa151e..b936c1dfb 100644 --- a/sql/uninstall.php +++ b/sql/uninstall.php @@ -38,6 +38,8 @@ $sql[] = 'DROP TABLE IF EXISTS `'._DB_PREFIX_.'mol_payment_method_issuer`;'; $sql[] = 'DROP TABLE IF EXISTS `'._DB_PREFIX_.'mol_order_fee`;'; $sql[] = 'DROP TABLE IF EXISTS `'._DB_PREFIX_.'mol_carrier_information`;'; +$sql[] = 'DROP TABLE IF EXISTS `'._DB_PREFIX_.'mol_pending_order_cart`;'; +$sql[] = 'DROP TABLE IF EXISTS `'._DB_PREFIX_.'mol_excluded_country`;'; foreach ($sql as $query) { if (Db::getInstance()->execute($query) == false) { diff --git a/src/Builder/FormBuilder.php b/src/Builder/FormBuilder.php index 6a5e15a26..04d23133a 100644 --- a/src/Builder/FormBuilder.php +++ b/src/Builder/FormBuilder.php @@ -35,6 +35,7 @@ NameSpace Mollie\Builder; +use _PhpScoper5eddef0da618a\Mollie\Api\Types\OrderStatus; use _PhpScoper5eddef0da618a\Mollie\Api\Types\PaymentMethod; use _PhpScoper5eddef0da618a\Mollie\Api\Types\PaymentStatus; use _PhpScoper5eddef0da618a\Mollie\Api\Types\RefundStatus; @@ -57,6 +58,8 @@ class FormBuilder { + const FILE_NAME = 'FormBuilder'; + /** * @var Mollie */ @@ -163,10 +166,10 @@ protected function getAccountSettingsSection($isApiKeyProvided) $input = [ [ 'type' => 'text', - 'label' => $this->module->l('API Key'), + 'label' => $this->module->l('API Key', self::FILE_NAME), 'tab' => $generalSettings, 'desc' => TagsUtility::ppTags( - $this->module->l('You can find your API key in your [1]Mollie Profile[/1]; it starts with test or live.'), + $this->module->l('You can find your API key in your [1]Mollie Profile[/1]; it starts with test or live.', self::FILE_NAME), [$this->module->display($this->module->getPathUri(), 'views/templates/admin/profile.tpl')] ), 'name' => Config::MOLLIE_API_KEY, @@ -178,7 +181,7 @@ protected function getAccountSettingsSection($isApiKeyProvided) $input = [ [ 'type' => 'mollie-switch', - 'label' => $this->module->l('Do you already have a Mollie account?'), + 'label' => $this->module->l('Do you already have a Mollie account?', self::FILE_NAME), 'name' => Config::MOLLIE_ACCOUNT_SWITCH, 'tab' => $generalSettings, 'is_bool' => true, @@ -186,24 +189,24 @@ protected function getAccountSettingsSection($isApiKeyProvided) [ 'id' => 'active_on', 'value' => true, - 'label' => Translate::getAdminTranslation('Enabled', 'AdminCarriers'), + 'label' => $this->module->l('Enabled', self::FILE_NAME), ], [ 'id' => 'active_off', 'value' => false, - 'label' => Translate::getAdminTranslation('Disabled', 'AdminCarriers'), + 'label' => $this->module->l('Disabled', self::FILE_NAME), ], ], 'desc' => $this->module->display( - $this->module->getPathUri() , 'views/templates/admin/create_new_account_link.tpl' + $this->module->getPathUri(), 'views/templates/admin/create_new_account_link.tpl' ), ], [ 'type' => 'text', - 'label' => $this->module->l('API Key'), + 'label' => $this->module->l('API Key', self::FILE_NAME), 'tab' => $generalSettings, 'desc' => TagsUtility::ppTags( - $this->module->l('You can find your API key in your [1]Mollie Profile[/1]; it starts with test or live.'), + $this->module->l('You can find your API key in your [1]Mollie Profile[/1]; it starts with test or live.', self::FILE_NAME), [$this->module->display($this->module->getPathUri(), 'views/templates/admin/profile.tpl')] ), 'name' => Config::MOLLIE_API_KEY, @@ -215,7 +218,7 @@ protected function getAccountSettingsSection($isApiKeyProvided) if ($isApiKeyProvided) { $input[] = [ 'type' => 'switch', - 'label' => $this->module->l('Use IFrame for credit card'), + 'label' => $this->module->l('Use IFrame for credit card', self::FILE_NAME), 'tab' => $generalSettings, 'name' => Config::MOLLIE_IFRAME, 'is_bool' => true, @@ -223,22 +226,22 @@ protected function getAccountSettingsSection($isApiKeyProvided) [ 'id' => 'active_on', 'value' => true, - 'label' => Translate::getAdminTranslation('Enabled', 'AdminCarriers'), + 'label' => $this->module->l('Enabled', self::FILE_NAME), ], [ 'id' => 'active_off', 'value' => false, - 'label' => Translate::getAdminTranslation('Disabled', 'AdminCarriers'), + 'label' => $this->module->l('Disabled', self::FILE_NAME), ], ], ]; $input[] = [ 'type' => 'text', - 'label' => $this->module->l('Profile ID'), + 'label' => $this->module->l('Profile ID', self::FILE_NAME), 'tab' => $generalSettings, 'desc' => TagsUtility::ppTags( - $this->module->l('You can find your API key in your [1]Mollie Profile[/1];'), + $this->module->l('You can find your API key in your [1]Mollie Profile[/1];', self::FILE_NAME), [$this->module->display($this->module->getPathUri(), 'views/templates/admin/profile.tpl')] ), 'name' => Config::MOLLIE_PROFILE_ID, @@ -251,23 +254,23 @@ protected function getAccountSettingsSection($isApiKeyProvided) 'type' => 'mollie-h3', 'tab' => $generalSettings, 'name' => '', - 'title' => $this->module->l('Orders API'), + 'title' => '', ], [ 'type' => 'select', - 'label' => $this->module->l('Issuer list'), + 'label' => $this->module->l('Issuer list', self::FILE_NAME), 'tab' => $generalSettings, - 'desc' => $this->module->l('Some payment methods (eg. iDEAL) have an issuer list. This setting specifies where it is shown.'), + 'desc' => $this->module->l('Some payment methods (eg. iDEAL) have an issuer list. This setting specifies where it is shown.', self::FILE_NAME), 'name' => Config::MOLLIE_ISSUERS, 'options' => [ 'query' => [ [ 'id' => Config::ISSUERS_ON_CLICK, - 'name' => $this->module->l('On click'), + 'name' => $this->module->l('On click', self::FILE_NAME), ], [ 'id' => Config::ISSUERS_PAYMENT_PAGE, - 'name' => $this->module->l('Payment page'), + 'name' => $this->module->l('Payment page', self::FILE_NAME), ], ], 'id' => 'id', @@ -280,7 +283,7 @@ protected function getAccountSettingsSection($isApiKeyProvided) 'type' => 'mollie-h2', 'tab' => $generalSettings, 'name' => '', - 'title' => $this->module->l('Payment methods'), + 'title' => $this->module->l('Payment methods', self::FILE_NAME), ]; $input[] = [ @@ -292,7 +295,7 @@ protected function getAccountSettingsSection($isApiKeyProvided) 'klarnaPayments' => [ PaymentMethod::KLARNA_PAY_LATER, PaymentMethod::KLARNA_SLICE_IT, - ], + ], 'displayErrors' => Configuration::get(Config::MOLLIE_DISPLAY_ERRORS), ]; } @@ -306,59 +309,64 @@ protected function getAdvancedSettingsSection() $input = []; $orderStatuses = []; $orderStatuses = array_merge($orderStatuses, OrderState::getOrderStates($this->lang->id)); + $input[] = [ + 'type' => 'select', + 'label' => $this->module->l('Push Locale to Payment Screen', self::FILE_NAME), + 'tab' => $advancedSettings, + 'desc' => TagsUtility::ppTags( + $this->module->l('When activated, Mollie will use your webshop\'s [1]Locale[/1]. If not, the browser\'s locale will be used.',self::FILE_NAME), + [$this->module->display($this->module->getPathUri(), 'views/templates/admin/locale_wiki.tpl')] + ), + 'name' => Config::MOLLIE_PAYMENTSCREEN_LOCALE, + 'options' => [ + 'query' => [ + [ + 'id' => Config::PAYMENTSCREEN_LOCALE_SEND_WEBSITE_LOCALE, + 'name' => $this->module->l('Yes, push webshop Locale', self::FILE_NAME), + ], + [ + 'id' => Config::PAYMENTSCREEN_LOCALE_BROWSER_LOCALE, + 'name' => $this->module->l('No, use browser\'s Locale', self::FILE_NAME), + ], + + ], + 'id' => 'id', + 'name' => 'name', + ], + ]; + if (Config::isVersion17()) { $input[] = [ 'type' => 'select', - 'label' => $this->module->l('Send locale for payment screen'), + 'label' => $this->module->l('Send order confirmation email', self::FILE_NAME), 'tab' => $advancedSettings, - 'desc' => TagsUtility::ppTags( - $this->module->l('Should the plugin send the current webshop [1]locale[/1] to Mollie. Mollie payment screens will be in the same language as your webshop. Mollie can also detect the language based on the user\'s browser language.'), - [$this->module->display($this->module->getPathUri(), 'views/templates/admin/locale_wiki.tpl')] - ), 'name' => Config::MOLLIE_SEND_ORDER_CONFIRMATION, 'options' => [ 'query' => [ [ - 'id' => Config::PAYMENTSCREEN_LOCALE_BROWSER_LOCALE, - 'name' => $this->module->l('Do not send locale using browser language'), + 'id' => Config::ORDER_CONF_MAIL_SEND_ON_CREATION, + 'name' => $this->module->l('When Order is created', self::FILE_NAME), ], [ - 'id' => Config::PAYMENTSCREEN_LOCALE_SEND_WEBSITE_LOCALE, - 'name' => $this->module->l('Send locale for payment screen'), + 'id' => Config::ORDER_CONF_MAIL_SEND_ON_PAID, + 'name' => $this->module->l('When Order is Paid', self::FILE_NAME), + ], + [ + 'id' => Config::ORDER_CONF_MAIL_SEND_ON_NEVER, + 'name' => $this->module->l('Never', self::FILE_NAME), ], ], 'id' => 'id', 'name' => 'name', ], - ]; + ];; } - $input[] = [ - 'type' => 'switch', - 'label' => $this->module->l('Send order confirmation email'), - 'tab' => $advancedSettings, - 'name' => Config::MOLLIE_SEND_ORDER_CONFIRMATION, - 'is_bool' => true, - 'desc' => $this->module->l('Send order confirmation email before payment is executed'), - 'values' => [ - [ - 'id' => 'active_on', - 'value' => true, - 'label' => Translate::getAdminTranslation('Enabled', 'AdminCarriers'), - ], - [ - 'id' => 'active_off', - 'value' => false, - 'label' => Translate::getAdminTranslation('Disabled', 'AdminCarriers'), - ], - ], - ]; - - $messageStatus = $this->module->l('Status for %s payments'); - $descriptionStatus = $this->module->l('`%s` payments get status `%s`'); - $messageMail = $this->module->l('Send mails when %s'); - $descriptionMail = $this->module->l('Send mails when transaction status becomes %s?'); - $allStatuses = array_merge([['id_order_state' => 0, 'name' => $this->module->l('Skip this status'), 'color' => '#565656']], OrderState::getOrderStates($this->lang->id)); + $messageStatus = $this->module->l('Status for %s payments', self::FILE_NAME); + $descriptionStatus = $this->module->l('`%s` payments get status `%s`', self::FILE_NAME); + $messageMail = $this->module->l('Send mails when %s', self::FILE_NAME); + $descriptionMail = $this->module->l('Send mails when transaction status becomes %s?, self::FILE_NAME', self::FILE_NAME); + $allStatuses = array_merge([['id_order_state' => 0, 'name' => $this->module->l('Skip this status', self::FILE_NAME), 'color' => '#565656']], OrderState::getOrderStates($this->lang->id)); $statuses = []; foreach (Config::getStatuses() as $name => $val) { if ($name === PaymentStatus::STATUS_AUTHORIZED) { @@ -377,7 +385,7 @@ protected function getAdvancedSettingsSection() ) ); } else { - $desc = sprintf($this->module->l('`%s` payments do not get a status'), $this->module->lang($name)); + $desc = sprintf($this->module->l('`%s` payments do not get a status', self::FILE_NAME), $this->module->lang($name)); } $statuses[] = [ 'name' => $name, @@ -395,18 +403,20 @@ protected function getAdvancedSettingsSection() 'type' => 'mollie-h2', 'name' => '', 'tab' => $advancedSettings, - 'title' => $this->module->l('Order statuses'), + 'title' => $this->module->l('Order statuses', self::FILE_NAME), ]; foreach (array_filter($statuses, function ($status) { return in_array($status['name'], [ PaymentStatus::STATUS_PAID, + OrderStatus::STATUS_COMPLETED, PaymentStatus::STATUS_AUTHORIZED, PaymentStatus::STATUS_CANCELED, PaymentStatus::STATUS_EXPIRED, RefundStatus::STATUS_REFUNDED, PaymentStatus::STATUS_OPEN, Config::PARTIAL_REFUND_CODE, + OrderStatus::STATUS_SHIPPING, ]); }) as $status) { if (!in_array($status['name'], [Config::PARTIAL_REFUND_CODE])) { @@ -420,12 +430,12 @@ protected function getAdvancedSettingsSection() [ 'id' => 'active_on', 'value' => true, - 'label' => Translate::getAdminTranslation('Enabled', 'AdminCarriers'), + 'label' => $this->module->l('Enabled', self::FILE_NAME), ], [ 'id' => 'active_off', 'value' => false, - 'label' => Translate::getAdminTranslation('Disabled', 'AdminCarriers'), + 'label' => $this->module->l('Disabled', self::FILE_NAME), ], ], ]; @@ -448,27 +458,27 @@ protected function getAdvancedSettingsSection() 'type' => 'mollie-h2', 'name' => '', 'tab' => $advancedSettings, - 'title' => $this->module->l('Visual Settings'), + 'title' => $this->module->l('Visual Settings', self::FILE_NAME), ], [ 'type' => 'select', - 'label' => $this->module->l('Images'), + 'label' => $this->module->l('Images', self::FILE_NAME), 'tab' => $advancedSettings, - 'desc' => $this->module->l('Show big, normal or no payment method logos on checkout.'), + 'desc' => $this->module->l('Show big, normal or no payment method logos on checkout.', self::FILE_NAME), 'name' => Config::MOLLIE_IMAGES, 'options' => [ 'query' => [ [ 'id' => Config::LOGOS_HIDE, - 'name' => $this->module->l('hide'), + 'name' => $this->module->l('hide', self::FILE_NAME), ], [ 'id' => Config::LOGOS_NORMAL, - 'name' => $this->module->l('normal'), + 'name' => $this->module->l('normal', self::FILE_NAME), ], [ 'id' => Config::LOGOS_BIG, - 'name' => $this->module->l('big'), + 'name' => $this->module->l('big', self::FILE_NAME), ], ], 'id' => 'id', @@ -478,10 +488,10 @@ protected function getAdvancedSettingsSection() [ 'type' => 'text', - 'label' => $this->module->l('CSS file'), + 'label' => $this->module->l('CSS file', self::FILE_NAME), 'tab' => $advancedSettings, 'desc' => TagsUtility::ppTags( - $this->module->l('Leave empty for default stylesheet. Should include file path when set. Hint: You can use [1]{BASE}[/1], [1]{THEME}[/1], [1]{CSS}[/1], [1]{MOBILE}[/1], [1]{MOBILE_CSS}[/1] and [1]{OVERRIDE}[/1] for easy folder mapping.'), + $this->module->l('Leave empty for default stylesheet. Should include file path when set. Hint: You can use [1]{BASE}[/1], [1]{THEME}[/1], [1]{CSS}[/1], [1]{MOBILE}[/1], [1]{MOBILE_CSS}[/1] and [1]{OVERRIDE}[/1] for easy folder mapping.', self::FILE_NAME), [$this->module->display($this->module->getPathUri(), 'views/templates/front/kbd.tpl')] ), 'name' => Config::MOLLIE_CSS, @@ -490,7 +500,7 @@ protected function getAdvancedSettingsSection() ]); $input[] = [ 'type' => 'mollie-carriers', - 'label' => $this->module->l('Shipment information'), + 'label' => $this->module->l('Shipment information', self::FILE_NAME), 'tab' => $advancedSettings, 'name' => Config::MOLLIE_TRACKING_URLS, 'depends' => Config::MOLLIE_API, @@ -499,21 +509,21 @@ protected function getAdvancedSettingsSection() ]; $input[] = [ 'type' => 'mollie-carrier-switch', - 'label' => $this->module->l('Automatically ship on marked statuses'), + 'label' => $this->module->l('Automatically ship on marked statuses', self::FILE_NAME), 'tab' => $advancedSettings, 'name' => Config::MOLLIE_AUTO_SHIP_MAIN, - 'desc' => $this->module->l('Enabling this feature will automatically send shipment information when an order gets marked status'), + 'desc' => $this->module->l('Enabling this feature will automatically send shipment information when an order gets marked status', self::FILE_NAME), 'is_bool' => true, 'values' => [ [ 'id' => 'active_on', 'value' => true, - 'label' => Translate::getAdminTranslation('Enabled', 'AdminCarriers'), + 'label' => $this->module->l('Enabled', self::FILE_NAME), ], [ 'id' => 'active_off', 'value' => false, - 'label' => Translate::getAdminTranslation('Disabled', 'AdminCarriers'), + 'label' => $this->module->l('Disabled', self::FILE_NAME), ], ], 'depends' => Config::MOLLIE_API, @@ -521,10 +531,10 @@ protected function getAdvancedSettingsSection() ]; $input[] = [ 'type' => 'checkbox', - 'label' => $this->module->l('Automatically ship when one of these statuses is reached'), + 'label' => $this->module->l('Automatically ship when one of these statuses is reached', self::FILE_NAME), 'tab' => $advancedSettings, 'desc' => - $this->module->l('If an order reaches one of these statuses the module will automatically send shipment information'), + $this->module->l('If an order reaches one of these statuses the module will automatically send shipment information', self::FILE_NAME), 'name' => Config::MOLLIE_AUTO_SHIP_STATUSES, 'multiple' => true, 'values' => [ @@ -535,15 +545,15 @@ protected function getAdvancedSettingsSection() 'expand' => (count($orderStatuses) > 10) ? [ 'print_total' => count($orderStatuses), 'default' => 'show', - 'show' => ['text' => $this->module->l('Show'), 'icon' => 'plus-sign-alt'], - 'hide' => ['text' => $this->module->l('Hide'), 'icon' => 'minus-sign-alt'], + 'show' => ['text' => $this->module->l('Show', self::FILE_NAME), 'icon' => 'plus-sign-alt'], + 'hide' => ['text' => $this->module->l('Hide', self::FILE_NAME), 'icon' => 'minus-sign-alt'], ] : null, 'depends' => Config::MOLLIE_API, 'depends_value' => Config::MOLLIE_ORDERS_API, ]; $orderStatuses = [ [ - 'name' => $this->module->l('Disable this status'), + 'name' => $this->module->l('Disable this status', self::FILE_NAME), 'id_order_state' => '0', ], ]; @@ -564,35 +574,35 @@ protected function getAdvancedSettingsSection() [ 'type' => 'mollie-h2', 'name' => '', - 'title' => $this->module->l('Debug level'), + 'title' => $this->module->l('Debug level', self::FILE_NAME), 'tab' => $advancedSettings, ], [ 'type' => 'switch', - 'label' => $this->module->l('Display errors'), + 'label' => $this->module->l('Display errors', self::FILE_NAME), 'tab' => $advancedSettings, 'name' => Config::MOLLIE_DISPLAY_ERRORS, - 'desc' => $this->module->l('Enabling this feature will display error messages (if any) on the front page. Use for debug purposes only!'), + 'desc' => $this->module->l('Enabling this feature will display error messages (if any) on the front page. Use for debug purposes only!', self::FILE_NAME), 'is_bool' => true, 'values' => [ [ 'id' => 'active_on', 'value' => true, - 'label' => Translate::getAdminTranslation('Enabled', 'AdminCarriers'), + 'label' => $this->module->l('Enabled', self::FILE_NAME), ], [ 'id' => 'active_off', 'value' => false, - 'label' => Translate::getAdminTranslation('Disabled', 'AdminCarriers'), + 'label' => $this->module->l('Disabled', self::FILE_NAME), ], ], ], [ 'type' => 'select', - 'label' => $this->module->l('Log level'), + 'label' => $this->module->l('Log level', self::FILE_NAME), 'tab' => $advancedSettings, 'desc' => TagsUtility::ppTags( - $this->module->l('Recommended level: Errors. Set to Everything to monitor incoming webhook requests. [1]View logs.[/1]'), + $this->module->l('Recommended level: Errors. Set to Everything to monitor incoming webhook requests. [1]View logs.[/1]', self::FILE_NAME), [ $this->module->display($this->module->getPathUri(), 'views/templates/admin/view_logs.tpl') ] @@ -602,15 +612,15 @@ protected function getAdvancedSettingsSection() 'query' => [ [ 'id' => Config::DEBUG_LOG_NONE, - 'name' => $this->module->l('Nothing'), + 'name' => $this->module->l('Nothing', self::FILE_NAME), ], [ 'id' => Config::DEBUG_LOG_ERRORS, - 'name' => $this->module->l('Errors'), + 'name' => $this->module->l('Errors', self::FILE_NAME), ], [ 'id' => Config::DEBUG_LOG_ALL, - 'name' => $this->module->l('Everything'), + 'name' => $this->module->l('Everything', self::FILE_NAME), ], ], 'id' => 'id', @@ -625,11 +635,11 @@ protected function getAdvancedSettingsSection() private function getSettingTabs($isApiKeyProvided) { $tabs = [ - 'general_settings' => $this->module->l('General settings'), + 'general_settings' => $this->module->l('General settings', self::FILE_NAME), ]; if ($isApiKeyProvided) { - $tabs['advanced_settings'] = $this->module->l('Advanced settings'); + $tabs['advanced_settings'] = $this->module->l('Advanced settings', self::FILE_NAME); } return $tabs; diff --git a/src/Config/Config.php b/src/Config/Config.php index b72d457ef..d47132c95 100644 --- a/src/Config/Config.php +++ b/src/Config/Config.php @@ -117,6 +117,7 @@ class Config const PAYMENTSCREEN_LOCALE_BROWSER_LOCALE = 'browser_locale'; const PAYMENTSCREEN_LOCALE_SEND_WEBSITE_LOCALE = 'website_locale'; + const DEFAULT_EMAIL_LANGUAGE_ISO_CODE = 'en'; const LOGOS_BIG = 'big'; const LOGOS_NORMAL = 'normal'; @@ -149,17 +150,23 @@ class Config const MOLLIE_AUTO_SHIP_STATUSES = 'MOLLIE_AS_STATUSES'; const MOLLIE_STATUS_OPEN = 'MOLLIE_STATUS_OPEN'; const MOLLIE_STATUS_PAID = 'MOLLIE_STATUS_PAID'; + const MOLLIE_STATUS_COMPLETED = 'MOLLIE_STATUS_COMPLETED'; const MOLLIE_STATUS_CANCELED = 'MOLLIE_STATUS_CANCELED'; const MOLLIE_STATUS_EXPIRED = 'MOLLIE_STATUS_EXPIRED'; const MOLLIE_STATUS_PARTIAL_REFUND = 'MOLLIE_STATUS_PARTIAL_REFUND'; const MOLLIE_STATUS_REFUNDED = 'MOLLIE_STATUS_REFUNDED'; + const MOLLIE_STATUS_SHIPPING = 'MOLLIE_STATUS_SHIPPING'; + const MOLLIE_MAIL_WHEN_SHIPPING = 'MOLLIE_MAIL_WHEN_SHIPPING'; const MOLLIE_MAIL_WHEN_OPEN = 'MOLLIE_MAIL_WHEN_OPEN'; const MOLLIE_MAIL_WHEN_PAID = 'MOLLIE_MAIL_WHEN_PAID'; + const MOLLIE_MAIL_WHEN_COMPLETED = 'MOLLIE_MAIL_WHEN_COMPLETED'; const MOLLIE_MAIL_WHEN_CANCELED = 'MOLLIE_MAIL_WHEN_CANCELED'; const MOLLIE_MAIL_WHEN_EXPIRED = 'MOLLIE_MAIL_WHEN_EXPIRED'; const MOLLIE_MAIL_WHEN_REFUNDED = 'MOLLIE_MAIL_WHEN_REFUNDED'; const PARTIAL_REFUND_CODE = 'partial_refund'; const MOLLIE_STATUS_INITIATED = 'MOLLIE_STATUS_INITIATED'; + const MOLLIE_STATUS_PARTIALLY_SHIPPED = 'MOLLIE_PARTIALLY_SHIPPED'; + const MOLLIE_STATUS_ORDER_COMPLETED = 'MOLLIE_STATUS_ORDER_COMPLETED'; const MOLLIE_CARRIER_URL_SOURCE = 'MOLLIE_CARRIER_URL_SOURCE_'; const MOLLIE_CARRIER_CUSTOM_URL = 'MOLLIE_CARRIER_CUSTOM_URL_'; @@ -170,6 +177,7 @@ class Config const MOLLIE_METHOD_DESCRIPTION = 'MOLLIE_METHOD_DESCRIPTION_'; const MOLLIE_METHOD_APPLICABLE_COUNTRIES = 'MOLLIE_METHOD_APPLICABLE_COUNTRIES_'; const MOLLIE_METHOD_CERTAIN_COUNTRIES = 'MOLLIE_METHOD_CERTAIN_COUNTRIES_'; + const MOLLIE_METHOD_EXCLUDE_CERTAIN_COUNTRIES = 'MOLLIE_METHOD_EXCLUDE_CERTAIN_COUNTRIES_'; const MOLLIE_METHOD_MINIMUM_ORDER_VALUE = 'MOLLIE_METHOD_MINIMUM_ORDER_VALUE_'; const MOLLIE_METHOD_MAX_ORDER_VALUE = 'MOLLIE_METHOD_MAX_ORDER_VALUE_'; const MOLLIE_METHOD_SURCHARGE_TYPE = 'MOLLIE_METHOD_SURCHARGE_TYPE_'; @@ -177,10 +185,6 @@ class Config const MOLLIE_METHOD_SURCHARGE_PERCENTAGE = 'MOLLIE_METHOD_SURCHARGE_PERCENTAGE_'; const MOLLIE_METHOD_SURCHARGE_LIMIT = 'MOLLIE_METHOD_SURCHARGE_LIMIT_'; - const MOLLIE_RESELLER_PARTNER_ID = 4602094; - const MOLLIE_RESELLER_PROFILE_KEY = 'B69C2D66'; - const MOLLIE_RESELLER_APP_SECRET = '49726EB7650EC592F732E7B82A4C1EFD6EE8A10F'; - const MOLLIE_CARRIER_NO_TRACKING_INFO = 'no_tracking_info'; const MOLLIE_CARRIER_MODULE = 'module'; const MOLLIE_CARRIER_CARRIER = 'carrier_url'; @@ -209,6 +213,13 @@ class Config const FEE_PERCENTAGE= 2; const FEE_FIXED_FEE_AND_PERCENTAGE = 3; + const MOLLIE_API_STATUS_PAYMENT = "payment"; + const MOLLIE_API_STATUS_ORDER = "order"; + + const ORDER_CONF_MAIL_SEND_ON_CREATION = 0; + const ORDER_CONF_MAIL_SEND_ON_PAID = 1; + const ORDER_CONF_MAIL_SEND_ON_NEVER = 2; + const CARTES_BANCAIRES = 'cartesbancaires'; /** @var array $methods */ @@ -223,14 +234,14 @@ class Config 'giftcard' => 'Giftcard', 'giropay' => 'Giropay', 'ideal' => 'iDEAL', - 'inghomepay ' => 'ING Homepay', + 'inghomepay' => 'ING Homepay', 'kbc' => 'KBC', 'bancontact' => 'Bancontact', 'paypal' => 'PayPal', 'paysafecard' => 'Paysafecard', 'sofort' => 'Sofort Banking', 'klarnapaylater' => 'Pay later.', - 'klarnaspliceit' => 'Slice it.', + 'klarnasliceit' => 'Slice it.', 'applepay' => 'Apple Pay', 'mybank' => 'MyBank', ]; @@ -239,13 +250,15 @@ public static function getStatuses() { return [ PaymentStatus::STATUS_PAID => Configuration::get(self::MOLLIE_STATUS_PAID), + OrderStatus::STATUS_COMPLETED => Configuration::get(self::MOLLIE_STATUS_COMPLETED), PaymentStatus::STATUS_AUTHORIZED => Configuration::get(self::MOLLIE_STATUS_PAID), PaymentStatus::STATUS_CANCELED => Configuration::get(self::MOLLIE_STATUS_CANCELED), PaymentStatus::STATUS_EXPIRED => Configuration::get(self::MOLLIE_STATUS_EXPIRED), RefundStatus::STATUS_REFUNDED => Configuration::get(self::MOLLIE_STATUS_REFUNDED), PaymentStatus::STATUS_OPEN => Configuration::get(self::MOLLIE_STATUS_OPEN), PaymentStatus::STATUS_FAILED => Configuration::get(self::MOLLIE_STATUS_CANCELED), - OrderStatus::STATUS_COMPLETED => Configuration::get(self::MOLLIE_STATUS_PAID), + PaymentStatus::STATUS_PENDING => Configuration::get(self::STATUS_MOLLIE_AWAITING), + OrderStatus::STATUS_SHIPPING => Configuration::get(self::MOLLIE_STATUS_SHIPPING), self::MOLLIE_AWAITING_PAYMENT => Configuration::get(self::STATUS_MOLLIE_AWAITING), self::PARTIAL_REFUND_CODE => Configuration::get(self::MOLLIE_STATUS_PARTIAL_REFUND), 'created' => Configuration::get(self::MOLLIE_STATUS_OPEN), @@ -269,4 +282,14 @@ public static function isTestMode() return false; } -} \ No newline at end of file + + public static function getMollieOrderStatuses() + { + return [ + self::MOLLIE_STATUS_PARTIALLY_SHIPPED, + self::MOLLIE_STATUS_PARTIAL_REFUND, + self::STATUS_MOLLIE_AWAITING, + self::MOLLIE_STATUS_ORDER_COMPLETED, + ]; + } +} diff --git a/src/Controller/AbstractMollieController.php b/src/Controller/AbstractMollieController.php index f23462ca4..e4f76f5e2 100644 --- a/src/Controller/AbstractMollieController.php +++ b/src/Controller/AbstractMollieController.php @@ -1,4 +1,36 @@ + * @copyright Mollie B.V. + * @license Berkeley Software Distribution License (BSD-License 2) http://www.opensource.org/licenses/bsd-license.php + * @category Mollie + * @package Mollie + * @link https://www.mollie.nl + */ namespace Mollie\Controller; @@ -30,5 +62,4 @@ public function redirectWithNotifications() } return call_user_func_array(['Tools', 'redirect'], func_get_args()); } - -} \ No newline at end of file +} diff --git a/src/Entity/MolPendingOrderCart.php b/src/Entity/MolPendingOrderCart.php new file mode 100644 index 000000000..19e298e03 --- /dev/null +++ b/src/Entity/MolPendingOrderCart.php @@ -0,0 +1,29 @@ + order id from which cart was duplicated. + */ +class MolPendingOrderCart extends ObjectModel +{ + /** + * @var int + */ + public $order_id; + + /** + * @var int + */ + public $cart_id; + + /** + * @var array + */ + public static $definition = [ + 'table' => 'mol_pending_order_cart', + 'primary' => 'id_mol_pending_order_cart', + 'fields' => [ + 'order_id' => ['type' => self::TYPE_INT, 'validate' => 'isInt'], + 'cart_id' => ['type' => self::TYPE_INT, 'validate' => 'isInt'], + ], + ]; +} \ No newline at end of file diff --git a/src/Exception/CancelPendingOrderException.php b/src/Exception/CancelPendingOrderException.php new file mode 100644 index 000000000..749a68b3c --- /dev/null +++ b/src/Exception/CancelPendingOrderException.php @@ -0,0 +1,7 @@ + + * @copyright Mollie B.V. + * @license Berkeley Software Distribution License (BSD-License 2) http://www.opensource.org/licenses/bsd-license.php + * @category Mollie + * @package Mollie + * @link https://www.mollie.nl + * @codingStandardsIgnoreStart + */ + +namespace Mollie\Factory; + +use Customer; +use Mollie\Utility\ContextUtility; + + +class CustomerFactory +{ + public function recreateFromRequest($customerId, $customerSecureKey, $context) + { + if ($customerId) { + $customer = new Customer($customerId); + if ($customer->secure_key === $customerSecureKey) { + return ContextUtility::setCustomerToContext($context, $customer); + } + } + + return $context; + } +} \ No newline at end of file diff --git a/src/Install/Installer.php b/src/Install/Installer.php index eae568ed3..0d2410fdd 100644 --- a/src/Install/Installer.php +++ b/src/Install/Installer.php @@ -42,12 +42,19 @@ use Exception; use Language; use Mollie; +use Mollie\Config\Config; +use Mollie\Service\imageService; +use Mollie\Utility\MultiLangUtility; use OrderState; use PrestaShopDatabaseException; use PrestaShopException; +use Tab; +use Tools; class Installer { + const FILE_NAME = 'Installer'; + /** * @var array */ @@ -57,10 +64,15 @@ class Installer * @var Mollie */ private $module; + /** + * @var imageService + */ + private $imageService; - public function __construct(Mollie $module) + public function __construct(Mollie $module, imageService $imageService) { $this->module = $module; + $this->imageService = $imageService; } public function install() @@ -69,28 +81,28 @@ public function install() $this->module->registerHook($hook); } - $context = Context::getContext(); - try { - $this->createMollieStatuses($context->language->id); + $this->createMollieStatuses(); } catch (Exception $e) { - $this->errors[] = $this->module->l('Unable to install Mollie statuses'); + $this->errors[] = $this->module->l('Unable to install Mollie statuses', self::FILE_NAME); return false; } try { $this->initConfig(); } catch (Exception $e) { - $this->errors[] = $this->module->l('Unable to install config'); + $this->errors[] = $this->module->l('Unable to install config', self::FILE_NAME); return false; } try { $this->setDefaultCarrierStatuses(); } catch (Exception $e) { - $this->errors[] = $this->module->l('Unable to install default carrier statuses'); + $this->errors[] = $this->module->l('Unable to install default carrier statuses', self::FILE_NAME); return false; } + $this->copyEmailTemplates(); + include(dirname(__FILE__) . '/../../sql/install.php'); return true; @@ -114,6 +126,9 @@ public static function getHooks() 'actionEmailSendBefore', 'actionOrderStatusUpdate', 'displayPDFInvoice', + 'actionAdminOrdersListingFieldsModifier', + 'actionAdminControllerSetMedia', + 'actionValidateOrder', ]; } @@ -127,20 +142,8 @@ public static function getHooks() * @since 2.0.0 * */ - private function partialRefundOrderState($languageId) + private function partialRefundOrderState() { - $stateExists = false; - $states = OrderState::getOrderStates((int)$languageId); - foreach ($states as $state) { - if ($this->module->lang('Mollie partially refunded') === $state['name']) { - Configuration::updateValue(Mollie\Config\Config::MOLLIE_STATUS_PARTIAL_REFUND, (int)$state[OrderState::$definition['primary']]); - $stateExists = true; - break; - } - } - if ($stateExists) { - return true; - } $orderState = new OrderState(); $orderState->send_email = false; $orderState->color = '#6F8C9F'; @@ -149,15 +152,9 @@ private function partialRefundOrderState($languageId) $orderState->logable = false; $orderState->invoice = false; $orderState->module_name = $this->module->name; - $orderState->name = []; - $languages = Language::getLanguages(false); - foreach ($languages as $language) { - $orderState->name[$language['id_lang']] = $this->module->lang('Mollie partially refunded'); - } + $orderState->name = MultiLangUtility::createMultiLangField('Mollie partially refunded'); if ($orderState->add()) { - $source = _PS_MODULE_DIR_ . 'mollie/views/img/logo_small.png'; - $destination = _PS_ROOT_DIR_ . '/img/os/' . (int)$orderState->id . '.gif'; - @copy($source, $destination); + $this->imageService->createOrderStateLogo($orderState->id); } Configuration::updateValue(Mollie\Config\Config::MOLLIE_STATUS_PARTIAL_REFUND, (int)$orderState->id); @@ -165,12 +162,44 @@ private function partialRefundOrderState($languageId) return true; } - public function createMollieStatuses($languageId) + /** + * @param $languageId + * @return bool + * @throws PrestaShopDatabaseException + * @throws PrestaShopException + */ + public function partialShippedOrderState() { - if (!$this->partialRefundOrderState($languageId)) { + $orderState = new OrderState(); + $orderState->send_email = false; + $orderState->color = '#8A2BE2'; + $orderState->hidden = false; + $orderState->delivery = false; + $orderState->logable = false; + $orderState->invoice = false; + $orderState->module_name = $this->module->name; + $orderState->name = MultiLangUtility::createMultiLangField('Partially shipped'); + + if ($orderState->add()) { + $this->imageService->createOrderStateLogo($orderState->id); + } + Configuration::updateValue(Mollie\Config\Config::MOLLIE_STATUS_PARTIALLY_SHIPPED, (int)$orderState->id); + + return true; + } + + public function createMollieStatuses() + { + if (!$this->partialRefundOrderState()) { + return false; + } + if (!$this->awaitingMollieOrderState()) { return false; } - if (!$this->awaitingMollieOrderState($languageId)) { + if(!$this->partialShippedOrderState()) { + return false; + } + if(!$this->orderCompletedOrderState()) { return false; } @@ -184,38 +213,48 @@ public function createMollieStatuses($languageId) * @throws PrestaShopDatabaseException * @throws PrestaShopException */ - private function awaitingMollieOrderState($languageId) + private function awaitingMollieOrderState() { - $stateExists = false; - $states = OrderState::getOrderStates((int)$languageId); - foreach ($states as $state) { - if ($this->module->lang('Awaiting Mollie payment') === $state['name']) { - Configuration::updateValue(Mollie\Config\Config::STATUS_MOLLIE_AWAITING, (int)$state[OrderState::$definition['primary']]); - $stateExists = true; - break; - } + $orderState = new OrderState(); + $orderState->send_email = false; + $orderState->color = '#4169E1'; + $orderState->hidden = false; + $orderState->delivery = false; + $orderState->logable = false; + $orderState->invoice = false; + $orderState->module_name = $this->module->name; + $orderState->name = MultiLangUtility::createMultiLangField('Awaiting Mollie payment'); + + if ($orderState->add()) { + $this->imageService->createOrderStateLogo($orderState->id); } - if (!$stateExists) { - $orderState = new OrderState(); - $orderState->send_email = false; - $orderState->color = '#4169E1'; - $orderState->hidden = false; - $orderState->delivery = false; - $orderState->logable = false; - $orderState->invoice = false; - $orderState->module_name = $this->module->name; - $orderState->name = []; - $languages = Language::getLanguages(false); - foreach ($languages as $language) { - $orderState->name[$language['id_lang']] = $this->module->lang('Awaiting Mollie payment'); - } - if ($orderState->add()) { - $source = _PS_MODULE_DIR_ . 'mollie/views/img/logo_small.png'; - $destination = _PS_ROOT_DIR_ . '/img/os/' . (int)$orderState->id . '.gif'; - @copy($source, $destination); - } - Configuration::updateValue(Mollie\Config\Config::STATUS_MOLLIE_AWAITING, (int)$orderState->id); + Configuration::updateValue(Mollie\Config\Config::STATUS_MOLLIE_AWAITING, (int)$orderState->id); + + return true; + } + + /** + * @param $languageId + * @return bool + * @throws PrestaShopDatabaseException + * @throws PrestaShopException + */ + public function orderCompletedOrderState() + { + $orderState = new OrderState(); + $orderState->send_email = false; + $orderState->color = '#3d7d1c'; + $orderState->hidden = false; + $orderState->delivery = false; + $orderState->logable = false; + $orderState->invoice = false; + $orderState->module_name = $this->module->name; + $orderState->name = MultiLangUtility::createMultiLangField('Completed'); + + if ($orderState->add()) { + $this->imageService->createOrderStateLogo($orderState->id); } + Configuration::updateValue(Mollie\Config\Config::MOLLIE_STATUS_ORDER_COMPLETED, (int)$orderState->id); return true; } @@ -242,6 +281,7 @@ protected function initConfig() Configuration::updateValue(Mollie\Config\Config::MOLLIE_DISPLAY_ERRORS, false); Configuration::updateValue(Mollie\Config\Config::MOLLIE_STATUS_OPEN, Configuration::get(Mollie\Config\Config::STATUS_MOLLIE_AWAITING)); Configuration::updateValue(Mollie\Config\Config::MOLLIE_STATUS_PAID, Configuration::get('PS_OS_PAYMENT')); + Configuration::updateValue(Mollie\Config\Config::MOLLIE_STATUS_COMPLETED, Configuration::get(Config::MOLLIE_STATUS_ORDER_COMPLETED)); Configuration::updateValue(Mollie\Config\Config::MOLLIE_STATUS_CANCELED, Configuration::get('PS_OS_CANCELED')); Configuration::updateValue(Mollie\Config\Config::MOLLIE_STATUS_EXPIRED, Configuration::get('PS_OS_CANCELED')); Configuration::updateValue( @@ -249,7 +289,10 @@ protected function initConfig() Configuration::get(Mollie\Config\Config::MOLLIE_STATUS_PARTIAL_REFUND) ); Configuration::updateValue(Mollie\Config\Config::MOLLIE_STATUS_REFUNDED, Configuration::get('PS_OS_REFUND')); + Configuration::updateValue(Mollie\Config\Config::MOLLIE_STATUS_SHIPPING, Configuration::get(Mollie\Config\Config::MOLLIE_STATUS_PARTIALLY_SHIPPED)); + Configuration::updateValue(Mollie\Config\Config::MOLLIE_MAIL_WHEN_SHIPPING, true); Configuration::updateValue(Mollie\Config\Config::MOLLIE_MAIL_WHEN_PAID, true); + Configuration::updateValue(Mollie\Config\Config::MOLLIE_MAIL_WHEN_COMPLETED, true); Configuration::updateValue(Mollie\Config\Config::MOLLIE_MAIL_WHEN_CANCELED, true); Configuration::updateValue(Mollie\Config\Config::MOLLIE_MAIL_WHEN_EXPIRED, true); Configuration::updateValue(Mollie\Config\Config::MOLLIE_MAIL_WHEN_REFUNDED, true); @@ -276,4 +319,61 @@ public function setDefaultCarrierStatuses() $defaultStatuses = array_map('intval', array_column($defaultStatuses, OrderState::$definition['primary'])); Configuration::updateValue(Mollie\Config\Config::MOLLIE_AUTO_SHIP_STATUSES, json_encode($defaultStatuses)); } -} \ No newline at end of file + + public function installTab($className, $parent, $name, $active = true) { + + $idParent = is_int($parent) ? $parent : Tab::getIdFromClassName($parent); + + $moduleTab = new Tab(); + $moduleTab->class_name = $className; + $moduleTab->id_parent = $idParent; + $moduleTab->module = $this->module->name; + $moduleTab->active = $active; + + $languages = Language::getLanguages(true); + foreach ($languages as $language) { + $moduleTab->name[$language['id_lang']] = $name; + } + + if (!$moduleTab->save()) { + return false; + } + + return true; + } + + /** + * Copies module email templates to all languages + * Collects error messages if email templates copy process is unsuccessful + * + * @param Module $module Module object + * @return bool Email templates copied successfully or not + */ + public function copyEmailTemplates() + { + $languages = Language::getLanguages(false); + + foreach ($languages as $language) { + if ($language['iso_code'] === Config::DEFAULT_EMAIL_LANGUAGE_ISO_CODE) { + continue; + } + + if (file_exists($this->module->getLocalPath() . 'mails/'.$language['iso_code'])) { + continue; + } + + try { + Tools::recurseCopy( + $this->module->getLocalPath() . 'mails/'.Config::DEFAULT_EMAIL_LANGUAGE_ISO_CODE, + $this->module->getLocalPath() . 'mails/'.$language['iso_code'] + ); + } catch (PrestaShopException $e) { + $this->errors[] = $this->module->l('Could not copy email templates:', self::FILE_NAME).' '.$e->getMessage(); + + return false; + } + } + + return true; + } +} diff --git a/src/Install/Uninstall.php b/src/Install/Uninstall.php index 762359f51..8a018cd05 100644 --- a/src/Install/Uninstall.php +++ b/src/Install/Uninstall.php @@ -38,6 +38,8 @@ use Configuration; use Mollie; use Mollie\Config\Config; +use OrderState; +use Validate; class Uninstall { @@ -58,6 +60,8 @@ public function __construct(Mollie $module) public function uninstall() { + $this->deleteMollieStatuses(); + $this->deleteConfig(); include(dirname(__FILE__) . '/../../sql/uninstall.php'); @@ -89,6 +93,8 @@ private function deleteConfig() Configuration::deleteByName(Config::MOLLIE_STATUS_EXPIRED); Configuration::deleteByName(Config::MOLLIE_STATUS_PARTIAL_REFUND); Configuration::deleteByName(Config::MOLLIE_STATUS_REFUNDED); + Configuration::deleteByName(Config::MOLLIE_STATUS_SHIPPING); + Configuration::deleteByName(Config::MOLLIE_MAIL_WHEN_SHIPPING); Configuration::deleteByName(Config::MOLLIE_MAIL_WHEN_OPEN); Configuration::deleteByName(Config::MOLLIE_MAIL_WHEN_PAID); Configuration::deleteByName(Config::MOLLIE_MAIL_WHEN_CANCELED); @@ -102,5 +108,23 @@ private function deleteConfig() Configuration::deleteByName(Config::MOLLIE_TRACKING_URLS); Configuration::deleteByName(Config::MOLLIE_METHODS_LAST_CHECK); Configuration::deleteByName(Config::METHODS_CONFIG); + Configuration::deleteByName(Config::MOLLIE_STATUS_PARTIALLY_SHIPPED); + Configuration::deleteByName(Config::MOLLIE_STATUS_COMPLETED); + Configuration::deleteByName(Config::MOLLIE_STATUS_ORDER_COMPLETED); + Configuration::deleteByName(Config::MOLLIE_MAIL_WHEN_COMPLETED); + Configuration::deleteByName(Config::STATUS_MOLLIE_AWAITING); + } + + private function deleteMollieStatuses() + { + foreach (Config::getMollieOrderStatuses() as $mollieStatus) { + $statusId = Configuration::get($mollieStatus); + $orderState = new OrderState($statusId); + if (!Validate::isLoadedObject($orderState)) { + return; + } + $orderState->deleted = 1; + $orderState->update(); + } } -} \ No newline at end of file +} diff --git a/src/Logger/PrestaLogger.php b/src/Logger/PrestaLogger.php new file mode 100644 index 000000000..7057cc987 --- /dev/null +++ b/src/Logger/PrestaLogger.php @@ -0,0 +1,66 @@ +getMessageWithContext($message, $context), + 2 + ); + } + + public function warning($message, array $context = array()) + { + throw new NotImplementedException('not implemented method'); + } + + public function notice($message, array $context = array()) + { + throw new NotImplementedException('not implemented method'); + } + + public function info($message, array $context = array()) + { + \PrestaShopLogger::addLog( + $this->getMessageWithContext($message, $context) + ); + } + + public function debug($message, array $context = array()) + { + throw new NotImplementedException('not implemented method'); + } + + public function log($level, $message, array $context = array()) + { + throw new NotImplementedException('not implemented method'); + } + + private function getMessageWithContext($message, array $context = array()) + { + $content = json_encode($context); + + return "{$message} . context: {$content}"; + } +} \ No newline at end of file diff --git a/src/Presenter/OrderListActionBuilder.php b/src/Presenter/OrderListActionBuilder.php new file mode 100644 index 000000000..7cb60d68f --- /dev/null +++ b/src/Presenter/OrderListActionBuilder.php @@ -0,0 +1,36 @@ +mollie = $mollie; + } + + public function buildOrderPaymentResendButton(Smarty_Data $smarty, $orderId) + { + $smarty->assign('idOrder', $orderId); + + $smarty->assign('message', + $this->mollie->l('You will resend email with payment link to the customer', self::FILE_NAME) + ); + $icon = $this->mollie->display( + $this->mollie->getLocalPath() , 'views/templates/hook/admin/order-list-save-label-icon.tpl'); + + $smarty->assign('icon', $icon); + + return $this->mollie->display( + $this->mollie->getLocalPath(), 'views/templates/hook/admin/order-list-icon-container.tpl'); + } +} \ No newline at end of file diff --git a/src/Repository/AbstractRepository.php b/src/Repository/AbstractRepository.php new file mode 100644 index 000000000..4283e366d --- /dev/null +++ b/src/Repository/AbstractRepository.php @@ -0,0 +1,43 @@ +fullyClassifiedClassName = $fullyClassifiedClassName; + } + + /** + * @param array $keyValueCriteria + * @return ObjectModel|null + * + * @throws PrestaShopException + */ + public function findOneBy(array $keyValueCriteria) + { + $psCollection = new PrestaShopCollection($this->fullyClassifiedClassName); + + foreach ($keyValueCriteria as $field => $value) { + $psCollection = $psCollection->where($field, '=', $value); + } + + $first = $psCollection->getFirst(); + + return false === $first ? null: $first; + } +} \ No newline at end of file diff --git a/src/Repository/CountryRepository.php b/src/Repository/CountryRepository.php index a9a1d8442..2f9d16738 100644 --- a/src/Repository/CountryRepository.php +++ b/src/Repository/CountryRepository.php @@ -81,4 +81,50 @@ public function updatePaymentMethodCountries($idMethod, $idCountries) return $response; } + + public function getExcludedCountryIds($methodId) + { + $sql = 'SELECT id_country + FROM `' . _DB_PREFIX_ . 'mol_excluded_country` + WHERE id_method = "' . pSQL($methodId) . '"'; + + $countryIds = Db::getInstance()->executeS($sql); + $countryIdsArray = []; + foreach ($countryIds as $countryId) { + $countryIdsArray[] = $countryId['id_country']; + } + + return $countryIdsArray; + } + + public function updatePaymentMethodExcludedCountries($idMethod, $idCountries) + { + + $sql = 'DELETE FROM ' . _DB_PREFIX_ . 'mol_excluded_country WHERE `id_method` = "' . $idMethod . '"'; + if (!Db::getInstance()->execute($sql)) { + return false; + } + + if ($idCountries == false) { + return true; + } + + $response = true; + foreach ($idCountries as $idCountry) { + $allCountries = 0; + $sql = 'INSERT INTO `' . _DB_PREFIX_ . 'mol_excluded_country` (id_method, id_country, all_countries) + VALUES ('; + + if ($idCountry === '0') { + $allCountries = 1; + } + $sql .= '"' . pSQL($idMethod) . '", ' . (int)$idCountry . ', ' . (int)$allCountries . ')'; + + if (!Db::getInstance()->execute($sql)) { + $response = false; + } + } + + return $response; + } } \ No newline at end of file diff --git a/src/Repository/MethodCountryRepository.php b/src/Repository/MethodCountryRepository.php index d113444d6..c9ad0db46 100644 --- a/src/Repository/MethodCountryRepository.php +++ b/src/Repository/MethodCountryRepository.php @@ -50,4 +50,17 @@ public function checkIfMethodIsAvailableInCountry($methodId, $countryId) return Db::getInstance()->getValue($sql); } + + public function checkIfCountryIsExcluded($methodId, $countryId) + { + $sql = new DbQuery(); + $sql->select('`id_mol_country`'); + $sql->from('mol_excluded_country'); + $sql->where('`id_method` = "' . pSQL($methodId) . '" AND ( id_country = ' . (int)$countryId . ' OR all_countries = 1)'); + + $result = Db::getInstance()->getValue($sql); + + return is_numeric($result) && $result > 0; + + } } \ No newline at end of file diff --git a/src/Repository/PaymentMethodRepository.php b/src/Repository/PaymentMethodRepository.php index 239f9f3d3..5486f9c66 100644 --- a/src/Repository/PaymentMethodRepository.php +++ b/src/Repository/PaymentMethodRepository.php @@ -38,11 +38,16 @@ use _PhpScoper5eddef0da618a\Mollie\Api\Types\PaymentStatus; use Db; use DbQuery; +use Exception; use PrestaShopDatabaseException; use PrestaShopException; class PaymentMethodRepository { + /** + * @param $paymentMethodId + * @return false|string|null + */ public function getPaymentMethodIssuersByPaymentMethodId($paymentMethodId) { $sql = 'Select issuers_json FROM `' . _DB_PREFIX_ . 'mol_payment_method_issuer` WHERE id_payment_method = "' . pSQL($paymentMethodId) . '"'; @@ -50,6 +55,10 @@ public function getPaymentMethodIssuersByPaymentMethodId($paymentMethodId) return Db::getInstance()->getValue($sql); } + /** + * @param $paymentMethodId + * @return bool + */ public function deletePaymentMethodIssuersByPaymentMethodId($paymentMethodId) { $sql = 'DELETE FROM `' . _DB_PREFIX_ . 'mol_payment_method_issuer` WHERE id_payment_method = "' . pSQL($paymentMethodId) . '"'; @@ -57,6 +66,10 @@ public function deletePaymentMethodIssuersByPaymentMethodId($paymentMethodId) return Db::getInstance()->execute($sql); } + /** + * @param $paymentMethodId + * @return false|string|null + */ public function getPaymentMethodIdByMethodId($paymentMethodId) { $sql = 'SELECT id_payment_method FROM `' . _DB_PREFIX_ . 'mol_payment_method` WHERE id_method = "' . pSQL($paymentMethodId) . '"'; @@ -140,6 +153,10 @@ public function tryAddOrderReferenceColumn() return true; } + /** + * @return array|false|\mysqli_result|\PDOStatement|resource|null + * @throws PrestaShopDatabaseException + */ public function getMethodsForCheckout() { $sql = new DbQuery(); @@ -148,4 +165,56 @@ public function getMethodsForCheckout() return Db::getInstance()->executeS($sql); } -} \ No newline at end of file + + /** + * @param $oldTransactionId + * @param $newTransactionId + * @return bool + */ + public function updateTransactionId($oldTransactionId, $newTransactionId) + { + return Db::getInstance()->update( + 'mollie_payments', + [ + 'transaction_id' => pSQL($newTransactionId), + + ], + '`transaction_id` = \'' . pSQL($oldTransactionId) . '\'' + ); + } + + public function savePaymentStatus($transactionId, $status, $orderId, $paymentMethod) + { + try { + return Db::getInstance()->update( + 'mollie_payments', + [ + 'updated_at' => ['type' => 'sql', 'value' => 'NOW()'], + 'bank_status' => pSQL($status), + 'order_id' => (int)$orderId, + 'method' => pSQL($paymentMethod), + ], + '`transaction_id` = \'' . pSQL($transactionId) . '\'' + ); + } catch (Exception $e) { + $this->tryAddOrderReferenceColumn(); + throw $e; + } + } + + public function addOpenStatusPayment($cartId, $orderPayment, $transactionId, $orderId, $orderReference) + { + return Db::getInstance()->insert( + 'mollie_payments', + [ + 'cart_id' => (int)$cartId, + 'method' => pSQL($orderPayment), + 'transaction_id' => pSQL($transactionId), + 'bank_status' => PaymentStatus::STATUS_OPEN, + 'order_id' => (int) $orderId, + 'order_reference' => psql($orderReference), + 'created_at' => array('type' => 'sql', 'value' => 'NOW()'), + ] + ); + } +} diff --git a/src/Repository/PendingOrderCartRepository.php b/src/Repository/PendingOrderCartRepository.php new file mode 100644 index 000000000..de80e32fb --- /dev/null +++ b/src/Repository/PendingOrderCartRepository.php @@ -0,0 +1,7 @@ + 5 ] + * + * @return ObjectModel + */ + public function findOneBy(array $keyValueCriteria); +} \ No newline at end of file diff --git a/src/Service/ApiService.php b/src/Service/ApiService.php index 28bc3087b..ed2909e7b 100644 --- a/src/Service/ApiService.php +++ b/src/Service/ApiService.php @@ -132,28 +132,6 @@ public function getMethodsForConfig($api, $path, $active = false) return []; } - $dbMethods = @json_decode(Configuration::get(Config::METHODS_CONFIG), true); - if (!$dbMethods) { - $dbMethods = []; - } - $keys = ['id', 'name', 'enabled', 'image', 'issuers', 'position']; - foreach ($dbMethods as $index => $dbMethod) { - if (count(array_intersect($keys, array_keys($dbMethod))) !== count($keys)) { - unset($dbMethods[$index]); - } - } - - if (!is_array($dbMethods)) { - $dbMethods = []; - $configMethods = []; - } else { - $configMethods = []; - foreach ($dbMethods as $dbMethod) { - $configMethods[$dbMethod['id']] = $dbMethod; - } - } - - $methodsFromDb = array_keys($configMethods); $methods = []; $deferredMethods = []; $isSSLEnabled = Configuration::get('PS_SSL_ENABLED_EVERYWHERE'); @@ -163,7 +141,7 @@ public function getMethodsForConfig($api, $path, $active = false) $notAvailable[] = $apiMethod->id; $tipEnableSSL = true; } - if (!in_array($apiMethod->id, $methodsFromDb) || !isset($configMethods[$apiMethod->id]['position'])) { + if (!isset($configMethods[$apiMethod->id]['position'])) { $deferredMethods[] = [ 'id' => $apiMethod->id, 'name' => $apiMethod->description, @@ -173,16 +151,6 @@ public function getMethodsForConfig($api, $path, $active = false) 'issuers' => $apiMethod->issuers, 'tipEnableSSL' => $tipEnableSSL ]; - } else { - $methods[$configMethods[$apiMethod->id]['position']] = [ - 'id' => $apiMethod->id, - 'name' => $apiMethod->description, - 'enabled' => $configMethods[$apiMethod->id]['enabled'], - 'available' => !in_array($apiMethod->id, $notAvailable), - 'image' => (array)$apiMethod->image, - 'issuers' => $apiMethod->issuers, - 'tipEnableSSL' => $tipEnableSSL - ]; } } $availableApiMethods = array_column(array_map(function ($apiMethod) { @@ -190,7 +158,6 @@ public function getMethodsForConfig($api, $path, $active = false) }, $apiMethods), 'id'); if (in_array('creditcard', $availableApiMethods)) { foreach ([Config::CARTES_BANCAIRES => 'Cartes Bancaires'] as $id => $name) { - if (!in_array($id, array_column($dbMethods, 'id'))) { $deferredMethods[] = [ 'id' => $id, 'name' => $name, @@ -203,22 +170,6 @@ public function getMethodsForConfig($api, $path, $active = false) ], 'issuers' => null, ]; - } else { - $cc = $dbMethods[array_search('creditcard', array_column($dbMethods, 'id'))]; - $thisMethod = $dbMethods[array_search($id, array_column($dbMethods, 'id'))]; - $methods[$configMethods[$id]['position']] = [ - 'id' => $id, - 'name' => $name, - 'enabled' => !empty($thisMethod['enabled']) && !empty($cc['enabled']), - 'available' => !in_array($id, $notAvailable), - 'image' => [ - 'size1x' => UrlPathUtility::getMediaPath("{$path}views/img/{$id}_small.png"), - 'size2x' => UrlPathUtility::getMediaPath("{$path}views/img/{$id}.png"), - 'svg' => UrlPathUtility::getMediaPath("{$path}views/img/{$id}.svg"), - ], - 'issuers' => null, - ]; - } } } ksort($methods); @@ -236,6 +187,7 @@ public function getMethodsForConfig($api, $path, $active = false) $methods = $this->getMethodsObjForConfig($methods); $methods = $this->getMethodsCountriesForConfig($methods); + $methods = $this->getExcludedCountriesForConfig($methods); return $methods; } @@ -300,6 +252,15 @@ private function getMethodsCountriesForConfig(&$methods) return $methods; } + private function getExcludedCountriesForConfig(&$methods) + { + foreach ($methods as $key => $method) { + $methods[$key]['excludedCountries'] = $this->countryRepository->getExcludedCountryIds($key); + } + + return $methods; + } + /** * @param string $transactionId * @param bool $process Process the new payment/order status diff --git a/src/Service/CartDuplicationService.php b/src/Service/CartDuplicationService.php index f6d2a3889..ee281f3f3 100755 --- a/src/Service/CartDuplicationService.php +++ b/src/Service/CartDuplicationService.php @@ -1,4 +1,36 @@ + * @copyright Mollie B.V. + * @license Berkeley Software Distribution License (BSD-License 2) http://www.opensource.org/licenses/bsd-license.php + * @category Mollie + * @package Mollie + * @link https://www.mollie.nl + */ namespace Mollie\Service; @@ -10,6 +42,15 @@ class CartDuplicationService { + /** + * + * + * @param int $cartId + * + * @return int + * + * @throws \PrestaShopDatabaseException + */ public function restoreCart($cartId) { $context = Context::getContext(); @@ -18,25 +59,14 @@ public function restoreCart($cartId) if ($duplication['success']) { /** @var Cart $duplicatedCart */ $duplicatedCart = $duplication['cart']; - foreach ($cart->getOrderedCartRulesIds() as $cartRuleId) { - $duplicatedCart->addCartRule($cartRuleId['id_cart_rule']); - $this->restoreCartRuleQuantity($cartId, $cartRuleId['id_cart_rule']); - } + $context->cookie->id_cart = $duplicatedCart->id; $context->cart = $duplicatedCart; - CartRule::autoAddToCart($context); $context->cookie->write(); - } - } - private function restoreCartRuleQuantity($cartId, $cartRuleId) - { - $cartRule = new CartRule($cartRuleId); - $cartRule->quantity++; - $cartRule->update(); + return $duplicatedCart->id; + } - $orderId = Order::getIdByCartId($cartId); - $sql = 'DELETE FROM `ps_order_cart_rule` WHERE id_order = ' . (int) $orderId . ' AND id_cart_rule = ' . (int) $cartRuleId; - DB::getInstance()->execute($sql); + return 0; } -} \ No newline at end of file +} diff --git a/src/Service/ConfigFieldService.php b/src/Service/ConfigFieldService.php index af3b8abb7..1c0cf28ec 100644 --- a/src/Service/ConfigFieldService.php +++ b/src/Service/ConfigFieldService.php @@ -87,12 +87,14 @@ public function getConfigFieldsValues() Config::MOLLIE_STATUS_OPEN => Configuration::get(Config::MOLLIE_STATUS_OPEN), Config::MOLLIE_STATUS_PAID => Configuration::get(Config::MOLLIE_STATUS_PAID), + Config::MOLLIE_STATUS_COMPLETED => Configuration::get(Config::MOLLIE_STATUS_COMPLETED), Config::MOLLIE_STATUS_CANCELED => Configuration::get(Config::MOLLIE_STATUS_CANCELED), Config::MOLLIE_STATUS_EXPIRED => Configuration::get(Config::MOLLIE_STATUS_EXPIRED), Config::MOLLIE_STATUS_PARTIAL_REFUND => Configuration::get(Config::MOLLIE_STATUS_PARTIAL_REFUND), Config::MOLLIE_STATUS_REFUNDED => Configuration::get(Config::MOLLIE_STATUS_REFUNDED), Config::MOLLIE_MAIL_WHEN_OPEN => Configuration::get(Config::MOLLIE_MAIL_WHEN_OPEN), Config::MOLLIE_MAIL_WHEN_PAID => Configuration::get(Config::MOLLIE_MAIL_WHEN_PAID), + Config::MOLLIE_MAIL_WHEN_COMPLETED => Configuration::get(Config::MOLLIE_MAIL_WHEN_COMPLETED), Config::MOLLIE_MAIL_WHEN_CANCELED => Configuration::get(Config::MOLLIE_MAIL_WHEN_CANCELED), Config::MOLLIE_MAIL_WHEN_EXPIRED => Configuration::get(Config::MOLLIE_MAIL_WHEN_EXPIRED), Config::MOLLIE_MAIL_WHEN_REFUNDED => Configuration::get(Config::MOLLIE_MAIL_WHEN_REFUNDED), @@ -103,6 +105,9 @@ public function getConfigFieldsValues() Config::MOLLIE_API => Configuration::get(Config::MOLLIE_API), Config::MOLLIE_AUTO_SHIP_MAIN => Configuration::get(Config::MOLLIE_AUTO_SHIP_MAIN), + + Config::MOLLIE_STATUS_SHIPPING => Configuration::get(Config::MOLLIE_STATUS_SHIPPING), + Config::MOLLIE_MAIL_WHEN_SHIPPING => Configuration::get(Config::MOLLIE_MAIL_WHEN_SHIPPING), ]; if (Configuration::get(Config::MOLLIE_API_KEY)) { diff --git a/src/Service/ErrorDisplayService.php b/src/Service/ErrorDisplayService.php index 72e5ba765..a566913ec 100644 --- a/src/Service/ErrorDisplayService.php +++ b/src/Service/ErrorDisplayService.php @@ -1,4 +1,36 @@ + * @copyright Mollie B.V. + * @license Berkeley Software Distribution License (BSD-License 2) http://www.opensource.org/licenses/bsd-license.php + * @category Mollie + * @package Mollie + * @link https://www.mollie.nl + */ namespace Mollie\Service; @@ -25,8 +57,8 @@ private function stripSlashesDeep($value) { $value = is_array($value) ? array_map('stripslashes', $value) : - stripslashes($value); + Tools::stripslashes($value); return $value; } -} \ No newline at end of file +} diff --git a/src/Service/LanguageService.php b/src/Service/LanguageService.php index bceb92ec7..f3e656ede 100644 --- a/src/Service/LanguageService.php +++ b/src/Service/LanguageService.php @@ -35,12 +35,15 @@ namespace Mollie\Service; +use _PhpScoper5eddef0da618a\Mollie\Api\Types\OrderStatus; use _PhpScoper5eddef0da618a\Mollie\Api\Types\PaymentStatus; use _PhpScoper5eddef0da618a\Mollie\Api\Types\RefundStatus; use Mollie; class LanguageService { + const FILE_NAME = 'LanguageService'; + /** * @var Mollie */ @@ -53,57 +56,69 @@ public function __construct(Mollie $module) public function getLang() { return [ - PaymentStatus::STATUS_PAID => $this->module->l('Paid'), - PaymentStatus::STATUS_AUTHORIZED => $this->module->l('Authorized'), - PaymentStatus::STATUS_CANCELED => $this->module->l('Canceled'), - PaymentStatus::STATUS_EXPIRED => $this->module->l('Expired'), - RefundStatus::STATUS_REFUNDED => $this->module->l('Refunded'), - PaymentStatus::STATUS_OPEN => $this->module->l('Bankwire pending'), - Mollie\Config\Config::PARTIAL_REFUND_CODE => $this->module->l('Partially refunded'), - 'created' => $this->module->l('Created'), - 'This payment method is not available.' => $this->module->l('This payment method is not available.'), - 'Click here to continue' => $this->module->l('Click here to continue'), - 'This payment method is only available for Euros.' => $this->module->l('This payment method is only available for Euros.'), - 'There was an error while processing your request: ' => $this->module->l('There was an error while processing your request: '), - 'The order with this id does not exist.' => $this->module->l('The order with this id does not exist.'), - 'We have not received a definite payment status. You will be notified as soon as we receive a confirmation of the bank/merchant.' => $this->module->l('We have not received a definite payment status. You will be notified as soon as we receive a confirmation of the bank/merchant.'), - 'Unfortunately your payment was expired.' => $this->module->l('Unfortunately your payment was expired.'), - 'Thank you. Your payment has been received.' => $this->module->l('Thank you. Your payment has been received.'), - 'The transaction has an unexpected status.' => $this->module->l('The transaction has an unexpected status.'), - 'You are not authorised to see this page.' => $this->module->l('You are not authorised to see this page.'), - 'Continue shopping' => $this->module->l('Continue shopping'), - 'Welcome back' => $this->module->l('Welcome back'), - 'Select your bank:' => $this->module->l('Select your bank:'), - 'OK' => $this->module->l('OK'), - 'Different payment method' => $this->module->l('Different payment method'), - 'Pay with %s' => $this->module->l('Pay with %s'), - 'Refund this order' => $this->module->l('Refund this order'), - 'Mollie refund' => $this->module->l('Mollie refund'), - 'Refund order #%d through the Mollie API.' => $this->module->l('Refund order #%d through the Mollie API.'), - 'The order has been refunded!' => $this->module->l('The order has been refunded!'), - 'Mollie B.V. will transfer the money back to the customer on the next business day.' => $this->module->l('Mollie B.V. will transfer the money back to the customer on the next business day.'), - 'Awaiting Mollie payment' => $this->module->l('Awaiting Mollie payment'), - 'Mollie partially refunded' => $this->module->l('Mollie partially refunded'), - 'iDEAL' => $this->module->l('iDEAL'), - 'Cartes Bancaires' => $this->module->l('Cartes Bancaires'), - 'Credit card' => $this->module->l('Credit card'), - 'Bancontact' => $this->module->l('Bancontact'), - 'SOFORT Banking' => $this->module->l('SOFORT Banking'), - 'SEPA Direct Debit' => $this->module->l('SEPA Direct Debit'), - 'Belfius Pay Button' => $this->module->l('Belfius Pay Button'), - 'Bitcoin' => $this->module->l('Bitcoin'), - 'PODIUM Cadeaukaart' => $this->module->l('PODIUM Cadeaukaart'), - 'Gift cards' => $this->module->l('Gift cards'), - 'Bank transfer' => $this->module->l('Bank transfer'), - 'PayPal' => $this->module->l('PayPal'), - 'paysafecard' => $this->module->l('paysafecard'), - 'KBC/CBC Payment Button' => $this->module->l('KBC/CBC Payment Button'), - 'ING Home\'Pay' => $this->module->l('ING Home\'Pay'), - 'Giropay' => $this->module->l('Giropay'), - 'eps' => $this->module->l('eps'), - 'Pay later.' => $this->module->l('Pay later.'), - 'Slice it.' => $this->module->l('Slice it.'), - 'MyBank' => $this->module->l('MyBank'), + PaymentStatus::STATUS_PAID => $this->module->l('Paid', self::FILE_NAME), + OrderStatus::STATUS_COMPLETED => $this->module->l('Completed', self::FILE_NAME), + PaymentStatus::STATUS_AUTHORIZED => $this->module->l('Authorized', self::FILE_NAME), + PaymentStatus::STATUS_CANCELED => $this->module->l('Canceled', self::FILE_NAME), + PaymentStatus::STATUS_EXPIRED => $this->module->l('Expired', self::FILE_NAME), + RefundStatus::STATUS_REFUNDED => $this->module->l('Refunded', self::FILE_NAME), + PaymentStatus::STATUS_OPEN => $this->module->l('Bankwire pending', self::FILE_NAME), + Mollie\Config\Config::PARTIAL_REFUND_CODE => $this->module->l('Partially refunded', self::FILE_NAME), + 'created' => $this->module->l('Created', self::FILE_NAME), + 'This payment method is not available.' => $this->module->l('This payment method is not available.', self::FILE_NAME), + 'Click here to continue' => $this->module->l('Click here to continue', self::FILE_NAME), + 'This payment method is only available for Euros.' => $this->module->l('This payment method is only available for Euros.', self::FILE_NAME), + 'There was an error while processing your request: ' => $this->module->l('There was an error while processing your request: ', self::FILE_NAME), + 'The order with this id does not exist.' => $this->module->l('The order with this id does not exist.', self::FILE_NAME), + 'We have not received a definite payment status. You will be notified as soon as we receive a confirmation of the bank/merchant.' => $this->module->l('We have not received a definite payment status. You will be notified as soon as we receive a confirmation of the bank/merchant.', self::FILE_NAME), + 'Unfortunately your payment was expired.' => $this->module->l('Unfortunately your payment was expired.', self::FILE_NAME), + 'Thank you. Your payment has been received.' => $this->module->l('Thank you. Your payment has been received.', self::FILE_NAME), + 'The transaction has an unexpected status.' => $this->module->l('The transaction has an unexpected status.', self::FILE_NAME), + 'You are not authorised to see this page.' => $this->module->l('You are not authorised to see this page.', self::FILE_NAME), + 'Continue shopping' => $this->module->l('Continue shopping', self::FILE_NAME), + 'Welcome back' => $this->module->l('Welcome back', self::FILE_NAME), + 'Select your bank:' => $this->module->l('Select your bank:', self::FILE_NAME), + 'OK' => $this->module->l('OK', self::FILE_NAME), + 'Different payment method' => $this->module->l('Different payment method', self::FILE_NAME), + 'Pay with %s' => $this->module->l('Pay with %s', self::FILE_NAME), + 'Refund this order' => $this->module->l('Refund this order', self::FILE_NAME), + 'Mollie refund' => $this->module->l('Mollie refund', self::FILE_NAME), + 'Refund order #%d through the Mollie API.' => $this->module->l('Refund order #%d through the Mollie API.', self::FILE_NAME), + 'The order has been refunded!' => $this->module->l('The order has been refunded!', self::FILE_NAME), + 'Mollie B.V. will transfer the money back to the customer on the next business day.' => $this->module->l('Mollie B.V. will transfer the money back to the customer on the next business day.', self::FILE_NAME), + 'Awaiting Mollie payment' => $this->module->l('Awaiting Mollie payment', self::FILE_NAME), + 'Mollie partially refunded' => $this->module->l('Mollie partially refunded', self::FILE_NAME), + 'iDEAL' => $this->module->l('iDEAL', self::FILE_NAME), + 'Cartes Bancaires' => $this->module->l('Cartes Bancaires', self::FILE_NAME), + 'Credit card' => $this->module->l('Credit card', self::FILE_NAME), + 'Bancontact' => $this->module->l('Bancontact', self::FILE_NAME), + 'SOFORT Banking' => $this->module->l('SOFORT Banking', self::FILE_NAME), + 'SEPA Direct Debit' => $this->module->l('SEPA Direct Debit', self::FILE_NAME), + 'Belfius Pay Button' => $this->module->l('Belfius Pay Button', self::FILE_NAME), + 'Bitcoin' => $this->module->l('Bitcoin', self::FILE_NAME), + 'PODIUM Cadeaukaart' => $this->module->l('PODIUM Cadeaukaart', self::FILE_NAME), + 'Gift cards' => $this->module->l('Gift cards', self::FILE_NAME), + 'Bank transfer' => $this->module->l('Bank transfer', self::FILE_NAME), + 'PayPal' => $this->module->l('PayPal', self::FILE_NAME), + 'paysafecard' => $this->module->l('paysafecard', self::FILE_NAME), + 'KBC/CBC Payment Button' => $this->module->l('KBC/CBC Payment Button', self::FILE_NAME), + 'ING Home\'Pay' => $this->module->l('ING Home\'Pay', self::FILE_NAME), + 'Giropay' => $this->module->l('Giropay', self::FILE_NAME), + 'eps' => $this->module->l('eps', self::FILE_NAME), + 'Pay later.' => $this->module->l('Pay later.', self::FILE_NAME), + 'Slice it.' => $this->module->l('Slice it.', self::FILE_NAME), + 'MyBank' => $this->module->l('MyBank', self::FILE_NAME), + 'Completed' => $this->module->l('Completed', self::FILE_NAME), ]; } + + public function lang($str) + { + $lang = $this->getLang(); + if (array_key_exists($str, $lang)) { + return $lang[$str]; + } + + return $str; + } } \ No newline at end of file diff --git a/src/Service/MailService.php b/src/Service/MailService.php new file mode 100644 index 000000000..88c864e32 --- /dev/null +++ b/src/Service/MailService.php @@ -0,0 +1,403 @@ +module = $module; + $this->context = Context::getContext(); + } + + public function sendSecondChanceMail(Customer $customer, $checkoutUrl, $methodName) + { + Mail::send( + $customer->id_lang, + 'mollie_payment', + Mail::l('Order payment'), + [ + '{checkoutUrl}' => $checkoutUrl, + '{firstName}' => $customer->firstname, + '{lastName}' => $customer->lastname, + '{paymentMethod}' => $methodName + ], + $customer->email, + null, + null, + null, + null, + null, + $this->module->getLocalPath() . 'mails/' + ); + } + + public function sendOrderConfMail(Order $order, $orderStateId) + { + $orderLanguage = new Language((int)$order->id_lang); + + $data = $this->getOrderConfData($order, $orderStateId); + $fileAttachment = $this->getFileAttachment($orderStateId, $order); + $customer = $order->getCustomer(); + Mail::Send( + (int)$order->id_lang, + 'order_conf', + $this->context->getTranslator()->trans( + 'Order confirmation', + [], + 'Emails.Subject', + $orderLanguage->locale + ), + $data, + $customer->email, + $customer->firstname . ' ' . $customer->lastname, + null, + null, + $fileAttachment, + null, _PS_MAIL_DIR_, false, (int)$order->id_shop + ); + } + + private function getOrderConfData(Order $order, $orderStateId) + { + $virtual_product = true; + $carrier = new Carrier($order->id_carrier); + + $product_var_tpl_list = []; + foreach ($order->getProducts() as $product) { + $price = Product::getPriceStatic((int)$product['id_product'], false, ($product['product_attribute_id'] ? (int)$product['product_attribute_id'] : null), 6, null, false, true, $product['product_quantity'], false, (int)$order->id_customer, (int)$order->id_cart, (int)$order->{Configuration::get('PS_TAX_ADDRESS_TYPE')}, $specific_price, true, true, null, true, $product['id_customization']); + $price_wt = Product::getPriceStatic((int)$product['id_product'], true, ($product['product_attribute_id'] ? (int)$product['product_attribute_id'] : null), 2, null, false, true, $product['product_quantity'], false, (int)$order->id_customer, (int)$order->id_cart, (int)$order->{Configuration::get('PS_TAX_ADDRESS_TYPE')}, $specific_price, true, true, null, true, $product['id_customization']); + + $product_price = Product::getTaxCalculationMethod() == PS_TAX_EXC ? Tools::ps_round($price, 2) : $price_wt; + + $attribute = new Attribute($product['product_attribute_id'], $this->context->language->id); + $product_var_tpl = [ + 'id_product' => $product['id_product'], + 'reference' => $product['reference'], + 'name' => $product['product_name'] . (isset($attribute) ? ' - ' . $attribute->name : ''), + 'price' => Tools::displayPrice($product_price * $product['product_quantity'], $this->context->currency, false), + 'quantity' => $product['product_quantity'], + 'customization' => [], + ]; + + if (isset($product['price']) && $product['price']) { + $product_var_tpl['unit_price'] = Tools::displayPrice($product_price, $this->context->currency, false); + $product_var_tpl['unit_price_full'] = Tools::displayPrice($product_price, $this->context->currency, false) + . ' ' . $product['unity']; + } else { + $product_var_tpl['unit_price'] = $product_var_tpl['unit_price_full'] = ''; + } + + $customized_datas = Product::getAllCustomizedDatas((int)$order->id_cart, null, true, null, (int)$product['id_customization']); + if (isset($customized_datas[$product['id_product']][$product['product_attribute_id']])) { + $product_var_tpl['customization'] = []; + foreach ($customized_datas[$product['id_product']][$product['product_attribute_id']][$order->id_address_delivery] as $customization) { + $customization_text = ''; + if (isset($customization['datas'][Product::CUSTOMIZE_TEXTFIELD])) { + foreach ($customization['datas'][Product::CUSTOMIZE_TEXTFIELD] as $text) { + $customization_text .= '' . $text['name'] . ': ' . $text['value'] . '
'; + } + } + + if (isset($customization['datas'][Product::CUSTOMIZE_FILE])) { + $customization_text .= $this->trans('%d image(s)', [count($customization['datas'][Product::CUSTOMIZE_FILE])], 'Admin.Payment.Notification') . '
'; + } + + $customization_quantity = (int)$customization['quantity']; + + $product_var_tpl['customization'][] = [ + 'customization_text' => $customization_text, + 'customization_quantity' => $customization_quantity, + 'quantity' => Tools::displayPrice($customization_quantity * $product_price, $this->context->currency, false), + ]; + } + } + + $product_var_tpl_list[] = $product_var_tpl; + // Check if is not a virutal product for the displaying of shipping + if (!$product['is_virtual']) { + $virtual_product &= false; + } + } + + $invoice = new Address((int)$order->id_address_invoice); + $delivery = new Address((int)$order->id_address_delivery); + $delivery_state = $delivery->id_state ? new State((int)$delivery->id_state) : false; + $invoice_state = $invoice->id_state ? new State((int)$invoice->id_state) : false; + + + $product_list_txt = ''; + $product_list_html = ''; + if (count($product_var_tpl_list) > 0) { + $product_list_txt = $this->getEmailTemplateContent('order_conf_product_list.txt', Mail::TYPE_TEXT, $product_var_tpl_list); + $product_list_html = $this->getEmailTemplateContent('order_conf_product_list.tpl', Mail::TYPE_HTML, $product_var_tpl_list); + } + + $cart_rules_list = $this->getCartRuleList($order, $orderStateId); + $cart_rules_list_txt = ''; + $cart_rules_list_html = ''; + if (count($cart_rules_list) > 0) { + $cart_rules_list_txt = $this->getEmailTemplateContent('order_conf_cart_rules.txt', Mail::TYPE_TEXT, $cart_rules_list); + $cart_rules_list_html = $this->getEmailTemplateContent('order_conf_cart_rules.tpl', Mail::TYPE_HTML, $cart_rules_list); + } + + return [ + '{firstname}' => $this->context->customer->firstname, + '{lastname}' => $this->context->customer->lastname, + '{email}' => $this->context->customer->email, + '{delivery_block_txt}' => $this->_getFormatedAddress($delivery, "\n"), + '{invoice_block_txt}' => $this->_getFormatedAddress($invoice, "\n"), + '{delivery_block_html}' => $this->_getFormatedAddress($delivery, '
', [ + 'firstname' => '%s', + 'lastname' => '%s', + ]), + '{invoice_block_html}' => $this->_getFormatedAddress($invoice, '
', [ + 'firstname' => '%s', + 'lastname' => '%s', + ]), + '{delivery_company}' => $delivery->company, + '{delivery_firstname}' => $delivery->firstname, + '{delivery_lastname}' => $delivery->lastname, + '{delivery_address1}' => $delivery->address1, + '{delivery_address2}' => $delivery->address2, + '{delivery_city}' => $delivery->city, + '{delivery_postal_code}' => $delivery->postcode, + '{delivery_country}' => $delivery->country, + '{delivery_state}' => $delivery->id_state ? $delivery_state->name : '', + '{delivery_phone}' => ($delivery->phone) ? $delivery->phone : $delivery->phone_mobile, + '{delivery_other}' => $delivery->other, + '{invoice_company}' => $invoice->company, + '{invoice_vat_number}' => $invoice->vat_number, + '{invoice_firstname}' => $invoice->firstname, + '{invoice_lastname}' => $invoice->lastname, + '{invoice_address2}' => $invoice->address2, + '{invoice_address1}' => $invoice->address1, + '{invoice_city}' => $invoice->city, + '{invoice_postal_code}' => $invoice->postcode, + '{invoice_country}' => $invoice->country, + '{invoice_state}' => $invoice->id_state ? $invoice_state->name : '', + '{invoice_phone}' => ($invoice->phone) ? $invoice->phone : $invoice->phone_mobile, + '{invoice_other}' => $invoice->other, + '{order_name}' => $order->getUniqReference(), + '{date}' => Tools::displayDate(date('Y-m-d H:i:s'), null, 1), + '{carrier}' => ($virtual_product || !isset($carrier->name)) ? $this->trans('No carrier', [], 'Admin.Payment.Notification') : $carrier->name, + '{payment}' => Tools::substr($order->payment, 0, 255), + '{products}' => $product_list_html, + '{products_txt}' => $product_list_txt, + '{discounts}' => $cart_rules_list_html, + '{discounts_txt}' => $cart_rules_list_txt, + '{total_paid}' => Tools::displayPrice($order->total_paid, $this->context->currency, false), + '{total_products}' => Tools::displayPrice(Product::getTaxCalculationMethod() == PS_TAX_EXC ? $order->total_products : $order->total_products_wt, $this->context->currency, false), + '{total_discounts}' => Tools::displayPrice($order->total_discounts, $this->context->currency, false), + '{total_shipping}' => Tools::displayPrice($order->total_shipping, $this->context->currency, false), + '{total_wrapping}' => Tools::displayPrice($order->total_wrapping, $this->context->currency, false), + '{total_tax_paid}' => Tools::displayPrice(($order->total_products_wt - $order->total_products) + ($order->total_shipping_tax_incl - $order->total_shipping_tax_excl), $this->context->currency, false), + ]; + } + + private function getCartRuleList(Order $order, $orderStateId) + { + $cart_rules = $this->context->cart->getCartRules(); + $order_list[] = $order; + $cart_rule_used = []; + + $cart_rules_list = []; + $total_reduction_value_ti = 0; + $total_reduction_value_tex = 0; + foreach ($cart_rules as $cart_rule) { + $package = ['id_carrier' => $order->id_carrier, 'id_address' => $order->id_address_delivery, 'products' => $order->product_list]; + $values = [ + 'tax_incl' => $cart_rule['obj']->getContextualValue(true, $this->context, CartRule::FILTER_ACTION_ALL_NOCAP, $package), + 'tax_excl' => $cart_rule['obj']->getContextualValue(false, $this->context, CartRule::FILTER_ACTION_ALL_NOCAP, $package), + ]; + + // If the reduction is not applicable to this order, then continue with the next one + if (!$values['tax_excl']) { + continue; + } + + // IF + // This is not multi-shipping + // The value of the voucher is greater than the total of the order + // Partial use is allowed + // This is an "amount" reduction, not a reduction in % or a gift + // THEN + // The voucher is cloned with a new value corresponding to the remainder + if (count($order_list) == 1 && $values['tax_incl'] > ($order->total_products_wt - $total_reduction_value_ti) && $cart_rule['obj']->partial_use == 1 && $cart_rule['obj']->reduction_amount > 0) { + // Create a new voucher from the original + $voucher = new CartRule((int)$cart_rule['obj']->id); // We need to instantiate the CartRule without lang parameter to allow saving it + unset($voucher->id); + + // Set a new voucher code + $voucher->code = empty($voucher->code) ? substr(md5($order->id . '-' . $order->id_customer . '-' . $cart_rule['obj']->id), 0, 16) : $voucher->code . '-2'; + if (preg_match('/\-([0-9]{1,2})\-([0-9]{1,2})$/', $voucher->code, $matches) && $matches[1] == $matches[2]) { + $voucher->code = preg_replace('/' . $matches[0] . '$/', '-' . (intval($matches[1]) + 1), $voucher->code); + } + + // Set the new voucher value + if ($voucher->reduction_tax) { + $voucher->reduction_amount = ($total_reduction_value_ti + $values['tax_incl']) - $order->total_products_wt; + + // Add total shipping amout only if reduction amount > total shipping + if ($voucher->free_shipping == 1 && $voucher->reduction_amount >= $order->total_shipping_tax_incl) { + $voucher->reduction_amount -= $order->total_shipping_tax_incl; + } + } else { + $voucher->reduction_amount = ($total_reduction_value_tex + $values['tax_excl']) - $order->total_products; + + // Add total shipping amout only if reduction amount > total shipping + if ($voucher->free_shipping == 1 && $voucher->reduction_amount >= $order->total_shipping_tax_excl) { + $voucher->reduction_amount -= $order->total_shipping_tax_excl; + } + } + if ($voucher->reduction_amount <= 0) { + continue; + } + + if ($this->context->customer->isGuest()) { + $voucher->id_customer = 0; + } else { + $voucher->id_customer = $order->id_customer; + } + + $voucher->quantity = 1; + $voucher->reduction_currency = $order->id_currency; + $voucher->quantity_per_user = 1; + if ($voucher->add()) { + // If the voucher has conditions, they are now copied to the new voucher + CartRule::copyConditions($cart_rule['obj']->id, $voucher->id); + $orderLanguage = new Language((int)$order->id_lang); + + $params = [ + '{voucher_amount}' => Tools::displayPrice($voucher->reduction_amount, $this->context->currency, false), + '{voucher_num}' => $voucher->code, + '{firstname}' => $this->context->customer->firstname, + '{lastname}' => $this->context->customer->lastname, + '{id_order}' => $order->reference, + '{order_name}' => $order->getUniqReference(), + ]; + Mail::Send( + (int)$order->id_lang, + 'voucher', + Context::getContext()->getTranslator()->trans( + 'New voucher for your order %s', + [$order->reference], + 'Emails.Subject', + $orderLanguage->locale + ), + $params, + $this->context->customer->email, + $this->context->customer->firstname . ' ' . $this->context->customer->lastname, + null, null, null, null, _PS_MAIL_DIR_, false, (int)$order->id_shop + ); + } + + $values['tax_incl'] = $order->total_products_wt - $total_reduction_value_ti; + $values['tax_excl'] = $order->total_products - $total_reduction_value_tex; + if (1 == $voucher->free_shipping) { + $values['tax_incl'] += $order->total_shipping_tax_incl; + $values['tax_excl'] += $order->total_shipping_tax_excl; + } + } + $total_reduction_value_ti += $values['tax_incl']; + $total_reduction_value_tex += $values['tax_excl']; + + $order->addCartRule($cart_rule['obj']->id, $cart_rule['obj']->name, $values, 0, $cart_rule['obj']->free_shipping); + + if ($orderStateId != Configuration::get('PS_OS_ERROR') && $orderStateId != Configuration::get('PS_OS_CANCELED') + && !in_array($cart_rule['obj']->id, $cart_rule_used)) { + $cart_rule_used[] = $cart_rule['obj']->id; + + // Create a new instance of Cart Rule without id_lang, in order to update its quantity + $cart_rule_to_update = new CartRule((int)$cart_rule['obj']->id); + $cart_rule_to_update->quantity = max(0, $cart_rule_to_update->quantity - 1); + $cart_rule_to_update->update(); + } + + $cart_rules_list[] = [ + 'voucher_name' => $cart_rule['obj']->name, + 'voucher_reduction' => ($values['tax_incl'] != 0.00 ? '-' : '') . Tools::displayPrice($values['tax_incl'], $this->context->currency, false), + ]; + } + + return $cart_rules_list; + } + + private function getFileAttachment($orderStatusId, Order $order) + { + $order_status = new OrderState((int) $orderStatusId, (int) $this->context->language->id); + + // Join PDF invoice + if ((int)Configuration::get('PS_INVOICE') && $order_status->invoice && $order->invoice_number) { + $order_invoice_list = $order->getInvoicesCollection(); + Hook::exec('actionPDFInvoiceRender', ['order_invoice_list' => $order_invoice_list]); + $pdf = new PDF($order_invoice_list, PDF::TEMPLATE_INVOICE, $this->context->smarty); + $fileAttachment['content'] = $pdf->render(false); + $fileAttachment['name'] = Configuration::get('PS_INVOICE_PREFIX', (int)$order->id_lang, null, $order->id_shop) . sprintf('%06d', $order->invoice_number) . '.pdf'; + $fileAttachment['mime'] = 'application/pdf'; + } else { + $fileAttachment = null; + } + + return $fileAttachment; + } + + private function getEmailTemplateContent($template_name, $mail_type, $var) + { + $email_configuration = Configuration::get('PS_MAIL_TYPE'); + if ($email_configuration != $mail_type && $email_configuration != Mail::TYPE_BOTH) { + return ''; + } + + $pathToFindEmail = [ + _PS_THEME_DIR_ . 'mails' . DIRECTORY_SEPARATOR . $this->context->language->iso_code . DIRECTORY_SEPARATOR . $template_name, + _PS_THEME_DIR_ . 'mails' . DIRECTORY_SEPARATOR . 'en' . DIRECTORY_SEPARATOR . $template_name, + _PS_MAIL_DIR_ . $this->context->language->iso_code . DIRECTORY_SEPARATOR . $template_name, + _PS_MAIL_DIR_ . 'en' . DIRECTORY_SEPARATOR . $template_name, + ]; + + foreach ($pathToFindEmail as $path) { + if (Tools::file_exists_cache($path)) { + $this->context->smarty->assign('list', $var); + + return $this->context->smarty->fetch($path); + } + } + + return ''; + } + + private function _getFormatedAddress(Address $the_address, $line_sep, $fields_style = []) + { + return AddressFormat::generateAddress($the_address, ['avoid' => []], $line_sep, ' ', $fields_style); + } +} \ No newline at end of file diff --git a/src/Service/MemorizeCartService.php b/src/Service/MemorizeCartService.php new file mode 100644 index 000000000..1cb4b38f5 --- /dev/null +++ b/src/Service/MemorizeCartService.php @@ -0,0 +1,24 @@ +orderCartAssociationService = $orderCartAssociationService; + } + + public function memorizeCart(Order $toBeProcessedOrder) + { + // create a pending cart so we can repeat the process once again + $this->orderCartAssociationService->createPendingCart($toBeProcessedOrder); + } +} \ No newline at end of file diff --git a/src/Service/MolliePaymentMailService.php b/src/Service/MolliePaymentMailService.php new file mode 100644 index 000000000..38214320d --- /dev/null +++ b/src/Service/MolliePaymentMailService.php @@ -0,0 +1,206 @@ +module = $module; + $this->paymentMethodRepository = $paymentMethodRepository; + $this->mailService = $mailService; + } + + public function sendSecondChanceMail($orderId) + { + $order = new Order($orderId); + $payment = $this->paymentMethodRepository->getPaymentBy('order_id', $orderId); + if (!$payment) { + return false; + } + + $response = [ + 'success' => false, + 'message' => $this->module->l('Failed to created second chance email!') + ]; + + $transactionId = $payment['transaction_id']; + + $customer = new Customer($order->id_customer); + + /** @var MollieApiClient $api */ + $api = $this->module->api; + if (TransactionUtility::isOrderTransaction($transactionId)) { + $response = $this->sendSecondChanceMailWithOrderAPI($api, $transactionId, $payment['method']); + } else { + $response = $this->sendSecondChanceMailWithPaymentApi($api, $transactionId); + } + + if ($response['success']) { + $this->mailService->sendSecondChanceMail($customer, $response['checkoutUrl'], $payment['method']); + } + + return $response; + } + + public function sendSecondChanceMailWithOrderAPI(MollieApiClient $api, $transactionId, $paymentMethod) + { + $orderApi = $api->orders->get($transactionId, ['embed' => 'payments']); + if ($orderApi->isPaid() || $orderApi->isAuthorized() || $orderApi->isShipping() || $orderApi->isCompleted()) { + return + [ + 'success' => false, + 'message' => $this->module->l('Failed to send second chance email! Order is already paid!') + ]; + } + + $molliePayments = $orderApi->payments(); + $checkoutUrl = $this->getCheckoutUrl($molliePayments); + + if (!$checkoutUrl) { + /** @var Payment $newPayment */ + $newPayment = $api->orders->get($transactionId)->createPayment( + [ + + ] + ); + $checkoutUrl = $newPayment->getCheckoutUrl(); + } + + return [ + 'success' => true, + 'message' => $this->module->l('Second chance email was successfully send!'), + 'checkoutUrl' => $checkoutUrl + ]; + } + + public function sendSecondChanceMailWithPaymentApi(MollieApiClient $api, $transactionId) + { + $context = Context::getContext(); + $paymentApi = $api->payments->get($transactionId); + + if ($paymentApi->isPaid() || $paymentApi->isAuthorized()) { + return + [ + 'success' => false, + 'message' => + $this->module->l('Failed to send second chance email! Order is already paid or expired!') + ]; + } + + if (null !== $paymentApi->getCheckoutUrl()) { + $checkoutUrl = $paymentApi->getCheckoutUrl(); + + return [ + 'success' => true, + 'message' => $this->module->l('Second chance email was successfully send!'), + 'checkoutUrl' => $checkoutUrl + ]; + } + + $cart = new Cart($paymentApi->metadata->cart_id); + $customer = new Customer($cart->id_customer); + $paymentData = [ + 'amount' => [ + 'value' => $paymentApi->amount->value, + 'currency' => $paymentApi->amount->currency, + ], + 'redirectUrl' => $context->link->getModuleLink( + 'mollie', + 'return', + [ + 'cart_id' => $paymentApi->metadata->cart_id, + 'utm_nooverride' => 1, + 'rand' => time(), + 'key' => $customer->secure_key, + 'customerId' => $customer->id + ], + true + + ), + 'description' => $paymentApi->description, + 'metadata' => [ + 'cart_id' => $paymentApi->metadata->cart_id, + 'order_reference' => $paymentApi->metadata->order_reference, + 'secure_key' => $paymentApi->metadata->secure_key + ], + ]; + + if (!EnvironmentUtility::isLocalEnvironment()) { + $paymentData['webhookUrl'] = $context->link->getModuleLink( + 'mollie', + 'webhook', + [], + true + ); + } + $newPayment = $api->payments->create($paymentData); + + if (isset($newPayment)) { + $updateTransactionId = $this->paymentMethodRepository->updateTransactionId($transactionId, $newPayment->id); + + if ($updateTransactionId) { + $checkoutUrl = $newPayment->getCheckoutUrl(); + return [ + 'success' => true, + 'message' => $this->module->l('Second chance email was successfully send!'), + 'checkoutUrl' => $checkoutUrl + ]; + } + } + + return + [ + 'success' => false, + 'message' => + $this->module->l('Failed to send second chance email!') + ]; + } + + private function getCheckoutUrl($molliePayments) + { + $checkoutUrl = ''; + /** @var Payment $molliePayment */ + foreach ($molliePayments as $molliePayment) { + if ($molliePayment->status === PaymentStatus::STATUS_OPEN || + $molliePayment->status === PaymentStatus::STATUS_PENDING + ) { + return $molliePayment->getCheckoutUrl(); + } + } + + return $checkoutUrl; + } +} \ No newline at end of file diff --git a/src/Service/OrderCartAssociationService.php b/src/Service/OrderCartAssociationService.php new file mode 100644 index 000000000..c897e2049 --- /dev/null +++ b/src/Service/OrderCartAssociationService.php @@ -0,0 +1,36 @@ +cartDuplication = $cartDuplication; + } + + /** + * @param Order $order + * + * @return bool + * + * @throws \PrestaShopDatabaseException + * @throws \PrestaShopException + */ + public function createPendingCart(Order $order) + { + // globally restores the cart. + $newCartId = $this->cartDuplication->restoreCart($order->id_cart); + + $pendingOrderCart = new MolPendingOrderCart(); + $pendingOrderCart->cart_id = $newCartId; + $pendingOrderCart->order_id = $order->id; + + return $pendingOrderCart->add(); + } +} \ No newline at end of file diff --git a/src/Service/OrderStatusService.php b/src/Service/OrderStatusService.php index d70a2475a..f94aacb16 100644 --- a/src/Service/OrderStatusService.php +++ b/src/Service/OrderStatusService.php @@ -35,9 +35,11 @@ namespace Mollie\Service; +use _PhpScoper5eddef0da618a\Mollie\Api\Types\PaymentStatus; use Configuration; use Context; use Mollie\Config\Config; +use Mollie\Utility\OrderStatusUtility; use Order; use OrderHistory; use OrderPayment; @@ -48,6 +50,16 @@ class OrderStatusService { + /** + * @var MailService + */ + private $mailService; + + public function __construct(MailService $mailService) + { + $this->mailService = $mailService; + } + /** * @param int $order * @param string|int $statusId @@ -96,9 +108,7 @@ public function setOrderStatus($order, $statusId, $useExistingPayment = null, $t if ((int)$order->current_state === (int)$statusId) { return; } - $history = array_map(function ($item) { - return (int)$item['id_order_state']; - }, $order->getHistory(Context::getContext()->language->id)); + if (!Validate::isLoadedObject($order) || !$status ) { @@ -122,6 +132,12 @@ public function setOrderStatus($order, $statusId, $useExistingPayment = null, $t } } + $status = OrderStatusUtility::transformPaymentStatusToPaid($status, Config::STATUS_PAID_ON_BACKORDER); + + if ($this->checkIfOrderConfNeedsToBeSend($statusId)) { + $this->mailService->sendOrderConfMail($order, $statusId); + } + if (Configuration::get('MOLLIE_MAIL_WHEN_' . Tools::strtoupper($status))) { $history->addWithemail(true, $templateVars); } else { @@ -129,4 +145,9 @@ public function setOrderStatus($order, $statusId, $useExistingPayment = null, $t } } + private function checkIfOrderConfNeedsToBeSend($statusId) + { + return ((int)$statusId === (int)Configuration::get(Config::MOLLIE_STATUS_PAID) && + (int)Configuration::get(Config::MOLLIE_SEND_ORDER_CONFIRMATION) === Config::ORDER_CONF_MAIL_SEND_ON_PAID); + } } \ No newline at end of file diff --git a/src/Service/PaymentMethodService.php b/src/Service/PaymentMethodService.php index cd1cc4561..099c15455 100644 --- a/src/Service/PaymentMethodService.php +++ b/src/Service/PaymentMethodService.php @@ -75,18 +75,25 @@ class PaymentMethodService * @var CartLinesService */ private $cartLinesService; + /** + * @var aymentsTranslationService + */ + private $paymentsTranslationService; + public function __construct( Mollie $module, PaymentMethodRepository $methodRepository, MethodCountryRepository $countryRepository, - CartLinesService $cartLinesService + CartLinesService $cartLinesService, + PaymentsTranslationService $paymentsTranslationService ) { $this->module = $module; $this->methodRepository = $methodRepository; $this->countryRepository = $countryRepository; $this->cartLinesService = $cartLinesService; + $this->paymentsTranslationService = $paymentsTranslationService; } public function savePaymentMethod($method) @@ -141,6 +148,7 @@ public function getMethodsForCheckout() } $countryCode = Tools::strtolower($context->country->iso_code); $unavailableMethods = []; + foreach (Mollie\Config\Config::$defaultMethodAvailability as $methodName => $countries) { if (!in_array($methodName, ['klarnapaylater', 'klarnasliceit']) || empty($countries) @@ -173,14 +181,20 @@ public function getMethodsForCheckout() if (version_compare(_PS_VERSION_, '1.6.0.9', '>')) { foreach ($methods as $index => $methodId) { $methodObj = new MolPaymentMethod($methodId['id_payment_method']); - if (!$methodObj->is_countries_applicable) { + if ($methodObj->is_countries_applicable) { if (!$this->countryRepository->checkIfMethodIsAvailableInCountry($methodObj->id_method, $country = Country::getByIso($countryCode))) { unset($methods[$index]); } + } else { + if ($this->countryRepository->checkIfCountryIsExcluded($methodObj->id_method, $country = Country::getByIso($countryCode))) { + unset($methods[$index]); + } } } } + $methods = $this->paymentsTranslationService->getTranslatedPaymentMethods($methods); + foreach ($methods as $key => $method) { $image = json_decode($method['images_json'], true); $methods[$key]['image'] = $image; @@ -245,14 +259,17 @@ public function getPaymentData( : $context->link->getModuleLink( 'mollie', 'return', - ['cart_id' => $cartId, 'utm_nooverride' => 1, 'rand' => time()], + [ + 'cart_id' => $cartId, + 'utm_nooverride' => 1, + 'rand' => time(), + 'key' => $secureKey, + 'customerId' => $customer->id + ], true ) ), ]; - if ($cardToken) { - $paymentData['cardToken'] = $cardToken; - } if (!EnvironmentUtility::isLocalEnvironment()) { $paymentData['webhookUrl'] = $context->link->getModuleLink( 'mollie', @@ -282,7 +299,7 @@ public function getPaymentData( } } - if ($molPaymentMethod->method === Mollie\Config\Config::MOLLIE_PAYMENTS_API) { + if ($molPaymentMethod->method !== Mollie\Config\Config::MOLLIE_ORDERS_API) { $paymentData['description'] = str_ireplace( ['%'], [$cartId], @@ -317,6 +334,10 @@ public function getPaymentData( } } + if ($cardToken) { + $paymentData['cardToken'] = $cardToken; + } + switch ($method) { case PaymentMethod::BANKTRANSFER: $paymentData['billingEmail'] = $customer->email; @@ -357,6 +378,9 @@ public function getPaymentData( $paymentData['lines'] = $this->cartLinesService->getCartLines($amount, $paymentFee, $cart); $paymentData['payment'] = []; + if ($cardToken) { + $paymentData['payment']['cardToken'] = $cardToken; + } if (!EnvironmentUtility::isLocalEnvironment()) { $paymentData['payment']['webhookUrl'] = $context->link->getModuleLink( 'mollie', diff --git a/src/Service/PaymentReturnService.php b/src/Service/PaymentReturnService.php new file mode 100644 index 000000000..945af7a10 --- /dev/null +++ b/src/Service/PaymentReturnService.php @@ -0,0 +1,210 @@ + + * @copyright Mollie B.V. + * @license Berkeley Software Distribution License (BSD-License 2) http://www.opensource.org/licenses/bsd-license.php + * @category Mollie + * @package Mollie + * @link https://www.mollie.nl + * @codingStandardsIgnoreStart + */ + +namespace Mollie\Service; + +use Cart; +use Context; +use Mollie; +use Mollie\Config\Config; +use Mollie\Repository\PaymentMethodRepository; +use Mollie\Utility\OrderStatusUtility; +use Order; +use OrderDetail; +use OrderPayment; + +class PaymentReturnService +{ + const PENDING = 1; + const DONE = 2; + + /** + * @var Mollie + */ + private $module; + + /** + * @var Context + */ + private $context; + + /** + * @var CartDuplicationService + */ + private $cartDuplicationService; + + /** + * @var PaymentMethodRepository + */ + private $paymentMethodRepository; + + /** + * @var RepeatOrderLinkFactory + */ + private $orderLinkFactory; + + + public function __construct( + Mollie $module, + CartDuplicationService $cartDuplicationService, + PaymentMethodRepository $paymentMethodRepository, + RepeatOrderLinkFactory $orderLinkFactory + ) { + $this->module = $module; + $this->context = Context::getContext(); + $this->cartDuplicationService = $cartDuplicationService; + $this->paymentMethodRepository = $paymentMethodRepository; + $this->orderLinkFactory = $orderLinkFactory; + } + + public function handlePendingStatus(Order $order, $transaction, $orderStatus, $paymentMethod, $stockManagement) + { + $cart = new Cart($order->id_cart); + $status = static::PENDING; + $orderDetails = $order->getOrderDetailList(); + /** @var OrderDetail $detail */ + foreach ($orderDetails as $detail) { + $orderDetail = new OrderDetail($detail['id_order_detail']); + if ( + $stockManagement && + ($orderDetail->getStockState() || $orderDetail->product_quantity_in_stock < 0) + ) { + $orderStatus = Config::STATUS_PENDING_ON_BACKORDER; + break; + } + } + + $this->updateTransactions($transaction->id, $order->id, $orderStatus, $paymentMethod); + + return $this->getStatusResponse($transaction, $status, $cart->id, $cart->secure_key); + } + + public function handlePaidStatus(Order $order, $transaction, $paymentMethod, $stockManagement) + { + $cart = new Cart($order->id_cart); + $status = static::DONE; + $orderStatus = OrderStatusUtility::transformPaymentStatusToRefunded($transaction); + $orderDetails = $order->getOrderDetailList(); + /** @var OrderDetail $detail */ + foreach ($orderDetails as $detail) { + $orderDetail = new OrderDetail($detail['id_order_detail']); + if ( + $stockManagement && + ($orderDetail->getStockState() || $orderDetail->product_quantity_in_stock < 0) + ) { + $orderStatus = Mollie\Config\Config::STATUS_PAID_ON_BACKORDER; + break; + } + } + $this->updateTransactions($transaction->id, $order->id, $orderStatus, $paymentMethod); + + return $this->getStatusResponse($transaction, $status, $cart->id, $cart->secure_key); + } + + public function handleFailedStatus(Order $order, $transaction, $orderStatus, $paymentMethod) + { + if(null !== $paymentMethod) { + + $this->cartDuplicationService->restoreCart($order->id_cart); + + $warning[] = $this->module->l('Your payment was not successful, please try again.'); + + $this->context->cookie->mollie_payment_canceled_error = + json_encode($warning); + + $this->updateTransactions($transaction->id, $order->id, $orderStatus, $paymentMethod); + } + + $orderLink = $this->orderLinkFactory->getLink(); + + return [ + 'success' => true, + 'status' => static::DONE, + 'response' => json_encode($transaction), + 'href' => $orderLink + ]; + } + + private function getStatusResponse($transaction, $status, $cartId, $cartSecureKey) + { + $successUrl = $this->context->link->getPageLink( + 'order-confirmation', + true, + null, + [ + 'id_cart' => (int)$cartId, + 'id_module' => (int)$this->module->id, + 'id_order' => (int)version_compare(_PS_VERSION_, '1.7.1.0', '>=') + ? Order::getIdByCartId((int)$cartId) + : Order::getOrderByCartId((int)$cartId), + 'key' => $cartSecureKey, + ] + ); + + return [ + 'success' => true, + 'status' => $status, + 'response' => json_encode($transaction), + 'href' => $successUrl + ]; + } + + private function updateTransactions($transactionId, $orderId, $orderStatus, $paymentMethod) + { + /** @var OrderStatusService $orderStatusService */ + $orderStatusService = $this->module->getContainer(OrderStatusService::class); + + $orderStatusId = (int)Mollie\Config\Config::getStatuses()[$orderStatus]; + $this->paymentMethodRepository->savePaymentStatus($transactionId, $orderStatus, $orderId, $paymentMethod); + + $order = new Order($orderId); + $order->payment = $paymentMethod; + $order->update(); + + $transactionInfo = [ + 'transactionId' => $transactionId, + 'paymentMethod' => $paymentMethod, + ]; + $orderStatusService->setOrderStatus($orderId, $orderStatusId, null, [], $transactionInfo); + + $orderPayments = OrderPayment::getByOrderId($orderId); + /** @var OrderPayment $orderPayment */ + foreach ($orderPayments as $orderPayment) { + $orderPayment->transaction_id = $transactionId; + $orderPayment->payment_method = $paymentMethod; + $orderPayment->update(); + } + } +} diff --git a/src/Service/PaymentsTranslationService.php b/src/Service/PaymentsTranslationService.php new file mode 100644 index 000000000..eed139065 --- /dev/null +++ b/src/Service/PaymentsTranslationService.php @@ -0,0 +1,67 @@ + + * @copyright Mollie B.V. + * @license Berkeley Software Distribution License (BSD-License 2) http://www.opensource.org/licenses/bsd-license.php + * @category Mollie + * @package Mollie + * @link https://www.mollie.nl + * @codingStandardsIgnoreStart + */ + +namespace Mollie\Service; + +use Mollie; + +class PaymentsTranslationService +{ + /** + * @var Mollie + */ + private $module; + /** + * @var LanguageService + */ + private $languageService; + + public function __construct( + Mollie $module, + LanguageService $languageService + ) { + + $this->module = $module; + $this->languageService = $languageService; + } + public function getTranslatedPaymentMethods($paymentMethods) { + + foreach ($paymentMethods as $method) { + $method['method_name'] = $this->languageService->lang($method['method_name']); + } + + return $paymentMethods; + } +} \ No newline at end of file diff --git a/src/Service/RefundService.php b/src/Service/RefundService.php index d1e406f84..f65ac9829 100644 --- a/src/Service/RefundService.php +++ b/src/Service/RefundService.php @@ -49,6 +49,8 @@ class RefundService { + const FILE_NAME = 'RefundService'; + /** * @var Mollie */ @@ -95,8 +97,8 @@ public function doPaymentRefund($transactionId, $amount = null) } catch (ApiException $e) { return [ 'status' => 'fail', - 'msg_fail' => $this->module->l('The order could not be refunded!'), - 'msg_details' => $this->module->l('Reason:') . ' ' . $e->getMessage(), + 'msg_fail' => $this->module->l('The order could not be refunded!', self::FILE_NAME), + 'msg_details' => $this->module->l('Reason:', self::FILE_NAME) . ' ' . $e->getMessage(), ]; } @@ -113,8 +115,8 @@ public function doPaymentRefund($transactionId, $amount = null) return [ 'status' => 'success', - 'msg_success' => $this->module->l('The order has been refunded!'), - 'msg_details' => $this->module->l('Mollie B.V. will transfer the money back to the customer on the next business day.'), + 'msg_success' => $this->module->l('The order has been refunded!', self::FILE_NAME), + 'msg_details' => $this->module->l('Mollie B.V. will transfer the money back to the customer on the next business day.', self::FILE_NAME), ]; } @@ -160,7 +162,7 @@ public function doRefundOrderLines($transactionId, $lines = []) } catch (ApiException $e) { return [ 'success' => false, - 'message' => $this->module->l('The product(s) could not be refunded!'), + 'message' => $this->module->l('The product(s) could not be refunded!', self::FILE_NAME), 'detailed' => $e->getMessage(), ]; } diff --git a/src/Service/RepeatOrderLinkFactory.php b/src/Service/RepeatOrderLinkFactory.php new file mode 100644 index 000000000..cc73ba2f6 --- /dev/null +++ b/src/Service/RepeatOrderLinkFactory.php @@ -0,0 +1,30 @@ +link->getPageLink( + 'order', + true, + null + ); + } + + return $globalContext->link->getPageLink( + 'cart', + null, + $globalContext->language->id, + [ + 'action' => 'show', + ] + ); + } +} \ No newline at end of file diff --git a/src/Service/RestorePendingCartService.php b/src/Service/RestorePendingCartService.php new file mode 100644 index 000000000..2c7678db5 --- /dev/null +++ b/src/Service/RestorePendingCartService.php @@ -0,0 +1,38 @@ +repository = $repository; + } + + public function restore(Order $order) + { + /** @var MolPendingOrderCart|null $pendingOrder */ + $pendingOrder = $this->repository->findOneBy([ + 'order_id' => $order->id, + ]); + + if (!$pendingOrder) { + return; + } + + $cart = new Cart($pendingOrder->cart_id); + + $context = Context::getContext(); + $context->cookie->id_cart = $cart->id; + $context->cart = $cart; + $context->cookie->write(); + } +} \ No newline at end of file diff --git a/src/Service/SettingsSaveService.php b/src/Service/SettingsSaveService.php index 7dcea52e0..f5a39aa71 100644 --- a/src/Service/SettingsSaveService.php +++ b/src/Service/SettingsSaveService.php @@ -144,7 +144,11 @@ public function saveSettings(&$errors = []) } $countries = Tools::getValue(Config::MOLLIE_METHOD_CERTAIN_COUNTRIES . $method['id']); + $excludedCountries = Tools::getValue( + Config::MOLLIE_METHOD_EXCLUDE_CERTAIN_COUNTRIES . $method['id'] + ); $this->countryRepository->updatePaymentMethodCountries($method['id'], $countries); + $this->countryRepository->updatePaymentMethodExcludedCountries($method['id'], $excludedCountries); } } diff --git a/src/Service/TransactionService.php b/src/Service/TransactionService.php new file mode 100644 index 000000000..8f51a6a66 --- /dev/null +++ b/src/Service/TransactionService.php @@ -0,0 +1,303 @@ +module = $module; + $this->paymentMethodRepository = $paymentMethodRepository; + $this->orderStatusService = $orderStatusService; + $this->country = Context::getContext()->country; + } + + /** + * @param MolliePaymentAlias|MollieOrderAlias $transaction + * + * @return string|MolliePaymentAlias Returns a single payment (in case of Orders API it returns the highest prio Payment object) or status string + * + * @throws PrestaShopDatabaseException + * @throws PrestaShopException + * @throws CoreException + * @throws ApiException + * @since 3.3.0 + * @since 3.3.2 Returns the ApiPayment / ApiOrder instead of OK string, NOT OK/NO ID stays the same + * @since 3.3.2 Returns the ApiPayment instead of ApiPayment / ApiOrder + */ + public function processTransaction($transaction) + { + if (empty($transaction)) { + if (Configuration::get(Mollie\Config\Config::MOLLIE_DEBUG_LOG) >= Mollie\Config\Config::DEBUG_LOG_ERRORS) { + PrestaShopLogger::addLog(__METHOD__ . ' said: Received webhook request without proper transaction ID.', Mollie\Config\Config::WARNING); + } + + return $this->module->l('Transaction failed', 'webhook'); + } + + // Ensure that we are dealing with a Payment object, in case of transaction ID or Payment object w/ Order ID, convert + if ($transaction instanceof MolliePaymentAlias) { + if (!empty($transaction->orderId) && TransactionUtility::isOrderTransaction($transaction->orderId)) { + // Part of order + $transaction = $this->module->api->orders->get($transaction->orderId, ['embed' => 'payments']); + } else { + // Single payment + $apiPayment = $transaction; + } + } + + if (!isset($apiPayment)) { + $apiPayments = []; + /** @var MollieOrderAlias $transaction */ + foreach ($transaction->_embedded->payments as $embeddedPayment) { + $apiPayment = ResourceFactory::createFromApiResult($embeddedPayment, new Payment($this->module->api)); + $apiPayments[] = $apiPayment; + unset($apiPayment); + } + if (count($apiPayments) === 1) { + $apiPayment = $apiPayments[0]; + } else { + // In case of multiple payments, the one with the paid status is leading + foreach ($apiPayments as $payment) { + if (in_array($payment->status, [PaymentStatus::STATUS_PAID, PaymentStatus::STATUS_AUTHORIZED])) { + $apiPayment = $payment; + break; + } + } + + // No paid/authorized payments found, looking for payments with a final status + if (!isset($apiPayment)) { + foreach ($apiPayments as $payment) { + if (in_array($payment->status, [ + PaymentStatus::STATUS_CANCELED, + PaymentStatus::STATUS_FAILED, + PaymentStatus::STATUS_EXPIRED, + ])) { + $apiPayment = $payment; + break; + } + } + } + + // In case there is no final payments, we are going to look for any pending payments + if (!isset($apiPayment)) { + foreach ($apiPayments as $payment) { + if (in_array($payment->status, [ + PaymentStatus::STATUS_PENDING, + ])) { + $apiPayment = $payment; + break; + } + } + } + } + if (isset($apiPayment)) { + $apiPayment->metadata = $transaction->metadata; + } + } + + if (!isset($apiPayment)) { + return $this->module->l('Transaction failed', 'webhook'); + } + + $psPayment = $this->paymentMethodRepository->getPaymentBy('transaction_id', $transaction->id); + $this->setCountryContextIfNotSet($apiPayment); + $orderId = Order::getOrderByCartId((int)$apiPayment->metadata->cart_id); + $cart = new Cart($apiPayment->metadata->cart_id); + + Db::getInstance()->update( + 'mollie_payments', + [ + 'updated_at' => ['type' => 'sql', 'value' => 'NOW()'], + 'bank_status' => pSQL(\Mollie\Config\Config::getStatuses()[$apiPayment->status]), + 'order_id' => (int)$orderId, + ], + '`transaction_id` = \'' . pSQL($transaction->id) . '\'' + ); + + if ($apiPayment->metadata->cart_id) { + if ($apiPayment->hasRefunds() || $apiPayment->hasChargebacks()) { + if (isset($apiPayment->settlementAmount->value, $apiPayment->amountRefunded->value) + && (float)$apiPayment->amountRefunded->value >= (float)$apiPayment->settlementAmount->value + ) { + $this->orderStatusService->setOrderStatus($orderId, RefundStatus::STATUS_REFUNDED); + } else { + $this->orderStatusService->setOrderStatus($orderId, Mollie\Config\Config::PARTIAL_REFUND_CODE); + } + } elseif ($psPayment['method'] === PaymentMethod::BANKTRANSFER + && $psPayment['bank_status'] === PaymentStatus::STATUS_OPEN + && $apiPayment->status === PaymentStatus::STATUS_PAID + ) { + $order = new Order($orderId); + $order->payment = isset(Mollie\Config\Config::$methods[$apiPayment->method]) + ? Mollie\Config\Config::$methods[$apiPayment->method] + : $this->module->displayName; + $order->update(); + + $this->orderStatusService->setOrderStatus($orderId, $apiPayment->status); + } elseif ($psPayment['method'] !== PaymentMethod::BANKTRANSFER + && ($apiPayment->isPaid() || $apiPayment->isAuthorized() || $apiPayment->isExpired()) + && Tools::encrypt($cart->secure_key) === $apiPayment->metadata->secure_key + ) { + $paymentStatus = (int)Mollie\Config\Config::getStatuses()[$apiPayment->status]; + + $this->orderStatusService->setOrderStatus($orderId, $paymentStatus); + + $orderId = Order::getOrderByCartId((int)$apiPayment->metadata->cart_id); + } + } + + // Store status in database + + $this->saveOrderTransactionData($apiPayment->id, $apiPayment->method, $orderId); + + if (!$this->savePaymentStatus($transaction->id, $apiPayment->status, $orderId)) { + if (Configuration::get(Mollie\Config\Config::MOLLIE_DEBUG_LOG) >= Mollie\Config\Config::DEBUG_LOG_ERRORS) { + PrestaShopLogger::addLog(__METHOD__ . ' said: Could not save Mollie payment status for transaction "' . $transaction->id . '". Reason: ' . Db::getInstance()->getMsgError(), Mollie\Config\Config::WARNING); + } + } + + // Log successful webhook requests in extended log mode only + if (Configuration::get(Mollie\Config\Config::MOLLIE_DEBUG_LOG) == Mollie\Config\Config::DEBUG_LOG_ALL) { + PrestaShopLogger::addLog(__METHOD__ . ' said: Received webhook request for order ' . (int)$orderId . ' / transaction ' . $transaction->id, Mollie\Config\Config::NOTICE); + } + + return $apiPayment; + } + + /** + * Retrieves the OrderPayment object, created at validateOrder. And add transaction data. + * + * @param string $molliePaymentId + * @param string $molliePaymentMethod + * @param int $orderId + * + * @return void + * @throws PrestaShopDatabaseException + * @throws PrestaShopException + */ + protected function saveOrderTransactionData($molliePaymentId, $molliePaymentMethod, $orderId) + { + // retrieve ALL payments of order. + // in the case of a cancel or expired on banktransfer, this will fire too. + // if no OrderPayment objects is retrieved in the collection, do nothing. + $order = new Order((int)$orderId); + $collection = OrderPayment::getByOrderReference($order->reference); + if (count($collection) > 0) { + /** @var OrderPayment $orderPayment */ + $orderPayment = $collection[0]; + + // for older versions (1.5) , we check if it hasn't been filled yet. + if (!$orderPayment->transaction_id) { + $orderPayment->transaction_id = $molliePaymentId; + $orderPayment->payment_method = $molliePaymentMethod; + $orderPayment->update(); + } + } + } + + /** + * @param string $transactionId + * @param int $status + * @param int $orderId + * + * @return bool + * @throws PrestaShopDatabaseException + * @throws PrestaShopException + */ + protected function savePaymentStatus($transactionId, $status, $orderId) + { + try { + return Db::getInstance()->update( + 'mollie_payments', + [ + 'updated_at' => ['type' => 'sql', 'value' => 'NOW()'], + 'bank_status' => pSQL($status), + 'order_id' => (int)$orderId, + ], + '`transaction_id` = \'' . pSQL($transactionId) . '\'' + ); + } catch (PrestaShopDatabaseException $e) { + /** @var PaymentMethodRepository $paymentMethodRepo */ + $paymentMethodRepo = $this->module->getContainer(PaymentMethodRepository::class); + $paymentMethodRepo->tryAddOrderReferenceColumn(); + throw $e; + } + } + + /** + * (Re)sets the controller country context. + * When Prestashop receives a call from Mollie (without context) + * Prestashop always has default context to fall back on, so context->country + * is allways Set before executing any controller methods + * + * @param MolliePaymentAlias $payment + * + * @throws Adapter_Exception + * @throws PrestaShopDatabaseException + * @throws PrestaShopException + * @throws CoreException + */ + protected function setCountryContextIfNotSet($payment) + { + if (empty($this->country) || !$this->country->active) { + if ($payment->metadata->cart_id) { + $cart = new Cart((int)$payment->metadata->cart_id); + if (!empty($cart)) { + $address = new Address($cart->id_address_delivery); + if (!empty($address)) { + $country = new Country($address->id_country); + if (!empty($country)) { + $this->country = $country; + } + } + } + } + } + } +} \ No newline at end of file diff --git a/src/Service/UrlPathService.php b/src/Service/UrlPathService.php deleted file mode 100644 index 983fd5a1d..000000000 --- a/src/Service/UrlPathService.php +++ /dev/null @@ -1,113 +0,0 @@ - - * @copyright Mollie B.V. - * @license Berkeley Software Distribution License (BSD-License 2) http://www.opensource.org/licenses/bsd-license.php - * @category Mollie - * @package Mollie - * @link https://www.mollie.nl - * @codingStandardsIgnoreStart - */ - -namespace Mollie\Service; - -use Mollie; -use Tools; - -class UrlPathService -{ - - /** - * @var Mollie - */ - private $module; - - public function __construct(Mollie $module) - { - $this->module = $module; - } - - /** - * Retrieve recursively all classes in a directory and its subdirectories - * - * @param string $path Relative path from root to the directory - * - * @return array - * - * @since 3.3.0 - */ - public function getClassesFromDir($path) - { - $classes = []; - $rootDir = $this->normalizeDirectory(_PS_ROOT_DIR_); - - foreach (scandir($rootDir . $path) as $file) { - if ($file[0] != '.') { - if (is_dir($rootDir . $path . $file)) { - $classes = array_merge($classes, $this->getClassesFromDir($path . $file . '/')); - } elseif (Tools::substr($file, -4) == '.php') { - $content = Tools::file_get_contents($rootDir . $path . $file); - - $namespacePattern = '[\\a-z0-9_]*[\\]'; - $pattern = '#\W((abstract\s+)?class|interface)\s+(?P' . $this->module->display($this->module->getPathUri(), 'views/templates/front/classname.tpl') . basename($file, '.php') . '(?:Core)?)' . '(?:\s+extends\s+' . $namespacePattern . '[a-z][a-z0-9_]*)?(?:\s+implements\s+' . $namespacePattern . '[a-z][\\a-z0-9_]*(?:\s*,\s*' . $namespacePattern . '[a-z][\\a-z0-9_]*)*)?\s*\{#i'; - - if (preg_match($pattern, $content, $m)) { - $classes[$m['classname']] = [ - 'path' => $path . $file, - 'type' => trim($m[1]), - 'override' => true, - ]; - - if (Tools::substr($m['classname'], -4) == 'Core') { - $classes[Tools::substr($m['classname'], 0, -4)] = [ - 'path' => '', - 'type' => $classes[$m['classname']]['type'], - 'override' => true, - ]; - } - } - } - } - } - - return $classes; - } - - /** - * Normalize directory - * - * @param string $directory - * - * @return string - * - * @since 3.3.0 - */ - private function normalizeDirectory($directory) - { - return rtrim($directory, '/\\') . DIRECTORY_SEPARATOR; - } -} \ No newline at end of file diff --git a/src/Service/imageService.php b/src/Service/imageService.php new file mode 100644 index 000000000..b2090f1ad --- /dev/null +++ b/src/Service/imageService.php @@ -0,0 +1,45 @@ + + * @copyright Mollie B.V. + * @license Berkeley Software Distribution License (BSD-License 2) http://www.opensource.org/licenses/bsd-license.php + * @category Mollie + * @package Mollie + * @link https://www.mollie.nl + */ + +namespace Mollie\Service; + +class imageService +{ + public function createOrderStateLogo($orderStateId) + { + $source = _PS_MODULE_DIR_ . 'mollie/views/img/logo_small.png'; + $destination = _PS_ORDER_STATE_IMG_DIR_ . $orderStateId . '.gif'; + @copy($source, $destination); + } +} \ No newline at end of file diff --git a/src/Utility/ArrayUtility.php b/src/Utility/ArrayUtility.php new file mode 100644 index 000000000..89fc79495 --- /dev/null +++ b/src/Utility/ArrayUtility.php @@ -0,0 +1,44 @@ + + * @copyright Mollie B.V. + * @license Berkeley Software Distribution License (BSD-License 2) http://www.opensource.org/licenses/bsd-license.php + * @category Mollie + * @package Mollie + * @link https://www.mollie.nl + * @codingStandardsIgnoreStart + */ + +namespace Mollie\Utility; + +class ArrayUtility +{ + public static function getLastElement($array) + { + return end($array); + } +} diff --git a/src/Utility/ContextUtility.php b/src/Utility/ContextUtility.php new file mode 100644 index 000000000..22187a481 --- /dev/null +++ b/src/Utility/ContextUtility.php @@ -0,0 +1,24 @@ +customer = $customer; + $context->cookie->id_customer = (int) $customer->id; + $context->cookie->customer_lastname = $customer->lastname; + $context->cookie->customer_firstname = $customer->firstname; + $context->cookie->logged = 1; + $context->cookie->check_cgv = 1; + $context->cookie->is_guest = $customer->isGuest(); + $context->cookie->passwd = $customer->passwd; + $context->cookie->email = $customer->email; + + return $context; + } +} \ No newline at end of file diff --git a/src/Utility/MollieStatusUtility.php b/src/Utility/MollieStatusUtility.php new file mode 100644 index 000000000..9a5976d6e --- /dev/null +++ b/src/Utility/MollieStatusUtility.php @@ -0,0 +1,20 @@ + + * @copyright Mollie B.V. + * @license Berkeley Software Distribution License (BSD-License 2) http://www.opensource.org/licenses/bsd-license.php + * @category Mollie + * @package Mollie + * @link https://www.mollie.nl + * @codingStandardsIgnoreStart + */ + +namespace Mollie\Utility; + +use Language; + +class MultiLangUtility +{ + public static function createMultiLangField($field, $languageIds = null) + { + $result = []; + + if (!$languageIds) { + $languageIds = Language::getIDs(false); + } + + foreach ($languageIds as $languageId) { + $result[$languageId] = $field; + } + + return $result; + } +} \ No newline at end of file diff --git a/src/Utility/NumberUtility.php b/src/Utility/NumberUtility.php new file mode 100644 index 000000000..7cc3ac98d --- /dev/null +++ b/src/Utility/NumberUtility.php @@ -0,0 +1,158 @@ + + * @copyright Mollie B.V. + * @license Berkeley Software Distribution License (BSD-License 2) http://www.opensource.org/licenses/bsd-license.php + * @category Mollie + * @package Mollie + * @link https://www.mollie.nl + * @codingStandardsIgnoreStart + */ + +namespace Mollie\Utility; + +use _PhpScoper5eddef0da618a\PrestaShop\Decimal\Number; + +class NumberUtility +{ + /** + * Decreases number by its given percentage + * E.g 75/1.5 = 50 + * + * @param float $number + * @param float $percentage + * + * @return float + */ + public static function decreaseByPercentage($number, $percentage) + { + if (!$percentage || $percentage <= 0) { + return $number; + } + $numberTransformed = self::toObject($number); + $totalDecrease = self::toPercentageIncrease($percentage); + $decrement = (string)$numberTransformed->dividedBy(self::toObject($totalDecrease)); + + return (float)$decrement; + } + + public static function increaseByPercentage($number, $percentage) + { + if (!$percentage || $percentage <= 0) { + return $number; + } + $numberTransformed = self::toObject($number); + $percentageIncrease = self::toPercentageIncrease($percentage); + $percentageIncreaseTransformed = self::toObject($percentageIncrease); + $result = (string)$numberTransformed->times($percentageIncreaseTransformed); + + return (float)$result; + } + + /** + * E.g 21% will become 1.21 + * + * @param float $percentage + * + * @return float + */ + public static function toPercentageIncrease($percentage) + { + $percentageNumber = self::toObject($percentage); + $smallerNumber = $percentageNumber->dividedBy(self::toObject(100)); + $result = (string)$smallerNumber->plus(self::toObject(1)); + + return (float)$result; + } + + /** + * ($a/$b) + * + * @param float $a + * @param float $b + * + * @return float + */ + public static function divide($a, $b) + { + $firstNumber = self::toObject($a); + $secondNumber = self::toObject($b); + $result = (string)$firstNumber->dividedBy($secondNumber); + + return (float)$result; + } + + public static function isEqual($a, $b) + { + $firstNumber = self::toObject($a); + $secondNumber = self::toObject($b); + + return $firstNumber->equals($secondNumber); + } + + public static function isLowerThan($a, $b) + { + $firstNumber = self::toObject($a); + $secondNumber = self::toObject($b); + + return $firstNumber->isLowerThan($secondNumber); + } + + public static function isLowerOrEqualThan($a, $b) + { + $firstNumber = self::toObject($a); + $secondNumber = self::toObject($b); + + return $firstNumber->isLowerOrEqualThan($secondNumber); + } + + public static function minus($a, $b) + { + $firstNumber = self::toObject($a); + $secondNumber = self::toObject($b); + + return (float)((string)$firstNumber->minus($secondNumber)); + } + + public static function plus($a, $b) + { + $firstNumber = self::toObject($a); + $secondNumber = self::toObject($b); + + return (float)((string)$firstNumber->plus($secondNumber)); + } + + /** + * @param float $number + * + * @return Number + */ + private static function toObject($number) + { + return new Number((string)$number); + } +} diff --git a/src/Utility/OrderStatusUtility.php b/src/Utility/OrderStatusUtility.php new file mode 100644 index 000000000..fb3ec36ec --- /dev/null +++ b/src/Utility/OrderStatusUtility.php @@ -0,0 +1,84 @@ + + * @copyright Mollie B.V. + * @license Berkeley Software Distribution License (BSD-License 2) http://www.opensource.org/licenses/bsd-license.php + * @category Mollie + * @package Mollie + * @link https://www.mollie.nl + * @codingStandardsIgnoreStart + */ + +namespace Mollie\Utility; + +use _PhpScoper5eddef0da618a\Mollie\Api\Resources\Order as MollieOrderAlias; +use _PhpScoper5eddef0da618a\Mollie\Api\Resources\Payment as MolliePaymentAlias; +use _PhpScoper5eddef0da618a\Mollie\Api\Types\PaymentStatus; +use _PhpScoper5eddef0da618a\Mollie\Api\Types\RefundStatus; +use Mollie\Config\Config; + + +class OrderStatusUtility +{ + /** + * @param string $status + * @param string $comparedStatus + * @return string + */ + public static function transformPaymentStatusToPaid($status, $comparedStatus) + { + if($status === $comparedStatus) { + return PaymentStatus::STATUS_PAID; + } + + return $status; + } + + /** + * @param MolliePaymentAlias|MollieOrderAlias $transaction + */ + public static function transformPaymentStatusToRefunded($transaction) + { + if ($transaction->amountRefunded === null || + $transaction->amountCaptured === null) { + return $transaction->status; + } + + $amountRefunded = $transaction->amountRefunded->value; + $amountPayed = $transaction->amountCaptured->value; + $isPartiallyRefunded = NumberUtility::isLowerThan($amountRefunded, $amountPayed); + $isFullyRefunded = NumberUtility::isEqual($amountRefunded, $amountPayed); + + if ($isPartiallyRefunded) { + return Config::PARTIAL_REFUND_CODE; + } elseif ($isFullyRefunded) { + return RefundStatus::STATUS_REFUNDED; + } + + return $transaction->status; + } +} diff --git a/src/Utility/PaymentMethodUtility.php b/src/Utility/PaymentMethodUtility.php new file mode 100644 index 000000000..8b0a46c5a --- /dev/null +++ b/src/Utility/PaymentMethodUtility.php @@ -0,0 +1,46 @@ + + * @copyright Mollie B.V. + * @license Berkeley Software Distribution License (BSD-License 2) http://www.opensource.org/licenses/bsd-license.php + * @category Mollie + * @package Mollie + * @link https://www.mollie.nl + * @codingStandardsIgnoreStart + */ + +namespace Mollie\Utility; + +use Mollie\Config\Config; + +class PaymentMethodUtility +{ + public static function getPaymentMethodName($method) + { + return array_key_exists($method, Config::$methods) ? Config::$methods[$method] : $method; + } +} diff --git a/src/Utility/TextGeneratorUtility.php b/src/Utility/TextGeneratorUtility.php index 06022777a..8a86885cd 100644 --- a/src/Utility/TextGeneratorUtility.php +++ b/src/Utility/TextGeneratorUtility.php @@ -53,7 +53,7 @@ class TextGeneratorUtility * @throws CoreException * @since 3.0.0 */ - public static function generateDescriptionFromCart($methodDescription, $cartId, $orderReference = '') + public static function generateDescriptionFromCart($methodDescription, $cartId, $orderReference) { if ($cartId instanceof Cart) { $cart = $cartId; @@ -83,7 +83,9 @@ public static function generateDescriptionFromCart($methodDescription, $cartId, $methodDescription ); - return $content; + $description = empty($content) ? $orderReference : $content; + + return $description; } } \ No newline at end of file diff --git a/src/Utility/TransactionUtility.php b/src/Utility/TransactionUtility.php new file mode 100644 index 000000000..e38b2a5fb --- /dev/null +++ b/src/Utility/TransactionUtility.php @@ -0,0 +1,13 @@ + + * @copyright Mollie B.V. + * @license Berkeley Software Distribution License (BSD-License 2) http://www.opensource.org/licenses/bsd-license.php + * @category Mollie + * @package Mollie + * @link https://www.mollie.nl + * @codingStandardsIgnoreStart + */ + +namespace Mollie\Validator; + +use Configuration; + +class OrderConfMailValidator +{ + public function validateOrderConfMailSend($orderConfSendStatus, $orderState) + { + switch (Configuration::get(\Mollie\Config\Config::MOLLIE_SEND_ORDER_CONFIRMATION)) { + case \Mollie\Config\Config::ORDER_CONF_MAIL_SEND_ON_CREATION: + return true; + case \Mollie\Config\Config::ORDER_CONF_MAIL_SEND_ON_PAID: + return (int)Configuration::get(\Mollie\Config\Config::MOLLIE_STATUS_PAID) === (int)$orderState; + case \Mollie\Config\Config::ORDER_CONF_MAIL_SEND_ON_NEVER: + return false; + } + } +} \ No newline at end of file diff --git a/translations/en.php b/translations/en.php index 3b960c429..13bc417b3 100644 --- a/translations/en.php +++ b/translations/en.php @@ -5,181 +5,24 @@ $_MODULE['<{mollie}prestashop>mollie_484f5a79672cebe198ebdde45a1d672f'] = 'Gift wrapping'; $_MODULE['<{mollie}prestashop>mollie_7c478959c218087ffc4ad5d96e7f66a6'] = 'Mollie'; $_MODULE['<{mollie}prestashop>mollie_95b3b272e06dcc3cc2aa62f6887aebc5'] = 'Mollie Payments'; -$_MODULE['<{mollie}prestashop>mollie_48dedcf43487615c84c1bbbf6fe76c76'] = 'Are you sure you want to uninstall the Mollie Payment Module?'; -$_MODULE['<{mollie}prestashop>mollie_ab552f085567bbe63872c10a3596cd27'] = 'Payment error:'; -$_MODULE['<{mollie}prestashop>mollie_e0010a0a1a3259ab5c06a19bad532851'] = 'Paid'; -$_MODULE['<{mollie}prestashop>mollie_a206428462686af481cb072b8db11784'] = 'Authorized'; -$_MODULE['<{mollie}prestashop>mollie_0e22fe7d45f8e5632a4abf369b24e29c'] = 'Canceled'; -$_MODULE['<{mollie}prestashop>mollie_24fe48030f7d3097d5882535b04c3fa8'] = 'Expired'; -$_MODULE['<{mollie}prestashop>mollie_cc61945cbbf46721a053467c395c666f'] = 'Refunded'; -$_MODULE['<{mollie}prestashop>mollie_7026e386bdad92fa6f232394f5b23ffb'] = 'Bankwire pending'; -$_MODULE['<{mollie}prestashop>mollie_9f004157e4c148dac71da3ae5906351f'] = 'Partially refunded'; -$_MODULE['<{mollie}prestashop>mollie_0eceeb45861f9585dd7a97a3e36f85c6'] = 'Created'; -$_MODULE['<{mollie}prestashop>mollie_e2b7dec8fa4b498156dfee6e4c84b156'] = 'This payment method is not available.'; -$_MODULE['<{mollie}prestashop>mollie_96d0e2862e0167af0c2fd0c99fe6bc5d'] = 'Click here to continue'; -$_MODULE['<{mollie}prestashop>mollie_540d9939d1ca9406e215ee3d78c76813'] = 'This payment method is only available for Euros.'; -$_MODULE['<{mollie}prestashop>mollie_1b2631119333d97dbda37f87e378b1e4'] = 'There was an error while processing your request:'; -$_MODULE['<{mollie}prestashop>mollie_11bab828edcc0a6d5c97cbf84d61e652'] = 'The order with this id does not exist.'; -$_MODULE['<{mollie}prestashop>mollie_b34487c5f391f47b893ee3c61f8f9ab7'] = 'We have not received a definite payment status. You will be notified as soon as we receive a confirmation of the bank/merchant.'; -$_MODULE['<{mollie}prestashop>mollie_57bc999b3b730ead92ac7243fe254a2a'] = 'Unfortunately your payment was expired.'; -$_MODULE['<{mollie}prestashop>mollie_3602f6d5ece34fdbd4ffe0a47f1a2ba4'] = 'Thank you. Your payment has been received.'; -$_MODULE['<{mollie}prestashop>mollie_7352a401b3c23b2966c68bc9ab97fa06'] = 'The transaction has an unexpected status.'; -$_MODULE['<{mollie}prestashop>mollie_3f83176ddc3f63a5a374a623840bfb09'] = 'You are not authorised to see this page.'; -$_MODULE['<{mollie}prestashop>mollie_300225ee958b6350abc51805dab83c24'] = 'Continue shopping'; -$_MODULE['<{mollie}prestashop>mollie_ebdf0f490b617d7efa3025d3625cec85'] = 'Welcome back'; -$_MODULE['<{mollie}prestashop>mollie_66dac2278292ff24611ef8a85bc94e0d'] = 'Select your bank:'; -$_MODULE['<{mollie}prestashop>mollie_e0aa021e21dddbd6d8cecec71e9cf564'] = 'OK'; -$_MODULE['<{mollie}prestashop>mollie_b98a07c82f0078a1de7fc219fae480fc'] = 'Different payment method'; -$_MODULE['<{mollie}prestashop>mollie_35895cff7df70dab18783453e2bd241f'] = 'Pay with %s'; -$_MODULE['<{mollie}prestashop>mollie_cc16e103e202a48009df202d9525f75f'] = 'Refund this order'; -$_MODULE['<{mollie}prestashop>mollie_c5222afef6530f674d3acf82ba2ce9dc'] = 'Mollie refund'; -$_MODULE['<{mollie}prestashop>mollie_7064b25aa8a732a3db7f230c9867be19'] = 'Refund order #%d through the Mollie API.'; -$_MODULE['<{mollie}prestashop>mollie_e12682d2e0650f2783b22d956d2b947a'] = 'The order has been refunded!'; -$_MODULE['<{mollie}prestashop>mollie_95be87afb2734075cf345b633e1384ec'] = 'Mollie B.V. will transfer the money back to the customer on the next business day.'; -$_MODULE['<{mollie}prestashop>mollie_9770d1f0124abb0d6a894cd79f8703b5'] = 'Awaiting Mollie payment'; -$_MODULE['<{mollie}prestashop>mollie_b9fc80a6e775cb75568db6b6538dacdd'] = 'Mollie partially refunded'; -$_MODULE['<{mollie}prestashop>mollie_f91ab041fe9d6057740394b8b7903a0f'] = 'iDEAL'; -$_MODULE['<{mollie}prestashop>mollie_b749cfb52bfd82c5b12aa49d6906b8e8'] = 'CartaSi'; -$_MODULE['<{mollie}prestashop>mollie_05bfde942b997737b56b9ab405450076'] = 'Cartes Bancaires'; -$_MODULE['<{mollie}prestashop>mollie_e7f9e382dc50889098cbe56f2554c77b'] = 'Credit card'; -$_MODULE['<{mollie}prestashop>mollie_870cf6bc77bbadd038aeb8d942c9cb18'] = 'Bancontact'; -$_MODULE['<{mollie}prestashop>mollie_8ad65eb3da9995173280649f090218c3'] = 'SOFORT Banking'; -$_MODULE['<{mollie}prestashop>mollie_c0db4214f9d7e1b846f4496e03e667d0'] = 'SEPA Direct Debit'; -$_MODULE['<{mollie}prestashop>mollie_5aa515ce21e8de1ed7416dce455a151f'] = 'Belfius Pay Button'; -$_MODULE['<{mollie}prestashop>mollie_d023ec040f79f1a9b2ac960b43785089'] = 'Bitcoin'; -$_MODULE['<{mollie}prestashop>mollie_4aa65ea89e54d83bb2604c2cc89babc3'] = 'PODIUM Cadeaukaart'; -$_MODULE['<{mollie}prestashop>mollie_58c25c4b8b4382e37d89e0ef6bd625cf'] = 'Gift cards'; -$_MODULE['<{mollie}prestashop>mollie_431647a0a8b2cd589c9fda9535f90d6d'] = 'Bank transfer'; -$_MODULE['<{mollie}prestashop>mollie_ad69e733ebae8d264bccaa38d68830e8'] = 'PayPal'; -$_MODULE['<{mollie}prestashop>mollie_6abcd8eecb4f2abb6ccfe36ef7046ba0'] = 'paysafecard'; -$_MODULE['<{mollie}prestashop>mollie_a81bd6195f1abce35fd03a870270100b'] = 'KBC/CBC Payment Button'; -$_MODULE['<{mollie}prestashop>mollie_8184c64d6318b4a42428ae21d0fe2311'] = 'ING Home\'Pay'; -$_MODULE['<{mollie}prestashop>mollie_31911306551723245e7417ae4520fecb'] = 'Giropay'; -$_MODULE['<{mollie}prestashop>mollie_ce8e4a184e2e534c09d6bf6ae773ca4a'] = 'eps'; -$_MODULE['<{mollie}prestashop>mollie_feb1a8ded87483b416427b8457bdc664'] = 'Pay later.'; -$_MODULE['<{mollie}prestashop>mollie_a388a6fba0d4b58c334cd9416071cdb1'] = 'Slice it.'; -$_MODULE['<{mollie}prestashop>mollie_d1913efa9eae51595a33f3c1410d465c'] = 'The method %s is overridden by module %s. This can cause interference with payments.'; $_MODULE['<{mollie}prestashop>mollie_6311eb29dee3acc14071e0b1efb173f5'] = '%s statuses:'; $_MODULE['<{mollie}prestashop>mollie_d438a1d96c6037fccad15d7594737a84'] = 'Visual settings:'; $_MODULE['<{mollie}prestashop>mollie_49259add73e416838e985a6bf4d0e571'] = 'Debug info:'; $_MODULE['<{mollie}prestashop>mollie_c9cc8cce247e49bae79f15173ce97354'] = 'Save'; -$_MODULE['<{mollie}prestashop>mollie_5bef23eb7efff2736c5583bda59e5eb7'] = 'Create your account'; -$_MODULE['<{mollie}prestashop>mollie_a77e6fc49f07414fc53336c8d879b73e'] = 'Do you already have an API Key? Then you can skip this step and proceed to entering your API key.'; -$_MODULE['<{mollie}prestashop>mollie_21aef67e8d12503aff027385d0a7867f'] = 'First and last name'; -$_MODULE['<{mollie}prestashop>mollie_b357b524e740bc85b9790a0712d84a30'] = 'Email address'; -$_MODULE['<{mollie}prestashop>mollie_e93c33bd1341ab74195430daeb63db13'] = 'Shop name'; -$_MODULE['<{mollie}prestashop>mollie_dd7bf230fde8d4836917806aff6a6b27'] = 'Address'; -$_MODULE['<{mollie}prestashop>mollie_8bcdc441379cbf584638b0589a3f9adb'] = 'Postcode'; -$_MODULE['<{mollie}prestashop>mollie_57d056ed0984166336b7879c2af3657f'] = 'City'; -$_MODULE['<{mollie}prestashop>mollie_59716c97497eb9694541f7c3d37b1a4d'] = 'Country'; -$_MODULE['<{mollie}prestashop>mollie_686e697538050e4664636337cc3b834f'] = 'Create'; -$_MODULE['<{mollie}prestashop>mollie_7c4e7ada50c8572336f872fced4d1852'] = 'Status for %s payments'; -$_MODULE['<{mollie}prestashop>mollie_bea3fae02ae079e541199ab6ffc2d1ad'] = '`%s` payments get status `%s`'; -$_MODULE['<{mollie}prestashop>mollie_b2a2c5a69af7c04c5adc48261fb5dc13'] = 'Send mails when %s'; -$_MODULE['<{mollie}prestashop>mollie_0511c3431d4e32ed4266fe943014d000'] = 'Send mails when transaction status becomes %s?'; -$_MODULE['<{mollie}prestashop>mollie_7aace0dc23ee8b74744bdf79c98a2605'] = 'Skip this status'; -$_MODULE['<{mollie}prestashop>mollie_dd59e3663865f3fab190e496260608e6'] = '`%s` payments do not get a status'; -$_MODULE['<{mollie}prestashop>mollie_824f2c1d1c2ee9aecac9977d9347a2ea'] = 'Mollie Settings'; -$_MODULE['<{mollie}prestashop>mollie_d876ff8da67c3731ae25d8335a4168b4'] = 'API Key'; -$_MODULE['<{mollie}prestashop>mollie_2ffbc7eb8899865f9e66682d34853195'] = 'You can find your API key in your [1]Mollie Profile[/1]; it starts with test or live.'; -$_MODULE['<{mollie}prestashop>mollie_b5a7adde1af5c87d7fd797b6245c2a39'] = 'Description'; -$_MODULE['<{mollie}prestashop>mollie_3150652ab7f22bafe11a78d4cc0de8be'] = 'Enter a description here. Note: Payment methods may have a character limit, best keep the description under 29 characters. You can use the following variables: %s'; -$_MODULE['<{mollie}prestashop>mollie_8d9143dfc520ed7d982d4e545c7bfc5e'] = 'Send locale for payment screen'; -$_MODULE['<{mollie}prestashop>mollie_1239c091f39c068882e924e34dd9cc40'] = 'Should the plugin send the current webshop [1]locale[/1] to Mollie. Mollie payment screens will be in the same language as your webshop. Mollie can also detect the language based on the user\'s browser language.'; -$_MODULE['<{mollie}prestashop>mollie_80a78bb16945665b6410ae8d93e81c69'] = 'Do not send locale using browser language'; -$_MODULE['<{mollie}prestashop>mollie_3ba67aae5d08d0fa90016685407926f1'] = 'Visual Settings'; -$_MODULE['<{mollie}prestashop>mollie_fff0d600f8a0b5e19e88bfb821dd1157'] = 'Images'; -$_MODULE['<{mollie}prestashop>mollie_f7a97136df639501521efb243047e0cf'] = 'Show big, normal or no payment method logos on checkout.'; -$_MODULE['<{mollie}prestashop>mollie_a88f05b6c963e145a45b58c47cd42a41'] = 'hide'; -$_MODULE['<{mollie}prestashop>mollie_fea087517c26fadd409bd4b9dc642555'] = 'normal'; -$_MODULE['<{mollie}prestashop>mollie_d861877da56b8b4ceb35c8cbfdf65bb4'] = 'big'; -$_MODULE['<{mollie}prestashop>mollie_85fb708f31f585607247f8b868efafcc'] = 'Issuer list'; -$_MODULE['<{mollie}prestashop>mollie_27db39558366d2a9001a0dec69eea4d6'] = 'Some payment methods (eg. iDEAL) have an issuer list. This setting specifies where it is shown.'; -$_MODULE['<{mollie}prestashop>mollie_85a692706bc7597b6e32bc1d354c138f'] = 'On click'; -$_MODULE['<{mollie}prestashop>mollie_ea9b19c6f2e9ef60b49778ab9396b293'] = 'Own page'; -$_MODULE['<{mollie}prestashop>mollie_d35acbb07d2841712a937d5748e9bdc2'] = 'Payment page'; -$_MODULE['<{mollie}prestashop>mollie_1547ffc579d657d13fd5fedf12cbfae5'] = 'CSS file'; -$_MODULE['<{mollie}prestashop>mollie_6602513644161421f58418528d93ac00'] = 'Leave empty for default stylesheet. Should include file path when set. Hint: You can use [1]{BASE}[/1], [1]{THEME}[/1], [1]{CSS}[/1], [1]{MOBILE}[/1], [1]{MOBILE_CSS}[/1] and [1]{OVERRIDE}[/1] for easy folder mapping.'; -$_MODULE['<{mollie}prestashop>mollie_0da8d9a75492046bea7f314521e07cae'] = 'Payment methods'; -$_MODULE['<{mollie}prestashop>mollie_75886bf821fc9c068a280fe52337c0f5'] = 'Enable or disable the payment methods. You can drag and drop to rearrange the payment methods.'; -$_MODULE['<{mollie}prestashop>mollie_c576349bf8ff5fba4e9369ab581a4074'] = 'Enable iDEAL QR'; -$_MODULE['<{mollie}prestashop>mollie_c0ae4417e7a196f3ccfadff68a5064ed'] = 'QR Codes are currently not supported by the Orders API. Our apologies for the inconvenience!'; -$_MODULE['<{mollie}prestashop>mollie_e17dc77fb83539b6a4687c6d515791e4'] = '%s statuses'; -$_MODULE['<{mollie}prestashop>mollie_4695fd3a22023ab53a2151f797975ff8'] = 'Disable this status'; -$_MODULE['<{mollie}prestashop>mollie_656f9fd6f874404599220855cfec17ef'] = 'Mollie API'; -$_MODULE['<{mollie}prestashop>mollie_a1532be2c9ef11d7dd5edd974fc6c98c'] = 'Select which Mollie API to use'; -$_MODULE['<{mollie}prestashop>mollie_9ce1d2272c82bfd7d501a6697df774c7'] = 'Should the plugin use the new Mollie Orders API? This enables payment methods such as Klarna Pay Later.'; -$_MODULE['<{mollie}prestashop>mollie_8c2b4949d892b39b236545951f10bbd4'] = 'Payments API'; -$_MODULE['<{mollie}prestashop>mollie_a8b0255f70ecc140a1b5134ae5217e51'] = 'Orders API'; -$_MODULE['<{mollie}prestashop>mollie_1191f888f0cc23f95aa77aacb094503b'] = 'Shipment information'; -$_MODULE['<{mollie}prestashop>mollie_d822c02513be98814bdd39f8bae598d1'] = 'Automatically ship when marked as `shipped`'; -$_MODULE['<{mollie}prestashop>mollie_0eccefc4c96190190add75fe0a74fcd4'] = 'Enabling this feature will automatically send shipment information when an order has been marked as `shipped`'; -$_MODULE['<{mollie}prestashop>mollie_c1df8a5243557cbd8a7a31c11aab3db4'] = 'Automatically ship when one of these statuses is reached'; -$_MODULE['<{mollie}prestashop>mollie_34bd91e87db36a10b9e847d6304be289'] = 'If an order reaches one of these statuses the module will automatically send shipment information'; -$_MODULE['<{mollie}prestashop>mollie_498f79c4c5bbde77f1bceb6c86fd0f6d'] = 'Show'; -$_MODULE['<{mollie}prestashop>mollie_62a5e490880a92eef74f167d9dc6dca0'] = 'Hide'; -$_MODULE['<{mollie}prestashop>mollie_dc6e4b439165a4e104c9f3cbfcfe2797'] = 'Debug level'; -$_MODULE['<{mollie}prestashop>mollie_500aa80d6aea3cc9701b566c5f92ed91'] = 'Display errors'; -$_MODULE['<{mollie}prestashop>mollie_37f9a8b20930c1dc7488b15872b6d36b'] = 'Enabling this feature will display error messages (if any) on the front page. Use for debug purposes only!'; -$_MODULE['<{mollie}prestashop>mollie_e5114c4b69585ba5883d456a74c1cd5d'] = 'Log level'; -$_MODULE['<{mollie}prestashop>mollie_921a6d905eef99631c9c73c317799e62'] = 'Recommended level: Errors. Set to Everything to monitor incoming webhook requests. [1]View logs.[/1]'; -$_MODULE['<{mollie}prestashop>mollie_f80a4ad87fee7c9fdc19b7769495fdb5'] = 'Nothing'; -$_MODULE['<{mollie}prestashop>mollie_5ef0c737746fae2ca90e66c39333f8f6'] = 'Errors'; -$_MODULE['<{mollie}prestashop>mollie_709468af25e91284821d1bdbfdded24c'] = 'Everything'; -$_MODULE['<{mollie}prestashop>mollie_1f93fc361bfc627c89a46e1b6c28df2b'] = 'The API key needs to start with test or live.'; -$_MODULE['<{mollie}prestashop>mollie_6ab1adfed768d989b47c908755fe677f'] = 'The configuration has been saved!'; -$_MODULE['<{mollie}prestashop>mollie_1f91cd9f380285bf1219c79ab7fcb75e'] = 'Warning: Could not retrieve update file from github.'; -$_MODULE['<{mollie}prestashop>mollie_ce1e483d5cd4b349af887fa3e198ea6b'] = 'No download package found for the latest release.'; $_MODULE['<{mollie}prestashop>mollie_048ef1905ff33382d7660a68a6199305'] = 'Warning: Could not retrieve update xml file from github.'; $_MODULE['<{mollie}prestashop>mollie_711d42f319398490b32cd0e2f0b8ccb8'] = 'Warning: Update xml file from github follows an unexpected format.'; +$_MODULE['<{mollie}prestashop>mollie_35895cff7df70dab18783453e2bd241f'] = 'Pay with %s'; +$_MODULE['<{mollie}prestashop>mollie_66dac2278292ff24611ef8a85bc94e0d'] = 'Select your bank:'; $_MODULE['<{mollie}prestashop>mollie_48fa0f621f79f451e58f200957da5b52'] = 'Choose your bank'; $_MODULE['<{mollie}prestashop>mollie_8953fb7da2299bf905b73720f611baa3'] = 'or pay by iDEAL QR'; $_MODULE['<{mollie}prestashop>mollie_961f2247a2070bedff9f9cd8d64e2650'] = 'Choose'; $_MODULE['<{mollie}prestashop>mollie_ea4788705e6873b424c65e91c2846b19'] = 'Cancel'; -$_MODULE['<{mollie}prestashop>mollie_ea9cf7e47ff33b2be14e6dd07cbcefc6'] = 'Shipping'; -$_MODULE['<{mollie}prestashop>mollie_f93bd0f858a6e5f70cb458c90a36172b'] = 'Unable to retieve info about the latest version'; -$_MODULE['<{mollie}prestashop>mollie_ced93781bca50eb4d970ea51abcc622c'] = 'You are already running the latest version!'; -$_MODULE['<{mollie}prestashop>mollie_43f3c5a157c67de9b6faf42b637f963a'] = 'Module archive could not be downloaded'; -$_MODULE['<{mollie}prestashop>mollie_31dd57eea0f762b4ea6f022b40acc390'] = 'There was an error while extracting the module file (file may be corrupted).'; -$_MODULE['<{mollie}prestashop>mollie_bad5a3ab102f6e9c27edd494d7e33323'] = 'Successfully created your new Mollie account. Please check your inbox for more information.'; -$_MODULE['<{mollie}prestashop>mollie_023b7ade110944180153fd73358fc50c'] = 'An unknown error occurred while trying to create your Mollie account'; -$_MODULE['<{mollie}prestashop>mollie_3a6e70059673992f825826f7cf89278d'] = 'The cart rule named \"%1s\" (ID %2s) used in this cart is not valid and has been withdrawn from cart'; -$_MODULE['<{mollie}prestashop>mollie_3a1048f8aa3a9f6b604fcf7982811752'] = 'Warning: the secure key is empty, check your payment account before validation'; -$_MODULE['<{mollie}prestashop>mollie_26beb437d3323bd4bfb0811b3e891315'] = '%d image(s)'; -$_MODULE['<{mollie}prestashop>mollie_0791970c961c09eb8caaa61aba6a3ca4'] = 'An error occurred while saving message'; -$_MODULE['<{mollie}prestashop>mollie_ed13b3693357ebed3751cb71cb639e65'] = 'No carrier'; -$_MODULE['<{mollie}prestashop>mollie_b08d3867be98e6fff3233cd40ab8134a'] = 'Order creation failed'; -$_MODULE['<{mollie}prestashop>mollie_43423b4056880b08f2c9aa50d8670531'] = 'Cart cannot be loaded or an order has already been placed using this cart'; -$_MODULE['<{mollie}prestashop>mollie_88183b946cc5f0e8c96b2e66e1c74a7e'] = 'Unknown'; +$_MODULE['<{mollie}prestashop>mollie_3602f6d5ece34fdbd4ffe0a47f1a2ba4'] = 'Thank you. Your payment has been received.'; $_MODULE['<{mollie}prestashop>mollie_32c86f9a2a28a9fa544b97024393633e'] = 'No payment methods found'; -$_MODULE['<{mollie}prestashop>mollie_301b87d33063f687d14004a9014f6759'] = 'You do not have permission to refund payments'; -$_MODULE['<{mollie}prestashop>mollie_8dc6945fb6b59aea38df7935cf3afb6d'] = 'You do not have permission to %s payments'; -$_MODULE['<{mollie}prestashop>mollie_1bda80f2be4d3658e0baa43fbe7ae8c1'] = 'view'; -$_MODULE['<{mollie}prestashop>mollie_de95b43bceeb4b998aed4aed5cef1ae7'] = 'edit'; -$_MODULE['<{mollie}prestashop>mollie_2a3f1166b041928019e4e8718d628665'] = 'ship'; -$_MODULE['<{mollie}prestashop>mollie_df6f85687a0d5820baa1a069a04eff2d'] = 'refund'; -$_MODULE['<{mollie}prestashop>mollie_10aec35353f9c4096a71c38654c3d402'] = 'cancel'; -$_MODULE['<{mollie}prestashop>payment_c2f73af3130be4b4967a475ab846c546'] = 'An error occurred while initializing your payment. Please contact our customer support.'; -$_MODULE['<{mollie}prestashop>download_update_6de6a882c42b757cd027830399a74de4'] = 'Update this module'; -$_MODULE['<{mollie}prestashop>download_update_902b0d55fddef6f8d651fe1035b7d4bd'] = 'Error'; -$_MODULE['<{mollie}prestashop>download_update_5d04a002ea59c8114f9d08b0c6250a3b'] = 'Unable to connect'; -$_MODULE['<{mollie}prestashop>download_update_3844b140df977eee8639de6d1de9e1e0'] = 'Unable to unzip new module'; -$_MODULE['<{mollie}prestashop>download_update_b50ec141b028d5a8acec4a17c32f3808'] = 'The module has been updated!'; +$_MODULE['<{mollie}prestashop>mollie_ab552f085567bbe63872c10a3596cd27'] = 'Payment error:'; $_MODULE['<{mollie}prestashop>new_release_b7f73af7318580996a3889353b282827'] = 'You are currently using version %s. We strongly recommend you to upgrade to the new version %s!'; -$_MODULE['<{mollie}prestashop>form_93cba07454f06a4a960172bbd6e2a435'] = 'Yes'; $_MODULE['<{mollie}prestashop>form_bafd7322c6e97d25b6299b5d6fe8920b'] = 'No'; -$_MODULE['<{mollie}prestashop>form_2d25c72c1b18e562f6654fff8e11711e'] = 'Not available'; -$_MODULE['<{mollie}prestashop>form_58afe6389d8f42880d1011ef0de542a2'] = 'This payment method is not available on the Payments API. Switch to the Orders API below in order to activate this method.'; -$_MODULE['<{mollie}prestashop>form_9b6a88a9d4a44740f87b1ffa7cb070c4'] = 'Unable to load payment methods'; -$_MODULE['<{mollie}prestashop>form_6327b4e59f58137083214a1fec358855'] = 'Retry'; -$_MODULE['<{mollie}prestashop>form_902b0d55fddef6f8d651fe1035b7d4bd'] = 'Error'; -$_MODULE['<{mollie}prestashop>form_49ee3087348e8d44e1feda1917443987'] = 'Name'; -$_MODULE['<{mollie}prestashop>form_b33f1d112cd9d2d32d37cb4cc9a340c8'] = 'URL Source'; -$_MODULE['<{mollie}prestashop>form_de28ef21a06f3978c05f3d808b15eaab'] = 'Carrier URL'; -$_MODULE['<{mollie}prestashop>form_812a48ba719daeda82e4da8e812d426c'] = 'Custom URL'; -$_MODULE['<{mollie}prestashop>form_e55f75a29310d7b60f7ac1d390c8ae42'] = 'Module'; -$_MODULE['<{mollie}prestashop>form_962b59a5f12eabd9c05f11b5f0aada85'] = 'Do not automatically ship'; -$_MODULE['<{mollie}prestashop>form_703490ffd308d337cd5aac50567e9670'] = 'No tracking information'; +$_MODULE['<{mollie}prestashop>form_93cba07454f06a4a960172bbd6e2a435'] = 'Yes'; $_MODULE['<{mollie}prestashop>form_f421a366f70bd2aa1193eeb5a2990546'] = 'Here you can configure what information about the shipment is sent to Mollie'; $_MODULE['<{mollie}prestashop>form_53b2566e3519ea98db40ae9e716883a5'] = 'You can use the following variables for the Carrier URLs'; $_MODULE['<{mollie}prestashop>form_910d956cb2615e5739ac06c7f08fba26'] = 'Shipping number'; @@ -188,22 +31,30 @@ $_MODULE['<{mollie}prestashop>form_0d12e3c554976ad1f76c5db2b6c127ad'] = 'Shipping country code'; $_MODULE['<{mollie}prestashop>form_c824a8b18f09dc7e58bacf446be6735c'] = 'Shipping postcode'; $_MODULE['<{mollie}prestashop>form_0a867177f4a701a9f33de5ab21c42593'] = '2-letter language code'; -$_MODULE['<{mollie}prestashop>form_f6fcb64e21455abc4209f8f4440d45f5'] = 'Unable to load carrier list'; +$_MODULE['<{mollie}prestashop>form_49ee3087348e8d44e1feda1917443987'] = 'Name'; +$_MODULE['<{mollie}prestashop>form_b33f1d112cd9d2d32d37cb4cc9a340c8'] = 'URL Source'; +$_MODULE['<{mollie}prestashop>form_812a48ba719daeda82e4da8e812d426c'] = 'Custom URL'; +$_MODULE['<{mollie}prestashop>form_962b59a5f12eabd9c05f11b5f0aada85'] = 'Do not automatically ship'; +$_MODULE['<{mollie}prestashop>form_703490ffd308d337cd5aac50567e9670'] = 'No tracking information'; +$_MODULE['<{mollie}prestashop>form_de28ef21a06f3978c05f3d808b15eaab'] = 'Carrier URL'; +$_MODULE['<{mollie}prestashop>form_e55f75a29310d7b60f7ac1d390c8ae42'] = 'Module'; $_MODULE['<{mollie}prestashop>form_da93de158db2fbe49f35f6038711584a'] = 'This option is not required for the currently selected API'; -$_MODULE['<{mollie}prestashop>17_error_47e1924c444fafe9fdfce444790f0ba9'] = 'Back to your shopping cart'; -$_MODULE['<{mollie}prestashop>error_c453a4b8e8d98e82f35b67f433e3b4da'] = 'Payment'; -$_MODULE['<{mollie}prestashop>error_1e97d97a923eaddd810e056c828e99ea'] = 'Payment error'; -$_MODULE['<{mollie}prestashop>error_a25c753ee3e4be15ec0daa5a40deb7b8'] = 'An error occurred'; -$_MODULE['<{mollie}prestashop>error_47e1924c444fafe9fdfce444790f0ba9'] = 'Back to your shopping cart'; -$_MODULE['<{mollie}prestashop>mollie_issuers_588540a38eeba525f3598b68e6b788e5'] = 'or choose a different payment method'; -$_MODULE['<{mollie}prestashop>mollie_return_ebdf0f490b617d7efa3025d3625cec85'] = 'Welcome back'; -$_MODULE['<{mollie}prestashop>mollie_return_300225ee958b6350abc51805dab83c24'] = 'Continue shopping'; $_MODULE['<{mollie}prestashop>mollie_wait_bc0dd919f9a70dbb62f3f7afd68d6d68'] = 'Awaiting payment status'; $_MODULE['<{mollie}prestashop>qr_done_92816c7248d010591f699db3aaf6287b'] = 'Mollie iDEAL QR'; $_MODULE['<{mollie}prestashop>qr_done_ebdf0f490b617d7efa3025d3625cec85'] = 'Welcome back'; $_MODULE['<{mollie}prestashop>qr_done_4bf3a07ce980c9b007f9e2eed42d7fa2'] = 'The payment has been canceled.'; $_MODULE['<{mollie}prestashop>qr_done_6933c118341d8c06f888c392aa1ceaf5'] = 'The payment has been completed. Thank you for your order!'; -$_MODULE['<{mollie}prestashop>ideal_dropdown_ab4147235204a436c09e00d6722082ab'] = 'Choose a bank'; +$_MODULE['<{mollie}prestashop>mollie_return_ebdf0f490b617d7efa3025d3625cec85'] = 'Welcome back'; +$_MODULE['<{mollie}prestashop>mollie_return_300225ee958b6350abc51805dab83c24'] = 'Continue shopping'; +$_MODULE['<{mollie}prestashop>error_c453a4b8e8d98e82f35b67f433e3b4da'] = 'Payment'; +$_MODULE['<{mollie}prestashop>error_1e97d97a923eaddd810e056c828e99ea'] = 'Payment error'; +$_MODULE['<{mollie}prestashop>error_a25c753ee3e4be15ec0daa5a40deb7b8'] = 'An error occurred'; +$_MODULE['<{mollie}prestashop>error_47e1924c444fafe9fdfce444790f0ba9'] = 'Back to your shopping cart'; +$_MODULE['<{mollie}prestashop>17_error_47e1924c444fafe9fdfce444790f0ba9'] = 'Back to your shopping cart'; +$_MODULE['<{mollie}prestashop>smarty_error_9d1fbbe0d150b89f068ba72a20366659'] = 'The configuration option \"%s\" has been set to \"%s\"'; +$_MODULE['<{mollie}prestashop>smarty_error_27dd3bb10855eb84f48efedf6924c4c1'] = 'This makes it impossible to list payment methods on the checkout'; +$_MODULE['<{mollie}prestashop>smarty_error_4ec23c59d832a2934277a2a040918789'] = 'Please navigate to the page \"%s\" and choose the other option'; +$_MODULE['<{mollie}prestashop>smarty_error_92b67311c1a5d6cfc28ba08a1066c901'] = 'Afterwards, clear the cache one more time, so the payment method list can be refreshed'; $_MODULE['<{mollie}prestashop>order_info_729a51874fe901b092899e9e8b31c97a'] = 'Are you sure?'; $_MODULE['<{mollie}prestashop>order_info_ccaf4ee393d094ecde7f21b15fdf8f1f'] = 'Are you sure you want to refund this order?'; $_MODULE['<{mollie}prestashop>order_info_76f0ed934de85cc7131910b32ede7714'] = 'Refund'; @@ -256,18 +107,12 @@ $_MODULE['<{mollie}prestashop>order_info_3efc29552025b4e68de04f415b0b8806'] = 'Unable to refund'; $_MODULE['<{mollie}prestashop>order_info_468a92810b526750be9664461db6bf91'] = 'Unable to cancel'; $_MODULE['<{mollie}prestashop>order_info_2427b479f15af44582dc151ef95a90d0'] = 'Refunds are currently unavailable'; -$_MODULE['<{mollie}prestashop>qr_code_9ba19ddbcfeddfc758eb84fb8ba26b5e'] = 'or scan the iDEAL QR code'; -$_MODULE['<{mollie}prestashop>refund_729a51874fe901b092899e9e8b31c97a'] = 'Are you sure?'; -$_MODULE['<{mollie}prestashop>refund_ccaf4ee393d094ecde7f21b15fdf8f1f'] = 'Are you sure you want to refund this order?'; -$_MODULE['<{mollie}prestashop>refund_76f0ed934de85cc7131910b32ede7714'] = 'Refund'; -$_MODULE['<{mollie}prestashop>refund_ea4788705e6873b424c65e91c2846b19'] = 'Cancel'; $_MODULE['<{mollie}prestashop>rounding_error_7783c757cd1177e60888080e0f4e0593'] = 'The configuration option \"%s\" has been set to an unsupported value'; $_MODULE['<{mollie}prestashop>rounding_error_2279522d0910767ffa75e0b39ed1629f'] = 'The selected rounding mode is not supported by the Mollie Orders API'; $_MODULE['<{mollie}prestashop>rounding_error_3a92c96baa86286685b233e6912e718d'] = 'Please navigate to the page \"%s\" and choose the option \"%s\"'; -$_MODULE['<{mollie}prestashop>smarty_error_9d1fbbe0d150b89f068ba72a20366659'] = 'The configuration option \"%s\" has been set to \"%s\"'; -$_MODULE['<{mollie}prestashop>smarty_error_27dd3bb10855eb84f48efedf6924c4c1'] = 'This makes it impossible to list payment methods on the checkout'; -$_MODULE['<{mollie}prestashop>smarty_error_4ec23c59d832a2934277a2a040918789'] = 'Please navigate to the page \"%s\" and choose the other option'; -$_MODULE['<{mollie}prestashop>smarty_error_92b67311c1a5d6cfc28ba08a1066c901'] = 'Afterwards, clear the cache one more time, so the payment method list can be refreshed'; +$_MODULE['<{mollie}prestashop>qr_code_9ba19ddbcfeddfc758eb84fb8ba26b5e'] = 'or scan the iDEAL QR code'; $_MODULE['<{mollie}prestashop>smarty_warning_9d1fbbe0d150b89f068ba72a20366659'] = 'The configuration option \"%s\" has been set to \"%s\"'; $_MODULE['<{mollie}prestashop>smarty_warning_e7e747f9c74f0b8222c87db24d34f018'] = 'It is possible that not all payment methods can be displayed'; $_MODULE['<{mollie}prestashop>smarty_warning_1238db0fb782f758b5a54d6333b3610e'] = 'To fix this problem, navigate to the page \"%s\" and clear the cache manually or choose one of the other options'; +$_MODULE['<{mollie}prestashop>ideal_dropdown_ab4147235204a436c09e00d6722082ab'] = 'Choose a bank'; +$_MODULE['<{mollie}prestashop>payment_c2f73af3130be4b4967a475ab846c546'] = 'An error occurred while initializing your payment. Please contact our customer support.'; diff --git a/upgrade/Upgrade-4.0.7.php b/upgrade/Upgrade-4.0.7.php new file mode 100644 index 000000000..94cabe527 --- /dev/null +++ b/upgrade/Upgrade-4.0.7.php @@ -0,0 +1,95 @@ + + * @copyright Mollie B.V. + * @license Berkeley Software Distribution License (BSD-License 2) http://www.opensource.org/licenses/bsd-license.php + * @category Mollie + * @package Mollie + * @link https://www.mollie.nl + */ + +use Mollie\Config\Config; + +if (!defined('_PS_VERSION_')) { + exit; +} + +/** + * @param Mollie $module + * @return bool + */ + +function upgrade_module_4_0_7($module) +{ + Configuration::updateValue(Config::MOLLIE_STATUS_SHIPPING, Configuration::get('PS_OS_SHIPPING')); + Configuration::updateValue(Config::MOLLIE_STATUS_SHIPPING, true); + Configuration::updateValue(Config::MOLLIE_SEND_ORDER_CONFIRMATION, Config::ORDER_CONF_MAIL_SEND_ON_NEVER); + + $sql= 'CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.'mol_excluded_country` ( + `id_mol_country` INT(64) NOT NULL PRIMARY KEY AUTO_INCREMENT, + `id_method` VARCHAR(64), + `id_country` INT(64), + `all_countries` tinyint + ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8;'; + + $sql .= ' + CREATE TABLE IF NOT EXISTS `'._DB_PREFIX_.'mol_pending_order_cart` ( + `id_mol_pending_order_cart` INT(64) NOT NULL PRIMARY KEY AUTO_INCREMENT, + `order_id` INT(64) NOT NULL, + `cart_id` INT(64) NOT NULL + ) ENGINE='._MYSQL_ENGINE_.' DEFAULT CHARSET=utf8; + '; + + if (Db::getInstance()->execute($sql) == false) { + return false; + } + + $module->registerHook('actionAdminOrdersListingFieldsModifier'); + $module->registerHook('actionAdminControllerSetMedia'); + $module->registerHook('actionValidateOrder'); + + $installer = new \Mollie\Install\Installer($module); + $installed = true; + + $installed &= $installer->installTab('AdminMollieAjax', 0, 'AdminMollieAjax', false); + $installed &= $installer->installTab('AdminMollieModule', 0, 'AdminMollieModule', false); + + $installed &= $installer->partialShippedOrderState(); + $installed &= $installer->orderCompletedOrderState(); + $installed &= $installer->copyEmailTemplates(); + + Configuration::updateValue(Config::MOLLIE_STATUS_COMPLETED, + Configuration::get(Config::MOLLIE_STATUS_ORDER_COMPLETED) + ); + Configuration::updateValue(Config::MOLLIE_MAIL_WHEN_COMPLETED, true); + + if(!$installed) { + return false; + } + + return true; +} diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php index 2a150ade2..af80d4163 100644 --- a/vendor/composer/autoload_classmap.php +++ b/vendor/composer/autoload_classmap.php @@ -6,6 +6,8 @@ $baseDir = dirname($vendorDir); return array( + 'AdminMollieAjaxController' => $baseDir . '/controllers/admin/AdminMollieAjaxController.php', + 'AdminMollieModuleController' => $baseDir . '/controllers/admin/AdminMollieModuleController.php', 'ComposerAutoloaderInit34e52f1f61572eebb98aa73f5240c82d' => $vendorDir . '/composer/autoload_real.php', 'Composer\\Autoload\\ClassLoader' => $vendorDir . '/composer/ClassLoader.php', 'Composer\\Autoload\\ComposerStaticInit34e52f1f61572eebb98aa73f5240c82d' => $vendorDir . '/composer/autoload_static.php', @@ -13,6 +15,7 @@ 'MolOrderFee' => $baseDir . '/src/Entity/MolOrderFee.php', 'MolPaymentMethod' => $baseDir . '/src/Entity/MolPaymentMethod.php', 'MolPaymentMethodIssuer' => $baseDir . '/src/Entity/MolPaymentMethodIssuer.php', + 'MolPendingOrderCart' => $baseDir . '/src/Entity/MolPendingOrderCart.php', 'MollieAjaxModuleFrontController' => $baseDir . '/controllers/front/ajax.php', 'MollieFailModuleFrontController' => $baseDir . '/controllers/front/fail.php', 'MolliePayScreenModuleFrontController' => $baseDir . '/controllers/front/payScreen.php', diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index 03e1645b4..086eac0ee 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -153,6 +153,8 @@ class ComposerStaticInit34e52f1f61572eebb98aa73f5240c82d ); public static $classMap = array ( + 'AdminMollieAjaxController' => __DIR__ . '/../..' . '/controllers/admin/AdminMollieAjaxController.php', + 'AdminMollieModuleController' => __DIR__ . '/../..' . '/controllers/admin/AdminMollieModuleController.php', 'ComposerAutoloaderInit34e52f1f61572eebb98aa73f5240c82d' => __DIR__ . '/..' . '/composer/autoload_real.php', 'Composer\\Autoload\\ClassLoader' => __DIR__ . '/..' . '/composer/ClassLoader.php', 'Composer\\Autoload\\ComposerStaticInit34e52f1f61572eebb98aa73f5240c82d' => __DIR__ . '/..' . '/composer/autoload_static.php', @@ -160,6 +162,7 @@ class ComposerStaticInit34e52f1f61572eebb98aa73f5240c82d 'MolOrderFee' => __DIR__ . '/../..' . '/src/Entity/MolOrderFee.php', 'MolPaymentMethod' => __DIR__ . '/../..' . '/src/Entity/MolPaymentMethod.php', 'MolPaymentMethodIssuer' => __DIR__ . '/../..' . '/src/Entity/MolPaymentMethodIssuer.php', + 'MolPendingOrderCart' => __DIR__ . '/../..' . '/src/Entity/MolPendingOrderCart.php', 'MollieAjaxModuleFrontController' => __DIR__ . '/../..' . '/controllers/front/ajax.php', 'MollieFailModuleFrontController' => __DIR__ . '/../..' . '/controllers/front/fail.php', 'MolliePayScreenModuleFrontController' => __DIR__ . '/../..' . '/controllers/front/payScreen.php', diff --git a/views/css/admin/order-list.css b/views/css/admin/order-list.css new file mode 100644 index 000000000..deea242cc --- /dev/null +++ b/views/css/admin/order-list.css @@ -0,0 +1,95 @@ +/* + * NOTICE OF LICENSE + * + * @author INVERTUS, UAB www.invertus.eu + * @copyright Copyright (c) permanent, INVERTUS, UAB + * @license Addons PrestaShop license limitation + * @see /LICENSE + * + * International Registered Trademark & Property of INVERTUS, UAB + */ + +.icon-send-mail-mollie::before { + content: ""; + top: -30px; + left: -20px; + position: absolute; + width: 50px; + height: 50px; +} + +.icon-send-mail-mollie::before { + background: url("../../../views/img/icon/icon-save-label.svg") no-repeat 0 0; +} + +.mollie-icon-container{ + position:relative; +} + +.mollie-message-container:before{ + position: absolute; + content: ''; + border-width: 10px; + border-style: solid; + left: 100%; + top: 20px; +} + +.mollie-icon-container:hover .mollie-message-container{ + display:block; +} + +.mollie-message-container{ + position: absolute; + display: none; + width: auto; + right: 90px; + border-radius: 10px; + padding: 10px; + color: white; + top: -30px; + z-index:100; + min-width:200px; +} + +.mollie-message-container{ + background-color: #92d097; +} +.mollie-message-container:before{ + position: absolute; + content: ''; + border-width: 10px; + border-style: solid; + left: 100%; + top: 20px; +} + +.mollie-message-container:before{ + border-color: rgba(255,255,255,0) rgba(255,255,255,0) rgba(255,255,255,0) #92d097; +} + +.mollie-icon-container:hover .mollie-message-container{ + display:block; +} + + +@media only screen and (max-width: 991px) { + .icon-send-mail-mollie::before { + left: 0; + display: inline-block; + position: unset; + } + + .mollie-message-container:before { + content: none; + } + + .mollie-message-container { + display: inline-block; + position: unset; + } + + .mollie-icon-container { + display: flex; + } +} diff --git a/views/css/mollie.css b/views/css/mollie.css index 3e575548a..df330c531 100644 --- a/views/css/mollie.css +++ b/views/css/mollie.css @@ -104,4 +104,8 @@ svg.mollie-svg { width: 1em; height: 1em; display: inline; -} \ No newline at end of file +} + +.payment-method .payment-check-link { + vertical-align: middle; +} diff --git a/views/img/icon/icon-save-label.svg b/views/img/icon/icon-save-label.svg new file mode 100644 index 000000000..4ade73958 --- /dev/null +++ b/views/img/icon/icon-save-label.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + diff --git a/views/js/admin/order_add.js b/views/js/admin/order_add.js new file mode 100644 index 000000000..3939c3a06 --- /dev/null +++ b/views/js/admin/order_add.js @@ -0,0 +1,72 @@ +/** + * Copyright (c) 2012-2020, Mollie B.V. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * - Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * @author Mollie B.V. + * @copyright Mollie B.V. + * @license Berkeley Software Distribution License (BSD-License 2) http://www.opensource.org/licenses/bsd-license.php + * @category Mollie + * @package Mollie + * @link https://www.mollie.nl + * @codingStandardsIgnoreStart + */ +$(document).ready(function () { + var $paymentSelector = $('select[name="payment_module_name"]'); + $paymentSelector.ready(function () { + $("#mollie-email-send-group").appendTo($paymentSelector.closest('div.form-group')); + isMollie = isMolliePayment($('select[name="payment_module_name"]').val()); + toggleOrderStatus(isMollie); + }); + + $(document).on('change', 'select[name="payment_module_name"]', function () { + var selectedPayment = $(this).val(); + var isMollie = isMolliePayment(selectedPayment); + + toggleOrderStatus(isMollie); + }); + + function toggleOrderStatus(isMolliePayment) { + var $molliePaymentCheckboxGroup = $('#mollie-email-send-group'); + var $orderStatusSelector = $('select[name="id_order_state"]'); + if (isMolliePayment) { + $orderStatusSelector.closest('div.form-group').toggleClass('hidden', true); + $('#send_email_to_customer').toggleClass('hidden', true); + $molliePaymentCheckboxGroup.toggleClass('hidden', false); + $orderStatusSelector.val(molliePendingStatus); + } else { + $orderStatusSelector.closest('div.form-group').toggleClass('hidden', false); + $('#send_email_to_customer').toggleClass('hidden', false); + $molliePaymentCheckboxGroup.toggleClass('hidden', true); + } + } + + function isMolliePayment(paymentName) { + var isMollie = false; + if (paymentName === 'mollie') { + isMollie = true; + } + + return isMollie; + } +}); \ No newline at end of file diff --git a/views/js/admin/order_list.js b/views/js/admin/order_list.js new file mode 100644 index 000000000..aea50329f --- /dev/null +++ b/views/js/admin/order_list.js @@ -0,0 +1,120 @@ +/* + * NOTICE OF LICENSE + * + * @author INVERTUS, UAB www.invertus.eu + * @copyright Copyright (c) permanent, INVERTUS, UAB + * @license Addons PrestaShop license limitation + * @see /LICENSE + * + * International Registered Trademark & Property of INVERTUS, UAB + */ + +$(document).ready(function () { + $(document).on('click', '.resend-payment-mail-mollie', function () { + // toggleMessageBoxPrinted(); + $.ajax(mollieHookAjaxUrl, { + data: { + id_order: $(this).data('id-order'), + action: 'resendPaymentMail', + ajax: 1 + }, + success: function (response) { + response = JSON.parse(response); + // toggleMessageBoxResult(response.success, response.message); + + // $.each(response.icon_replacers, function (key, value) { + // replaceIcon(value.id_order, value.icon_replacer); + // }) + + if (response.success) { + showSuccessMessage(response.message); + return; + } + + if (!response.success) { + showErrorMessage(response.message); + return; + } + + + }, + error: function(xhr, ajaxOptions, thrownError) { + toggleMessageBoxResult(false, xhr.statusText); + } + }); + return false; + }); + + function toggleMessageBoxPrinted() + { + messageBox = $('.message-text-box'); + messageBox.html(shipmentIsBeingPrintedMessage); + messageBox.addClass('alert-warning'); + messageBox.removeClass('alert-danger'); + messageBox.removeClass('alert-success'); + $("html, body").animate({ scrollTop: 0 }, "slow"); + $('.label-printing-message').show(); + } + + function toggleMessageBoxResult(success, message) { + messageBox.html(message); + if (success) { + messageBox.removeClass('alert-warning'); + messageBox.addClass('alert-success'); + } + + if (!success) { + messageBox.removeClass('alert-warning'); + messageBox.addClass('alert-danger'); + } + } + + function replaceIcon(idOrder, iconReplacer) + { + $('.dpd-icon-container[data-id-order="' + idOrder + '"]').html(iconReplacer); + } + + $(document).on('click', '#download-selected-labels', function (event) { + event.preventDefault(); + var selectedOrderIdArray = new Array(); + + $(".row-selector input:checked").each(function() + { + selectedOrderIdArray.push($(this).val()); + }); + + toggleMessageBoxPrinted(); + if (selectedOrderIdArray.length == 0) { + toggleMessageBoxResult(false, noOrdersSelectedMessage); + return; + } + $.ajax(dpdHookAjaxUrl, { + data: { + orders: JSON.stringify(selectedOrderIdArray), + action: 'saveMultipleLabelsFromOrderList', + ajax: 1 + }, + success: function (response) { + response = JSON.parse(response); + toggleMessageBoxResult(response.success, response.message); + + $.each(response.icon_replacers, function (key, value) { + replaceIcon(value.id_order, value.icon_replacer); + }); + + if (response.success) { + var labelPrintUrl = dpdHookAjaxUrl + + '&action=downloadLabelFromManifests' + + '&manifests= ' + JSON.stringify(response.manifests) + + '&ajax=1'; + window.location = labelPrintUrl; + } + }, + error: function(xhr, ajaxOptions, thrownError) { + toggleMessageBoxResult(false, xhr.statusText) + } + }); + }); + + $('.bulk-actions .dropdown-menu').append('
  • Print Labels
  • '); +}); diff --git a/views/js/admin/settings.js b/views/js/admin/settings.js index 98c62e9f4..9ae804cd4 100644 --- a/views/js/admin/settings.js +++ b/views/js/admin/settings.js @@ -31,17 +31,67 @@ * @link https://www.mollie.nl * @codingStandardsIgnoreStart */ -$(document).ready(function() { - $('.js-mollie-amount').keypress(function(event) { +$(document).ready(function () { + $('.js-mollie-amount').keypress(function (event) { if ((event.which != 46 || $(this).val().indexOf('.') != -1) && (event.which < 48 || event.which > 57)) { event.preventDefault(); } }); $('select[name^="MOLLIE_CARRIER_URL_SOURCE"]').on('change', function () { var customUrlDisabled = true; - if($(this).val() === 'custom_url') { + if ($(this).val() === 'custom_url') { customUrlDisabled = false; } $(this).closest('tr').find('input').attr('disabled', customUrlDisabled); }) -}); \ No newline at end of file + + var $apiPaymentMethodSelect = $('select[name^="MOLLIE_METHOD_API"]'); + + $apiPaymentMethodSelect.each(function () { + togglePaymentMethodDescriptions($(this)); + }); + + $apiPaymentMethodSelect.on('change', function () { + togglePaymentMethodDescriptions($(this)); + }); +}); + +function togglePaymentMethod($button, paymentId) { + var $clickedButton = $($button); + $.ajax(ajaxUrl, { + method: 'POST', + data: { + 'paymentMethod': paymentId, + 'status' : $clickedButton.data('action'), + 'action': 'togglePaymentMethod', + 'ajax' : 1 + }, + success: function (response) { + response = JSON.parse(response); + var checkInputClass = 'icon-check text-success'; + var clearInputClass = 'icon-remove text-danger'; + if (response.success) { + if (response.paymentStatus) { + $clickedButton.data('action', 'deactivate'); + $clickedButton.find('i').removeClass(clearInputClass).addClass(checkInputClass); + } else { + $clickedButton.data('action', 'activate'); + $clickedButton.find('i').removeClass(checkInputClass).addClass(clearInputClass); + } + + if(response.paymentStatus !== undefined) { + $clickedButton.closest('.payment-method').find('select[name^="MOLLIE_METHOD_ENABLED"] option[value="' + response.paymentStatus +'"]').prop('selected', true); + + } + } + } + }) +} + +function togglePaymentMethodDescriptions(apiPaymentMethodSelect){ + if (apiPaymentMethodSelect.val() === 'payments') { + apiPaymentMethodSelect.closest('.payment-method').find('.payment-api-description').slideDown(); + } else { + apiPaymentMethodSelect.closest('.payment-method').find('.payment-api-description').slideUp(); + } +} diff --git a/views/js/validation.js b/views/js/validation.js index 0660ea4cc..06bd3888c 100644 --- a/views/js/validation.js +++ b/views/js/validation.js @@ -133,10 +133,13 @@ $(document).ready(function () { var $paymentMethodForm = $(method).closest('.payment-method'); var $countrySelectType = $paymentMethodForm.find('select[name^="MOLLIE_METHOD_APPLICABLE_COUNTRIES"]'); var $countrySelect = $paymentMethodForm.find('select[name^="MOLLIE_METHOD_CERTAIN_COUNTRIES"]'); - if ($countrySelectType.val() === "0") { + var $excludedCountrySelect = $paymentMethodForm.find('select[name^="MOLLIE_METHOD_EXCLUDE_CERTAIN_COUNTRIES"]'); + if ($countrySelectType.val() === "1") { $countrySelect.closest('.form-group').show(); + $excludedCountrySelect.closest('.form-group').hide(); } else { $countrySelect.closest('.form-group').hide(); + $excludedCountrySelect.closest('.form-group').show(); } } diff --git a/views/templates/admin/_configure/helpers/form/form.tpl b/views/templates/admin/_configure/helpers/form/form.tpl index 403702660..84e382b4f 100644 --- a/views/templates/admin/_configure/helpers/form/form.tpl +++ b/views/templates/admin/_configure/helpers/form/form.tpl @@ -64,6 +64,21 @@ {l s=$paymentMethod.name mod='mollie'} + + {if $methodObj->enabled} + + + + {else} + + + + {/if} +
    -
    +
    @@ -115,12 +130,14 @@ {l s='The description to be used for this transaction. These variables ara available:' mod='mollie'}

    - {l s='{orderNumber}' mod='mollie'} - {l s=': The order number for this transaciton' mod='mollie'} -

    -

    - {l s='{storeName}' mod='mollie'} - {l s=': The name of the store' mod='mollie'} + {l s='{orderNumber}' mod='mollie'}, + {l s='{storeName}' mod='mollie'}, + {l s='{cart.id}' mod='mollie'}, + {l s='{order.reference}' mod='mollie'}, + {l s='{customer.firstname}' mod='mollie'}, + {l s='{customer.lastname}' mod='mollie'}, + {l s='{customer.company}' mod='mollie'}, + {l s='{storename}' mod='mollie'}.

    {l s='(Note: This only works when the method is set to Payments API)' mod='mollie'} @@ -129,19 +146,19 @@

    + {foreach $input.countries as $excludedCountry} + + {/foreach} + +
    +
    {*
    *} {*