From bdefda4b8bd75ac8dcdcd6c59e5eba08b516b876 Mon Sep 17 00:00:00 2001 From: Mark Smit Date: Mon, 9 Jul 2018 15:41:14 +0200 Subject: [PATCH 1/8] updated submodule --- catalog/controller/extension/payment/mollie-api-client | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/catalog/controller/extension/payment/mollie-api-client b/catalog/controller/extension/payment/mollie-api-client index 33e09a0b..8514f298 160000 --- a/catalog/controller/extension/payment/mollie-api-client +++ b/catalog/controller/extension/payment/mollie-api-client @@ -1 +1 @@ -Subproject commit 33e09a0be5edfcf5b7bf8100e253666de514b4cf +Subproject commit 8514f2988d22096f23080a1c9e22bdec8fccc501 From 043a2f3c32958a7017afafe22f62596fb8074748 Mon Sep 17 00:00:00 2001 From: Mark Smit Date: Tue, 10 Jul 2018 14:46:03 +0200 Subject: [PATCH 2/8] COMDEVNL-1156 -- implemented 2.0 api as far as possible --- CHANGELOG.md | 2 +- .../extension/payment/mollie/base.php | 21 +- .../extension/payment/mollie_bancontact.php | 7 + .../extension/payment/mollie_mistercash.php | 7 - .../dutch/extension/payment/mollie.php | 4 +- .../extension/payment/mollie_bancontact.php | 4 + .../extension/payment/mollie_mistercash.php | 4 - .../en-gb/extension/payment/mollie.php | 4 +- .../extension/payment/mollie_bancontact.php | 4 + .../extension/payment/mollie_mistercash.php | 4 - .../english/extension/payment/mollie.php | 4 +- .../extension/payment/mollie_bancontact.php | 4 + .../extension/payment/mollie_mistercash.php | 4 - .../fr-fr/extension/payment/mollie.php | 4 +- .../extension/payment/mollie_bancontact.php | 4 + .../extension/payment/mollie_mistercash.php | 4 - .../french/extension/payment/mollie.php | 4 +- .../extension/payment/mollie_bancontact.php | 4 + .../extension/payment/mollie_mistercash.php | 4 - .../nl-nl/extension/payment/mollie.php | 4 +- .../extension/payment/mollie_bancontact.php | 4 + .../extension/payment/mollie_mistercash.php | 4 - .../extension/payment/mollie/base.php | 1247 +++++++++-------- .../extension/payment/mollie/helper.php | 16 +- .../extension/payment/mollie_bancontact.php | 7 + .../extension/payment/mollie_mistercash.php | 7 - .../dutch/extension/payment/mollie.php | 2 +- .../en-gb/extension/payment/mollie.php | 2 +- .../english/extension/payment/mollie.php | 2 +- .../fr-fr/extension/payment/mollie.php | 2 +- .../french/extension/payment/mollie.php | 2 +- .../nl-nl/extension/payment/mollie.php | 2 +- .../model/extension/payment/mollie/base.php | 4 +- .../extension/payment/mollie_bancontact.php | 7 + .../extension/payment/mollie_mistercash.php | 7 - readme.md | 4 +- 36 files changed, 718 insertions(+), 702 deletions(-) create mode 100644 admin/controller/extension/payment/mollie_bancontact.php delete mode 100644 admin/controller/extension/payment/mollie_mistercash.php create mode 100644 admin/language/dutch/extension/payment/mollie_bancontact.php delete mode 100644 admin/language/dutch/extension/payment/mollie_mistercash.php create mode 100644 admin/language/en-gb/extension/payment/mollie_bancontact.php delete mode 100644 admin/language/en-gb/extension/payment/mollie_mistercash.php create mode 100644 admin/language/english/extension/payment/mollie_bancontact.php delete mode 100644 admin/language/english/extension/payment/mollie_mistercash.php create mode 100644 admin/language/fr-fr/extension/payment/mollie_bancontact.php delete mode 100644 admin/language/fr-fr/extension/payment/mollie_mistercash.php create mode 100644 admin/language/french/extension/payment/mollie_bancontact.php delete mode 100644 admin/language/french/extension/payment/mollie_mistercash.php create mode 100644 admin/language/nl-nl/extension/payment/mollie_bancontact.php delete mode 100644 admin/language/nl-nl/extension/payment/mollie_mistercash.php create mode 100644 catalog/controller/extension/payment/mollie_bancontact.php delete mode 100644 catalog/controller/extension/payment/mollie_mistercash.php create mode 100644 catalog/model/extension/payment/mollie_bancontact.php delete mode 100644 catalog/model/extension/payment/mollie_mistercash.php diff --git a/CHANGELOG.md b/CHANGELOG.md index 5bebfbe4..a94f59fd 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -169,7 +169,7 @@ #### Wijzigigen in versie 5.0 + De module gebruikt nu de nieuwe betalings-API van Mollie. Dit betekent dat de module naast [iDEAL](https://www.mollie.com/ideal/), nu - ook [creditcard](https://www.mollie.com/creditcard/), [Mister Cash](https://www.mollie.com/mistercash/) en [paysafecard](https://www.mollie.com/paysafecard/) + ook [creditcard](https://www.mollie.com/creditcard/), [Mister Cash](https://www.mollie.com/bancontact/) en [paysafecard](https://www.mollie.com/paysafecard/) ondersteunt. Mocht een betaling om wat voor reden dan ook niet lukken, dat kan uw klant het gelijk nog een keer proberen. U hoeft hiervoor niets extra's te implementeren. In de toekomst zullen ook nog nieuwe betaalmethodes toegevoegd worden. Deze zijn dan direct beschikbaar in uw webshop. + Het instellingenscherm in de admin toont nu gelijk of de module correct kan communiceren met de Mollie API. Hierdoor kunnen we u beter helpen wanneer diff --git a/admin/controller/extension/payment/mollie/base.php b/admin/controller/extension/payment/mollie/base.php index 5bd1cd17..cc913ca5 100644 --- a/admin/controller/extension/payment/mollie/base.php +++ b/admin/controller/extension/payment/mollie/base.php @@ -42,6 +42,10 @@ * @property URL $url * @property User $user */ +use Mollie\Api\Exceptions\ApiException; +use Mollie\Api\Exceptions\IncompatiblePlatform; +use Mollie\Api\MollieApiClient; + require_once(dirname(DIR_SYSTEM) . "/catalog/controller/extension/payment/mollie/helper.php"); class ControllerExtensionPaymentMollieBase extends Controller @@ -57,7 +61,7 @@ class ControllerExtensionPaymentMollieBase extends Controller /** * @param int $store The Store ID - * @return Mollie_API_Client + * @return MollieApiClient */ protected function getAPIClient ($store = 0) { @@ -408,7 +412,7 @@ public function index () $allowed_methods[] = $api_method->id; } } - catch (Mollie_API_Exception $e) + catch (Mollie\Api\Exceptions\ApiException $e) { // If we have an unauthorized request, our API key is likely invalid. if ($data['stores'][$store['id']][$code . '_api_key'] !== NULL && strpos($e->getMessage(), "Unauthorized request") >= 0) @@ -472,7 +476,10 @@ public function index () )); } - public function validate_api_key() + /** + * + */ + public function validate_api_key() { $json = array( 'error' => false, @@ -497,10 +504,10 @@ public function validate_api_key() $json['valid'] = true; $json['message'] = 'Ok.'; } - } catch (Mollie_API_Exception_IncompatiblePlatform $e) { + } catch (IncompatiblePlatform $e) { $json['error'] = true; $json['message'] = $e->getMessage() . ' You can ask your hosting provider to help with this.'; - } catch (Mollie_API_Exception $e) { + } catch (ApiException $e) { $json['error'] = true; $json['message'] = 'Communicating with Mollie failed:
' . htmlspecialchars($e->getMessage()) @@ -565,9 +572,9 @@ protected function checkCommunicationStatus ($api_key = null) $client->methods->all(); return 'OK'; - } catch (Mollie_API_Exception_IncompatiblePlatform $e) { + } catch (Mollie\Api\Exceptions\ApiException_IncompatiblePlatform $e) { return '' . $e->getMessage() . ' You can ask your hosting provider to help with this.'; - } catch (Mollie_API_Exception $e) { + } catch (Mollie\Api\Exceptions\ApiException $e) { return '' . 'Communicating with Mollie failed:
' . htmlspecialchars($e->getMessage()) diff --git a/admin/controller/extension/payment/mollie_bancontact.php b/admin/controller/extension/payment/mollie_bancontact.php new file mode 100644 index 00000000..c37c6d61 --- /dev/null +++ b/admin/controller/extension/payment/mollie_bancontact.php @@ -0,0 +1,7 @@ +config); - } - - /** - * - * Keep a log of Mollie transactions. - * - * @param $line string - * @param $alsoEcho bool - */ - protected function writeToMollieLog($line, $alsoEcho = false) - { - $log = new Log('Mollie.log'); - $log->write($line); - if ($alsoEcho) echo $line; - } - - /** - * @return ModelExtensionPaymentMollieBase - */ - protected function getModuleModel() - { - $model_name = "model_extension_payment_mollie_" . static::MODULE_NAME; - - if (!isset($this->$model_name)) { - $this->load->model("extension/payment/mollie_" . static::MODULE_NAME); - } - - return $this->$model_name; - } - - /** - * @return bool - */ - protected function getOrderID() - { - if (empty($this->session->data['order_id']) && !isset($this->request->get['order_id'])) { - return false; - } - if (isset($this->request->get['order_id'])) { - return $this->request->get['order_id']; - } - return $this->session->data['order_id']; - } - - /** - * Get the order we are processing from OpenCart. - * - * @return array - */ - protected function getOpenCartOrder($order_id) - { - $this->load->model("checkout/order"); - // Load last order from session - return $this->model_checkout_order->getOrder($order_id); - } - - /** - * This gets called by OpenCart at the final checkout step and should generate a confirmation button. - * @return string - */ - public function index() - { - $this->load->language("extension/payment/mollie"); - - $payment_method = $this->getAPIClient()->methods->get(static::MODULE_NAME, array('include' => 'issuers')); - - // Set template data. - $data['action'] = $this->url->link("extension/payment/mollie_" . static::MODULE_NAME . "/payment", "", "SSL"); - $data['image'] = $payment_method->image->normal; - $data['message'] = $this->language; - $data['issuers'] = isset($payment_method->issuers) ? $payment_method->issuers : array(); - $data['text_issuer'] = $this->language->get("text_issuer_" . static::MODULE_NAME); - $data['set_issuer_url'] = $this->url->link("extension/payment/mollie_" . static::MODULE_NAME . "/set_issuer", "", "SSL"); - - // Return HTML output - it will get appended to confirm.tpl. - return $this->renderTemplate('mollie_checkout_form', $data, array(), false); - } - - /** - * The payment action creates the payment and redirects the customer to the selected bank. - * - * It is called when the customer submits the button generated in the mollie_checkout_form template. - */ - public function payment() - { - if ($this->request->server['REQUEST_METHOD'] != "POST") { - return; - } - try { - $api = $this->getAPIClient(); - } catch (Mollie_API_Exception $e) { - $this->showErrorPage($e->getMessage()); - $this->writeToMollieLog("Creating payment failed, API did not load; " . $e->getMessage()); - return; - } - - // Load essentials - $this->load->language("extension/payment/mollie"); - - $model = $this->getModuleModel(); - $order_id = $this->getOrderID(); - $order = $this->getOpenCartOrder($order_id); - - $amount = $this->currency->convert($order['total'], $this->config->get("config_currency"), "EUR"); - - $amount = round($amount, 2); - $description = str_replace("%", $order['order_id'], html_entity_decode($this->config->get(MollieHelper::getModuleCode() . "_ideal_description"), ENT_QUOTES, "UTF-8")); - $return_url = $this->url->link("extension/payment/mollie_" . static::MODULE_NAME . "/callback&order_id=" . $order['order_id'], "", "SSL"); - $issuer = $this->getIssuer(); - - try { - $data = array( - "amount" => $amount, - "description" => $description, - "redirectUrl" => $return_url, - "webhookUrl" => $this->getWebhookUrl(), - "metadata" => array("order_id" => $order['order_id']), - "method" => static::MODULE_NAME, - "issuer" => $issuer, - - /* - * This data is sent along for credit card payments / fraud checks. You can remove this but you will - * have a higher conversion if you leave it here. - */ - - "billingCity" => $order['payment_city'], - "billingRegion" => $order['payment_zone'], - "billingPostal" => $order['payment_postcode'], - "billingCountry" => $order['payment_iso_code_2'], - - "shippingAddress" => $order['shipping_address_1'] ? $order['shipping_address_1'] : null, - "shippingCity" => $order['shipping_city'] ? $order['shipping_city'] : $order['payment_city'], - "shippingRegion" => $order['shipping_zone'] ? $order['shipping_zone'] : $order['payment_zone'], - "shippingPostal" => $order['shipping_postcode'] ? $order['shipping_postcode'] : $order['payment_postcode'], - "shippingCountry" => $order['shipping_iso_code_2'] ? $order['shipping_iso_code_2'] : $order['payment_iso_code_2'], - ); - - $locales = array( - 'en_US', - 'de_AT', - 'de_CH', - 'de_DE', - 'es_ES', - 'fr_BE', - 'fr_FR', - 'nl_BE', - 'nl_NL' - ); - - if (strstr($this->session->data['language'], '-')) { - list ($language, $country) = explode('-', $this->session->data['language']); - $locale = strtolower($language) . '_' . strtoupper($country); - } - - if (!in_array($locale, $locales)) { - $locale = 'en_US'; - } - - $payment = $api->payments->create($data); - } catch (Mollie_Api_Exception $e) { - $this->showErrorPage($e->getMessage()); - $this->writeToMollieLog("Creating payment failed; " . $e->getMessage()); - return; - } - - // Some payment methods can't be cancelled. They need an initial order status. - if ($this->startAsPending()) { - $this->addOrderHistory($order, $this->config->get(MollieHelper::getModuleCode() . "_ideal_pending_status_id"), $this->language->get("text_redirected"), false); - } - - $model->setPayment($order['order_id'], $payment->id); - - // Redirect to payment gateway. - $this->redirect($payment->links->paymentUrl); - } - - /** - * Some payment methods can't be cancelled. They need 'pending' as an initial order status. - * - * @return bool - */ - protected function startAsPending() - { - return false; - } - - /** - * This action is getting called by Mollie to report the payment status - */ - public function webhook() - { - if(empty($this->request->post['id'])) { - header("HTTP/1.0 400 Bad Request"); - $this->writeToMollieLog("Webhook called but no ID received.", true); - return; - } - - $moduleCode = MollieHelper::getModuleCode(); - $payment_id = $this->request->post['id']; - $this->writeToMollieLog("Received webhook for payment_id " . $payment_id); - - $payment = $this->getAPIClient()->payments->get($payment_id); - - // Load essentials - $this->load->model("checkout/order"); - $this->getModuleModel(); - $this->load->language("extension/payment/mollie"); - - //Get order_id of this transaction from db - $order = $this->model_checkout_order->getOrder($payment->metadata->order_id); - - if (empty($order)) { - header("HTTP/1.0 404 Not Found"); - echo "Could not find order."; - return; - } - - // Only process the status if the order is stateless or in 'pending' status. - if (!empty($order['order_status_id']) && $order['order_status_id'] != $this->config->get($moduleCode . "_ideal_pending_status_id")) { - $this->writeToMollieLog("The order was already processed before (order status ID: " . intval($order['order_status_id']) . ")"); - return; - } - - // Order paid ('processed'). - if ($payment->isPaid()) { - $new_status_id = intval($this->config->get($moduleCode . "_ideal_processing_status_id")); - - if (!$new_status_id) { - $this->writeToMollieLog("The payment has been received. No 'processing' status ID is configured, so the order status could not be updated.", true); - return; - } - $this->addOrderHistory($order, $new_status_id, $this->language->get("response_success"), true); - $this->writeToMollieLog("The payment was received and the order was moved to the 'processing' status (new status ID: {$new_status_id}.", true); - return; - } - - // Order cancelled. - if ($payment->status == Mollie_API_Object_Payment::STATUS_CANCELLED) { - $new_status_id = intval($this->config->get($moduleCode . "_ideal_canceled_status_id")); - - if (!$new_status_id) { - $this->writeToMollieLog("The payment was cancelled. No 'cancelled' status ID is configured, so the order status could not be updated.", true); - return; - } - $this->addOrderHistory($order, $new_status_id, $this->language->get("response_cancelled"), false); - $this->writeToMollieLog("The payment was cancelled and the order was moved to the 'cancelled' status (new status ID: {$new_status_id}).", true); - return; - } - - // Order expired. - if ($payment->status == Mollie_API_Object_Payment::STATUS_EXPIRED) { - $new_status_id = intval($this->config->get($moduleCode . "_ideal_expired_status_id")); - - if (!$new_status_id) { - $this->writeToMollieLog("The payment expired. No 'expired' status ID is configured, so the order status could not be updated.", true); - return; - } - $this->addOrderHistory($order, $new_status_id, $this->language->get("response_expired"), false); - $this->writeToMollieLog("The payment expired and the order was moved to the 'expired' status (new status ID: {$new_status_id}).", true); - return; - } - - // Otherwise, order failed. - $new_status_id = intval($this->config->get($moduleCode . "_ideal_failed_status_id")); - - if (!$new_status_id) { - $this->writeToMollieLog("The payment failed. No 'failed' status ID is configured, so the order status could not be updated.", true); - return; - } - $this->addOrderHistory($order, $new_status_id, $this->language->get("response_unknown"), false); - $this->writeToMollieLog("The payment failed for an unknown reason and the order was moved to the 'failed' status (new status ID: {$new_status_id}).", true); - return; - - } - - /** - * Gets called via AJAX from the checkout form to store the selected issuer. - */ - public function set_issuer() - { - if (!empty($this->request->post['mollie_issuer_id'])) { - $this->session->data['mollie_issuer'] = $this->request->post['mollie_issuer_id']; - } else { - $this->session->data['mollie_issuer'] = null; - } - - echo $this->session->data['mollie_issuer']; - } - - /** - * Retrieve the issuer if one was selected. Return null otherwise. - * - * @return string|null - */ - protected function getIssuer() - { - if (!empty($this->request->post['mollie_issuer'])) { - return $this->request->post['mollie_issuer']; - } - - if (!empty($this->session->data['mollie_issuer'])) { - return $this->session->data['mollie_issuer']; - } - - return null; - } - - /** - * Customer returning from the bank with an transaction_id - * Depending on what the state of the payment is they get redirected to the corresponding page - * - * @return string - */ - public function callback() - { - $moduleCode = MollieHelper::getModuleCode(); - $order_id = $this->getOrderID(); - - if ($order_id === false) { - $this->writeToMollieLog("Failed to get order id."); - - return $this->showReturnPage( - $this->language->get("heading_failed"), - $this->language->get("msg_failed") - ); - } - - $order = $this->getOpenCartOrder($order_id); - - if (empty($order)) { - $this->writeToMollieLog("Failed to get order for order id: " . $order_id); - - return $this->showReturnPage( - $this->language->get("heading_failed"), - $this->language->get("msg_failed") - ); - } - - $this->writeToMollieLog("Received callback for order " . $order_id); - - // Load required translations. - $this->load->language("extension/payment/mollie"); - - // Double-check whether or not the status of the order is correct. - $model = $this->getModuleModel(); - - $paid_status_id = intval($this->config->get($moduleCode . "_ideal_processing_status_id")); - $payment_id = $model->getPaymentID($order['order_id']); - - if ($payment_id === false) { - $this->writeToMollieLog("Error getting payment id for order " . $order['order_id']); - - return $this->showReturnPage( - $this->language->get("heading_failed"), - $this->language->get("msg_failed") - ); - } - - $payment = $this->getAPIClient()->payments->get($payment_id); - - if ($payment->isPaid() && $order['order_status_id'] != $paid_status_id) { - $this->addOrderHistory($order, $paid_status_id, $this->language->get("response_success"), true); - $order['order_status_id'] = $paid_status_id; - } - - // Show a 'transaction failed' page if we couldn't find the order or if the payment failed. - $failed_status_id = $this->config->get($moduleCode . "_ideal_failed_status_id"); - - if (!$order || ($failed_status_id && $order['order_status_id'] == $failed_status_id)) { - if ($failed_status_id && $order['order_status_id'] == $failed_status_id) { - $this->writeToMollieLog("Error payment failed for order " . $order['order_id']); - } else { - $this->writeToMollieLog("Error couldn't find order"); - } - - return $this->showReturnPage( - $this->language->get("heading_failed"), - $this->language->get("msg_failed") - ); - } - - // If the order status is 'processing' (i.e. 'paid'), redirect to OpenCart's default 'success' page. - if ($order["order_status_id"] == $this->config->get($moduleCode . "_ideal_processing_status_id")) { - $this->writeToMollieLog("Success redirect to success page for order " . $order['order_id']); - - if ($this->cart) { - $this->cart->clear(); - } - - // Redirect to 'success' page. - $this->redirect($this->url->link("checkout/success", "", "SSL")); - return ''; - } - - // If the status is 'pending' (i.e. a bank transfer), the report is not delivered yet. - if ($order['order_status_id'] == $this->config->get($moduleCode . "_ideal_pending_status_id")) { - $this->writeToMollieLog("Unknown payment status for order " . $order['order_id']); - - if ($this->cart) { - $this->cart->clear(); - } - - return $this->showReturnPage( - $this->language->get("heading_unknown"), - $this->language->get("msg_unknown"), - null, - false - ); - } - - // The status is probably 'cancelled'. Allow the admin to redirect their customers back to the shopping cart directly in these cases. - if (!(bool)$this->config->get($moduleCode . "_show_order_canceled_page")) { - $this->redirect($this->url->link("checkout/checkout", "", "SSL")); - } - - // Show a 'transaction failed' page if all else fails. - $this->writeToMollieLog("Everything else failed for order " . $order['order_id']); - - return $this->showReturnPage( - $this->language->get("heading_failed"), - $this->language->get("msg_failed") - ); - } - - /** - * @param &$data - */ - protected function setBreadcrumbs(&$data) - { - $data['breadcrumbs'] = array(); - - $data['breadcrumbs'][] = array( - "href" => $this->url->link("common/home", (isset($this->session->data['token'])) ? "token=" . $this->session->data['token'] : "", "SSL"), - "text" => $this->language->get("text_home"), - "separator" => false, - ); - } - - /** - * @param $message - * - * @return string - */ - protected function showErrorPage($message) - { - $this->load->language("extension/payment/mollie"); - - $this->log->write("Error setting up transaction with Mollie: {$message}."); - - return $this->showReturnPage( - $this->language->get("heading_error"), - $this->language->get("text_error"), - $message - ); - } - - /** - * Render a return page. - * - * @param string $title The title of the status page. - * @param string $body The status message. - * @param string|null $api_error Show an API error, if applicable. - * @param bool $show_retry_button Show a retry button that redirects the customer back to the checkout page. - * - * @return string - */ - protected function showReturnPage($title, $body, $api_error = null, $show_retry_button = true) - { - $this->load->language("extension/payment/mollie"); - - $data['message_title'] = $title; - $data['message_text'] = $body; - - if ($api_error) { - $data['mollie_error'] = $api_error; - } - - if ($show_retry_button) { - $data['checkout_url'] = $this->url->link("checkout/checkout", "", "SSL"); - $data['button_retry'] = $this->language->get("button_retry"); - } - - $this->document->setTitle($this->language->get("ideal_title")); - - $this->setBreadcrumbs($data); - - return $this->renderTemplate("mollie_return", $data, array( - "column_left", - "column_right", - "content_top", - "content_bottom", - "footer", - "header", - )); - } - - /** - * We check for and remove the admin url in the webhook link. - * - * @return string|null - */ - public function getWebhookUrl() - { - $system_webhook_url = $this->url->link("extension/payment/mollie_" . static::MODULE_NAME . "/webhook", "", "SSL"); - - if (strpos($system_webhook_url, $this->getAdminDirectory()) !== false) { - return str_replace($this->getAdminDirectory(), "", $system_webhook_url); - } - - return $system_webhook_url ? $system_webhook_url : null; - } - - /** - * Retrieves the admin directoryname from the catalog and admin urls. - * - * @return string - */ - protected function getAdminDirectory() - { - // if no default admin URL defined in the config, use the default admin directory. - if (!defined('HTTP_ADMIN')) { - return "admin/"; - } - - return str_replace(HTTP_SERVER, "", HTTP_ADMIN); - } - - /** - * Map payment status history handling for different Opencart versions. - * - * @param array $order - * @param int|string $order_status_id - * @param string $comment - * @param bool $notify - */ - protected function addOrderHistory($order, $order_status_id, $comment = "", $notify = false) - { - if (MollieHelper::isOpenCart2x()) { - $this->model_checkout_order->addOrderHistory($order['order_id'], $order_status_id, $comment, $notify); - } else { - if (empty($order['order_status_id'])) { - $this->model_checkout_order->confirm($order['order_id'], $order_status_id, $comment, $notify); - } else { - $this->model_checkout_order->update($order['order_id'], $order_status_id, $comment, $notify); - } - } - } - - /** - * Map template handling for different Opencart versions. - * - * @param string $template - * @param array $data - * @param array $common_children - * @param bool $echo - * @return string - */ - protected function renderTemplate($template, $data, $common_children = array(), $echo = true) - { - if (!MollieHelper::isOpenCart3x()) { - $template .= '.tpl'; - } - - if (file_exists(DIR_TEMPLATE . $this->config->get('config_template') . '/template/payment/' . $template)) { - $template = $this->config->get('config_template') . '/template/payment/' . $template; - } else if (file_exists(DIR_TEMPLATE . 'default/template/payment/' . $template)) { - $template = 'default/template/payment/' . $template; - } else { - $template = 'extension/payment/' . $template; - } - - if (MollieHelper::isOpenCart2x()) { - foreach ($common_children as $child) { - $data[$child] = $this->load->controller("common/" . $child); - } - - $html = $this->load->view($template, $data); - } else { - $this->template = $template; - $this->children = array(); - - foreach ($data as $field => $value) { - $this->data[$field] = $value; - } - - foreach($common_children as $child) { - if ($child === 'column_left') { - continue; - } - - $this->children[] = "common/" . $child; - } - - $html = $this->render(); - } - - if ($echo) { - return $this->response->setOutput($html); - } - - return $html; - } - - /** - * @param string $url - * @param int $status - */ - protected function redirect($url, $status = 302) - { - $this->response->redirect($url, $status); - } + // Current module name - should be overwritten by subclass using one of the values below. + const MODULE_NAME = null; + + /** + * @return MollieApiClient + */ + protected function getAPIClient() + { + return MollieHelper::getAPIClient($this->config); + } + + /** + * + * Keep a log of Mollie transactions. + * + * @param $line string + * @param $alsoEcho bool + */ + protected function writeToMollieLog($line, $alsoEcho = false) + { + $log = new Log('Mollie.log'); + $log->write($line); + if ($alsoEcho) echo $line; + } + + /** + * @return ModelExtensionPaymentMollieBase + */ + protected function getModuleModel() + { + $model_name = "model_extension_payment_mollie_" . static::MODULE_NAME; + + if (!isset($this->$model_name)) { + $this->load->model("extension/payment/mollie_" . static::MODULE_NAME); + } + + return $this->$model_name; + } + + /** + * @return bool + */ + protected function getOrderID() + { + if (empty($this->session->data['order_id']) && !isset($this->request->get['order_id'])) { + return false; + } + if (isset($this->request->get['order_id'])) { + return $this->request->get['order_id']; + } + return $this->session->data['order_id']; + } + + /** + * Get the order we are processing from OpenCart. + * + * @return array + */ + protected function getOpenCartOrder($order_id) + { + $this->load->model("checkout/order"); + // Load last order from session + return $this->model_checkout_order->getOrder($order_id); + } + + /** + * This gets called by OpenCart at the final checkout step and should generate a confirmation button. + * @return string + */ + public function index() + { + $this->load->language("extension/payment/mollie"); + + $payment_method = $this->getAPIClient()->methods->get(static::MODULE_NAME, array('include' => 'issuers')); + + // Set template data. + $data['action'] = $this->url->link("extension/payment/mollie_" . static::MODULE_NAME . "/payment", "", "SSL"); + $data['image'] = $payment_method->image->normal; + $data['message'] = $this->language; + $data['issuers'] = isset($payment_method->issuers) ? $payment_method->issuers : array(); + $data['text_issuer'] = $this->language->get("text_issuer_" . static::MODULE_NAME); + $data['set_issuer_url'] = $this->url->link("extension/payment/mollie_" . static::MODULE_NAME . "/set_issuer", "", "SSL"); + + // Return HTML output - it will get appended to confirm.tpl. + return $this->renderTemplate('mollie_checkout_form', $data, array(), false); + } + + /** + * The payment action creates the payment and redirects the customer to the selected bank. + * + * It is called when the customer submits the button generated in the mollie_checkout_form template. + */ + public function payment() + { + if ($this->request->server['REQUEST_METHOD'] != "POST") { + return; + } + try { + $api = $this->getAPIClient(); + } catch (Mollie\Api\Exceptions\ApiException $e) { + $this->showErrorPage($e->getMessage()); + $this->writeToMollieLog("Creating payment failed, API did not load; " . $e->getMessage()); + return; + } + + // Load essentials + $this->load->language("extension/payment/mollie"); + + $model = $this->getModuleModel(); + $order_id = $this->getOrderID(); + $order = $this->getOpenCartOrder($order_id); + + $amount = $this->currency->convert($order['total'], $this->config->get("config_currency"), "EUR"); + $amount = round($amount, 2); + $description = str_replace("%", $order['order_id'], html_entity_decode($this->config->get(MollieHelper::getModuleCode() . "_ideal_description"), ENT_QUOTES, "UTF-8")); + $return_url = $this->url->link("extension/payment/mollie_" . static::MODULE_NAME . "/callback&order_id=" . $order['order_id'], "", "SSL"); + $issuer = $this->getIssuer(); + + try { + $data = array( + "amount" => ["currency" => "EUR", "value" => $amount], + "description" => $description, + "redirectUrl" => $return_url, + "webhookUrl" => $this->getWebhookUrl(), + "metadata" => array("order_id" => $order['order_id']), + "method" => static::MODULE_NAME, + "issuer" => $issuer, + + /* + * This data is sent along for credit card payments / fraud checks. You can remove this but you will + * have a higher conversion if you leave it here. + */ + + "billingAddress" => [ + "city" => $order['payment_city'], + "region" => $order['payment_zone'], + "postalCode" => $order['payment_postcode'], + "country" => $order['payment_iso_code_2'] + ], + + "shippingAddress" => [ + "city" => $order['shipping_city'], + "region" => $order['shipping_zone'], + "postalCode" => $order['shipping_postcode'], + "country" => $order['shipping_iso_code_2'] + ], + ); + + $locales = array( + 'en_US', + 'de_AT', + 'de_CH', + 'de_DE', + 'es_ES', + 'fr_BE', + 'fr_FR', + 'nl_BE', + 'nl_NL' + ); + + if (strstr($this->session->data['language'], '-')) { + list ($language, $country) = explode('-', $this->session->data['language']); + $locale = strtolower($language) . '_' . strtoupper($country); + } + + if (!in_array($locale, $locales)) { + $locale = 'nl_NL'; + } + + $data["locale"]=$locale; + + $payment = $api->payments->create($data); + } catch (Mollie\Api\Exceptions\ApiException $e) { + $this->showErrorPage($e->getMessage()); + $this->writeToMollieLog("Creating payment failed; " . $e->getMessage()); + return; + } + + // Some payment methods can't be cancelled. They need an initial order status. + if ($this->startAsPending()) { + $this->addOrderHistory($order, $this->config->get(MollieHelper::getModuleCode() . "_ideal_pending_status_id"), $this->language->get("text_redirected"), false); + } + + $model->setPayment($order['order_id'], $payment->id); + + // Redirect to payment gateway. + $this->redirect($payment->_links->checkout); + } + + /** + * Some payment methods can't be cancelled. They need 'pending' as an initial order status. + * + * @return bool + */ + protected function startAsPending() + { + return false; + } + + /** + * This action is getting called by Mollie to report the payment status + */ + public function webhook() + { + if (empty($this->request->post['id'])) { + header("HTTP/1.0 400 Bad Request"); + $this->writeToMollieLog("Webhook called but no ID received.", true); + return; + } + + $moduleCode = MollieHelper::getModuleCode(); + $payment_id = $this->request->post['id']; + $this->writeToMollieLog("Received webhook for payment_id " . $payment_id); + + $payment = $this->getAPIClient()->payments->get($payment_id); + + // Load essentials + $this->load->model("checkout/order"); + $this->getModuleModel(); + $this->load->language("extension/payment/mollie"); + + //Get order_id of this transaction from db + $order = $this->model_checkout_order->getOrder($payment->metadata->order_id); + + if (empty($order)) { + header("HTTP/1.0 404 Not Found"); + echo "Could not find order."; + return; + } + + // Only process the status if the order is stateless or in 'pending' status. + if (!empty($order['order_status_id']) && $order['order_status_id'] != $this->config->get($moduleCode . "_ideal_pending_status_id")) { + $this->writeToMollieLog("The order was already processed before (order status ID: " . intval($order['order_status_id']) . ")"); + return; + } + + // Order paid ('processed'). + if ($payment->isPaid()) { + $new_status_id = intval($this->config->get($moduleCode . "_ideal_processing_status_id")); + + if (!$new_status_id) { + $this->writeToMollieLog("The payment has been received. No 'processing' status ID is configured, so the order status could not be updated.", true); + return; + } + $this->addOrderHistory($order, $new_status_id, $this->language->get("response_success"), true); + $this->writeToMollieLog("The payment was received and the order was moved to the 'processing' status (new status ID: {$new_status_id}.", true); + return; + } + + // Order cancelled. + if ($payment->status == PaymentStatus::STATUS_CANCELED) { + $new_status_id = intval($this->config->get($moduleCode . "_ideal_canceled_status_id")); + + if (!$new_status_id) { + $this->writeToMollieLog("The payment was cancelled. No 'cancelled' status ID is configured, so the order status could not be updated.", true); + return; + } + $this->addOrderHistory($order, $new_status_id, $this->language->get("response_cancelled"), false); + $this->writeToMollieLog("The payment was cancelled and the order was moved to the 'cancelled' status (new status ID: {$new_status_id}).", true); + return; + } + + // Order expired. + if ($payment->status == PaymentStatus::STATUS_CANCELED) { + $new_status_id = intval($this->config->get($moduleCode . "_ideal_expired_status_id")); + + if (!$new_status_id) { + $this->writeToMollieLog("The payment expired. No 'expired' status ID is configured, so the order status could not be updated.", true); + return; + } + $this->addOrderHistory($order, $new_status_id, $this->language->get("response_expired"), false); + $this->writeToMollieLog("The payment expired and the order was moved to the 'expired' status (new status ID: {$new_status_id}).", true); + return; + } + + // Otherwise, order failed. + $new_status_id = intval($this->config->get($moduleCode . "_ideal_failed_status_id")); + + if (!$new_status_id) { + $this->writeToMollieLog("The payment failed. No 'failed' status ID is configured, so the order status could not be updated.", true); + return; + } + $this->addOrderHistory($order, $new_status_id, $this->language->get("response_unknown"), false); + $this->writeToMollieLog("The payment failed for an unknown reason and the order was moved to the 'failed' status (new status ID: {$new_status_id}).", true); + return; + + } + + /** + * Gets called via AJAX from the checkout form to store the selected issuer. + */ + public function set_issuer() + { + if (!empty($this->request->post['mollie_issuer_id'])) { + $this->session->data['mollie_issuer'] = $this->request->post['mollie_issuer_id']; + } else { + $this->session->data['mollie_issuer'] = null; + } + + echo $this->session->data['mollie_issuer']; + } + + /** + * Retrieve the issuer if one was selected. Return null otherwise. + * + * @return string|null + */ + protected function getIssuer() + { + if (!empty($this->request->post['mollie_issuer'])) { + return $this->request->post['mollie_issuer']; + } + + if (!empty($this->session->data['mollie_issuer'])) { + return $this->session->data['mollie_issuer']; + } + + return null; + } + + /** + * Customer returning from the bank with an transaction_id + * Depending on what the state of the payment is they get redirected to the corresponding page + * + * @return string + */ + public function callback() + { + $moduleCode = MollieHelper::getModuleCode(); + $order_id = $this->getOrderID(); + + if ($order_id === false) { + $this->writeToMollieLog("Failed to get order id."); + + return $this->showReturnPage( + $this->language->get("heading_failed"), + $this->language->get("msg_failed") + ); + } + + $order = $this->getOpenCartOrder($order_id); + + if (empty($order)) { + $this->writeToMollieLog("Failed to get order for order id: " . $order_id); + + return $this->showReturnPage( + $this->language->get("heading_failed"), + $this->language->get("msg_failed") + ); + } + + $this->writeToMollieLog("Received callback for order " . $order_id); + + // Load required translations. + $this->load->language("extension/payment/mollie"); + + // Double-check whether or not the status of the order is correct. + $model = $this->getModuleModel(); + + $paid_status_id = intval($this->config->get($moduleCode . "_ideal_processing_status_id")); + $payment_id = $model->getPaymentID($order['order_id']); + + if ($payment_id === false) { + $this->writeToMollieLog("Error getting payment id for order " . $order['order_id']); + + return $this->showReturnPage( + $this->language->get("heading_failed"), + $this->language->get("msg_failed") + ); + } + + $payment = $this->getAPIClient()->payments->get($payment_id); + + if ($payment->isPaid() && $order['order_status_id'] != $paid_status_id) { + $this->addOrderHistory($order, $paid_status_id, $this->language->get("response_success"), true); + $order['order_status_id'] = $paid_status_id; + } + + // Show a 'transaction failed' page if we couldn't find the order or if the payment failed. + $failed_status_id = $this->config->get($moduleCode . "_ideal_failed_status_id"); + + if (!$order || ($failed_status_id && $order['order_status_id'] == $failed_status_id)) { + if ($failed_status_id && $order['order_status_id'] == $failed_status_id) { + $this->writeToMollieLog("Error payment failed for order " . $order['order_id']); + } else { + $this->writeToMollieLog("Error couldn't find order"); + } + + return $this->showReturnPage( + $this->language->get("heading_failed"), + $this->language->get("msg_failed") + ); + } + + // If the order status is 'processing' (i.e. 'paid'), redirect to OpenCart's default 'success' page. + if ($order["order_status_id"] == $this->config->get($moduleCode . "_ideal_processing_status_id")) { + $this->writeToMollieLog("Success redirect to success page for order " . $order['order_id']); + + if ($this->cart) { + $this->cart->clear(); + } + + // Redirect to 'success' page. + $this->redirect($this->url->link("checkout/success", "", "SSL")); + return ''; + } + + // If the status is 'pending' (i.e. a bank transfer), the report is not delivered yet. + if ($order['order_status_id'] == $this->config->get($moduleCode . "_ideal_pending_status_id")) { + $this->writeToMollieLog("Unknown payment status for order " . $order['order_id']); + + if ($this->cart) { + $this->cart->clear(); + } + + return $this->showReturnPage( + $this->language->get("heading_unknown"), + $this->language->get("msg_unknown"), + null, + false + ); + } + + // The status is probably 'cancelled'. Allow the admin to redirect their customers back to the shopping cart directly in these cases. + if (!(bool)$this->config->get($moduleCode . "_show_order_canceled_page")) { + $this->redirect($this->url->link("checkout/checkout", "", "SSL")); + } + + // Show a 'transaction failed' page if all else fails. + $this->writeToMollieLog("Everything else failed for order " . $order['order_id']); + + return $this->showReturnPage( + $this->language->get("heading_failed"), + $this->language->get("msg_failed") + ); + } + + /** + * @param &$data + */ + protected function setBreadcrumbs(&$data) + { + $data['breadcrumbs'] = array(); + + $data['breadcrumbs'][] = array( + "href" => $this->url->link("common/home", (isset($this->session->data['token'])) ? "token=" . $this->session->data['token'] : "", "SSL"), + "text" => $this->language->get("text_home"), + "separator" => false, + ); + } + + /** + * @param $message + * + * @return string + */ + protected function showErrorPage($message) + { + $this->load->language("extension/payment/mollie"); + + $this->log->write("Error setting up transaction with Mollie: {$message}."); + + return $this->showReturnPage( + $this->language->get("heading_error"), + $this->language->get("text_error"), + $message + ); + } + + /** + * Render a return page. + * + * @param string $title The title of the status page. + * @param string $body The status message. + * @param string|null $api_error Show an API error, if applicable. + * @param bool $show_retry_button Show a retry button that redirects the customer back to the checkout page. + * + * @return string + */ + protected function showReturnPage($title, $body, $api_error = null, $show_retry_button = true) + { + $this->load->language("extension/payment/mollie"); + + $data['message_title'] = $title; + $data['message_text'] = $body; + + if ($api_error) { + $data['mollie_error'] = $api_error; + } + + if ($show_retry_button) { + $data['checkout_url'] = $this->url->link("checkout/checkout", "", "SSL"); + $data['button_retry'] = $this->language->get("button_retry"); + } + + $this->document->setTitle($this->language->get("ideal_title")); + + $this->setBreadcrumbs($data); + + return $this->renderTemplate("mollie_return", $data, array( + "column_left", + "column_right", + "content_top", + "content_bottom", + "footer", + "header", + )); + } + + /** + * We check for and remove the admin url in the webhook link. + * + * @return string|null + */ + public function getWebhookUrl() + { + $system_webhook_url = $this->url->link("extension/payment/mollie_" . static::MODULE_NAME . "/webhook", "", "SSL"); + + if (strpos($system_webhook_url, $this->getAdminDirectory()) !== false) { + return str_replace($this->getAdminDirectory(), "", $system_webhook_url); + } + + return $system_webhook_url ? $system_webhook_url : null; + } + + /** + * Retrieves the admin directoryname from the catalog and admin urls. + * + * @return string + */ + protected function getAdminDirectory() + { + // if no default admin URL defined in the config, use the default admin directory. + if (!defined('HTTP_ADMIN')) { + return "admin/"; + } + + return str_replace(HTTP_SERVER, "", HTTP_ADMIN); + } + + /** + * Map payment status history handling for different Opencart versions. + * + * @param array $order + * @param int|string $order_status_id + * @param string $comment + * @param bool $notify + */ + protected function addOrderHistory($order, $order_status_id, $comment = "", $notify = false) + { + if (MollieHelper::isOpenCart2x()) { + $this->model_checkout_order->addOrderHistory($order['order_id'], $order_status_id, $comment, $notify); + } else { + if (empty($order['order_status_id'])) { + $this->model_checkout_order->confirm($order['order_id'], $order_status_id, $comment, $notify); + } else { + $this->model_checkout_order->update($order['order_id'], $order_status_id, $comment, $notify); + } + } + } + + /** + * Map template handling for different Opencart versions. + * + * @param string $template + * @param array $data + * @param array $common_children + * @param bool $echo + * @return string + */ + protected function renderTemplate($template, $data, $common_children = array(), $echo = true) + { + if (!MollieHelper::isOpenCart3x()) { + $template .= '.tpl'; + } + + if (file_exists(DIR_TEMPLATE . $this->config->get('config_template') . '/template/payment/' . $template)) { + $template = $this->config->get('config_template') . '/template/payment/' . $template; + } else if (file_exists(DIR_TEMPLATE . 'default/template/payment/' . $template)) { + $template = 'default/template/payment/' . $template; + } else { + $template = 'extension/payment/' . $template; + } + + if (MollieHelper::isOpenCart2x()) { + foreach ($common_children as $child) { + $data[$child] = $this->load->controller("common/" . $child); + } + + $html = $this->load->view($template, $data); + } else { + $this->template = $template; + $this->children = array(); + + foreach ($data as $field => $value) { + $this->data[$field] = $value; + } + + foreach ($common_children as $child) { + if ($child === 'column_left') { + continue; + } + + $this->children[] = "common/" . $child; + } + + $html = $this->render(); + } + + if ($echo) { + return $this->response->setOutput($html); + } + + return $html; + } + + /** + * @param string $url + * @param int $status + */ + protected function redirect($url, $status = 302) + { + $this->response->redirect($url, $status); + } } diff --git a/catalog/controller/extension/payment/mollie/helper.php b/catalog/controller/extension/payment/mollie/helper.php index b2c46eac..581d4827 100644 --- a/catalog/controller/extension/payment/mollie/helper.php +++ b/catalog/controller/extension/payment/mollie/helper.php @@ -1,4 +1,6 @@ setApiKey($config->get(self::getModuleCode() . '_api_key')); @@ -81,13 +83,13 @@ public static function getAPIClient ($config) * * @param array $config * - * @return Mollie_API_Client + * @return MollieApiClient */ public static function getAPIClientAdmin ($config) { require_once(realpath(DIR_SYSTEM . "/..") . "/catalog/controller/extension/payment/mollie-api-client/src/Mollie/API/Autoloader.php"); - $mollie = new Mollie_API_Client; + $mollie = new MollieApiClient; $mollie->setApiKey(isset($config[self::getModuleCode() . '_api_key']) ? $config[self::getModuleCode() . '_api_key'] : null); @@ -101,7 +103,7 @@ public static function getAPIClientForKey($key = null) { require_once(realpath(DIR_SYSTEM . "/..") . "/catalog/controller/extension/payment/mollie-api-client/src/Mollie/API/Autoloader.php"); - $mollie = new Mollie_API_Client; + $mollie = new MollieApiClient; $mollie->setApiKey(!empty($key) ? $key : null); diff --git a/catalog/controller/extension/payment/mollie_bancontact.php b/catalog/controller/extension/payment/mollie_bancontact.php new file mode 100644 index 00000000..c37c6d61 --- /dev/null +++ b/catalog/controller/extension/payment/mollie_bancontact.php @@ -0,0 +1,7 @@ +log->write("Error retrieving payment method '" . static::MODULE_NAME . "' from Mollie: {$e->getMessage()}."); return NULL; diff --git a/catalog/model/extension/payment/mollie_bancontact.php b/catalog/model/extension/payment/mollie_bancontact.php new file mode 100644 index 00000000..ac70ab29 --- /dev/null +++ b/catalog/model/extension/payment/mollie_bancontact.php @@ -0,0 +1,7 @@ + Date: Tue, 17 Jul 2018 14:57:14 +0200 Subject: [PATCH 3/8] COMDEVNL-1156 -- Removed autoloader references --- catalog/controller/extension/payment/mollie/helper.php | 6 ------ 1 file changed, 6 deletions(-) diff --git a/catalog/controller/extension/payment/mollie/helper.php b/catalog/controller/extension/payment/mollie/helper.php index 581d4827..f4caf9a6 100644 --- a/catalog/controller/extension/payment/mollie/helper.php +++ b/catalog/controller/extension/payment/mollie/helper.php @@ -63,8 +63,6 @@ public static function getAPIClient ($config) { if (!self::$api_client && self::apiClientFound()) { - require_once(realpath(DIR_SYSTEM . "/..") . "/catalog/controller/extension/payment/mollie-api-client/src/Mollie/API/Autoloader.php"); - $mollie = new MollieApiClient; $mollie->setApiKey($config->get(self::getModuleCode() . '_api_key')); @@ -87,8 +85,6 @@ public static function getAPIClient ($config) */ public static function getAPIClientAdmin ($config) { - require_once(realpath(DIR_SYSTEM . "/..") . "/catalog/controller/extension/payment/mollie-api-client/src/Mollie/API/Autoloader.php"); - $mollie = new MollieApiClient; $mollie->setApiKey(isset($config[self::getModuleCode() . '_api_key']) ? $config[self::getModuleCode() . '_api_key'] : null); @@ -101,8 +97,6 @@ public static function getAPIClientAdmin ($config) public static function getAPIClientForKey($key = null) { - require_once(realpath(DIR_SYSTEM . "/..") . "/catalog/controller/extension/payment/mollie-api-client/src/Mollie/API/Autoloader.php"); - $mollie = new MollieApiClient; $mollie->setApiKey(!empty($key) ? $key : null); From a8f2894e8e82be8bb33bc0fff676cce8dacb50e3 Mon Sep 17 00:00:00 2001 From: Maxim Janssens Date: Tue, 17 Jul 2018 16:31:44 +0200 Subject: [PATCH 4/8] COMDEVNL-1156 -- Fix a bunch of errors. --- admin/controller/extension/payment/mollie/base.php | 4 +++- catalog/controller/extension/payment/mollie/helper.php | 3 +++ release.sh | 4 +++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/admin/controller/extension/payment/mollie/base.php b/admin/controller/extension/payment/mollie/base.php index cc913ca5..19c9f93f 100644 --- a/admin/controller/extension/payment/mollie/base.php +++ b/admin/controller/extension/payment/mollie/base.php @@ -106,7 +106,9 @@ public function cleanUp() $catalogThemeDir = DIR_CATALOG . 'view/theme/default/template/extension/payment/'; // Remove old template from previous version. - unlink($adminThemeDir . 'mollie_2.tpl'); + if (file_exists($adminThemeDir . 'mollie_2.tpl')) { + unlink($adminThemeDir . 'mollie_2.tpl'); + } if (MollieHelper::isOpenCart3x()) { unlink($adminThemeDir . 'mollie_1.tpl'); diff --git a/catalog/controller/extension/payment/mollie/helper.php b/catalog/controller/extension/payment/mollie/helper.php index f4caf9a6..f36b900b 100644 --- a/catalog/controller/extension/payment/mollie/helper.php +++ b/catalog/controller/extension/payment/mollie/helper.php @@ -63,6 +63,7 @@ public static function getAPIClient ($config) { if (!self::$api_client && self::apiClientFound()) { + require_once(realpath(DIR_SYSTEM . "/..") . "/catalog/controller/extension/payment/mollie-api-client/vendor/autoload.php"); $mollie = new MollieApiClient; $mollie->setApiKey($config->get(self::getModuleCode() . '_api_key')); @@ -85,6 +86,7 @@ public static function getAPIClient ($config) */ public static function getAPIClientAdmin ($config) { + require_once(realpath(DIR_SYSTEM . "/..") . "/catalog/controller/extension/payment/mollie-api-client/vendor/autoload.php"); $mollie = new MollieApiClient; $mollie->setApiKey(isset($config[self::getModuleCode() . '_api_key']) ? $config[self::getModuleCode() . '_api_key'] : null); @@ -97,6 +99,7 @@ public static function getAPIClientAdmin ($config) public static function getAPIClientForKey($key = null) { + require_once(realpath(DIR_SYSTEM . "/..") . "/catalog/controller/extension/payment/mollie-api-client/vendor/autoload.php"); $mollie = new MollieApiClient; $mollie->setApiKey(!empty($key) ? $key : null); diff --git a/release.sh b/release.sh index a4c54068..4a50959c 100755 --- a/release.sh +++ b/release.sh @@ -1,10 +1,12 @@ #!/bin/bash -echo "Retrieving Mollie API client v1..." +echo "Retrieving Mollie API client v2..." git submodule update --init rmdir ./catalog/controller/extension/payment/mollie-api-client/examples rmdir ./catalog/controller/extension/payment/mollie-api-client/tests +composer update + echo "1/8 Create temporary zip..." zip -9 -rq temp.zip admin catalog echo "2/8 Create temporary upload dir..." From 5dab9cae1024df89c98f4d54cf235d85d2d96348 Mon Sep 17 00:00:00 2001 From: Maxim Janssens Date: Tue, 17 Jul 2018 17:04:44 +0200 Subject: [PATCH 5/8] COMDEVNL-1156 -- Fix 2 more errors in checkout --- .../controller/extension/payment/mollie/base.php | 2 +- catalog/model/extension/payment/mollie/base.php | 14 +------------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/catalog/controller/extension/payment/mollie/base.php b/catalog/controller/extension/payment/mollie/base.php index 02c749fc..14e54f80 100644 --- a/catalog/controller/extension/payment/mollie/base.php +++ b/catalog/controller/extension/payment/mollie/base.php @@ -129,7 +129,7 @@ public function index() // Set template data. $data['action'] = $this->url->link("extension/payment/mollie_" . static::MODULE_NAME . "/payment", "", "SSL"); - $data['image'] = $payment_method->image->normal; + $data['image'] = $payment_method->image->size1x; $data['message'] = $this->language; $data['issuers'] = isset($payment_method->issuers) ? $payment_method->issuers : array(); $data['text_issuer'] = $this->language->get("text_issuer_" . static::MODULE_NAME); diff --git a/catalog/model/extension/payment/mollie/base.php b/catalog/model/extension/payment/mollie/base.php index 1b576075..4c5fe3dd 100644 --- a/catalog/model/extension/payment/mollie/base.php +++ b/catalog/model/extension/payment/mollie/base.php @@ -69,19 +69,7 @@ public function getMethod($address, $total) try { $payment_method = $this->getAPIClient()->methods->get(static::MODULE_NAME); - // Quick checkout provides an array wile the default checkout provides only the total. - $amount = is_array($total) ? $total[0]['value'] : round($total, 2); - - $minimum = $payment_method->getMinimumAmount(); - $maximum = $payment_method->getMaximumAmount(); - - if ($minimum && $minimum > $amount) { - return NULL; - } - - if ($maximum && $maximum < $amount) { - return NULL; - } + // TODO: Add fields in admin for minimum and maximum amount for each payment method to be set in the module. } catch (Mollie\Api\Exceptions\ApiException $e) { $this->log->write("Error retrieving payment method '" . static::MODULE_NAME . "' from Mollie: {$e->getMessage()}."); From 72eea6b1a0608a4cd26ed7e511e2b68efd623946 Mon Sep 17 00:00:00 2001 From: Maxim Janssens Date: Tue, 17 Jul 2018 19:23:39 +0200 Subject: [PATCH 6/8] COMDEVNL-1156 -- Fix a bunch more errors. --- .../extension/payment/mollie/base.php | 32 ++++++---- .../ModelExtensionPaymentMollieIdealTest.php | 63 ------------------- 2 files changed, 19 insertions(+), 76 deletions(-) diff --git a/catalog/controller/extension/payment/mollie/base.php b/catalog/controller/extension/payment/mollie/base.php index 14e54f80..cc270158 100644 --- a/catalog/controller/extension/payment/mollie/base.php +++ b/catalog/controller/extension/payment/mollie/base.php @@ -172,33 +172,39 @@ public function payment() try { $data = array( - "amount" => ["currency" => "EUR", "value" => $amount], + "amount" => ["currency" => "EUR", "value" => (string)$amount], "description" => $description, "redirectUrl" => $return_url, "webhookUrl" => $this->getWebhookUrl(), "metadata" => array("order_id" => $order['order_id']), "method" => static::MODULE_NAME, "issuer" => $issuer, - + ); /* * This data is sent along for credit card payments / fraud checks. You can remove this but you will * have a higher conversion if you leave it here. */ - "billingAddress" => [ + $data["billingAddress"] = [ + "streetAndNumber" => $order['payment_address_1'] . ' ' . $order['payment_address_2'], "city" => $order['payment_city'], "region" => $order['payment_zone'], "postalCode" => $order['payment_postcode'], "country" => $order['payment_iso_code_2'] - ], - - "shippingAddress" => [ - "city" => $order['shipping_city'], - "region" => $order['shipping_zone'], - "postalCode" => $order['shipping_postcode'], - "country" => $order['shipping_iso_code_2'] - ], - ); + ]; + + if (!empty($order['shipping_firstname']) || !empty($order['shipping_lastname'])) { + $data["shippingAddress"] = [ + "streetAndNumber" => $order['shipping_address_1'] . ' ' . $order['shipping_address_2'], + "city" => $order['shipping_city'], + "region" => $order['shipping_zone'], + "postalCode" => $order['shipping_postcode'], + "country" => $order['shipping_iso_code_2'] + ]; + } else { + $data["shippingAddress"] = $data["billingAddress"]; + } + $locales = array( 'en_US', @@ -238,7 +244,7 @@ public function payment() $model->setPayment($order['order_id'], $payment->id); // Redirect to payment gateway. - $this->redirect($payment->_links->checkout); + $this->redirect($payment->_links->checkout['href']); } /** diff --git a/tests/unittests/catalog/model/ModelExtensionPaymentMollieIdealTest.php b/tests/unittests/catalog/model/ModelExtensionPaymentMollieIdealTest.php index 40ce2c1d..50823096 100755 --- a/tests/unittests/catalog/model/ModelExtensionPaymentMollieIdealTest.php +++ b/tests/unittests/catalog/model/ModelExtensionPaymentMollieIdealTest.php @@ -54,69 +54,6 @@ public function setUp() $this->model->url = $this->getMock("stub", array("link")); } - /** - * Add mock payment methods to the list of payment methods the API client will return. - * - * @param string $id - * @param int $min_amount - * @param int $max_amount - * - * @return mixed - */ - protected function addPaymentMethod ($id = "method", $min_amount = 0, $max_amount = 999) - { - $method = $this->getMock("stub", array("getMinimumAmount", "getMaximumAmount")); - - $method->id = $id; - $method->description = $id; - - $method->image = new StdClass; - $method->image->normal = NULL; - $method->image->bigger = NULL; - - $method->method("getMinimumAmount")->willReturn($min_amount); - $method->method("getMaximumAmount")->willReturn($max_amount); - - $this->client_methods[] = $method; - - return $method; - } - - /** - * Retrieve the correct payment methods for a specified amount. - */ - public function testGetMethodCanReturnNULL () - { - $method = $this->getMock("stub", array("getMinimumAmount", "getMaximumAmount")); - - $method->id = NULL; - $method->description = NULL; - - $this->client_methods - ->expects($this->exactly(3)) - ->method("get") - ->with(NULL) - ->willReturn($method); - - $method - ->expects($this->exactly(3)) - ->method("getMinimumAmount") - ->willReturn(100); - - $method - ->expects($this->exactly(3)) - ->method("getMaximumAmount") - ->willReturn(200); - - $return_50 = $this->model->getMethod(array('zone_id'=>1, 'country_id'=>1), 50); - $return_150 = $this->model->getMethod(array('zone_id'=>1, 'country_id'=>1), 150); - $return_250 = $this->model->getMethod(array('zone_id'=>1, 'country_id'=>1), 250); - - $this->assertNull($return_50); - $this->assertNotNull($return_150); - $this->assertNull($return_250); - } - public function testSetPaymentReturnsFalseIfArgumentsOmitted() { $this->assertFalse($this->model->setPayment(NULL, NULL)); From de17fab8f98d822cc5dc91285f4fd4a9281c032d Mon Sep 17 00:00:00 2001 From: Maxim Janssens Date: Tue, 17 Jul 2018 19:28:03 +0200 Subject: [PATCH 7/8] COMDEVNL-1156 -- Typo. --- catalog/controller/extension/payment/mollie/base.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/catalog/controller/extension/payment/mollie/base.php b/catalog/controller/extension/payment/mollie/base.php index cc270158..800a6597 100644 --- a/catalog/controller/extension/payment/mollie/base.php +++ b/catalog/controller/extension/payment/mollie/base.php @@ -244,7 +244,7 @@ public function payment() $model->setPayment($order['order_id'], $payment->id); // Redirect to payment gateway. - $this->redirect($payment->_links->checkout['href']); + $this->redirect($payment->_links->checkout->href); } /** From 4bf4a60218af2924775e87e7cc7e084a4b2fa36f Mon Sep 17 00:00:00 2001 From: Maxim Janssens Date: Wed, 18 Jul 2018 17:31:46 +0200 Subject: [PATCH 8/8] COMDEVNL-1156 -- Some more bugfixes that popped up while working on 2.2 and lower. --- catalog/controller/extension/payment/mollie/base.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/catalog/controller/extension/payment/mollie/base.php b/catalog/controller/extension/payment/mollie/base.php index 800a6597..148d3b06 100644 --- a/catalog/controller/extension/payment/mollie/base.php +++ b/catalog/controller/extension/payment/mollie/base.php @@ -172,7 +172,7 @@ public function payment() try { $data = array( - "amount" => ["currency" => "EUR", "value" => (string)$amount], + "amount" => ["currency" => "EUR", "value" => (string)number_format($amount,2)], "description" => $description, "redirectUrl" => $return_url, "webhookUrl" => $this->getWebhookUrl(), @@ -221,6 +221,8 @@ public function payment() if (strstr($this->session->data['language'], '-')) { list ($language, $country) = explode('-', $this->session->data['language']); $locale = strtolower($language) . '_' . strtoupper($country); + } else { + $locale = strtolower($this->session->data['language']) . '_' . strtoupper($this->session->data['language']); } if (!in_array($locale, $locales)) {