diff --git a/mollie/controllers/front/payment.php b/mollie/controllers/front/payment.php index 7d8a34589..fd3b8b591 100644 --- a/mollie/controllers/front/payment.php +++ b/mollie/controllers/front/payment.php @@ -53,7 +53,6 @@ class MolliePaymentModuleFrontController extends ModuleFrontController public function initContent() { parent::initContent(); - /** @var Cart $cart */ $cart = $this->context->cart; $customer = new Customer($cart->id_customer); @@ -99,30 +98,16 @@ public function initContent() $orig_amount = $cart->getOrderTotal(TRUE, Cart::BOTH); $amount = $this->_convertCurrencyToEuro($orig_amount); - // Validate - $this->module->validateOrder( - (int) $cart->id, - /* Set initial status to the status selected in the admin, in case of custom status definitions */ - $this->module->statuses[Mollie_API_Object_Payment::STATUS_OPEN], - $orig_amount, - $method, - NULL, - array(), - NULL, - FALSE, - $customer->secure_key - ); - // Prepare payment - $order_id = $this->module->currentOrder; - $payment_data = $this->_getPaymentData($amount, $method, $issuer, $order_id); + $payment_data = $this->_getPaymentData($amount, $method, $issuer, (int) $cart->id); $payment = $this->_createPayment($payment_data); - // Store payment + + // Store payment linked to cart Db::getInstance()->insert( 'mollie_payments', array( - 'order_id' => $order_id, + 'cart_id' => (int) $cart->id, 'method' => $payment->method, 'transaction_id' => $payment->id, 'bank_status' => Mollie_API_Object_Payment::STATUS_OPEN, @@ -247,24 +232,19 @@ protected function _convertCurrencyToEuro($amount) * @param float $amount * @param string $method * @param string|null $issuer - * @param int $order_id + * @param int $cart_id * @return array */ - protected function _getPaymentData($amount, $method, $issuer, $order_id) + protected function _getPaymentData($amount, $method, $issuer, $cart_id) { $payment_data = array( "amount" => $amount, "method" => $method, "issuer" => $issuer, - "description" => str_replace('%', $order_id, $this->module->getConfigValue('MOLLIE_DESCRIPTION')), - "redirectUrl" => $this->context->link->getModuleLink('mollie','return', array( - 'id' => $order_id, - 'ref' => Order::getUniqReferenceOf($order_id)) - ), + "description" => str_replace('%', '', $this->module->getConfigValue('MOLLIE_DESCRIPTION')), + "redirectUrl" => $this->context->link->getModuleLink('mollie','return', array('cart_id' => $cart_id)), "webhookUrl" => $this->context->link->getModuleLink('mollie', 'webhook'), - "metadata" => array( - "order_id" => $order_id, - ), + "metadata" => array("cart_id" => $cart_id) ); if (isset($this->context, $this->context->cart)) diff --git a/mollie/controllers/front/return.php b/mollie/controllers/front/return.php index 91277ce65..2cb760626 100644 --- a/mollie/controllers/front/return.php +++ b/mollie/controllers/front/return.php @@ -53,22 +53,38 @@ class MollieReturnModuleFrontController extends ModuleFrontController public function initContent() { parent::initContent(); + $data = array(); + /** + * Set ref is indicative of a payment that is tied to an order instead of a cart, which + * we still support for transitional reasons. + */ + if (isset($_GET['ref'])) + { + $order_id = (int) $_GET['id']; - $order_id = (int) $_GET['id']; + // Check if user is allowed to be on the return page + $data['auth'] = Order::getUniqReferenceOf($order_id) === $_GET['ref']; + if ($data['auth']) + { + $data['mollie_info'] = $this->module->getPaymentBy('order_id', (int)$order_id); + } + } + elseif (isset($_GET['cart_id'])) + { + $cart_id = (int) $_GET['cart_id']; - // Check if user is allowed to be on the return page - $data['auth'] = Order::getUniqReferenceOf($order_id) === $_GET['ref']; + // Check if user that's seeing this is the cart-owner + $cart = new Cart($cart_id); + $data['auth'] = (int)$cart->id_customer === $this->context->customer->id; + if ($data['auth']) + { + $data['mollie_info'] = $this->module->getPaymentBy('cart_id', (int)$cart_id); + } + } - // Get order information (if user is allowed to see it) - if ($data['auth']) + if (isset($data['auth']) && $data['auth']) { - $data['mollie_info'] = Db::getInstance()->getRow( - sprintf( - 'SELECT * FROM `%s` WHERE `order_id` = %d', - _DB_PREFIX_ . 'mollie_payments', - $order_id - ) - ); + // any paid payments for this cart? if ($data['mollie_info'] === FALSE) { @@ -83,12 +99,16 @@ public function initContent() $data['msg_details'] = $this->module->lang('We have not received a definite payment status. You will be notified as soon as we receive a confirmation of the bank/merchant.'); break; case Mollie_API_Object_Payment::STATUS_CANCELLED: - $data['msg_details'] = $this->module->lang('You have cancelled your order.'); + Tools::redirect('/index.php?controller=order&step=3'); break; case Mollie_API_Object_Payment::STATUS_EXPIRED: $data['msg_details'] = $this->module->lang('Unfortunately your order was expired.'); break; case Mollie_API_Object_Payment::STATUS_PAID: + if(isset($cart_id)) + { + Tools::redirectLink(__PS_BASE_URI__ . 'index.php?controller=order-confirmation&id_cart=' . $cart_id .'&id_module='. $this->module->id .'&id_order=' . Order::getOrderByCartId(intval($cart_id)) . '&key=' . $this->context->customer->secure_key); + } $data['msg_details'] = $this->module->lang('Thank you. Your order has been received.'); break; default: diff --git a/mollie/controllers/front/webhook.php b/mollie/controllers/front/webhook.php index 76011a34f..9457f8e3a 100644 --- a/mollie/controllers/front/webhook.php +++ b/mollie/controllers/front/webhook.php @@ -73,9 +73,9 @@ protected function _executeWebhook() return 'OK'; } - $id = Tools::getValue('id'); + $transaction_id = Tools::getValue('id'); - if (empty($id)) + if (empty($transaction_id)) { if ($this->module->getConfigValue('MOLLIE_DEBUG_LOG') == Mollie::DEBUG_LOG_ERRORS) { @@ -86,36 +86,66 @@ protected function _executeWebhook() try { - /** @var Mollie_API_Object_Payment $payment */ - $payment = $this->module->api->payments->get($id); - $order_id = $payment->metadata->order_id; - $status = $payment->status; + /** @var Mollie_API_Object_Payment $api_payment */ + $api_payment = $this->module->api->payments->get($transaction_id); + $transaction_id = $api_payment->id; } catch (Exception $e) { if ($this->module->getConfigValue('MOLLIE_DEBUG_LOG') == Mollie::DEBUG_LOG_ERRORS) { - Logger::addLog(__METHOD__ . 'said: Could not retrieve payment details for id "' . $id . '". Reason: ' . $e->getMessage(), Mollie::WARNING); + Logger::addLog(__METHOD__ . 'said: Could not retrieve payment details for transaction_id "' . $transaction_id . '". Reason: ' . $e->getMessage(), Mollie::WARNING); } return 'NOT OK'; } + $ps_payment = $this->module->getPaymentBy('transaction_id', $transaction_id); + + if (isset($api_payment->metadata->cart_id)) + { + if ( + $ps_payment['bank_status'] === Mollie_API_Object_Payment::STATUS_OPEN && + $api_payment->status === Mollie_API_Object_Payment::STATUS_PAID ) + { + // Misnomer ahead: think of validateOrder as "createOrderFromCart" + $this->module->validateOrder( + (int) $api_payment->metadata->cart_id, + $this->module->statuses[$api_payment->status], + $api_payment->amount, + $api_payment->method + ); + + $order_id = $this->module->currentOrder; + } + } + + /** + * Older versions tie payments to orders, and create a cart upon payment creation. + * In order to support the transition between these two cases we check for the + * occurrence of order_id in the metadata. In these cases we only update the order status + */ + + elseif (isset($api_payment->metadata->order_id)) + { + + $order_id = $api_payment->metadata->order_id; + + $this->module->setOrderStatus($order_id, $api_payment->status); + } + // Store status in database - if (!$this->_saveOrderStatus($order_id, $status)) + if (!$this->_savePaymentStatus($transaction_id, $api_payment->status)) { if ($this->module->getConfigValue('MOLLIE_DEBUG_LOG') == Mollie::DEBUG_LOG_ERRORS) { - Logger::addLog(__METHOD__ . 'said: Could not save order status for payment "' . $id . '". Reason: ' . Db::getInstance()->getMsgError(), Mollie::WARNING); + Logger::addLog(__METHOD__ . 'said: Could not save Mollie payment status for transaction "' . $transaction_id . '". Reason: ' . Db::getInstance()->getMsgError(), Mollie::WARNING); } } - // Tell status to Shop - $this->module->setOrderStatus($order_id, $status); - // Log successful webhook requests in extended log mode only if ($this->module->getConfigValue('MOLLIE_DEBUG_LOG') == Mollie::DEBUG_LOG_ALL) { - Logger::addLog(__METHOD__ . 'said: Received webhook request for order ' . (int) $order_id . ' / transaction ' . htmlentities($id), Mollie::NOTICE); + Logger::addLog(__METHOD__ . 'said: Received webhook request for order ' . (int) $order_id . ' / transaction ' . $transaction_id, Mollie::NOTICE); } return 'OK'; } @@ -126,13 +156,13 @@ protected function _executeWebhook() * @param $status * @return bool */ - protected function _saveOrderStatus($order_id, $status) + protected function _savePaymentStatus($transaction_id, $status) { $data = array( 'updated_at' => date("Y-m-d H:i:s"), 'bank_status' => $status, ); - return Db::getInstance()->update('mollie_payments', $data, '`order_id` = ' . (int)$order_id); + return Db::getInstance()->update('mollie_payments', $data, '`transaction_id` = \'' . Db::getInstance()->escape($transaction_id) . '\''); } } diff --git a/mollie/mollie.php b/mollie/mollie.php index 573e9ab70..9c8a7f6bf 100644 --- a/mollie/mollie.php +++ b/mollie/mollie.php @@ -61,7 +61,7 @@ class Mollie extends PaymentModule public $statuses = array(); public $name = 'mollie'; public $tab = 'payments_gateways'; - public $version = '1.0.2'; + public $version = '1.0.3'; public $author = 'Mollie B.V.'; public $need_instance = TRUE; public $ps_versions_compliancy = array('min' => '1.5', 'max' => '2'); @@ -113,7 +113,6 @@ public function __construct() } $this->statuses = array( - Mollie_API_Object_Payment::STATUS_OPEN => $this->getConfigValue('MOLLIE_STATUS_OPEN'), Mollie_API_Object_Payment::STATUS_PAID => $this->getConfigValue('MOLLIE_STATUS_PAID'), Mollie_API_Object_Payment::STATUS_CANCELLED => $this->getConfigValue('MOLLIE_STATUS_CANCELLED'), Mollie_API_Object_Payment::STATUS_EXPIRED => $this->getConfigValue('MOLLIE_STATUS_EXPIRED'), @@ -122,7 +121,6 @@ public function __construct() // Load all translatable text here so we have a single translation point $this->lang = array( - Mollie_API_Object_Payment::STATUS_OPEN => $this->l('open'), Mollie_API_Object_Payment::STATUS_PAID => $this->l('paid'), Mollie_API_Object_Payment::STATUS_CANCELLED => $this->l('cancelled'), Mollie_API_Object_Payment::STATUS_EXPIRED => $this->l('expired'), @@ -134,9 +132,9 @@ public function __construct() 'The order with this id does not exist.' => $this->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->l('We have not received a definite payment status. You will be notified as soon as we receive a confirmation of the bank/merchant.'), - 'You have cancelled your order.' => $this->l('You have cancelled your order.'), - 'Unfortunately your order was expired.' => $this->l('Unfortunately your order was expired.'), - 'Thank you. Your order has been received.' => $this->l('Thank you. Your order has been received.'), + 'You have cancelled your payment.' => $this->l('You have cancelled your payment.'), + 'Unfortunately your payment was expired.' => $this->l('Unfortunately your payment was expired.'), + 'Thank you. Your payment has been received.' => $this->l('Thank you. Your payment has been received.'), 'The transaction has an unexpected status.' => $this->l('The transaction has an unexpected status.'), 'You are not authorised to see this page.' => $this->l('You are not authorised to see this page.'), 'Continue shopping' => $this->l('Continue shopping'), @@ -193,21 +191,16 @@ public function install() return FALSE; } - if (!$this->installOpenState()) - { - $this->_errors[] = 'Unable to install new OPEN state.'; - return FALSE; - } - $sql = sprintf(' CREATE TABLE IF NOT EXISTS `%s` ( - `order_id` INT(64) NOT NULL PRIMARY KEY, + `transaction_id` VARCHAR(64) NOT NULL PRIMARY KEY, + `cart_id` INT(64), + `order_id` INT(64), `method` VARCHAR(128) NOT NULL, - `transaction_id` VARCHAR(64) NOT NULL, `bank_status` VARCHAR(64) NOT NULL, `created_at` DATETIME NOT NULL, `updated_at` DATETIME DEFAULT NULL, - UNIQUE KEY `transaction_id` (`transaction_id`) + INDEX (cart_id) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;', _DB_PREFIX_ . 'mollie_payments' ); @@ -273,12 +266,18 @@ public function uninstall() public function reinstall() { return - $this->_unregisterHooks() && - $this->_registerHooks() && + $this->reinstallHooks() && $this->_initConfig() ; } + public function reinstallHooks() + { + return + $this->_unregisterHooks() && + $this->_registerHooks() + ; + } /** * @return bool @@ -287,9 +286,11 @@ protected function _registerHooks() { return $this->registerHook('displayPayment') && + $this->registerHook('displayPaymentTop') && $this->registerHook('displayAdminOrder') && $this->registerHook('displayHeader') && - $this->registerHook('displayBackOfficeHeader') + $this->registerHook('displayBackOfficeHeader') && + $this->registerHook('displayOrderConfirmation') ; } @@ -300,9 +301,11 @@ protected function _unregisterHooks() { return $this->unregisterHook('displayPayment') && + $this->unregisterHook('displayPaymentTop') && $this->unregisterHook('displayAdminOrder') && $this->unregisterHook('displayHeader') && - $this->unregisterHook('displayBackOfficeHeader') + $this->unregisterHook('displayBackOfficeHeader') && + $this->unregisterHook('displayOrderConfirmation') ; } @@ -314,7 +317,7 @@ protected function _initConfig() return $this->initConfigValue('MOLLIE_VERSION', $this->version) && $this->initConfigValue('MOLLIE_API_KEY', '') && - $this->initConfigValue('MOLLIE_DESCRIPTION', 'Order %') && + $this->initConfigValue('MOLLIE_DESCRIPTION', 'Order') && $this->initConfigValue('MOLLIE_IMAGES', self::LOGOS_NORMAL) && $this->initConfigValue('MOLLIE_ISSUERS', self::ISSUERS_ON_CLICK) && $this->initConfigValue('MOLLIE_CSS', '') && @@ -516,6 +519,40 @@ public function setOrderStatus($order_id, $status) return $history; } + /** + * @param $order_id + * @return array + */ + + public function getPaymentBy($column,$id) + { + $paid_payment = Db::getInstance()->getRow( + sprintf( + 'SELECT * FROM `%s` WHERE `%s` = \'%s\' AND bank_status = \'%s\'', + _DB_PREFIX_ . 'mollie_payments', + $column, + $id, + Mollie_API_Object_Payment::STATUS_PAID + ) + ); + + if ($paid_payment) + { + return $paid_payment; + } + + $non_paid_payment = Db::getInstance()->getRow( + sprintf( + 'SELECT * FROM `%s` WHERE `%s` = \'%s\' ORDER BY created_at DESC', + _DB_PREFIX_ . 'mollie_payments', + $column, + $id + ) + ); + + return $non_paid_payment; + } + /** * @param array $image_options * @param array $issuer_options @@ -829,44 +866,40 @@ public function hookDisplayPayment() return $this->display(__FILE__, 'mollie_methods.tpl'); } - public function installOpenState () + public function hookDisplayPaymentTop() { - $order_state = new OrderState(); - $order_state->name = array(); - - foreach (Language::getLanguages() as $language) + $payment = $this->getPaymentBy('cart_id',(int)$this->context->cart->id); + if ($payment && $payment['bank_status'] == Mollie_API_Object_Payment::STATUS_CANCELLED) { - switch (Tools::strtolower($language['iso_code'])) - { - case 'nl': - $translation = 'Wachten op betaling'; - break; - - case 'fr': - $translation = 'En attente de paiement'; - break; - - default: - $translation = 'Pending payment'; - break; - } + return '